2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = (
9 Roo.each(document.styleSheets, function(s) {
10 if ( s.href && 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','d-none']);
418 this.fireEvent('show', this);
423 * Hide a component - adds 'hidden' class
427 if(!this.getVisibilityEl()){
431 this.getVisibilityEl().addClass(['hidden','d-none']);
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 + ' hidden' + size + '-down';;
1078 cfg.cls += ' col-' + size + '-' + settings[size] + (
1079 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1085 cfg.cls += ' hidden';
1088 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1089 cfg.cls +=' alert alert-' + this.alert;
1093 if (this.html.length) {
1094 cfg.html = this.html;
1098 if (this.fasize > 1) {
1099 fasize = ' fa-' + this.fasize + 'x';
1101 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1106 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1125 * @class Roo.bootstrap.Container
1126 * @extends Roo.bootstrap.Component
1127 * Bootstrap Container class
1128 * @cfg {Boolean} jumbotron is it a jumbotron element
1129 * @cfg {String} html content of element
1130 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1131 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1132 * @cfg {String} header content of header (for panel)
1133 * @cfg {String} footer content of footer (for panel)
1134 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1135 * @cfg {String} tag (header|aside|section) type of HTML tag.
1136 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1137 * @cfg {String} fa font awesome icon
1138 * @cfg {String} icon (info-sign|check|...) glyphicon name
1139 * @cfg {Boolean} hidden (true|false) hide the element
1140 * @cfg {Boolean} expandable (true|false) default false
1141 * @cfg {Boolean} expanded (true|false) default true
1142 * @cfg {String} rheader contet on the right of header
1143 * @cfg {Boolean} clickable (true|false) default false
1147 * Create a new Container
1148 * @param {Object} config The config object
1151 Roo.bootstrap.Container = function(config){
1152 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1158 * After the panel has been expand
1160 * @param {Roo.bootstrap.Container} this
1165 * After the panel has been collapsed
1167 * @param {Roo.bootstrap.Container} this
1172 * When a element is chick
1173 * @param {Roo.bootstrap.Container} this
1174 * @param {Roo.EventObject} e
1180 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1198 getChildContainer : function() {
1204 if (this.panel.length) {
1205 return this.el.select('.panel-body',true).first();
1212 getAutoCreate : function(){
1215 tag : this.tag || 'div',
1219 if (this.jumbotron) {
1220 cfg.cls = 'jumbotron';
1225 // - this is applied by the parent..
1227 // cfg.cls = this.cls + '';
1230 if (this.sticky.length) {
1232 var bd = Roo.get(document.body);
1233 if (!bd.hasClass('bootstrap-sticky')) {
1234 bd.addClass('bootstrap-sticky');
1235 Roo.select('html',true).setStyle('height', '100%');
1238 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1242 if (this.well.length) {
1243 switch (this.well) {
1246 cfg.cls +=' well well-' +this.well;
1255 cfg.cls += ' hidden';
1259 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1260 cfg.cls +=' alert alert-' + this.alert;
1265 if (this.panel.length) {
1266 cfg.cls += ' panel panel-' + this.panel;
1268 if (this.header.length) {
1272 if(this.expandable){
1274 cfg.cls = cfg.cls + ' expandable';
1278 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1286 cls : 'panel-title',
1287 html : (this.expandable ? ' ' : '') + this.header
1291 cls: 'panel-header-right',
1297 cls : 'panel-heading',
1298 style : this.expandable ? 'cursor: pointer' : '',
1306 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1311 if (this.footer.length) {
1313 cls : 'panel-footer',
1322 body.html = this.html || cfg.html;
1323 // prefix with the icons..
1325 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1328 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1333 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1334 cfg.cls = 'container';
1340 initEvents: function()
1342 if(this.expandable){
1343 var headerEl = this.headerEl();
1346 headerEl.on('click', this.onToggleClick, this);
1351 this.el.on('click', this.onClick, this);
1356 onToggleClick : function()
1358 var headerEl = this.headerEl();
1374 if(this.fireEvent('expand', this)) {
1376 this.expanded = true;
1378 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1380 this.el.select('.panel-body',true).first().removeClass('hide');
1382 var toggleEl = this.toggleEl();
1388 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1393 collapse : function()
1395 if(this.fireEvent('collapse', this)) {
1397 this.expanded = false;
1399 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1400 this.el.select('.panel-body',true).first().addClass('hide');
1402 var toggleEl = this.toggleEl();
1408 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1412 toggleEl : function()
1414 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1418 return this.el.select('.panel-heading .fa',true).first();
1421 headerEl : function()
1423 if(!this.el || !this.panel.length || !this.header.length){
1427 return this.el.select('.panel-heading',true).first()
1432 if(!this.el || !this.panel.length){
1436 return this.el.select('.panel-body',true).first()
1439 titleEl : function()
1441 if(!this.el || !this.panel.length || !this.header.length){
1445 return this.el.select('.panel-title',true).first();
1448 setTitle : function(v)
1450 var titleEl = this.titleEl();
1456 titleEl.dom.innerHTML = v;
1459 getTitle : function()
1462 var titleEl = this.titleEl();
1468 return titleEl.dom.innerHTML;
1471 setRightTitle : function(v)
1473 var t = this.el.select('.panel-header-right',true).first();
1479 t.dom.innerHTML = v;
1482 onClick : function(e)
1486 this.fireEvent('click', this, e);
1493 * This is BS4's Card element.. - similar to our containers probably..
1497 * @class Roo.bootstrap.Card
1498 * @extends Roo.bootstrap.Component
1499 * Bootstrap Card class
1502 * possible... may not be implemented..
1503 * @cfg {String} header_image src url of image.
1504 * @cfg {String} header
1505 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1507 * @cfg {String} title
1508 * @cfg {String} subtitle
1509 * @cfg {String} html -- html contents - or just use children..
1510 * @cfg {String} footer
1512 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1514 * @cfg {String} margin (0|1|2|3|4|5|auto)
1515 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1516 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1517 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1518 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1519 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1520 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1522 * @cfg {String} padding (0|1|2|3|4|5)
1523 * @cfg {String} padding_top (0|1|2|3|4|5)
1524 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1525 * @cfg {String} padding_left (0|1|2|3|4|5)
1526 * @cfg {String} padding_right (0|1|2|3|4|5)
1527 * @cfg {String} padding_x (0|1|2|3|4|5)
1528 * @cfg {String} padding_y (0|1|2|3|4|5)
1530 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1531 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1532 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1533 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1534 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1537 * Create a new Container
1538 * @param {Object} config The config object
1541 Roo.bootstrap.Card = function(config){
1542 Roo.bootstrap.Card.superclass.constructor.call(this, config);
1550 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
1555 margin: '', /// may be better in component?
1585 childContainer : false,
1587 layoutCls : function()
1592 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
1593 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
1595 if (t['margin' + (v.length ? '_' : '') + v].length) {
1596 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
1598 if (t['padding' + (v.length ? '_' : '') + v].length) {
1599 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
1603 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
1604 if (t['display' + (v.length ? '_' : '') + v].length) {
1605 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v].length
1609 // more generic support?
1617 // Roo.log("Call onRender: " + this.xtype);
1618 /* We are looking at something like this.
1620 <img src="..." class="card-img-top" alt="...">
1621 <div class="card-body">
1622 <h5 class="card-title">Card title</h5>
1623 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
1625 >> this bit is really the body...
1626 <div> << we will ad dthis in hopefully it will not break shit.
1628 ** card text does not actually have any styling...
1630 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
1633 <a href="#" class="card-link">Card link</a>
1636 <div class="card-footer">
1637 <small class="text-muted">Last updated 3 mins ago</small>
1641 getAutoCreate : function(){
1649 if (this.weight.length && this.weight != 'light') {
1650 cfg.cls += ' text-white'
1652 if (this.weight.length) {
1653 cfg.cls += ' bg-' + this.weight;
1656 cfg.cls += this.layoutCls();
1658 if (this.header.length) {
1660 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
1661 cls : 'card-header',
1662 html : this.header // escape?
1665 if (this.header_image.length) {
1668 cls : 'card-img-top',
1669 src: this.header_image // escape?
1680 if (this.title.length) {
1684 src: this.title // escape?
1688 if (this.subtitle.length) {
1692 src: this.subtitle // escape?
1698 cls : 'roo-card-body-ctr'
1701 // fixme ? handle objects?
1702 if (this.footer.length) {
1705 cls : 'card-footer',
1706 html: this.footer // escape?
1715 getChildContainer : function()
1721 return this.el.select('.roo-card-body-ctr',true).first();
1733 * @class Roo.bootstrap.Img
1734 * @extends Roo.bootstrap.Component
1735 * Bootstrap Img class
1736 * @cfg {Boolean} imgResponsive false | true
1737 * @cfg {String} border rounded | circle | thumbnail
1738 * @cfg {String} src image source
1739 * @cfg {String} alt image alternative text
1740 * @cfg {String} href a tag href
1741 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1742 * @cfg {String} xsUrl xs image source
1743 * @cfg {String} smUrl sm image source
1744 * @cfg {String} mdUrl md image source
1745 * @cfg {String} lgUrl lg image source
1748 * Create a new Input
1749 * @param {Object} config The config object
1752 Roo.bootstrap.Img = function(config){
1753 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1759 * The img click event for the img.
1760 * @param {Roo.EventObject} e
1766 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1768 imgResponsive: true,
1778 getAutoCreate : function()
1780 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1781 return this.createSingleImg();
1786 cls: 'roo-image-responsive-group',
1791 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1793 if(!_this[size + 'Url']){
1799 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1800 html: _this.html || cfg.html,
1801 src: _this[size + 'Url']
1804 img.cls += ' roo-image-responsive-' + size;
1806 var s = ['xs', 'sm', 'md', 'lg'];
1808 s.splice(s.indexOf(size), 1);
1810 Roo.each(s, function(ss){
1811 img.cls += ' hidden-' + ss;
1814 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1815 cfg.cls += ' img-' + _this.border;
1819 cfg.alt = _this.alt;
1832 a.target = _this.target;
1836 cfg.cn.push((_this.href) ? a : img);
1843 createSingleImg : function()
1847 cls: (this.imgResponsive) ? 'img-responsive' : '',
1849 src : 'about:blank' // just incase src get's set to undefined?!?
1852 cfg.html = this.html || cfg.html;
1854 cfg.src = this.src || cfg.src;
1856 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1857 cfg.cls += ' img-' + this.border;
1874 a.target = this.target;
1879 return (this.href) ? a : cfg;
1882 initEvents: function()
1885 this.el.on('click', this.onClick, this);
1890 onClick : function(e)
1892 Roo.log('img onclick');
1893 this.fireEvent('click', this, e);
1896 * Sets the url of the image - used to update it
1897 * @param {String} url the url of the image
1900 setSrc : function(url)
1904 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1905 this.el.dom.src = url;
1909 this.el.select('img', true).first().dom.src = url;
1925 * @class Roo.bootstrap.Link
1926 * @extends Roo.bootstrap.Component
1927 * Bootstrap Link Class
1928 * @cfg {String} alt image alternative text
1929 * @cfg {String} href a tag href
1930 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1931 * @cfg {String} html the content of the link.
1932 * @cfg {String} anchor name for the anchor link
1933 * @cfg {String} fa - favicon
1935 * @cfg {Boolean} preventDefault (true | false) default false
1939 * Create a new Input
1940 * @param {Object} config The config object
1943 Roo.bootstrap.Link = function(config){
1944 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1950 * The img click event for the img.
1951 * @param {Roo.EventObject} e
1957 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1961 preventDefault: false,
1967 getAutoCreate : function()
1969 var html = this.html || '';
1971 if (this.fa !== false) {
1972 html = '<i class="fa fa-' + this.fa + '"></i>';
1977 // anchor's do not require html/href...
1978 if (this.anchor === false) {
1980 cfg.href = this.href || '#';
1982 cfg.name = this.anchor;
1983 if (this.html !== false || this.fa !== false) {
1986 if (this.href !== false) {
1987 cfg.href = this.href;
1991 if(this.alt !== false){
1996 if(this.target !== false) {
1997 cfg.target = this.target;
2003 initEvents: function() {
2005 if(!this.href || this.preventDefault){
2006 this.el.on('click', this.onClick, this);
2010 onClick : function(e)
2012 if(this.preventDefault){
2015 //Roo.log('img onclick');
2016 this.fireEvent('click', this, e);
2029 * @class Roo.bootstrap.Header
2030 * @extends Roo.bootstrap.Component
2031 * Bootstrap Header class
2032 * @cfg {String} html content of header
2033 * @cfg {Number} level (1|2|3|4|5|6) default 1
2036 * Create a new Header
2037 * @param {Object} config The config object
2041 Roo.bootstrap.Header = function(config){
2042 Roo.bootstrap.Header.superclass.constructor.call(this, config);
2045 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
2053 getAutoCreate : function(){
2058 tag: 'h' + (1 *this.level),
2059 html: this.html || ''
2071 * Ext JS Library 1.1.1
2072 * Copyright(c) 2006-2007, Ext JS, LLC.
2074 * Originally Released Under LGPL - original licence link has changed is not relivant.
2077 * <script type="text/javascript">
2081 * @class Roo.bootstrap.MenuMgr
2082 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
2085 Roo.bootstrap.MenuMgr = function(){
2086 var menus, active, groups = {}, attached = false, lastShow = new Date();
2088 // private - called when first menu is created
2091 active = new Roo.util.MixedCollection();
2092 Roo.get(document).addKeyListener(27, function(){
2093 if(active.length > 0){
2101 if(active && active.length > 0){
2102 var c = active.clone();
2112 if(active.length < 1){
2113 Roo.get(document).un("mouseup", onMouseDown);
2121 var last = active.last();
2122 lastShow = new Date();
2125 Roo.get(document).on("mouseup", onMouseDown);
2130 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
2131 m.parentMenu.activeChild = m;
2132 }else if(last && last.isVisible()){
2133 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
2138 function onBeforeHide(m){
2140 m.activeChild.hide();
2142 if(m.autoHideTimer){
2143 clearTimeout(m.autoHideTimer);
2144 delete m.autoHideTimer;
2149 function onBeforeShow(m){
2150 var pm = m.parentMenu;
2151 if(!pm && !m.allowOtherMenus){
2153 }else if(pm && pm.activeChild && active != m){
2154 pm.activeChild.hide();
2158 // private this should really trigger on mouseup..
2159 function onMouseDown(e){
2160 Roo.log("on Mouse Up");
2162 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
2163 Roo.log("MenuManager hideAll");
2172 function onBeforeCheck(mi, state){
2174 var g = groups[mi.group];
2175 for(var i = 0, l = g.length; i < l; i++){
2177 g[i].setChecked(false);
2186 * Hides all menus that are currently visible
2188 hideAll : function(){
2193 register : function(menu){
2197 menus[menu.id] = menu;
2198 menu.on("beforehide", onBeforeHide);
2199 menu.on("hide", onHide);
2200 menu.on("beforeshow", onBeforeShow);
2201 menu.on("show", onShow);
2203 if(g && menu.events["checkchange"]){
2207 groups[g].push(menu);
2208 menu.on("checkchange", onCheck);
2213 * Returns a {@link Roo.menu.Menu} object
2214 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
2215 * be used to generate and return a new Menu instance.
2217 get : function(menu){
2218 if(typeof menu == "string"){ // menu id
2220 }else if(menu.events){ // menu instance
2223 /*else if(typeof menu.length == 'number'){ // array of menu items?
2224 return new Roo.bootstrap.Menu({items:menu});
2225 }else{ // otherwise, must be a config
2226 return new Roo.bootstrap.Menu(menu);
2233 unregister : function(menu){
2234 delete menus[menu.id];
2235 menu.un("beforehide", onBeforeHide);
2236 menu.un("hide", onHide);
2237 menu.un("beforeshow", onBeforeShow);
2238 menu.un("show", onShow);
2240 if(g && menu.events["checkchange"]){
2241 groups[g].remove(menu);
2242 menu.un("checkchange", onCheck);
2247 registerCheckable : function(menuItem){
2248 var g = menuItem.group;
2253 groups[g].push(menuItem);
2254 menuItem.on("beforecheckchange", onBeforeCheck);
2259 unregisterCheckable : function(menuItem){
2260 var g = menuItem.group;
2262 groups[g].remove(menuItem);
2263 menuItem.un("beforecheckchange", onBeforeCheck);
2275 * @class Roo.bootstrap.Menu
2276 * @extends Roo.bootstrap.Component
2277 * Bootstrap Menu class - container for MenuItems
2278 * @cfg {String} type (dropdown|treeview|submenu) type of menu
2279 * @cfg {bool} hidden if the menu should be hidden when rendered.
2280 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
2281 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
2285 * @param {Object} config The config object
2289 Roo.bootstrap.Menu = function(config){
2290 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2291 if (this.registerMenu && this.type != 'treeview') {
2292 Roo.bootstrap.MenuMgr.register(this);
2299 * Fires before this menu is displayed (return false to block)
2300 * @param {Roo.menu.Menu} this
2305 * Fires before this menu is hidden (return false to block)
2306 * @param {Roo.menu.Menu} this
2311 * Fires after this menu is displayed
2312 * @param {Roo.menu.Menu} this
2317 * Fires after this menu is hidden
2318 * @param {Roo.menu.Menu} this
2323 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2324 * @param {Roo.menu.Menu} this
2325 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2326 * @param {Roo.EventObject} e
2331 * Fires when the mouse is hovering over this menu
2332 * @param {Roo.menu.Menu} this
2333 * @param {Roo.EventObject} e
2334 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2339 * Fires when the mouse exits this menu
2340 * @param {Roo.menu.Menu} this
2341 * @param {Roo.EventObject} e
2342 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2347 * Fires when a menu item contained in this menu is clicked
2348 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2349 * @param {Roo.EventObject} e
2353 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2356 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2360 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2363 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2365 registerMenu : true,
2367 menuItems :false, // stores the menu items..
2377 getChildContainer : function() {
2381 getAutoCreate : function(){
2383 //if (['right'].indexOf(this.align)!==-1) {
2384 // cfg.cn[1].cls += ' pull-right'
2390 cls : 'dropdown-menu' ,
2391 style : 'z-index:1000'
2395 if (this.type === 'submenu') {
2396 cfg.cls = 'submenu active';
2398 if (this.type === 'treeview') {
2399 cfg.cls = 'treeview-menu';
2404 initEvents : function() {
2406 // Roo.log("ADD event");
2407 // Roo.log(this.triggerEl.dom);
2409 this.triggerEl.on('click', this.onTriggerClick, this);
2411 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2414 if (this.triggerEl.hasClass('nav-item')) {
2415 // dropdown toggle on the 'a' in BS4?
2416 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2418 this.triggerEl.addClass('dropdown-toggle');
2421 this.el.on('touchstart' , this.onTouch, this);
2423 this.el.on('click' , this.onClick, this);
2425 this.el.on("mouseover", this.onMouseOver, this);
2426 this.el.on("mouseout", this.onMouseOut, this);
2430 findTargetItem : function(e)
2432 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2436 //Roo.log(t); Roo.log(t.id);
2438 //Roo.log(this.menuitems);
2439 return this.menuitems.get(t.id);
2441 //return this.items.get(t.menuItemId);
2447 onTouch : function(e)
2449 Roo.log("menu.onTouch");
2450 //e.stopEvent(); this make the user popdown broken
2454 onClick : function(e)
2456 Roo.log("menu.onClick");
2458 var t = this.findTargetItem(e);
2459 if(!t || t.isContainer){
2464 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2465 if(t == this.activeItem && t.shouldDeactivate(e)){
2466 this.activeItem.deactivate();
2467 delete this.activeItem;
2471 this.setActiveItem(t, true);
2479 Roo.log('pass click event');
2483 this.fireEvent("click", this, t, e);
2487 if(!t.href.length || t.href == '#'){
2488 (function() { _this.hide(); }).defer(100);
2493 onMouseOver : function(e){
2494 var t = this.findTargetItem(e);
2497 // if(t.canActivate && !t.disabled){
2498 // this.setActiveItem(t, true);
2502 this.fireEvent("mouseover", this, e, t);
2504 isVisible : function(){
2505 return !this.hidden;
2507 onMouseOut : function(e){
2508 var t = this.findTargetItem(e);
2511 // if(t == this.activeItem && t.shouldDeactivate(e)){
2512 // this.activeItem.deactivate();
2513 // delete this.activeItem;
2516 this.fireEvent("mouseout", this, e, t);
2521 * Displays this menu relative to another element
2522 * @param {String/HTMLElement/Roo.Element} element The element to align to
2523 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2524 * the element (defaults to this.defaultAlign)
2525 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2527 show : function(el, pos, parentMenu)
2529 if (false === this.fireEvent("beforeshow", this)) {
2530 Roo.log("show canceled");
2533 this.parentMenu = parentMenu;
2538 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2541 * Displays this menu at a specific xy position
2542 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2543 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2545 showAt : function(xy, parentMenu, /* private: */_e){
2546 this.parentMenu = parentMenu;
2551 this.fireEvent("beforeshow", this);
2552 //xy = this.el.adjustForConstraints(xy);
2556 this.hideMenuItems();
2557 this.hidden = false;
2558 this.triggerEl.addClass('open');
2559 this.el.addClass('show');
2561 // reassign x when hitting right
2562 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2563 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2566 // reassign y when hitting bottom
2567 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2568 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2571 // but the list may align on trigger left or trigger top... should it be a properity?
2573 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2578 this.fireEvent("show", this);
2584 this.doFocus.defer(50, this);
2588 doFocus : function(){
2590 this.focusEl.focus();
2595 * Hides this menu and optionally all parent menus
2596 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2598 hide : function(deep)
2600 if (false === this.fireEvent("beforehide", this)) {
2601 Roo.log("hide canceled");
2604 this.hideMenuItems();
2605 if(this.el && this.isVisible()){
2607 if(this.activeItem){
2608 this.activeItem.deactivate();
2609 this.activeItem = null;
2611 this.triggerEl.removeClass('open');;
2612 this.el.removeClass('show');
2614 this.fireEvent("hide", this);
2616 if(deep === true && this.parentMenu){
2617 this.parentMenu.hide(true);
2621 onTriggerClick : function(e)
2623 Roo.log('trigger click');
2625 var target = e.getTarget();
2627 Roo.log(target.nodeName.toLowerCase());
2629 if(target.nodeName.toLowerCase() === 'i'){
2635 onTriggerPress : function(e)
2637 Roo.log('trigger press');
2638 //Roo.log(e.getTarget());
2639 // Roo.log(this.triggerEl.dom);
2641 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2642 var pel = Roo.get(e.getTarget());
2643 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2644 Roo.log('is treeview or dropdown?');
2648 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2652 if (this.isVisible()) {
2657 this.show(this.triggerEl, '?', false);
2660 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2667 hideMenuItems : function()
2669 Roo.log("hide Menu Items");
2674 this.el.select('.open',true).each(function(aa) {
2676 aa.removeClass('open');
2680 addxtypeChild : function (tree, cntr) {
2681 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2683 this.menuitems.add(comp);
2695 this.getEl().dom.innerHTML = '';
2696 this.menuitems.clear();
2710 * @class Roo.bootstrap.MenuItem
2711 * @extends Roo.bootstrap.Component
2712 * Bootstrap MenuItem class
2713 * @cfg {String} html the menu label
2714 * @cfg {String} href the link
2715 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2716 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2717 * @cfg {Boolean} active used on sidebars to highlight active itesm
2718 * @cfg {String} fa favicon to show on left of menu item.
2719 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2723 * Create a new MenuItem
2724 * @param {Object} config The config object
2728 Roo.bootstrap.MenuItem = function(config){
2729 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2734 * The raw click event for the entire grid.
2735 * @param {Roo.bootstrap.MenuItem} this
2736 * @param {Roo.EventObject} e
2742 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2746 preventDefault: false,
2747 isContainer : false,
2751 getAutoCreate : function(){
2753 if(this.isContainer){
2756 cls: 'dropdown-menu-item '
2766 cls : 'dropdown-item',
2771 if (this.fa !== false) {
2774 cls : 'fa fa-' + this.fa
2783 cls: 'dropdown-menu-item',
2786 if (this.parent().type == 'treeview') {
2787 cfg.cls = 'treeview-menu';
2790 cfg.cls += ' active';
2795 anc.href = this.href || cfg.cn[0].href ;
2796 ctag.html = this.html || cfg.cn[0].html ;
2800 initEvents: function()
2802 if (this.parent().type == 'treeview') {
2803 this.el.select('a').on('click', this.onClick, this);
2807 this.menu.parentType = this.xtype;
2808 this.menu.triggerEl = this.el;
2809 this.menu = this.addxtype(Roo.apply({}, this.menu));
2813 onClick : function(e)
2815 Roo.log('item on click ');
2817 if(this.preventDefault){
2820 //this.parent().hideMenuItems();
2822 this.fireEvent('click', this, e);
2841 * @class Roo.bootstrap.MenuSeparator
2842 * @extends Roo.bootstrap.Component
2843 * Bootstrap MenuSeparator class
2846 * Create a new MenuItem
2847 * @param {Object} config The config object
2851 Roo.bootstrap.MenuSeparator = function(config){
2852 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2855 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2857 getAutoCreate : function(){
2876 * @class Roo.bootstrap.Modal
2877 * @extends Roo.bootstrap.Component
2878 * Bootstrap Modal class
2879 * @cfg {String} title Title of dialog
2880 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2881 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2882 * @cfg {Boolean} specificTitle default false
2883 * @cfg {Array} buttons Array of buttons or standard button set..
2884 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2885 * @cfg {Boolean} animate default true
2886 * @cfg {Boolean} allow_close default true
2887 * @cfg {Boolean} fitwindow default false
2888 * @cfg {Number} width fixed width - usefull for chrome extension only really.
2889 * @cfg {Number} height fixed height - usefull for chrome extension only really.
2890 * @cfg {String} size (sm|lg) default empty
2891 * @cfg {Number} max_width set the max width of modal
2895 * Create a new Modal Dialog
2896 * @param {Object} config The config object
2899 Roo.bootstrap.Modal = function(config){
2900 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2905 * The raw btnclick event for the button
2906 * @param {Roo.EventObject} e
2911 * Fire when dialog resize
2912 * @param {Roo.bootstrap.Modal} this
2913 * @param {Roo.EventObject} e
2917 this.buttons = this.buttons || [];
2920 this.tmpl = Roo.factory(this.tmpl);
2925 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2927 title : 'test dialog',
2937 specificTitle: false,
2939 buttonPosition: 'right',
2962 onRender : function(ct, position)
2964 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2967 var cfg = Roo.apply({}, this.getAutoCreate());
2970 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2972 //if (!cfg.name.length) {
2976 cfg.cls += ' ' + this.cls;
2979 cfg.style = this.style;
2981 this.el = Roo.get(document.body).createChild(cfg, position);
2983 //var type = this.el.dom.type;
2986 if(this.tabIndex !== undefined){
2987 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2990 this.dialogEl = this.el.select('.modal-dialog',true).first();
2991 this.bodyEl = this.el.select('.modal-body',true).first();
2992 this.closeEl = this.el.select('.modal-header .close', true).first();
2993 this.headerEl = this.el.select('.modal-header',true).first();
2994 this.titleEl = this.el.select('.modal-title',true).first();
2995 this.footerEl = this.el.select('.modal-footer',true).first();
2997 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2999 //this.el.addClass("x-dlg-modal");
3001 if (this.buttons.length) {
3002 Roo.each(this.buttons, function(bb) {
3003 var b = Roo.apply({}, bb);
3004 b.xns = b.xns || Roo.bootstrap;
3005 b.xtype = b.xtype || 'Button';
3006 if (typeof(b.listeners) == 'undefined') {
3007 b.listeners = { click : this.onButtonClick.createDelegate(this) };
3010 var btn = Roo.factory(b);
3012 btn.render(this.getButtonContainer());
3016 // render the children.
3019 if(typeof(this.items) != 'undefined'){
3020 var items = this.items;
3023 for(var i =0;i < items.length;i++) {
3024 nitems.push(this.addxtype(Roo.apply({}, items[i])));
3028 this.items = nitems;
3030 // where are these used - they used to be body/close/footer
3034 //this.el.addClass([this.fieldClass, this.cls]);
3038 getAutoCreate : function()
3040 // we will default to modal-body-overflow - might need to remove or make optional later.
3042 cls : 'modal-body enable-modal-body-overflow ',
3043 html : this.html || ''
3048 cls : 'modal-title',
3052 if(this.specificTitle){
3058 if (this.allow_close && Roo.bootstrap.version == 3) {
3068 if (this.allow_close && Roo.bootstrap.version == 4) {
3078 if(this.size.length){
3079 size = 'modal-' + this.size;
3082 var footer = Roo.bootstrap.version == 3 ?
3084 cls : 'modal-footer',
3088 cls: 'btn-' + this.buttonPosition
3093 { // BS4 uses mr-auto on left buttons....
3094 cls : 'modal-footer'
3105 cls: "modal-dialog " + size,
3108 cls : "modal-content",
3111 cls : 'modal-header',
3126 modal.cls += ' fade';
3132 getChildContainer : function() {
3137 getButtonContainer : function() {
3139 return Roo.bootstrap.version == 4 ?
3140 this.el.select('.modal-footer',true).first()
3141 : this.el.select('.modal-footer div',true).first();
3144 initEvents : function()
3146 if (this.allow_close) {
3147 this.closeEl.on('click', this.hide, this);
3149 Roo.EventManager.onWindowResize(this.resize, this, true);
3157 this.maskEl.setSize(
3158 Roo.lib.Dom.getViewWidth(true),
3159 Roo.lib.Dom.getViewHeight(true)
3162 if (this.fitwindow) {
3166 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
3167 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
3172 if(this.max_width !== 0) {
3174 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
3177 this.setSize(w, this.height);
3181 if(this.max_height) {
3182 this.setSize(w,Math.min(
3184 Roo.lib.Dom.getViewportHeight(true) - 60
3190 if(!this.fit_content) {
3191 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
3195 this.setSize(w, Math.min(
3197 this.headerEl.getHeight() +
3198 this.footerEl.getHeight() +
3199 this.getChildHeight(this.bodyEl.dom.childNodes),
3200 Roo.lib.Dom.getViewportHeight(true) - 60)
3206 setSize : function(w,h)
3217 if (!this.rendered) {
3221 //this.el.setStyle('display', 'block');
3222 this.el.removeClass('hideing');
3223 this.el.dom.style.display='block';
3225 Roo.get(document.body).addClass('modal-open');
3227 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
3230 this.el.addClass('show');
3231 this.el.addClass('in');
3234 this.el.addClass('show');
3235 this.el.addClass('in');
3238 // not sure how we can show data in here..
3240 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3243 Roo.get(document.body).addClass("x-body-masked");
3245 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3246 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3247 this.maskEl.dom.style.display = 'block';
3248 this.maskEl.addClass('show');
3253 this.fireEvent('show', this);
3255 // set zindex here - otherwise it appears to be ignored...
3256 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3259 this.items.forEach( function(e) {
3260 e.layout ? e.layout() : false;
3268 if(this.fireEvent("beforehide", this) !== false){
3270 this.maskEl.removeClass('show');
3272 this.maskEl.dom.style.display = '';
3273 Roo.get(document.body).removeClass("x-body-masked");
3274 this.el.removeClass('in');
3275 this.el.select('.modal-dialog', true).first().setStyle('transform','');
3277 if(this.animate){ // why
3278 this.el.addClass('hideing');
3279 this.el.removeClass('show');
3281 if (!this.el.hasClass('hideing')) {
3282 return; // it's been shown again...
3285 this.el.dom.style.display='';
3287 Roo.get(document.body).removeClass('modal-open');
3288 this.el.removeClass('hideing');
3292 this.el.removeClass('show');
3293 this.el.dom.style.display='';
3294 Roo.get(document.body).removeClass('modal-open');
3297 this.fireEvent('hide', this);
3300 isVisible : function()
3303 return this.el.hasClass('show') && !this.el.hasClass('hideing');
3307 addButton : function(str, cb)
3311 var b = Roo.apply({}, { html : str } );
3312 b.xns = b.xns || Roo.bootstrap;
3313 b.xtype = b.xtype || 'Button';
3314 if (typeof(b.listeners) == 'undefined') {
3315 b.listeners = { click : cb.createDelegate(this) };
3318 var btn = Roo.factory(b);
3320 btn.render(this.getButtonContainer());
3326 setDefaultButton : function(btn)
3328 //this.el.select('.modal-footer').()
3331 resizeTo: function(w,h)
3333 this.dialogEl.setWidth(w);
3335 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
3337 this.bodyEl.setHeight(h - diff);
3339 this.fireEvent('resize', this);
3342 setContentSize : function(w, h)
3346 onButtonClick: function(btn,e)
3349 this.fireEvent('btnclick', btn.name, e);
3352 * Set the title of the Dialog
3353 * @param {String} str new Title
3355 setTitle: function(str) {
3356 this.titleEl.dom.innerHTML = str;
3359 * Set the body of the Dialog
3360 * @param {String} str new Title
3362 setBody: function(str) {
3363 this.bodyEl.dom.innerHTML = str;
3366 * Set the body of the Dialog using the template
3367 * @param {Obj} data - apply this data to the template and replace the body contents.
3369 applyBody: function(obj)
3372 Roo.log("Error - using apply Body without a template");
3375 this.tmpl.overwrite(this.bodyEl, obj);
3378 getChildHeight : function(child_nodes)
3382 child_nodes.length == 0
3387 var child_height = 0;
3389 for(var i = 0; i < child_nodes.length; i++) {
3392 * for modal with tabs...
3393 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3395 var layout_childs = child_nodes[i].childNodes;
3397 for(var j = 0; j < layout_childs.length; j++) {
3399 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3401 var layout_body_childs = layout_childs[j].childNodes;
3403 for(var k = 0; k < layout_body_childs.length; k++) {
3405 if(layout_body_childs[k].classList.contains('navbar')) {
3406 child_height += layout_body_childs[k].offsetHeight;
3410 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3412 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3414 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3416 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3417 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3432 child_height += child_nodes[i].offsetHeight;
3433 // Roo.log(child_nodes[i].offsetHeight);
3436 return child_height;
3442 Roo.apply(Roo.bootstrap.Modal, {
3444 * Button config that displays a single OK button
3453 * Button config that displays Yes and No buttons
3469 * Button config that displays OK and Cancel buttons
3484 * Button config that displays Yes, No and Cancel buttons
3508 * messagebox - can be used as a replace
3512 * @class Roo.MessageBox
3513 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3517 Roo.Msg.alert('Status', 'Changes saved successfully.');
3519 // Prompt for user data:
3520 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3522 // process text value...
3526 // Show a dialog using config options:
3528 title:'Save Changes?',
3529 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3530 buttons: Roo.Msg.YESNOCANCEL,
3537 Roo.bootstrap.MessageBox = function(){
3538 var dlg, opt, mask, waitTimer;
3539 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3540 var buttons, activeTextEl, bwidth;
3544 var handleButton = function(button){
3546 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3550 var handleHide = function(){
3552 dlg.el.removeClass(opt.cls);
3555 // Roo.TaskMgr.stop(waitTimer);
3556 // waitTimer = null;
3561 var updateButtons = function(b){
3564 buttons["ok"].hide();
3565 buttons["cancel"].hide();
3566 buttons["yes"].hide();
3567 buttons["no"].hide();
3568 dlg.footerEl.hide();
3572 dlg.footerEl.show();
3573 for(var k in buttons){
3574 if(typeof buttons[k] != "function"){
3577 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3578 width += buttons[k].el.getWidth()+15;
3588 var handleEsc = function(d, k, e){
3589 if(opt && opt.closable !== false){
3599 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3600 * @return {Roo.BasicDialog} The BasicDialog element
3602 getDialog : function(){
3604 dlg = new Roo.bootstrap.Modal( {
3607 //constraintoviewport:false,
3609 //collapsible : false,
3614 //buttonAlign:"center",
3615 closeClick : function(){
3616 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3619 handleButton("cancel");
3624 dlg.on("hide", handleHide);
3626 //dlg.addKeyListener(27, handleEsc);
3628 this.buttons = buttons;
3629 var bt = this.buttonText;
3630 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3631 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3632 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3633 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3635 bodyEl = dlg.bodyEl.createChild({
3637 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3638 '<textarea class="roo-mb-textarea"></textarea>' +
3639 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3641 msgEl = bodyEl.dom.firstChild;
3642 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3643 textboxEl.enableDisplayMode();
3644 textboxEl.addKeyListener([10,13], function(){
3645 if(dlg.isVisible() && opt && opt.buttons){
3648 }else if(opt.buttons.yes){
3649 handleButton("yes");
3653 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3654 textareaEl.enableDisplayMode();
3655 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3656 progressEl.enableDisplayMode();
3658 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3659 var pf = progressEl.dom.firstChild;
3661 pp = Roo.get(pf.firstChild);
3662 pp.setHeight(pf.offsetHeight);
3670 * Updates the message box body text
3671 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3672 * the XHTML-compliant non-breaking space character '&#160;')
3673 * @return {Roo.MessageBox} This message box
3675 updateText : function(text)
3677 if(!dlg.isVisible() && !opt.width){
3678 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3679 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3681 msgEl.innerHTML = text || ' ';
3683 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3684 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3686 Math.min(opt.width || cw , this.maxWidth),
3687 Math.max(opt.minWidth || this.minWidth, bwidth)
3690 activeTextEl.setWidth(w);
3692 if(dlg.isVisible()){
3693 dlg.fixedcenter = false;
3695 // to big, make it scroll. = But as usual stupid IE does not support
3698 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3699 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3700 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3702 bodyEl.dom.style.height = '';
3703 bodyEl.dom.style.overflowY = '';
3706 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3708 bodyEl.dom.style.overflowX = '';
3711 dlg.setContentSize(w, bodyEl.getHeight());
3712 if(dlg.isVisible()){
3713 dlg.fixedcenter = true;
3719 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3720 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3721 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3722 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3723 * @return {Roo.MessageBox} This message box
3725 updateProgress : function(value, text){
3727 this.updateText(text);
3730 if (pp) { // weird bug on my firefox - for some reason this is not defined
3731 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3732 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3738 * Returns true if the message box is currently displayed
3739 * @return {Boolean} True if the message box is visible, else false
3741 isVisible : function(){
3742 return dlg && dlg.isVisible();
3746 * Hides the message box if it is displayed
3749 if(this.isVisible()){
3755 * Displays a new message box, or reinitializes an existing message box, based on the config options
3756 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3757 * The following config object properties are supported:
3759 Property Type Description
3760 ---------- --------------- ------------------------------------------------------------------------------------
3761 animEl String/Element An id or Element from which the message box should animate as it opens and
3762 closes (defaults to undefined)
3763 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3764 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3765 closable Boolean False to hide the top-right close button (defaults to true). Note that
3766 progress and wait dialogs will ignore this property and always hide the
3767 close button as they can only be closed programmatically.
3768 cls String A custom CSS class to apply to the message box element
3769 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3770 displayed (defaults to 75)
3771 fn Function A callback function to execute after closing the dialog. The arguments to the
3772 function will be btn (the name of the button that was clicked, if applicable,
3773 e.g. "ok"), and text (the value of the active text field, if applicable).
3774 Progress and wait dialogs will ignore this option since they do not respond to
3775 user actions and can only be closed programmatically, so any required function
3776 should be called by the same code after it closes the dialog.
3777 icon String A CSS class that provides a background image to be used as an icon for
3778 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3779 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3780 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3781 modal Boolean False to allow user interaction with the page while the message box is
3782 displayed (defaults to true)
3783 msg String A string that will replace the existing message box body text (defaults
3784 to the XHTML-compliant non-breaking space character ' ')
3785 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3786 progress Boolean True to display a progress bar (defaults to false)
3787 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3788 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3789 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3790 title String The title text
3791 value String The string value to set into the active textbox element if displayed
3792 wait Boolean True to display a progress bar (defaults to false)
3793 width Number The width of the dialog in pixels
3800 msg: 'Please enter your address:',
3802 buttons: Roo.MessageBox.OKCANCEL,
3805 animEl: 'addAddressBtn'
3808 * @param {Object} config Configuration options
3809 * @return {Roo.MessageBox} This message box
3811 show : function(options)
3814 // this causes nightmares if you show one dialog after another
3815 // especially on callbacks..
3817 if(this.isVisible()){
3820 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3821 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3822 Roo.log("New Dialog Message:" + options.msg )
3823 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3824 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3827 var d = this.getDialog();
3829 d.setTitle(opt.title || " ");
3830 d.closeEl.setDisplayed(opt.closable !== false);
3831 activeTextEl = textboxEl;
3832 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3837 textareaEl.setHeight(typeof opt.multiline == "number" ?
3838 opt.multiline : this.defaultTextHeight);
3839 activeTextEl = textareaEl;
3848 progressEl.setDisplayed(opt.progress === true);
3850 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3852 this.updateProgress(0);
3853 activeTextEl.dom.value = opt.value || "";
3855 dlg.setDefaultButton(activeTextEl);
3857 var bs = opt.buttons;
3861 }else if(bs && bs.yes){
3862 db = buttons["yes"];
3864 dlg.setDefaultButton(db);
3866 bwidth = updateButtons(opt.buttons);
3867 this.updateText(opt.msg);
3869 d.el.addClass(opt.cls);
3871 d.proxyDrag = opt.proxyDrag === true;
3872 d.modal = opt.modal !== false;
3873 d.mask = opt.modal !== false ? mask : false;
3875 // force it to the end of the z-index stack so it gets a cursor in FF
3876 document.body.appendChild(dlg.el.dom);
3877 d.animateTarget = null;
3878 d.show(options.animEl);
3884 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3885 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3886 * and closing the message box when the process is complete.
3887 * @param {String} title The title bar text
3888 * @param {String} msg The message box body text
3889 * @return {Roo.MessageBox} This message box
3891 progress : function(title, msg){
3898 minWidth: this.minProgressWidth,
3905 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3906 * If a callback function is passed it will be called after the user clicks the button, and the
3907 * id of the button that was clicked will be passed as the only parameter to the callback
3908 * (could also be the top-right close button).
3909 * @param {String} title The title bar text
3910 * @param {String} msg The message box body text
3911 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3912 * @param {Object} scope (optional) The scope of the callback function
3913 * @return {Roo.MessageBox} This message box
3915 alert : function(title, msg, fn, scope)
3930 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3931 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3932 * You are responsible for closing the message box when the process is complete.
3933 * @param {String} msg The message box body text
3934 * @param {String} title (optional) The title bar text
3935 * @return {Roo.MessageBox} This message box
3937 wait : function(msg, title){
3948 waitTimer = Roo.TaskMgr.start({
3950 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3958 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3959 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3960 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3961 * @param {String} title The title bar text
3962 * @param {String} msg The message box body text
3963 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3964 * @param {Object} scope (optional) The scope of the callback function
3965 * @return {Roo.MessageBox} This message box
3967 confirm : function(title, msg, fn, scope){
3971 buttons: this.YESNO,
3980 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3981 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3982 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3983 * (could also be the top-right close button) and the text that was entered will be passed as the two
3984 * parameters to the callback.
3985 * @param {String} title The title bar text
3986 * @param {String} msg The message box body text
3987 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3988 * @param {Object} scope (optional) The scope of the callback function
3989 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3990 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3991 * @return {Roo.MessageBox} This message box
3993 prompt : function(title, msg, fn, scope, multiline){
3997 buttons: this.OKCANCEL,
4002 multiline: multiline,
4009 * Button config that displays a single OK button
4014 * Button config that displays Yes and No buttons
4017 YESNO : {yes:true, no:true},
4019 * Button config that displays OK and Cancel buttons
4022 OKCANCEL : {ok:true, cancel:true},
4024 * Button config that displays Yes, No and Cancel buttons
4027 YESNOCANCEL : {yes:true, no:true, cancel:true},
4030 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
4033 defaultTextHeight : 75,
4035 * The maximum width in pixels of the message box (defaults to 600)
4040 * The minimum width in pixels of the message box (defaults to 100)
4045 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
4046 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
4049 minProgressWidth : 250,
4051 * An object containing the default button text strings that can be overriden for localized language support.
4052 * Supported properties are: ok, cancel, yes and no.
4053 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
4066 * Shorthand for {@link Roo.MessageBox}
4068 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
4069 Roo.Msg = Roo.Msg || Roo.MessageBox;
4078 * @class Roo.bootstrap.Navbar
4079 * @extends Roo.bootstrap.Component
4080 * Bootstrap Navbar class
4083 * Create a new Navbar
4084 * @param {Object} config The config object
4088 Roo.bootstrap.Navbar = function(config){
4089 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
4093 * @event beforetoggle
4094 * Fire before toggle the menu
4095 * @param {Roo.EventObject} e
4097 "beforetoggle" : true
4101 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
4110 getAutoCreate : function(){
4113 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
4117 initEvents :function ()
4119 //Roo.log(this.el.select('.navbar-toggle',true));
4120 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
4127 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
4129 var size = this.el.getSize();
4130 this.maskEl.setSize(size.width, size.height);
4131 this.maskEl.enableDisplayMode("block");
4140 getChildContainer : function()
4142 if (this.el && this.el.select('.collapse').getCount()) {
4143 return this.el.select('.collapse',true).first();
4158 onToggle : function()
4161 if(this.fireEvent('beforetoggle', this) === false){
4164 var ce = this.el.select('.navbar-collapse',true).first();
4166 if (!ce.hasClass('show')) {
4176 * Expand the navbar pulldown
4178 expand : function ()
4181 var ce = this.el.select('.navbar-collapse',true).first();
4182 if (ce.hasClass('collapsing')) {
4185 ce.dom.style.height = '';
4187 ce.addClass('in'); // old...
4188 ce.removeClass('collapse');
4189 ce.addClass('show');
4190 var h = ce.getHeight();
4192 ce.removeClass('show');
4193 // at this point we should be able to see it..
4194 ce.addClass('collapsing');
4196 ce.setHeight(0); // resize it ...
4197 ce.on('transitionend', function() {
4198 //Roo.log('done transition');
4199 ce.removeClass('collapsing');
4200 ce.addClass('show');
4201 ce.removeClass('collapse');
4203 ce.dom.style.height = '';
4204 }, this, { single: true} );
4206 ce.dom.scrollTop = 0;
4209 * Collapse the navbar pulldown
4211 collapse : function()
4213 var ce = this.el.select('.navbar-collapse',true).first();
4215 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
4216 // it's collapsed or collapsing..
4219 ce.removeClass('in'); // old...
4220 ce.setHeight(ce.getHeight());
4221 ce.removeClass('show');
4222 ce.addClass('collapsing');
4224 ce.on('transitionend', function() {
4225 ce.dom.style.height = '';
4226 ce.removeClass('collapsing');
4227 ce.addClass('collapse');
4228 }, this, { single: true} );
4248 * @class Roo.bootstrap.NavSimplebar
4249 * @extends Roo.bootstrap.Navbar
4250 * Bootstrap Sidebar class
4252 * @cfg {Boolean} inverse is inverted color
4254 * @cfg {String} type (nav | pills | tabs)
4255 * @cfg {Boolean} arrangement stacked | justified
4256 * @cfg {String} align (left | right) alignment
4258 * @cfg {Boolean} main (true|false) main nav bar? default false
4259 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4261 * @cfg {String} tag (header|footer|nav|div) default is nav
4263 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4267 * Create a new Sidebar
4268 * @param {Object} config The config object
4272 Roo.bootstrap.NavSimplebar = function(config){
4273 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4276 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
4292 getAutoCreate : function(){
4296 tag : this.tag || 'div',
4297 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
4299 if (['light','white'].indexOf(this.weight) > -1) {
4300 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4302 cfg.cls += ' bg-' + this.weight;
4305 cfg.cls += ' navbar-inverse';
4309 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4311 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4320 cls: 'nav nav-' + this.xtype,
4326 this.type = this.type || 'nav';
4327 if (['tabs','pills'].indexOf(this.type) != -1) {
4328 cfg.cn[0].cls += ' nav-' + this.type
4332 if (this.type!=='nav') {
4333 Roo.log('nav type must be nav/tabs/pills')
4335 cfg.cn[0].cls += ' navbar-nav'
4341 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4342 cfg.cn[0].cls += ' nav-' + this.arrangement;
4346 if (this.align === 'right') {
4347 cfg.cn[0].cls += ' navbar-right';
4372 * navbar-expand-md fixed-top
4376 * @class Roo.bootstrap.NavHeaderbar
4377 * @extends Roo.bootstrap.NavSimplebar
4378 * Bootstrap Sidebar class
4380 * @cfg {String} brand what is brand
4381 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4382 * @cfg {String} brand_href href of the brand
4383 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4384 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4385 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4386 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4389 * Create a new Sidebar
4390 * @param {Object} config The config object
4394 Roo.bootstrap.NavHeaderbar = function(config){
4395 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4399 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4406 desktopCenter : false,
4409 getAutoCreate : function(){
4412 tag: this.nav || 'nav',
4413 cls: 'navbar navbar-expand-md',
4419 if (this.desktopCenter) {
4420 cn.push({cls : 'container', cn : []});
4428 cls: 'navbar-toggle navbar-toggler',
4429 'data-toggle': 'collapse',
4434 html: 'Toggle navigation'
4438 cls: 'icon-bar navbar-toggler-icon'
4451 cn.push( Roo.bootstrap.version == 4 ? btn : {
4453 cls: 'navbar-header',
4462 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
4466 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4468 if (['light','white'].indexOf(this.weight) > -1) {
4469 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4471 cfg.cls += ' bg-' + this.weight;
4474 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4475 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4477 // tag can override this..
4479 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4482 if (this.brand !== '') {
4483 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4484 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4486 href: this.brand_href ? this.brand_href : '#',
4487 cls: 'navbar-brand',
4495 cfg.cls += ' main-nav';
4503 getHeaderChildContainer : function()
4505 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4506 return this.el.select('.navbar-header',true).first();
4509 return this.getChildContainer();
4513 initEvents : function()
4515 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4517 if (this.autohide) {
4522 Roo.get(document).on('scroll',function(e) {
4523 var ns = Roo.get(document).getScroll().top;
4524 var os = prevScroll;
4528 ft.removeClass('slideDown');
4529 ft.addClass('slideUp');
4532 ft.removeClass('slideUp');
4533 ft.addClass('slideDown');
4554 * @class Roo.bootstrap.NavSidebar
4555 * @extends Roo.bootstrap.Navbar
4556 * Bootstrap Sidebar class
4559 * Create a new Sidebar
4560 * @param {Object} config The config object
4564 Roo.bootstrap.NavSidebar = function(config){
4565 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4568 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4570 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4572 getAutoCreate : function(){
4577 cls: 'sidebar sidebar-nav'
4599 * @class Roo.bootstrap.NavGroup
4600 * @extends Roo.bootstrap.Component
4601 * Bootstrap NavGroup class
4602 * @cfg {String} align (left|right)
4603 * @cfg {Boolean} inverse
4604 * @cfg {String} type (nav|pills|tab) default nav
4605 * @cfg {String} navId - reference Id for navbar.
4609 * Create a new nav group
4610 * @param {Object} config The config object
4613 Roo.bootstrap.NavGroup = function(config){
4614 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4617 Roo.bootstrap.NavGroup.register(this);
4621 * Fires when the active item changes
4622 * @param {Roo.bootstrap.NavGroup} this
4623 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4624 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4631 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4642 getAutoCreate : function()
4644 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4650 if (Roo.bootstrap.version == 4) {
4651 if (['tabs','pills'].indexOf(this.type) != -1) {
4652 cfg.cls += ' nav-' + this.type;
4654 // trying to remove so header bar can right align top?
4655 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
4656 // do not use on header bar...
4657 cfg.cls += ' navbar-nav';
4662 if (['tabs','pills'].indexOf(this.type) != -1) {
4663 cfg.cls += ' nav-' + this.type
4665 if (this.type !== 'nav') {
4666 Roo.log('nav type must be nav/tabs/pills')
4668 cfg.cls += ' navbar-nav'
4672 if (this.parent() && this.parent().sidebar) {
4675 cls: 'dashboard-menu sidebar-menu'
4681 if (this.form === true) {
4684 cls: 'navbar-form form-inline'
4686 //nav navbar-right ml-md-auto
4687 if (this.align === 'right') {
4688 cfg.cls += ' navbar-right ml-md-auto';
4690 cfg.cls += ' navbar-left';
4694 if (this.align === 'right') {
4695 cfg.cls += ' navbar-right ml-md-auto';
4697 cfg.cls += ' mr-auto';
4701 cfg.cls += ' navbar-inverse';
4709 * sets the active Navigation item
4710 * @param {Roo.bootstrap.NavItem} the new current navitem
4712 setActiveItem : function(item)
4715 Roo.each(this.navItems, function(v){
4720 v.setActive(false, true);
4727 item.setActive(true, true);
4728 this.fireEvent('changed', this, item, prev);
4733 * gets the active Navigation item
4734 * @return {Roo.bootstrap.NavItem} the current navitem
4736 getActive : function()
4740 Roo.each(this.navItems, function(v){
4751 indexOfNav : function()
4755 Roo.each(this.navItems, function(v,i){
4766 * adds a Navigation item
4767 * @param {Roo.bootstrap.NavItem} the navitem to add
4769 addItem : function(cfg)
4771 if (this.form && Roo.bootstrap.version == 4) {
4774 var cn = new Roo.bootstrap.NavItem(cfg);
4776 cn.parentId = this.id;
4777 cn.onRender(this.el, null);
4781 * register a Navigation item
4782 * @param {Roo.bootstrap.NavItem} the navitem to add
4784 register : function(item)
4786 this.navItems.push( item);
4787 item.navId = this.navId;
4792 * clear all the Navigation item
4795 clearAll : function()
4798 this.el.dom.innerHTML = '';
4801 getNavItem: function(tabId)
4804 Roo.each(this.navItems, function(e) {
4805 if (e.tabId == tabId) {
4815 setActiveNext : function()
4817 var i = this.indexOfNav(this.getActive());
4818 if (i > this.navItems.length) {
4821 this.setActiveItem(this.navItems[i+1]);
4823 setActivePrev : function()
4825 var i = this.indexOfNav(this.getActive());
4829 this.setActiveItem(this.navItems[i-1]);
4831 clearWasActive : function(except) {
4832 Roo.each(this.navItems, function(e) {
4833 if (e.tabId != except.tabId && e.was_active) {
4834 e.was_active = false;
4841 getWasActive : function ()
4844 Roo.each(this.navItems, function(e) {
4859 Roo.apply(Roo.bootstrap.NavGroup, {
4863 * register a Navigation Group
4864 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4866 register : function(navgrp)
4868 this.groups[navgrp.navId] = navgrp;
4872 * fetch a Navigation Group based on the navigation ID
4873 * @param {string} the navgroup to add
4874 * @returns {Roo.bootstrap.NavGroup} the navgroup
4876 get: function(navId) {
4877 if (typeof(this.groups[navId]) == 'undefined') {
4879 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4881 return this.groups[navId] ;
4896 * @class Roo.bootstrap.NavItem
4897 * @extends Roo.bootstrap.Component
4898 * Bootstrap Navbar.NavItem class
4899 * @cfg {String} href link to
4900 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4902 * @cfg {String} html content of button
4903 * @cfg {String} badge text inside badge
4904 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4905 * @cfg {String} glyphicon DEPRICATED - use fa
4906 * @cfg {String} icon DEPRICATED - use fa
4907 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4908 * @cfg {Boolean} active Is item active
4909 * @cfg {Boolean} disabled Is item disabled
4911 * @cfg {Boolean} preventDefault (true | false) default false
4912 * @cfg {String} tabId the tab that this item activates.
4913 * @cfg {String} tagtype (a|span) render as a href or span?
4914 * @cfg {Boolean} animateRef (true|false) link to element default false
4917 * Create a new Navbar Item
4918 * @param {Object} config The config object
4920 Roo.bootstrap.NavItem = function(config){
4921 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4926 * The raw click event for the entire grid.
4927 * @param {Roo.EventObject} e
4932 * Fires when the active item active state changes
4933 * @param {Roo.bootstrap.NavItem} this
4934 * @param {boolean} state the new state
4940 * Fires when scroll to element
4941 * @param {Roo.bootstrap.NavItem} this
4942 * @param {Object} options
4943 * @param {Roo.EventObject} e
4951 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4960 preventDefault : false,
4968 button_outline : false,
4972 getAutoCreate : function(){
4980 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4982 if (this.disabled) {
4983 cfg.cls += ' disabled';
4987 if (this.button_weight.length) {
4988 cfg.tag = this.href ? 'a' : 'button';
4989 cfg.html = this.html || '';
4990 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4992 cfg.href = this.href;
4995 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4998 // menu .. should add dropdown-menu class - so no need for carat..
5000 if (this.badge !== '') {
5002 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5007 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
5011 href : this.href || "#",
5012 html: this.html || ''
5015 if (this.tagtype == 'a') {
5016 cfg.cn[0].cls = 'nav-link';
5019 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
5022 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
5024 if(this.glyphicon) {
5025 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
5030 cfg.cn[0].html += " <span class='caret'></span>";
5034 if (this.badge !== '') {
5036 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5044 onRender : function(ct, position)
5046 // Roo.log("Call onRender: " + this.xtype);
5047 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
5051 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
5052 this.navLink = this.el.select('.nav-link',true).first();
5057 initEvents: function()
5059 if (typeof (this.menu) != 'undefined') {
5060 this.menu.parentType = this.xtype;
5061 this.menu.triggerEl = this.el;
5062 this.menu = this.addxtype(Roo.apply({}, this.menu));
5065 this.el.select('a',true).on('click', this.onClick, this);
5067 if(this.tagtype == 'span'){
5068 this.el.select('span',true).on('click', this.onClick, this);
5071 // at this point parent should be available..
5072 this.parent().register(this);
5075 onClick : function(e)
5077 if (e.getTarget('.dropdown-menu-item')) {
5078 // did you click on a menu itemm.... - then don't trigger onclick..
5083 this.preventDefault ||
5086 Roo.log("NavItem - prevent Default?");
5090 if (this.disabled) {
5094 var tg = Roo.bootstrap.TabGroup.get(this.navId);
5095 if (tg && tg.transition) {
5096 Roo.log("waiting for the transitionend");
5102 //Roo.log("fire event clicked");
5103 if(this.fireEvent('click', this, e) === false){
5107 if(this.tagtype == 'span'){
5111 //Roo.log(this.href);
5112 var ael = this.el.select('a',true).first();
5115 if(ael && this.animateRef && this.href.indexOf('#') > -1){
5116 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
5117 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
5118 return; // ignore... - it's a 'hash' to another page.
5120 Roo.log("NavItem - prevent Default?");
5122 this.scrollToElement(e);
5126 var p = this.parent();
5128 if (['tabs','pills'].indexOf(p.type)!==-1) {
5129 if (typeof(p.setActiveItem) !== 'undefined') {
5130 p.setActiveItem(this);
5134 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
5135 if (p.parentType == 'NavHeaderbar' && !this.menu) {
5136 // remove the collapsed menu expand...
5137 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
5141 isActive: function () {
5144 setActive : function(state, fire, is_was_active)
5146 if (this.active && !state && this.navId) {
5147 this.was_active = true;
5148 var nv = Roo.bootstrap.NavGroup.get(this.navId);
5150 nv.clearWasActive(this);
5154 this.active = state;
5157 this.el.removeClass('active');
5158 this.navLink ? this.navLink.removeClass('active') : false;
5159 } else if (!this.el.hasClass('active')) {
5161 this.el.addClass('active');
5162 if (Roo.bootstrap.version == 4 && this.navLink ) {
5163 this.navLink.addClass('active');
5168 this.fireEvent('changed', this, state);
5171 // show a panel if it's registered and related..
5173 if (!this.navId || !this.tabId || !state || is_was_active) {
5177 var tg = Roo.bootstrap.TabGroup.get(this.navId);
5181 var pan = tg.getPanelByName(this.tabId);
5185 // if we can not flip to new panel - go back to old nav highlight..
5186 if (false == tg.showPanel(pan)) {
5187 var nv = Roo.bootstrap.NavGroup.get(this.navId);
5189 var onav = nv.getWasActive();
5191 onav.setActive(true, false, true);
5200 // this should not be here...
5201 setDisabled : function(state)
5203 this.disabled = state;
5205 this.el.removeClass('disabled');
5206 } else if (!this.el.hasClass('disabled')) {
5207 this.el.addClass('disabled');
5213 * Fetch the element to display the tooltip on.
5214 * @return {Roo.Element} defaults to this.el
5216 tooltipEl : function()
5218 return this.el.select('' + this.tagtype + '', true).first();
5221 scrollToElement : function(e)
5223 var c = document.body;
5226 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
5228 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
5229 c = document.documentElement;
5232 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5238 var o = target.calcOffsetsTo(c);
5245 this.fireEvent('scrollto', this, options, e);
5247 Roo.get(c).scrollTo('top', options.value, true);
5260 * <span> icon </span>
5261 * <span> text </span>
5262 * <span>badge </span>
5266 * @class Roo.bootstrap.NavSidebarItem
5267 * @extends Roo.bootstrap.NavItem
5268 * Bootstrap Navbar.NavSidebarItem class
5269 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5270 * {Boolean} open is the menu open
5271 * {Boolean} buttonView use button as the tigger el rather that a (default false)
5272 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5273 * {String} buttonSize (sm|md|lg)the extra classes for the button
5274 * {Boolean} showArrow show arrow next to the text (default true)
5276 * Create a new Navbar Button
5277 * @param {Object} config The config object
5279 Roo.bootstrap.NavSidebarItem = function(config){
5280 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5285 * The raw click event for the entire grid.
5286 * @param {Roo.EventObject} e
5291 * Fires when the active item active state changes
5292 * @param {Roo.bootstrap.NavSidebarItem} this
5293 * @param {boolean} state the new state
5301 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
5303 badgeWeight : 'default',
5309 buttonWeight : 'default',
5315 getAutoCreate : function(){
5320 href : this.href || '#',
5326 if(this.buttonView){
5329 href : this.href || '#',
5330 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5343 cfg.cls += ' active';
5346 if (this.disabled) {
5347 cfg.cls += ' disabled';
5350 cfg.cls += ' open x-open';
5353 if (this.glyphicon || this.icon) {
5354 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
5355 a.cn.push({ tag : 'i', cls : c }) ;
5358 if(!this.buttonView){
5361 html : this.html || ''
5368 if (this.badge !== '') {
5369 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
5375 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5378 a.cls += ' dropdown-toggle treeview' ;
5384 initEvents : function()
5386 if (typeof (this.menu) != 'undefined') {
5387 this.menu.parentType = this.xtype;
5388 this.menu.triggerEl = this.el;
5389 this.menu = this.addxtype(Roo.apply({}, this.menu));
5392 this.el.on('click', this.onClick, this);
5394 if(this.badge !== ''){
5395 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5400 onClick : function(e)
5407 if(this.preventDefault){
5411 this.fireEvent('click', this, e);
5414 disable : function()
5416 this.setDisabled(true);
5421 this.setDisabled(false);
5424 setDisabled : function(state)
5426 if(this.disabled == state){
5430 this.disabled = state;
5433 this.el.addClass('disabled');
5437 this.el.removeClass('disabled');
5442 setActive : function(state)
5444 if(this.active == state){
5448 this.active = state;
5451 this.el.addClass('active');
5455 this.el.removeClass('active');
5460 isActive: function ()
5465 setBadge : function(str)
5471 this.badgeEl.dom.innerHTML = str;
5488 * @class Roo.bootstrap.Row
5489 * @extends Roo.bootstrap.Component
5490 * Bootstrap Row class (contains columns...)
5494 * @param {Object} config The config object
5497 Roo.bootstrap.Row = function(config){
5498 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5501 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5503 getAutoCreate : function(){
5522 * @class Roo.bootstrap.Element
5523 * @extends Roo.bootstrap.Component
5524 * Bootstrap Element class
5525 * @cfg {String} html contents of the element
5526 * @cfg {String} tag tag of the element
5527 * @cfg {String} cls class of the element
5528 * @cfg {Boolean} preventDefault (true|false) default false
5529 * @cfg {Boolean} clickable (true|false) default false
5532 * Create a new Element
5533 * @param {Object} config The config object
5536 Roo.bootstrap.Element = function(config){
5537 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5543 * When a element is chick
5544 * @param {Roo.bootstrap.Element} this
5545 * @param {Roo.EventObject} e
5551 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5556 preventDefault: false,
5559 getAutoCreate : function(){
5563 // cls: this.cls, double assign in parent class Component.js :: onRender
5570 initEvents: function()
5572 Roo.bootstrap.Element.superclass.initEvents.call(this);
5575 this.el.on('click', this.onClick, this);
5580 onClick : function(e)
5582 if(this.preventDefault){
5586 this.fireEvent('click', this, e);
5589 getValue : function()
5591 return this.el.dom.innerHTML;
5594 setValue : function(value)
5596 this.el.dom.innerHTML = value;
5611 * @class Roo.bootstrap.Pagination
5612 * @extends Roo.bootstrap.Component
5613 * Bootstrap Pagination class
5614 * @cfg {String} size xs | sm | md | lg
5615 * @cfg {Boolean} inverse false | true
5618 * Create a new Pagination
5619 * @param {Object} config The config object
5622 Roo.bootstrap.Pagination = function(config){
5623 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5626 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5632 getAutoCreate : function(){
5638 cfg.cls += ' inverse';
5644 cfg.cls += " " + this.cls;
5662 * @class Roo.bootstrap.PaginationItem
5663 * @extends Roo.bootstrap.Component
5664 * Bootstrap PaginationItem class
5665 * @cfg {String} html text
5666 * @cfg {String} href the link
5667 * @cfg {Boolean} preventDefault (true | false) default true
5668 * @cfg {Boolean} active (true | false) default false
5669 * @cfg {Boolean} disabled default false
5673 * Create a new PaginationItem
5674 * @param {Object} config The config object
5678 Roo.bootstrap.PaginationItem = function(config){
5679 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5684 * The raw click event for the entire grid.
5685 * @param {Roo.EventObject} e
5691 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5695 preventDefault: true,
5700 getAutoCreate : function(){
5706 href : this.href ? this.href : '#',
5707 html : this.html ? this.html : ''
5717 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5721 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5727 initEvents: function() {
5729 this.el.on('click', this.onClick, this);
5732 onClick : function(e)
5734 Roo.log('PaginationItem on click ');
5735 if(this.preventDefault){
5743 this.fireEvent('click', this, e);
5759 * @class Roo.bootstrap.Slider
5760 * @extends Roo.bootstrap.Component
5761 * Bootstrap Slider class
5764 * Create a new Slider
5765 * @param {Object} config The config object
5768 Roo.bootstrap.Slider = function(config){
5769 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5772 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5774 getAutoCreate : function(){
5778 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5782 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5794 * Ext JS Library 1.1.1
5795 * Copyright(c) 2006-2007, Ext JS, LLC.
5797 * Originally Released Under LGPL - original licence link has changed is not relivant.
5800 * <script type="text/javascript">
5805 * @class Roo.grid.ColumnModel
5806 * @extends Roo.util.Observable
5807 * This is the default implementation of a ColumnModel used by the Grid. It defines
5808 * the columns in the grid.
5811 var colModel = new Roo.grid.ColumnModel([
5812 {header: "Ticker", width: 60, sortable: true, locked: true},
5813 {header: "Company Name", width: 150, sortable: true},
5814 {header: "Market Cap.", width: 100, sortable: true},
5815 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5816 {header: "Employees", width: 100, sortable: true, resizable: false}
5821 * The config options listed for this class are options which may appear in each
5822 * individual column definition.
5823 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5825 * @param {Object} config An Array of column config objects. See this class's
5826 * config objects for details.
5828 Roo.grid.ColumnModel = function(config){
5830 * The config passed into the constructor
5832 this.config = config;
5835 // if no id, create one
5836 // if the column does not have a dataIndex mapping,
5837 // map it to the order it is in the config
5838 for(var i = 0, len = config.length; i < len; i++){
5840 if(typeof c.dataIndex == "undefined"){
5843 if(typeof c.renderer == "string"){
5844 c.renderer = Roo.util.Format[c.renderer];
5846 if(typeof c.id == "undefined"){
5849 if(c.editor && c.editor.xtype){
5850 c.editor = Roo.factory(c.editor, Roo.grid);
5852 if(c.editor && c.editor.isFormField){
5853 c.editor = new Roo.grid.GridEditor(c.editor);
5855 this.lookup[c.id] = c;
5859 * The width of columns which have no width specified (defaults to 100)
5862 this.defaultWidth = 100;
5865 * Default sortable of columns which have no sortable specified (defaults to false)
5868 this.defaultSortable = false;
5872 * @event widthchange
5873 * Fires when the width of a column changes.
5874 * @param {ColumnModel} this
5875 * @param {Number} columnIndex The column index
5876 * @param {Number} newWidth The new width
5878 "widthchange": true,
5880 * @event headerchange
5881 * Fires when the text of a header changes.
5882 * @param {ColumnModel} this
5883 * @param {Number} columnIndex The column index
5884 * @param {Number} newText The new header text
5886 "headerchange": true,
5888 * @event hiddenchange
5889 * Fires when a column is hidden or "unhidden".
5890 * @param {ColumnModel} this
5891 * @param {Number} columnIndex The column index
5892 * @param {Boolean} hidden true if hidden, false otherwise
5894 "hiddenchange": true,
5896 * @event columnmoved
5897 * Fires when a column is moved.
5898 * @param {ColumnModel} this
5899 * @param {Number} oldIndex
5900 * @param {Number} newIndex
5902 "columnmoved" : true,
5904 * @event columlockchange
5905 * Fires when a column's locked state is changed
5906 * @param {ColumnModel} this
5907 * @param {Number} colIndex
5908 * @param {Boolean} locked true if locked
5910 "columnlockchange" : true
5912 Roo.grid.ColumnModel.superclass.constructor.call(this);
5914 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5916 * @cfg {String} header The header text to display in the Grid view.
5919 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5920 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5921 * specified, the column's index is used as an index into the Record's data Array.
5924 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5925 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5928 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5929 * Defaults to the value of the {@link #defaultSortable} property.
5930 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5933 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5936 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5939 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5942 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5945 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5946 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5947 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5948 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5951 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5954 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5957 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5960 * @cfg {String} cursor (Optional)
5963 * @cfg {String} tooltip (Optional)
5966 * @cfg {Number} xs (Optional)
5969 * @cfg {Number} sm (Optional)
5972 * @cfg {Number} md (Optional)
5975 * @cfg {Number} lg (Optional)
5978 * Returns the id of the column at the specified index.
5979 * @param {Number} index The column index
5980 * @return {String} the id
5982 getColumnId : function(index){
5983 return this.config[index].id;
5987 * Returns the column for a specified id.
5988 * @param {String} id The column id
5989 * @return {Object} the column
5991 getColumnById : function(id){
5992 return this.lookup[id];
5997 * Returns the column for a specified dataIndex.
5998 * @param {String} dataIndex The column dataIndex
5999 * @return {Object|Boolean} the column or false if not found
6001 getColumnByDataIndex: function(dataIndex){
6002 var index = this.findColumnIndex(dataIndex);
6003 return index > -1 ? this.config[index] : false;
6007 * Returns the index for a specified column id.
6008 * @param {String} id The column id
6009 * @return {Number} the index, or -1 if not found
6011 getIndexById : function(id){
6012 for(var i = 0, len = this.config.length; i < len; i++){
6013 if(this.config[i].id == id){
6021 * Returns the index for a specified column dataIndex.
6022 * @param {String} dataIndex The column dataIndex
6023 * @return {Number} the index, or -1 if not found
6026 findColumnIndex : function(dataIndex){
6027 for(var i = 0, len = this.config.length; i < len; i++){
6028 if(this.config[i].dataIndex == dataIndex){
6036 moveColumn : function(oldIndex, newIndex){
6037 var c = this.config[oldIndex];
6038 this.config.splice(oldIndex, 1);
6039 this.config.splice(newIndex, 0, c);
6040 this.dataMap = null;
6041 this.fireEvent("columnmoved", this, oldIndex, newIndex);
6044 isLocked : function(colIndex){
6045 return this.config[colIndex].locked === true;
6048 setLocked : function(colIndex, value, suppressEvent){
6049 if(this.isLocked(colIndex) == value){
6052 this.config[colIndex].locked = value;
6054 this.fireEvent("columnlockchange", this, colIndex, value);
6058 getTotalLockedWidth : function(){
6060 for(var i = 0; i < this.config.length; i++){
6061 if(this.isLocked(i) && !this.isHidden(i)){
6062 this.totalWidth += this.getColumnWidth(i);
6068 getLockedCount : function(){
6069 for(var i = 0, len = this.config.length; i < len; i++){
6070 if(!this.isLocked(i)){
6075 return this.config.length;
6079 * Returns the number of columns.
6082 getColumnCount : function(visibleOnly){
6083 if(visibleOnly === true){
6085 for(var i = 0, len = this.config.length; i < len; i++){
6086 if(!this.isHidden(i)){
6092 return this.config.length;
6096 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
6097 * @param {Function} fn
6098 * @param {Object} scope (optional)
6099 * @return {Array} result
6101 getColumnsBy : function(fn, scope){
6103 for(var i = 0, len = this.config.length; i < len; i++){
6104 var c = this.config[i];
6105 if(fn.call(scope||this, c, i) === true){
6113 * Returns true if the specified column is sortable.
6114 * @param {Number} col The column index
6117 isSortable : function(col){
6118 if(typeof this.config[col].sortable == "undefined"){
6119 return this.defaultSortable;
6121 return this.config[col].sortable;
6125 * Returns the rendering (formatting) function defined for the column.
6126 * @param {Number} col The column index.
6127 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
6129 getRenderer : function(col){
6130 if(!this.config[col].renderer){
6131 return Roo.grid.ColumnModel.defaultRenderer;
6133 return this.config[col].renderer;
6137 * Sets the rendering (formatting) function for a column.
6138 * @param {Number} col The column index
6139 * @param {Function} fn The function to use to process the cell's raw data
6140 * to return HTML markup for the grid view. The render function is called with
6141 * the following parameters:<ul>
6142 * <li>Data value.</li>
6143 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
6144 * <li>css A CSS style string to apply to the table cell.</li>
6145 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
6146 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
6147 * <li>Row index</li>
6148 * <li>Column index</li>
6149 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
6151 setRenderer : function(col, fn){
6152 this.config[col].renderer = fn;
6156 * Returns the width for the specified column.
6157 * @param {Number} col The column index
6160 getColumnWidth : function(col){
6161 return this.config[col].width * 1 || this.defaultWidth;
6165 * Sets the width for a column.
6166 * @param {Number} col The column index
6167 * @param {Number} width The new width
6169 setColumnWidth : function(col, width, suppressEvent){
6170 this.config[col].width = width;
6171 this.totalWidth = null;
6173 this.fireEvent("widthchange", this, col, width);
6178 * Returns the total width of all columns.
6179 * @param {Boolean} includeHidden True to include hidden column widths
6182 getTotalWidth : function(includeHidden){
6183 if(!this.totalWidth){
6184 this.totalWidth = 0;
6185 for(var i = 0, len = this.config.length; i < len; i++){
6186 if(includeHidden || !this.isHidden(i)){
6187 this.totalWidth += this.getColumnWidth(i);
6191 return this.totalWidth;
6195 * Returns the header for the specified column.
6196 * @param {Number} col The column index
6199 getColumnHeader : function(col){
6200 return this.config[col].header;
6204 * Sets the header for a column.
6205 * @param {Number} col The column index
6206 * @param {String} header The new header
6208 setColumnHeader : function(col, header){
6209 this.config[col].header = header;
6210 this.fireEvent("headerchange", this, col, header);
6214 * Returns the tooltip for the specified column.
6215 * @param {Number} col The column index
6218 getColumnTooltip : function(col){
6219 return this.config[col].tooltip;
6222 * Sets the tooltip for a column.
6223 * @param {Number} col The column index
6224 * @param {String} tooltip The new tooltip
6226 setColumnTooltip : function(col, tooltip){
6227 this.config[col].tooltip = tooltip;
6231 * Returns the dataIndex for the specified column.
6232 * @param {Number} col The column index
6235 getDataIndex : function(col){
6236 return this.config[col].dataIndex;
6240 * Sets the dataIndex for a column.
6241 * @param {Number} col The column index
6242 * @param {Number} dataIndex The new dataIndex
6244 setDataIndex : function(col, dataIndex){
6245 this.config[col].dataIndex = dataIndex;
6251 * Returns true if the cell is editable.
6252 * @param {Number} colIndex The column index
6253 * @param {Number} rowIndex The row index - this is nto actually used..?
6256 isCellEditable : function(colIndex, rowIndex){
6257 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6261 * Returns the editor defined for the cell/column.
6262 * return false or null to disable editing.
6263 * @param {Number} colIndex The column index
6264 * @param {Number} rowIndex The row index
6267 getCellEditor : function(colIndex, rowIndex){
6268 return this.config[colIndex].editor;
6272 * Sets if a column is editable.
6273 * @param {Number} col The column index
6274 * @param {Boolean} editable True if the column is editable
6276 setEditable : function(col, editable){
6277 this.config[col].editable = editable;
6282 * Returns true if the column is hidden.
6283 * @param {Number} colIndex The column index
6286 isHidden : function(colIndex){
6287 return this.config[colIndex].hidden;
6292 * Returns true if the column width cannot be changed
6294 isFixed : function(colIndex){
6295 return this.config[colIndex].fixed;
6299 * Returns true if the column can be resized
6302 isResizable : function(colIndex){
6303 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6306 * Sets if a column is hidden.
6307 * @param {Number} colIndex The column index
6308 * @param {Boolean} hidden True if the column is hidden
6310 setHidden : function(colIndex, hidden){
6311 this.config[colIndex].hidden = hidden;
6312 this.totalWidth = null;
6313 this.fireEvent("hiddenchange", this, colIndex, hidden);
6317 * Sets the editor for a column.
6318 * @param {Number} col The column index
6319 * @param {Object} editor The editor object
6321 setEditor : function(col, editor){
6322 this.config[col].editor = editor;
6326 Roo.grid.ColumnModel.defaultRenderer = function(value)
6328 if(typeof value == "object") {
6331 if(typeof value == "string" && value.length < 1){
6335 return String.format("{0}", value);
6338 // Alias for backwards compatibility
6339 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6342 * Ext JS Library 1.1.1
6343 * Copyright(c) 2006-2007, Ext JS, LLC.
6345 * Originally Released Under LGPL - original licence link has changed is not relivant.
6348 * <script type="text/javascript">
6352 * @class Roo.LoadMask
6353 * A simple utility class for generically masking elements while loading data. If the element being masked has
6354 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6355 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
6356 * element's UpdateManager load indicator and will be destroyed after the initial load.
6358 * Create a new LoadMask
6359 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6360 * @param {Object} config The config object
6362 Roo.LoadMask = function(el, config){
6363 this.el = Roo.get(el);
6364 Roo.apply(this, config);
6366 this.store.on('beforeload', this.onBeforeLoad, this);
6367 this.store.on('load', this.onLoad, this);
6368 this.store.on('loadexception', this.onLoadException, this);
6369 this.removeMask = false;
6371 var um = this.el.getUpdateManager();
6372 um.showLoadIndicator = false; // disable the default indicator
6373 um.on('beforeupdate', this.onBeforeLoad, this);
6374 um.on('update', this.onLoad, this);
6375 um.on('failure', this.onLoad, this);
6376 this.removeMask = true;
6380 Roo.LoadMask.prototype = {
6382 * @cfg {Boolean} removeMask
6383 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6384 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
6388 * The text to display in a centered loading message box (defaults to 'Loading...')
6392 * @cfg {String} msgCls
6393 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6395 msgCls : 'x-mask-loading',
6398 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6404 * Disables the mask to prevent it from being displayed
6406 disable : function(){
6407 this.disabled = true;
6411 * Enables the mask so that it can be displayed
6413 enable : function(){
6414 this.disabled = false;
6417 onLoadException : function()
6421 if (typeof(arguments[3]) != 'undefined') {
6422 Roo.MessageBox.alert("Error loading",arguments[3]);
6426 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6427 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6434 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6439 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6443 onBeforeLoad : function(){
6445 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6450 destroy : function(){
6452 this.store.un('beforeload', this.onBeforeLoad, this);
6453 this.store.un('load', this.onLoad, this);
6454 this.store.un('loadexception', this.onLoadException, this);
6456 var um = this.el.getUpdateManager();
6457 um.un('beforeupdate', this.onBeforeLoad, this);
6458 um.un('update', this.onLoad, this);
6459 um.un('failure', this.onLoad, this);
6470 * @class Roo.bootstrap.Table
6471 * @extends Roo.bootstrap.Component
6472 * Bootstrap Table class
6473 * @cfg {String} cls table class
6474 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6475 * @cfg {String} bgcolor Specifies the background color for a table
6476 * @cfg {Number} border Specifies whether the table cells should have borders or not
6477 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6478 * @cfg {Number} cellspacing Specifies the space between cells
6479 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6480 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6481 * @cfg {String} sortable Specifies that the table should be sortable
6482 * @cfg {String} summary Specifies a summary of the content of a table
6483 * @cfg {Number} width Specifies the width of a table
6484 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6486 * @cfg {boolean} striped Should the rows be alternative striped
6487 * @cfg {boolean} bordered Add borders to the table
6488 * @cfg {boolean} hover Add hover highlighting
6489 * @cfg {boolean} condensed Format condensed
6490 * @cfg {boolean} responsive Format condensed
6491 * @cfg {Boolean} loadMask (true|false) default false
6492 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6493 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6494 * @cfg {Boolean} rowSelection (true|false) default false
6495 * @cfg {Boolean} cellSelection (true|false) default false
6496 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6497 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6498 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6499 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6503 * Create a new Table
6504 * @param {Object} config The config object
6507 Roo.bootstrap.Table = function(config){
6508 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6513 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6514 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6515 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6516 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6518 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6520 this.sm.grid = this;
6521 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6522 this.sm = this.selModel;
6523 this.sm.xmodule = this.xmodule || false;
6526 if (this.cm && typeof(this.cm.config) == 'undefined') {
6527 this.colModel = new Roo.grid.ColumnModel(this.cm);
6528 this.cm = this.colModel;
6529 this.cm.xmodule = this.xmodule || false;
6532 this.store= Roo.factory(this.store, Roo.data);
6533 this.ds = this.store;
6534 this.ds.xmodule = this.xmodule || false;
6537 if (this.footer && this.store) {
6538 this.footer.dataSource = this.ds;
6539 this.footer = Roo.factory(this.footer);
6546 * Fires when a cell is clicked
6547 * @param {Roo.bootstrap.Table} this
6548 * @param {Roo.Element} el
6549 * @param {Number} rowIndex
6550 * @param {Number} columnIndex
6551 * @param {Roo.EventObject} e
6555 * @event celldblclick
6556 * Fires when a cell is double clicked
6557 * @param {Roo.bootstrap.Table} this
6558 * @param {Roo.Element} el
6559 * @param {Number} rowIndex
6560 * @param {Number} columnIndex
6561 * @param {Roo.EventObject} e
6563 "celldblclick" : true,
6566 * Fires when a row is clicked
6567 * @param {Roo.bootstrap.Table} this
6568 * @param {Roo.Element} el
6569 * @param {Number} rowIndex
6570 * @param {Roo.EventObject} e
6574 * @event rowdblclick
6575 * Fires when a row is double clicked
6576 * @param {Roo.bootstrap.Table} this
6577 * @param {Roo.Element} el
6578 * @param {Number} rowIndex
6579 * @param {Roo.EventObject} e
6581 "rowdblclick" : true,
6584 * Fires when a mouseover occur
6585 * @param {Roo.bootstrap.Table} this
6586 * @param {Roo.Element} el
6587 * @param {Number} rowIndex
6588 * @param {Number} columnIndex
6589 * @param {Roo.EventObject} e
6594 * Fires when a mouseout occur
6595 * @param {Roo.bootstrap.Table} this
6596 * @param {Roo.Element} el
6597 * @param {Number} rowIndex
6598 * @param {Number} columnIndex
6599 * @param {Roo.EventObject} e
6604 * Fires when a row is rendered, so you can change add a style to it.
6605 * @param {Roo.bootstrap.Table} this
6606 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6610 * @event rowsrendered
6611 * Fires when all the rows have been rendered
6612 * @param {Roo.bootstrap.Table} this
6614 'rowsrendered' : true,
6616 * @event contextmenu
6617 * The raw contextmenu event for the entire grid.
6618 * @param {Roo.EventObject} e
6620 "contextmenu" : true,
6622 * @event rowcontextmenu
6623 * Fires when a row is right clicked
6624 * @param {Roo.bootstrap.Table} this
6625 * @param {Number} rowIndex
6626 * @param {Roo.EventObject} e
6628 "rowcontextmenu" : true,
6630 * @event cellcontextmenu
6631 * Fires when a cell is right clicked
6632 * @param {Roo.bootstrap.Table} this
6633 * @param {Number} rowIndex
6634 * @param {Number} cellIndex
6635 * @param {Roo.EventObject} e
6637 "cellcontextmenu" : true,
6639 * @event headercontextmenu
6640 * Fires when a header is right clicked
6641 * @param {Roo.bootstrap.Table} this
6642 * @param {Number} columnIndex
6643 * @param {Roo.EventObject} e
6645 "headercontextmenu" : true
6649 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6675 rowSelection : false,
6676 cellSelection : false,
6679 // Roo.Element - the tbody
6681 // Roo.Element - thead element
6684 container: false, // used by gridpanel...
6690 auto_hide_footer : false,
6692 getAutoCreate : function()
6694 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6701 if (this.scrollBody) {
6702 cfg.cls += ' table-body-fixed';
6705 cfg.cls += ' table-striped';
6709 cfg.cls += ' table-hover';
6711 if (this.bordered) {
6712 cfg.cls += ' table-bordered';
6714 if (this.condensed) {
6715 cfg.cls += ' table-condensed';
6717 if (this.responsive) {
6718 cfg.cls += ' table-responsive';
6722 cfg.cls+= ' ' +this.cls;
6725 // this lot should be simplifed...
6738 ].forEach(function(k) {
6746 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6749 if(this.store || this.cm){
6750 if(this.headerShow){
6751 cfg.cn.push(this.renderHeader());
6754 cfg.cn.push(this.renderBody());
6756 if(this.footerShow){
6757 cfg.cn.push(this.renderFooter());
6759 // where does this come from?
6760 //cfg.cls+= ' TableGrid';
6763 return { cn : [ cfg ] };
6766 initEvents : function()
6768 if(!this.store || !this.cm){
6771 if (this.selModel) {
6772 this.selModel.initEvents();
6776 //Roo.log('initEvents with ds!!!!');
6778 this.mainBody = this.el.select('tbody', true).first();
6779 this.mainHead = this.el.select('thead', true).first();
6780 this.mainFoot = this.el.select('tfoot', true).first();
6786 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6787 e.on('click', _this.sort, _this);
6790 this.mainBody.on("click", this.onClick, this);
6791 this.mainBody.on("dblclick", this.onDblClick, this);
6793 // why is this done????? = it breaks dialogs??
6794 //this.parent().el.setStyle('position', 'relative');
6798 this.footer.parentId = this.id;
6799 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6802 this.el.select('tfoot tr td').first().addClass('hide');
6807 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6810 this.store.on('load', this.onLoad, this);
6811 this.store.on('beforeload', this.onBeforeLoad, this);
6812 this.store.on('update', this.onUpdate, this);
6813 this.store.on('add', this.onAdd, this);
6814 this.store.on("clear", this.clear, this);
6816 this.el.on("contextmenu", this.onContextMenu, this);
6818 this.mainBody.on('scroll', this.onBodyScroll, this);
6820 this.cm.on("headerchange", this.onHeaderChange, this);
6822 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6826 onContextMenu : function(e, t)
6828 this.processEvent("contextmenu", e);
6831 processEvent : function(name, e)
6833 if (name != 'touchstart' ) {
6834 this.fireEvent(name, e);
6837 var t = e.getTarget();
6839 var cell = Roo.get(t);
6845 if(cell.findParent('tfoot', false, true)){
6849 if(cell.findParent('thead', false, true)){
6851 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6852 cell = Roo.get(t).findParent('th', false, true);
6854 Roo.log("failed to find th in thead?");
6855 Roo.log(e.getTarget());
6860 var cellIndex = cell.dom.cellIndex;
6862 var ename = name == 'touchstart' ? 'click' : name;
6863 this.fireEvent("header" + ename, this, cellIndex, e);
6868 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6869 cell = Roo.get(t).findParent('td', false, true);
6871 Roo.log("failed to find th in tbody?");
6872 Roo.log(e.getTarget());
6877 var row = cell.findParent('tr', false, true);
6878 var cellIndex = cell.dom.cellIndex;
6879 var rowIndex = row.dom.rowIndex - 1;
6883 this.fireEvent("row" + name, this, rowIndex, e);
6887 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6893 onMouseover : function(e, el)
6895 var cell = Roo.get(el);
6901 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6902 cell = cell.findParent('td', false, true);
6905 var row = cell.findParent('tr', false, true);
6906 var cellIndex = cell.dom.cellIndex;
6907 var rowIndex = row.dom.rowIndex - 1; // start from 0
6909 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6913 onMouseout : function(e, el)
6915 var cell = Roo.get(el);
6921 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6922 cell = cell.findParent('td', false, true);
6925 var row = cell.findParent('tr', false, true);
6926 var cellIndex = cell.dom.cellIndex;
6927 var rowIndex = row.dom.rowIndex - 1; // start from 0
6929 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6933 onClick : function(e, el)
6935 var cell = Roo.get(el);
6937 if(!cell || (!this.cellSelection && !this.rowSelection)){
6941 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6942 cell = cell.findParent('td', false, true);
6945 if(!cell || typeof(cell) == 'undefined'){
6949 var row = cell.findParent('tr', false, true);
6951 if(!row || typeof(row) == 'undefined'){
6955 var cellIndex = cell.dom.cellIndex;
6956 var rowIndex = this.getRowIndex(row);
6958 // why??? - should these not be based on SelectionModel?
6959 if(this.cellSelection){
6960 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6963 if(this.rowSelection){
6964 this.fireEvent('rowclick', this, row, rowIndex, e);
6970 onDblClick : function(e,el)
6972 var cell = Roo.get(el);
6974 if(!cell || (!this.cellSelection && !this.rowSelection)){
6978 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6979 cell = cell.findParent('td', false, true);
6982 if(!cell || typeof(cell) == 'undefined'){
6986 var row = cell.findParent('tr', false, true);
6988 if(!row || typeof(row) == 'undefined'){
6992 var cellIndex = cell.dom.cellIndex;
6993 var rowIndex = this.getRowIndex(row);
6995 if(this.cellSelection){
6996 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6999 if(this.rowSelection){
7000 this.fireEvent('rowdblclick', this, row, rowIndex, e);
7004 sort : function(e,el)
7006 var col = Roo.get(el);
7008 if(!col.hasClass('sortable')){
7012 var sort = col.attr('sort');
7015 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7019 this.store.sortInfo = {field : sort, direction : dir};
7022 Roo.log("calling footer first");
7023 this.footer.onClick('first');
7026 this.store.load({ params : { start : 0 } });
7030 renderHeader : function()
7038 this.totalWidth = 0;
7040 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7042 var config = cm.config[i];
7046 cls : 'x-hcol-' + i,
7048 html: cm.getColumnHeader(i)
7053 if(typeof(config.sortable) != 'undefined' && config.sortable){
7055 c.html = '<i class="glyphicon"></i>' + c.html;
7058 // could use BS4 hidden-..-down
7060 if(typeof(config.lgHeader) != 'undefined'){
7061 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
7064 if(typeof(config.mdHeader) != 'undefined'){
7065 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
7068 if(typeof(config.smHeader) != 'undefined'){
7069 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
7072 if(typeof(config.xsHeader) != 'undefined'){
7073 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
7080 if(typeof(config.tooltip) != 'undefined'){
7081 c.tooltip = config.tooltip;
7084 if(typeof(config.colspan) != 'undefined'){
7085 c.colspan = config.colspan;
7088 if(typeof(config.hidden) != 'undefined' && config.hidden){
7089 c.style += ' display:none;';
7092 if(typeof(config.dataIndex) != 'undefined'){
7093 c.sort = config.dataIndex;
7098 if(typeof(config.align) != 'undefined' && config.align.length){
7099 c.style += ' text-align:' + config.align + ';';
7102 if(typeof(config.width) != 'undefined'){
7103 c.style += ' width:' + config.width + 'px;';
7104 this.totalWidth += config.width;
7106 this.totalWidth += 100; // assume minimum of 100 per column?
7109 if(typeof(config.cls) != 'undefined'){
7110 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
7113 ['xs','sm','md','lg'].map(function(size){
7115 if(typeof(config[size]) == 'undefined'){
7119 if (!config[size]) { // 0 = hidden
7120 // BS 4 '0' is treated as hide that column and below.
7121 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
7125 c.cls += ' col-' + size + '-' + config[size] + (
7126 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7138 renderBody : function()
7148 colspan : this.cm.getColumnCount()
7158 renderFooter : function()
7168 colspan : this.cm.getColumnCount()
7182 // Roo.log('ds onload');
7187 var ds = this.store;
7189 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7190 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
7191 if (_this.store.sortInfo) {
7193 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
7194 e.select('i', true).addClass(['glyphicon-arrow-up']);
7197 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
7198 e.select('i', true).addClass(['glyphicon-arrow-down']);
7203 var tbody = this.mainBody;
7205 if(ds.getCount() > 0){
7206 ds.data.each(function(d,rowIndex){
7207 var row = this.renderRow(cm, ds, rowIndex);
7209 tbody.createChild(row);
7213 if(row.cellObjects.length){
7214 Roo.each(row.cellObjects, function(r){
7215 _this.renderCellObject(r);
7222 var tfoot = this.el.select('tfoot', true).first();
7224 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7226 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7228 var total = this.ds.getTotalCount();
7230 if(this.footer.pageSize < total){
7231 this.mainFoot.show();
7235 Roo.each(this.el.select('tbody td', true).elements, function(e){
7236 e.on('mouseover', _this.onMouseover, _this);
7239 Roo.each(this.el.select('tbody td', true).elements, function(e){
7240 e.on('mouseout', _this.onMouseout, _this);
7242 this.fireEvent('rowsrendered', this);
7248 onUpdate : function(ds,record)
7250 this.refreshRow(record);
7254 onRemove : function(ds, record, index, isUpdate){
7255 if(isUpdate !== true){
7256 this.fireEvent("beforerowremoved", this, index, record);
7258 var bt = this.mainBody.dom;
7260 var rows = this.el.select('tbody > tr', true).elements;
7262 if(typeof(rows[index]) != 'undefined'){
7263 bt.removeChild(rows[index].dom);
7266 // if(bt.rows[index]){
7267 // bt.removeChild(bt.rows[index]);
7270 if(isUpdate !== true){
7271 //this.stripeRows(index);
7272 //this.syncRowHeights(index, index);
7274 this.fireEvent("rowremoved", this, index, record);
7278 onAdd : function(ds, records, rowIndex)
7280 //Roo.log('on Add called');
7281 // - note this does not handle multiple adding very well..
7282 var bt = this.mainBody.dom;
7283 for (var i =0 ; i < records.length;i++) {
7284 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7285 //Roo.log(records[i]);
7286 //Roo.log(this.store.getAt(rowIndex+i));
7287 this.insertRow(this.store, rowIndex + i, false);
7294 refreshRow : function(record){
7295 var ds = this.store, index;
7296 if(typeof record == 'number'){
7298 record = ds.getAt(index);
7300 index = ds.indexOf(record);
7302 this.insertRow(ds, index, true);
7304 this.onRemove(ds, record, index+1, true);
7306 //this.syncRowHeights(index, index);
7308 this.fireEvent("rowupdated", this, index, record);
7311 insertRow : function(dm, rowIndex, isUpdate){
7314 this.fireEvent("beforerowsinserted", this, rowIndex);
7316 //var s = this.getScrollState();
7317 var row = this.renderRow(this.cm, this.store, rowIndex);
7318 // insert before rowIndex..
7319 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7323 if(row.cellObjects.length){
7324 Roo.each(row.cellObjects, function(r){
7325 _this.renderCellObject(r);
7330 this.fireEvent("rowsinserted", this, rowIndex);
7331 //this.syncRowHeights(firstRow, lastRow);
7332 //this.stripeRows(firstRow);
7339 getRowDom : function(rowIndex)
7341 var rows = this.el.select('tbody > tr', true).elements;
7343 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7346 // returns the object tree for a tr..
7349 renderRow : function(cm, ds, rowIndex)
7351 var d = ds.getAt(rowIndex);
7355 cls : 'x-row-' + rowIndex,
7359 var cellObjects = [];
7361 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7362 var config = cm.config[i];
7364 var renderer = cm.getRenderer(i);
7368 if(typeof(renderer) !== 'undefined'){
7369 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7371 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7372 // and are rendered into the cells after the row is rendered - using the id for the element.
7374 if(typeof(value) === 'object'){
7384 rowIndex : rowIndex,
7389 this.fireEvent('rowclass', this, rowcfg);
7393 cls : rowcfg.rowClass + ' x-col-' + i,
7395 html: (typeof(value) === 'object') ? '' : value
7402 if(typeof(config.colspan) != 'undefined'){
7403 td.colspan = config.colspan;
7406 if(typeof(config.hidden) != 'undefined' && config.hidden){
7407 td.style += ' display:none;';
7410 if(typeof(config.align) != 'undefined' && config.align.length){
7411 td.style += ' text-align:' + config.align + ';';
7413 if(typeof(config.valign) != 'undefined' && config.valign.length){
7414 td.style += ' vertical-align:' + config.valign + ';';
7417 if(typeof(config.width) != 'undefined'){
7418 td.style += ' width:' + config.width + 'px;';
7421 if(typeof(config.cursor) != 'undefined'){
7422 td.style += ' cursor:' + config.cursor + ';';
7425 if(typeof(config.cls) != 'undefined'){
7426 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7429 ['xs','sm','md','lg'].map(function(size){
7431 if(typeof(config[size]) == 'undefined'){
7437 if (!config[size]) { // 0 = hidden
7438 // BS 4 '0' is treated as hide that column and below.
7439 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7443 td.cls += ' col-' + size + '-' + config[size] + (
7444 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7454 row.cellObjects = cellObjects;
7462 onBeforeLoad : function()
7471 this.el.select('tbody', true).first().dom.innerHTML = '';
7474 * Show or hide a row.
7475 * @param {Number} rowIndex to show or hide
7476 * @param {Boolean} state hide
7478 setRowVisibility : function(rowIndex, state)
7480 var bt = this.mainBody.dom;
7482 var rows = this.el.select('tbody > tr', true).elements;
7484 if(typeof(rows[rowIndex]) == 'undefined'){
7487 rows[rowIndex].dom.style.display = state ? '' : 'none';
7491 getSelectionModel : function(){
7493 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7495 return this.selModel;
7498 * Render the Roo.bootstrap object from renderder
7500 renderCellObject : function(r)
7504 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7506 var t = r.cfg.render(r.container);
7509 Roo.each(r.cfg.cn, function(c){
7511 container: t.getChildContainer(),
7514 _this.renderCellObject(child);
7519 getRowIndex : function(row)
7523 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7534 * Returns the grid's underlying element = used by panel.Grid
7535 * @return {Element} The element
7537 getGridEl : function(){
7541 * Forces a resize - used by panel.Grid
7542 * @return {Element} The element
7544 autoSize : function()
7546 //var ctr = Roo.get(this.container.dom.parentElement);
7547 var ctr = Roo.get(this.el.dom);
7549 var thd = this.getGridEl().select('thead',true).first();
7550 var tbd = this.getGridEl().select('tbody', true).first();
7551 var tfd = this.getGridEl().select('tfoot', true).first();
7553 var cw = ctr.getWidth();
7557 tbd.setWidth(ctr.getWidth());
7558 // if the body has a max height - and then scrolls - we should perhaps set up the height here
7559 // this needs fixing for various usage - currently only hydra job advers I think..
7561 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7563 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7566 cw = Math.max(cw, this.totalWidth);
7567 this.getGridEl().select('tr',true).setWidth(cw);
7568 // resize 'expandable coloumn?
7570 return; // we doe not have a view in this design..
7573 onBodyScroll: function()
7575 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7577 this.mainHead.setStyle({
7578 'position' : 'relative',
7579 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7585 var scrollHeight = this.mainBody.dom.scrollHeight;
7587 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7589 var height = this.mainBody.getHeight();
7591 if(scrollHeight - height == scrollTop) {
7593 var total = this.ds.getTotalCount();
7595 if(this.footer.cursor + this.footer.pageSize < total){
7597 this.footer.ds.load({
7599 start : this.footer.cursor + this.footer.pageSize,
7600 limit : this.footer.pageSize
7610 onHeaderChange : function()
7612 var header = this.renderHeader();
7613 var table = this.el.select('table', true).first();
7615 this.mainHead.remove();
7616 this.mainHead = table.createChild(header, this.mainBody, false);
7619 onHiddenChange : function(colModel, colIndex, hidden)
7621 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7622 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7624 this.CSS.updateRule(thSelector, "display", "");
7625 this.CSS.updateRule(tdSelector, "display", "");
7628 this.CSS.updateRule(thSelector, "display", "none");
7629 this.CSS.updateRule(tdSelector, "display", "none");
7632 this.onHeaderChange();
7636 setColumnWidth: function(col_index, width)
7638 // width = "md-2 xs-2..."
7639 if(!this.colModel.config[col_index]) {
7643 var w = width.split(" ");
7645 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7647 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7650 for(var j = 0; j < w.length; j++) {
7656 var size_cls = w[j].split("-");
7658 if(!Number.isInteger(size_cls[1] * 1)) {
7662 if(!this.colModel.config[col_index][size_cls[0]]) {
7666 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7670 h_row[0].classList.replace(
7671 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7672 "col-"+size_cls[0]+"-"+size_cls[1]
7675 for(var i = 0; i < rows.length; i++) {
7677 var size_cls = w[j].split("-");
7679 if(!Number.isInteger(size_cls[1] * 1)) {
7683 if(!this.colModel.config[col_index][size_cls[0]]) {
7687 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7691 rows[i].classList.replace(
7692 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7693 "col-"+size_cls[0]+"-"+size_cls[1]
7697 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7712 * @class Roo.bootstrap.TableCell
7713 * @extends Roo.bootstrap.Component
7714 * Bootstrap TableCell class
7715 * @cfg {String} html cell contain text
7716 * @cfg {String} cls cell class
7717 * @cfg {String} tag cell tag (td|th) default td
7718 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7719 * @cfg {String} align Aligns the content in a cell
7720 * @cfg {String} axis Categorizes cells
7721 * @cfg {String} bgcolor Specifies the background color of a cell
7722 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7723 * @cfg {Number} colspan Specifies the number of columns a cell should span
7724 * @cfg {String} headers Specifies one or more header cells a cell is related to
7725 * @cfg {Number} height Sets the height of a cell
7726 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7727 * @cfg {Number} rowspan Sets the number of rows a cell should span
7728 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7729 * @cfg {String} valign Vertical aligns the content in a cell
7730 * @cfg {Number} width Specifies the width of a cell
7733 * Create a new TableCell
7734 * @param {Object} config The config object
7737 Roo.bootstrap.TableCell = function(config){
7738 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7741 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7761 getAutoCreate : function(){
7762 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7782 cfg.align=this.align
7788 cfg.bgcolor=this.bgcolor
7791 cfg.charoff=this.charoff
7794 cfg.colspan=this.colspan
7797 cfg.headers=this.headers
7800 cfg.height=this.height
7803 cfg.nowrap=this.nowrap
7806 cfg.rowspan=this.rowspan
7809 cfg.scope=this.scope
7812 cfg.valign=this.valign
7815 cfg.width=this.width
7834 * @class Roo.bootstrap.TableRow
7835 * @extends Roo.bootstrap.Component
7836 * Bootstrap TableRow class
7837 * @cfg {String} cls row class
7838 * @cfg {String} align Aligns the content in a table row
7839 * @cfg {String} bgcolor Specifies a background color for a table row
7840 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7841 * @cfg {String} valign Vertical aligns the content in a table row
7844 * Create a new TableRow
7845 * @param {Object} config The config object
7848 Roo.bootstrap.TableRow = function(config){
7849 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7852 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7860 getAutoCreate : function(){
7861 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7871 cfg.align = this.align;
7874 cfg.bgcolor = this.bgcolor;
7877 cfg.charoff = this.charoff;
7880 cfg.valign = this.valign;
7898 * @class Roo.bootstrap.TableBody
7899 * @extends Roo.bootstrap.Component
7900 * Bootstrap TableBody class
7901 * @cfg {String} cls element class
7902 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7903 * @cfg {String} align Aligns the content inside the element
7904 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7905 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7908 * Create a new TableBody
7909 * @param {Object} config The config object
7912 Roo.bootstrap.TableBody = function(config){
7913 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7916 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7924 getAutoCreate : function(){
7925 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7939 cfg.align = this.align;
7942 cfg.charoff = this.charoff;
7945 cfg.valign = this.valign;
7952 // initEvents : function()
7959 // this.store = Roo.factory(this.store, Roo.data);
7960 // this.store.on('load', this.onLoad, this);
7962 // this.store.load();
7966 // onLoad: function ()
7968 // this.fireEvent('load', this);
7978 * Ext JS Library 1.1.1
7979 * Copyright(c) 2006-2007, Ext JS, LLC.
7981 * Originally Released Under LGPL - original licence link has changed is not relivant.
7984 * <script type="text/javascript">
7987 // as we use this in bootstrap.
7988 Roo.namespace('Roo.form');
7990 * @class Roo.form.Action
7991 * Internal Class used to handle form actions
7993 * @param {Roo.form.BasicForm} el The form element or its id
7994 * @param {Object} config Configuration options
7999 // define the action interface
8000 Roo.form.Action = function(form, options){
8002 this.options = options || {};
8005 * Client Validation Failed
8008 Roo.form.Action.CLIENT_INVALID = 'client';
8010 * Server Validation Failed
8013 Roo.form.Action.SERVER_INVALID = 'server';
8015 * Connect to Server Failed
8018 Roo.form.Action.CONNECT_FAILURE = 'connect';
8020 * Reading Data from Server Failed
8023 Roo.form.Action.LOAD_FAILURE = 'load';
8025 Roo.form.Action.prototype = {
8027 failureType : undefined,
8028 response : undefined,
8032 run : function(options){
8037 success : function(response){
8042 handleResponse : function(response){
8046 // default connection failure
8047 failure : function(response){
8049 this.response = response;
8050 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8051 this.form.afterAction(this, false);
8054 processResponse : function(response){
8055 this.response = response;
8056 if(!response.responseText){
8059 this.result = this.handleResponse(response);
8063 // utility functions used internally
8064 getUrl : function(appendParams){
8065 var url = this.options.url || this.form.url || this.form.el.dom.action;
8067 var p = this.getParams();
8069 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
8075 getMethod : function(){
8076 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
8079 getParams : function(){
8080 var bp = this.form.baseParams;
8081 var p = this.options.params;
8083 if(typeof p == "object"){
8084 p = Roo.urlEncode(Roo.applyIf(p, bp));
8085 }else if(typeof p == 'string' && bp){
8086 p += '&' + Roo.urlEncode(bp);
8089 p = Roo.urlEncode(bp);
8094 createCallback : function(){
8096 success: this.success,
8097 failure: this.failure,
8099 timeout: (this.form.timeout*1000),
8100 upload: this.form.fileUpload ? this.success : undefined
8105 Roo.form.Action.Submit = function(form, options){
8106 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
8109 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
8112 haveProgress : false,
8113 uploadComplete : false,
8115 // uploadProgress indicator.
8116 uploadProgress : function()
8118 if (!this.form.progressUrl) {
8122 if (!this.haveProgress) {
8123 Roo.MessageBox.progress("Uploading", "Uploading");
8125 if (this.uploadComplete) {
8126 Roo.MessageBox.hide();
8130 this.haveProgress = true;
8132 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
8134 var c = new Roo.data.Connection();
8136 url : this.form.progressUrl,
8141 success : function(req){
8142 //console.log(data);
8146 rdata = Roo.decode(req.responseText)
8148 Roo.log("Invalid data from server..");
8152 if (!rdata || !rdata.success) {
8154 Roo.MessageBox.alert(Roo.encode(rdata));
8157 var data = rdata.data;
8159 if (this.uploadComplete) {
8160 Roo.MessageBox.hide();
8165 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
8166 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
8169 this.uploadProgress.defer(2000,this);
8172 failure: function(data) {
8173 Roo.log('progress url failed ');
8184 // run get Values on the form, so it syncs any secondary forms.
8185 this.form.getValues();
8187 var o = this.options;
8188 var method = this.getMethod();
8189 var isPost = method == 'POST';
8190 if(o.clientValidation === false || this.form.isValid()){
8192 if (this.form.progressUrl) {
8193 this.form.findField('UPLOAD_IDENTIFIER').setValue(
8194 (new Date() * 1) + '' + Math.random());
8199 Roo.Ajax.request(Roo.apply(this.createCallback(), {
8200 form:this.form.el.dom,
8201 url:this.getUrl(!isPost),
8203 params:isPost ? this.getParams() : null,
8204 isUpload: this.form.fileUpload,
8205 formData : this.form.formData
8208 this.uploadProgress();
8210 }else if (o.clientValidation !== false){ // client validation failed
8211 this.failureType = Roo.form.Action.CLIENT_INVALID;
8212 this.form.afterAction(this, false);
8216 success : function(response)
8218 this.uploadComplete= true;
8219 if (this.haveProgress) {
8220 Roo.MessageBox.hide();
8224 var result = this.processResponse(response);
8225 if(result === true || result.success){
8226 this.form.afterAction(this, true);
8230 this.form.markInvalid(result.errors);
8231 this.failureType = Roo.form.Action.SERVER_INVALID;
8233 this.form.afterAction(this, false);
8235 failure : function(response)
8237 this.uploadComplete= true;
8238 if (this.haveProgress) {
8239 Roo.MessageBox.hide();
8242 this.response = response;
8243 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8244 this.form.afterAction(this, false);
8247 handleResponse : function(response){
8248 if(this.form.errorReader){
8249 var rs = this.form.errorReader.read(response);
8252 for(var i = 0, len = rs.records.length; i < len; i++) {
8253 var r = rs.records[i];
8257 if(errors.length < 1){
8261 success : rs.success,
8267 ret = Roo.decode(response.responseText);
8271 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8281 Roo.form.Action.Load = function(form, options){
8282 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8283 this.reader = this.form.reader;
8286 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8291 Roo.Ajax.request(Roo.apply(
8292 this.createCallback(), {
8293 method:this.getMethod(),
8294 url:this.getUrl(false),
8295 params:this.getParams()
8299 success : function(response){
8301 var result = this.processResponse(response);
8302 if(result === true || !result.success || !result.data){
8303 this.failureType = Roo.form.Action.LOAD_FAILURE;
8304 this.form.afterAction(this, false);
8307 this.form.clearInvalid();
8308 this.form.setValues(result.data);
8309 this.form.afterAction(this, true);
8312 handleResponse : function(response){
8313 if(this.form.reader){
8314 var rs = this.form.reader.read(response);
8315 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8317 success : rs.success,
8321 return Roo.decode(response.responseText);
8325 Roo.form.Action.ACTION_TYPES = {
8326 'load' : Roo.form.Action.Load,
8327 'submit' : Roo.form.Action.Submit
8336 * @class Roo.bootstrap.Form
8337 * @extends Roo.bootstrap.Component
8338 * Bootstrap Form class
8339 * @cfg {String} method GET | POST (default POST)
8340 * @cfg {String} labelAlign top | left (default top)
8341 * @cfg {String} align left | right - for navbars
8342 * @cfg {Boolean} loadMask load mask when submit (default true)
8347 * @param {Object} config The config object
8351 Roo.bootstrap.Form = function(config){
8353 Roo.bootstrap.Form.superclass.constructor.call(this, config);
8355 Roo.bootstrap.Form.popover.apply();
8359 * @event clientvalidation
8360 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8361 * @param {Form} this
8362 * @param {Boolean} valid true if the form has passed client-side validation
8364 clientvalidation: true,
8366 * @event beforeaction
8367 * Fires before any action is performed. Return false to cancel the action.
8368 * @param {Form} this
8369 * @param {Action} action The action to be performed
8373 * @event actionfailed
8374 * Fires when an action fails.
8375 * @param {Form} this
8376 * @param {Action} action The action that failed
8378 actionfailed : true,
8380 * @event actioncomplete
8381 * Fires when an action is completed.
8382 * @param {Form} this
8383 * @param {Action} action The action that completed
8385 actioncomplete : true
8389 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
8392 * @cfg {String} method
8393 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8398 * The URL to use for form actions if one isn't supplied in the action options.
8401 * @cfg {Boolean} fileUpload
8402 * Set to true if this form is a file upload.
8406 * @cfg {Object} baseParams
8407 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8411 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8415 * @cfg {Sting} align (left|right) for navbar forms
8420 activeAction : null,
8423 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8424 * element by passing it or its id or mask the form itself by passing in true.
8427 waitMsgTarget : false,
8432 * @cfg {Boolean} errorMask (true|false) default false
8437 * @cfg {Number} maskOffset Default 100
8442 * @cfg {Boolean} maskBody
8446 getAutoCreate : function(){
8450 method : this.method || 'POST',
8451 id : this.id || Roo.id(),
8454 if (this.parent().xtype.match(/^Nav/)) {
8455 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8459 if (this.labelAlign == 'left' ) {
8460 cfg.cls += ' form-horizontal';
8466 initEvents : function()
8468 this.el.on('submit', this.onSubmit, this);
8469 // this was added as random key presses on the form where triggering form submit.
8470 this.el.on('keypress', function(e) {
8471 if (e.getCharCode() != 13) {
8474 // we might need to allow it for textareas.. and some other items.
8475 // check e.getTarget().
8477 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8481 Roo.log("keypress blocked");
8489 onSubmit : function(e){
8494 * Returns true if client-side validation on the form is successful.
8497 isValid : function(){
8498 var items = this.getItems();
8502 items.each(function(f){
8508 Roo.log('invalid field: ' + f.name);
8512 if(!target && f.el.isVisible(true)){
8518 if(this.errorMask && !valid){
8519 Roo.bootstrap.Form.popover.mask(this, target);
8526 * Returns true if any fields in this form have changed since their original load.
8529 isDirty : function(){
8531 var items = this.getItems();
8532 items.each(function(f){
8542 * Performs a predefined action (submit or load) or custom actions you define on this form.
8543 * @param {String} actionName The name of the action type
8544 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8545 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8546 * accept other config options):
8548 Property Type Description
8549 ---------------- --------------- ----------------------------------------------------------------------------------
8550 url String The url for the action (defaults to the form's url)
8551 method String The form method to use (defaults to the form's method, or POST if not defined)
8552 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8553 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8554 validate the form on the client (defaults to false)
8556 * @return {BasicForm} this
8558 doAction : function(action, options){
8559 if(typeof action == 'string'){
8560 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8562 if(this.fireEvent('beforeaction', this, action) !== false){
8563 this.beforeAction(action);
8564 action.run.defer(100, action);
8570 beforeAction : function(action){
8571 var o = action.options;
8576 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8578 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8581 // not really supported yet.. ??
8583 //if(this.waitMsgTarget === true){
8584 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8585 //}else if(this.waitMsgTarget){
8586 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8587 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8589 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8595 afterAction : function(action, success){
8596 this.activeAction = null;
8597 var o = action.options;
8602 Roo.get(document.body).unmask();
8608 //if(this.waitMsgTarget === true){
8609 // this.el.unmask();
8610 //}else if(this.waitMsgTarget){
8611 // this.waitMsgTarget.unmask();
8613 // Roo.MessageBox.updateProgress(1);
8614 // Roo.MessageBox.hide();
8621 Roo.callback(o.success, o.scope, [this, action]);
8622 this.fireEvent('actioncomplete', this, action);
8626 // failure condition..
8627 // we have a scenario where updates need confirming.
8628 // eg. if a locking scenario exists..
8629 // we look for { errors : { needs_confirm : true }} in the response.
8631 (typeof(action.result) != 'undefined') &&
8632 (typeof(action.result.errors) != 'undefined') &&
8633 (typeof(action.result.errors.needs_confirm) != 'undefined')
8636 Roo.log("not supported yet");
8639 Roo.MessageBox.confirm(
8640 "Change requires confirmation",
8641 action.result.errorMsg,
8646 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8656 Roo.callback(o.failure, o.scope, [this, action]);
8657 // show an error message if no failed handler is set..
8658 if (!this.hasListener('actionfailed')) {
8659 Roo.log("need to add dialog support");
8661 Roo.MessageBox.alert("Error",
8662 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8663 action.result.errorMsg :
8664 "Saving Failed, please check your entries or try again"
8669 this.fireEvent('actionfailed', this, action);
8674 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8675 * @param {String} id The value to search for
8678 findField : function(id){
8679 var items = this.getItems();
8680 var field = items.get(id);
8682 items.each(function(f){
8683 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8690 return field || null;
8693 * Mark fields in this form invalid in bulk.
8694 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8695 * @return {BasicForm} this
8697 markInvalid : function(errors){
8698 if(errors instanceof Array){
8699 for(var i = 0, len = errors.length; i < len; i++){
8700 var fieldError = errors[i];
8701 var f = this.findField(fieldError.id);
8703 f.markInvalid(fieldError.msg);
8709 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8710 field.markInvalid(errors[id]);
8714 //Roo.each(this.childForms || [], function (f) {
8715 // f.markInvalid(errors);
8722 * Set values for fields in this form in bulk.
8723 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8724 * @return {BasicForm} this
8726 setValues : function(values){
8727 if(values instanceof Array){ // array of objects
8728 for(var i = 0, len = values.length; i < len; i++){
8730 var f = this.findField(v.id);
8732 f.setValue(v.value);
8733 if(this.trackResetOnLoad){
8734 f.originalValue = f.getValue();
8738 }else{ // object hash
8741 if(typeof values[id] != 'function' && (field = this.findField(id))){
8743 if (field.setFromData &&
8745 field.displayField &&
8746 // combos' with local stores can
8747 // be queried via setValue()
8748 // to set their value..
8749 (field.store && !field.store.isLocal)
8753 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8754 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8755 field.setFromData(sd);
8757 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8759 field.setFromData(values);
8762 field.setValue(values[id]);
8766 if(this.trackResetOnLoad){
8767 field.originalValue = field.getValue();
8773 //Roo.each(this.childForms || [], function (f) {
8774 // f.setValues(values);
8781 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8782 * they are returned as an array.
8783 * @param {Boolean} asString
8786 getValues : function(asString){
8787 //if (this.childForms) {
8788 // copy values from the child forms
8789 // Roo.each(this.childForms, function (f) {
8790 // this.setValues(f.getValues());
8796 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8797 if(asString === true){
8800 return Roo.urlDecode(fs);
8804 * Returns the fields in this form as an object with key/value pairs.
8805 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8808 getFieldValues : function(with_hidden)
8810 var items = this.getItems();
8812 items.each(function(f){
8818 var v = f.getValue();
8820 if (f.inputType =='radio') {
8821 if (typeof(ret[f.getName()]) == 'undefined') {
8822 ret[f.getName()] = ''; // empty..
8825 if (!f.el.dom.checked) {
8833 if(f.xtype == 'MoneyField'){
8834 ret[f.currencyName] = f.getCurrency();
8837 // not sure if this supported any more..
8838 if ((typeof(v) == 'object') && f.getRawValue) {
8839 v = f.getRawValue() ; // dates..
8841 // combo boxes where name != hiddenName...
8842 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8843 ret[f.name] = f.getRawValue();
8845 ret[f.getName()] = v;
8852 * Clears all invalid messages in this form.
8853 * @return {BasicForm} this
8855 clearInvalid : function(){
8856 var items = this.getItems();
8858 items.each(function(f){
8867 * @return {BasicForm} this
8870 var items = this.getItems();
8871 items.each(function(f){
8875 Roo.each(this.childForms || [], function (f) {
8883 getItems : function()
8885 var r=new Roo.util.MixedCollection(false, function(o){
8886 return o.id || (o.id = Roo.id());
8888 var iter = function(el) {
8895 Roo.each(el.items,function(e) {
8904 hideFields : function(items)
8906 Roo.each(items, function(i){
8908 var f = this.findField(i);
8919 showFields : function(items)
8921 Roo.each(items, function(i){
8923 var f = this.findField(i);
8936 Roo.apply(Roo.bootstrap.Form, {
8963 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8964 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8965 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8966 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8969 this.maskEl.top.enableDisplayMode("block");
8970 this.maskEl.left.enableDisplayMode("block");
8971 this.maskEl.bottom.enableDisplayMode("block");
8972 this.maskEl.right.enableDisplayMode("block");
8974 this.toolTip = new Roo.bootstrap.Tooltip({
8975 cls : 'roo-form-error-popover',
8977 'left' : ['r-l', [-2,0], 'right'],
8978 'right' : ['l-r', [2,0], 'left'],
8979 'bottom' : ['tl-bl', [0,2], 'top'],
8980 'top' : [ 'bl-tl', [0,-2], 'bottom']
8984 this.toolTip.render(Roo.get(document.body));
8986 this.toolTip.el.enableDisplayMode("block");
8988 Roo.get(document.body).on('click', function(){
8992 Roo.get(document.body).on('touchstart', function(){
8996 this.isApplied = true
8999 mask : function(form, target)
9003 this.target = target;
9005 if(!this.form.errorMask || !target.el){
9009 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9011 Roo.log(scrollable);
9013 var ot = this.target.el.calcOffsetsTo(scrollable);
9015 var scrollTo = ot[1] - this.form.maskOffset;
9017 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9019 scrollable.scrollTo('top', scrollTo);
9021 var box = this.target.el.getBox();
9023 var zIndex = Roo.bootstrap.Modal.zIndex++;
9026 this.maskEl.top.setStyle('position', 'absolute');
9027 this.maskEl.top.setStyle('z-index', zIndex);
9028 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9029 this.maskEl.top.setLeft(0);
9030 this.maskEl.top.setTop(0);
9031 this.maskEl.top.show();
9033 this.maskEl.left.setStyle('position', 'absolute');
9034 this.maskEl.left.setStyle('z-index', zIndex);
9035 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9036 this.maskEl.left.setLeft(0);
9037 this.maskEl.left.setTop(box.y - this.padding);
9038 this.maskEl.left.show();
9040 this.maskEl.bottom.setStyle('position', 'absolute');
9041 this.maskEl.bottom.setStyle('z-index', zIndex);
9042 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9043 this.maskEl.bottom.setLeft(0);
9044 this.maskEl.bottom.setTop(box.bottom + this.padding);
9045 this.maskEl.bottom.show();
9047 this.maskEl.right.setStyle('position', 'absolute');
9048 this.maskEl.right.setStyle('z-index', zIndex);
9049 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9050 this.maskEl.right.setLeft(box.right + this.padding);
9051 this.maskEl.right.setTop(box.y - this.padding);
9052 this.maskEl.right.show();
9054 this.toolTip.bindEl = this.target.el;
9056 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9058 var tip = this.target.blankText;
9060 if(this.target.getValue() !== '' ) {
9062 if (this.target.invalidText.length) {
9063 tip = this.target.invalidText;
9064 } else if (this.target.regexText.length){
9065 tip = this.target.regexText;
9069 this.toolTip.show(tip);
9071 this.intervalID = window.setInterval(function() {
9072 Roo.bootstrap.Form.popover.unmask();
9075 window.onwheel = function(){ return false;};
9077 (function(){ this.isMasked = true; }).defer(500, this);
9083 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
9087 this.maskEl.top.setStyle('position', 'absolute');
9088 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
9089 this.maskEl.top.hide();
9091 this.maskEl.left.setStyle('position', 'absolute');
9092 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
9093 this.maskEl.left.hide();
9095 this.maskEl.bottom.setStyle('position', 'absolute');
9096 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
9097 this.maskEl.bottom.hide();
9099 this.maskEl.right.setStyle('position', 'absolute');
9100 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
9101 this.maskEl.right.hide();
9103 this.toolTip.hide();
9105 this.toolTip.el.hide();
9107 window.onwheel = function(){ return true;};
9109 if(this.intervalID){
9110 window.clearInterval(this.intervalID);
9111 this.intervalID = false;
9114 this.isMasked = false;
9124 * Ext JS Library 1.1.1
9125 * Copyright(c) 2006-2007, Ext JS, LLC.
9127 * Originally Released Under LGPL - original licence link has changed is not relivant.
9130 * <script type="text/javascript">
9133 * @class Roo.form.VTypes
9134 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
9137 Roo.form.VTypes = function(){
9138 // closure these in so they are only created once.
9139 var alpha = /^[a-zA-Z_]+$/;
9140 var alphanum = /^[a-zA-Z0-9_]+$/;
9141 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
9142 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
9144 // All these messages and functions are configurable
9147 * The function used to validate email addresses
9148 * @param {String} value The email address
9150 'email' : function(v){
9151 return email.test(v);
9154 * The error text to display when the email validation function returns false
9157 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
9159 * The keystroke filter mask to be applied on email input
9162 'emailMask' : /[a-z0-9_\.\-@]/i,
9165 * The function used to validate URLs
9166 * @param {String} value The URL
9168 'url' : function(v){
9172 * The error text to display when the url validation function returns false
9175 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
9178 * The function used to validate alpha values
9179 * @param {String} value The value
9181 'alpha' : function(v){
9182 return alpha.test(v);
9185 * The error text to display when the alpha validation function returns false
9188 'alphaText' : 'This field should only contain letters and _',
9190 * The keystroke filter mask to be applied on alpha input
9193 'alphaMask' : /[a-z_]/i,
9196 * The function used to validate alphanumeric values
9197 * @param {String} value The value
9199 'alphanum' : function(v){
9200 return alphanum.test(v);
9203 * The error text to display when the alphanumeric validation function returns false
9206 'alphanumText' : 'This field should only contain letters, numbers and _',
9208 * The keystroke filter mask to be applied on alphanumeric input
9211 'alphanumMask' : /[a-z0-9_]/i
9221 * @class Roo.bootstrap.Input
9222 * @extends Roo.bootstrap.Component
9223 * Bootstrap Input class
9224 * @cfg {Boolean} disabled is it disabled
9225 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9226 * @cfg {String} name name of the input
9227 * @cfg {string} fieldLabel - the label associated
9228 * @cfg {string} placeholder - placeholder to put in text.
9229 * @cfg {string} before - input group add on before
9230 * @cfg {string} after - input group add on after
9231 * @cfg {string} size - (lg|sm) or leave empty..
9232 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9233 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9234 * @cfg {Number} md colspan out of 12 for computer-sized screens
9235 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9236 * @cfg {string} value default value of the input
9237 * @cfg {Number} labelWidth set the width of label
9238 * @cfg {Number} labellg set the width of label (1-12)
9239 * @cfg {Number} labelmd set the width of label (1-12)
9240 * @cfg {Number} labelsm set the width of label (1-12)
9241 * @cfg {Number} labelxs set the width of label (1-12)
9242 * @cfg {String} labelAlign (top|left)
9243 * @cfg {Boolean} readOnly Specifies that the field should be read-only
9244 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9245 * @cfg {String} indicatorpos (left|right) default left
9246 * @cfg {String} capture (user|camera) use for file input only. (default empty)
9247 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9249 * @cfg {String} align (left|center|right) Default left
9250 * @cfg {Boolean} forceFeedback (true|false) Default false
9253 * Create a new Input
9254 * @param {Object} config The config object
9257 Roo.bootstrap.Input = function(config){
9259 Roo.bootstrap.Input.superclass.constructor.call(this, config);
9264 * Fires when this field receives input focus.
9265 * @param {Roo.form.Field} this
9270 * Fires when this field loses input focus.
9271 * @param {Roo.form.Field} this
9276 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
9277 * {@link Roo.EventObject#getKey} to determine which key was pressed.
9278 * @param {Roo.form.Field} this
9279 * @param {Roo.EventObject} e The event object
9284 * Fires just before the field blurs if the field value has changed.
9285 * @param {Roo.form.Field} this
9286 * @param {Mixed} newValue The new value
9287 * @param {Mixed} oldValue The original value
9292 * Fires after the field has been marked as invalid.
9293 * @param {Roo.form.Field} this
9294 * @param {String} msg The validation message
9299 * Fires after the field has been validated with no errors.
9300 * @param {Roo.form.Field} this
9305 * Fires after the key up
9306 * @param {Roo.form.Field} this
9307 * @param {Roo.EventObject} e The event Object
9313 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
9315 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9316 automatic validation (defaults to "keyup").
9318 validationEvent : "keyup",
9320 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9322 validateOnBlur : true,
9324 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9326 validationDelay : 250,
9328 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9330 focusClass : "x-form-focus", // not needed???
9334 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9336 invalidClass : "has-warning",
9339 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9341 validClass : "has-success",
9344 * @cfg {Boolean} hasFeedback (true|false) default true
9349 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9351 invalidFeedbackClass : "glyphicon-warning-sign",
9354 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9356 validFeedbackClass : "glyphicon-ok",
9359 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9361 selectOnFocus : false,
9364 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9368 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9373 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9375 disableKeyFilter : false,
9378 * @cfg {Boolean} disabled True to disable the field (defaults to false).
9382 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9386 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9388 blankText : "Please complete this mandatory field",
9391 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9395 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9397 maxLength : Number.MAX_VALUE,
9399 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9401 minLengthText : "The minimum length for this field is {0}",
9403 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9405 maxLengthText : "The maximum length for this field is {0}",
9409 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9410 * If available, this function will be called only after the basic validators all return true, and will be passed the
9411 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9415 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9416 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9417 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9421 * @cfg {String} regexText -- Depricated - use Invalid Text
9426 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9432 autocomplete: false,
9451 formatedValue : false,
9452 forceFeedback : false,
9454 indicatorpos : 'left',
9464 parentLabelAlign : function()
9467 while (parent.parent()) {
9468 parent = parent.parent();
9469 if (typeof(parent.labelAlign) !='undefined') {
9470 return parent.labelAlign;
9477 getAutoCreate : function()
9479 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9485 if(this.inputType != 'hidden'){
9486 cfg.cls = 'form-group' //input-group
9492 type : this.inputType,
9494 cls : 'form-control',
9495 placeholder : this.placeholder || '',
9496 autocomplete : this.autocomplete || 'new-password'
9499 if(this.capture.length){
9500 input.capture = this.capture;
9503 if(this.accept.length){
9504 input.accept = this.accept + "/*";
9508 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9511 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9512 input.maxLength = this.maxLength;
9515 if (this.disabled) {
9516 input.disabled=true;
9519 if (this.readOnly) {
9520 input.readonly=true;
9524 input.name = this.name;
9528 input.cls += ' input-' + this.size;
9532 ['xs','sm','md','lg'].map(function(size){
9533 if (settings[size]) {
9534 cfg.cls += ' col-' + size + '-' + settings[size];
9538 var inputblock = input;
9542 cls: 'glyphicon form-control-feedback'
9545 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9548 cls : 'has-feedback',
9556 if (this.before || this.after) {
9559 cls : 'input-group',
9563 if (this.before && typeof(this.before) == 'string') {
9565 inputblock.cn.push({
9567 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9571 if (this.before && typeof(this.before) == 'object') {
9572 this.before = Roo.factory(this.before);
9574 inputblock.cn.push({
9576 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9577 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9581 inputblock.cn.push(input);
9583 if (this.after && typeof(this.after) == 'string') {
9584 inputblock.cn.push({
9586 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9590 if (this.after && typeof(this.after) == 'object') {
9591 this.after = Roo.factory(this.after);
9593 inputblock.cn.push({
9595 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9596 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9600 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9601 inputblock.cls += ' has-feedback';
9602 inputblock.cn.push(feedback);
9607 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9608 tooltip : 'This field is required'
9610 if (Roo.bootstrap.version == 4) {
9613 style : 'display-none'
9616 if (align ==='left' && this.fieldLabel.length) {
9618 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
9625 cls : 'control-label col-form-label',
9626 html : this.fieldLabel
9637 var labelCfg = cfg.cn[1];
9638 var contentCfg = cfg.cn[2];
9640 if(this.indicatorpos == 'right'){
9645 cls : 'control-label col-form-label',
9649 html : this.fieldLabel
9663 labelCfg = cfg.cn[0];
9664 contentCfg = cfg.cn[1];
9668 if(this.labelWidth > 12){
9669 labelCfg.style = "width: " + this.labelWidth + 'px';
9672 if(this.labelWidth < 13 && this.labelmd == 0){
9673 this.labelmd = this.labelWidth;
9676 if(this.labellg > 0){
9677 labelCfg.cls += ' col-lg-' + this.labellg;
9678 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9681 if(this.labelmd > 0){
9682 labelCfg.cls += ' col-md-' + this.labelmd;
9683 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9686 if(this.labelsm > 0){
9687 labelCfg.cls += ' col-sm-' + this.labelsm;
9688 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9691 if(this.labelxs > 0){
9692 labelCfg.cls += ' col-xs-' + this.labelxs;
9693 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9697 } else if ( this.fieldLabel.length) {
9702 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9703 tooltip : 'This field is required'
9707 //cls : 'input-group-addon',
9708 html : this.fieldLabel
9716 if(this.indicatorpos == 'right'){
9721 //cls : 'input-group-addon',
9722 html : this.fieldLabel
9727 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9728 tooltip : 'This field is required'
9748 if (this.parentType === 'Navbar' && this.parent().bar) {
9749 cfg.cls += ' navbar-form';
9752 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9753 // on BS4 we do this only if not form
9754 cfg.cls += ' navbar-form';
9762 * return the real input element.
9764 inputEl: function ()
9766 return this.el.select('input.form-control',true).first();
9769 tooltipEl : function()
9771 return this.inputEl();
9774 indicatorEl : function()
9776 if (Roo.bootstrap.version == 4) {
9777 return false; // not enabled in v4 yet.
9780 var indicator = this.el.select('i.roo-required-indicator',true).first();
9790 setDisabled : function(v)
9792 var i = this.inputEl().dom;
9794 i.removeAttribute('disabled');
9798 i.setAttribute('disabled','true');
9800 initEvents : function()
9803 this.inputEl().on("keydown" , this.fireKey, this);
9804 this.inputEl().on("focus", this.onFocus, this);
9805 this.inputEl().on("blur", this.onBlur, this);
9807 this.inputEl().relayEvent('keyup', this);
9809 this.indicator = this.indicatorEl();
9812 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9815 // reference to original value for reset
9816 this.originalValue = this.getValue();
9817 //Roo.form.TextField.superclass.initEvents.call(this);
9818 if(this.validationEvent == 'keyup'){
9819 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9820 this.inputEl().on('keyup', this.filterValidation, this);
9822 else if(this.validationEvent !== false){
9823 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9826 if(this.selectOnFocus){
9827 this.on("focus", this.preFocus, this);
9830 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9831 this.inputEl().on("keypress", this.filterKeys, this);
9833 this.inputEl().relayEvent('keypress', this);
9836 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9837 this.el.on("click", this.autoSize, this);
9840 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9841 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9844 if (typeof(this.before) == 'object') {
9845 this.before.render(this.el.select('.roo-input-before',true).first());
9847 if (typeof(this.after) == 'object') {
9848 this.after.render(this.el.select('.roo-input-after',true).first());
9851 this.inputEl().on('change', this.onChange, this);
9854 filterValidation : function(e){
9855 if(!e.isNavKeyPress()){
9856 this.validationTask.delay(this.validationDelay);
9860 * Validates the field value
9861 * @return {Boolean} True if the value is valid, else false
9863 validate : function(){
9864 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9865 if(this.disabled || this.validateValue(this.getRawValue())){
9876 * Validates a value according to the field's validation rules and marks the field as invalid
9877 * if the validation fails
9878 * @param {Mixed} value The value to validate
9879 * @return {Boolean} True if the value is valid, else false
9881 validateValue : function(value)
9883 if(this.getVisibilityEl().hasClass('hidden')){
9887 if(value.length < 1) { // if it's blank
9888 if(this.allowBlank){
9894 if(value.length < this.minLength){
9897 if(value.length > this.maxLength){
9901 var vt = Roo.form.VTypes;
9902 if(!vt[this.vtype](value, this)){
9906 if(typeof this.validator == "function"){
9907 var msg = this.validator(value);
9911 if (typeof(msg) == 'string') {
9912 this.invalidText = msg;
9916 if(this.regex && !this.regex.test(value)){
9924 fireKey : function(e){
9925 //Roo.log('field ' + e.getKey());
9926 if(e.isNavKeyPress()){
9927 this.fireEvent("specialkey", this, e);
9930 focus : function (selectText){
9932 this.inputEl().focus();
9933 if(selectText === true){
9934 this.inputEl().dom.select();
9940 onFocus : function(){
9941 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9942 // this.el.addClass(this.focusClass);
9945 this.hasFocus = true;
9946 this.startValue = this.getValue();
9947 this.fireEvent("focus", this);
9951 beforeBlur : Roo.emptyFn,
9955 onBlur : function(){
9957 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9958 //this.el.removeClass(this.focusClass);
9960 this.hasFocus = false;
9961 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9964 var v = this.getValue();
9965 if(String(v) !== String(this.startValue)){
9966 this.fireEvent('change', this, v, this.startValue);
9968 this.fireEvent("blur", this);
9971 onChange : function(e)
9973 var v = this.getValue();
9974 if(String(v) !== String(this.startValue)){
9975 this.fireEvent('change', this, v, this.startValue);
9981 * Resets the current field value to the originally loaded value and clears any validation messages
9984 this.setValue(this.originalValue);
9988 * Returns the name of the field
9989 * @return {Mixed} name The name field
9991 getName: function(){
9995 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9996 * @return {Mixed} value The field value
9998 getValue : function(){
10000 var v = this.inputEl().getValue();
10005 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
10006 * @return {Mixed} value The field value
10008 getRawValue : function(){
10009 var v = this.inputEl().getValue();
10015 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
10016 * @param {Mixed} value The value to set
10018 setRawValue : function(v){
10019 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10022 selectText : function(start, end){
10023 var v = this.getRawValue();
10025 start = start === undefined ? 0 : start;
10026 end = end === undefined ? v.length : end;
10027 var d = this.inputEl().dom;
10028 if(d.setSelectionRange){
10029 d.setSelectionRange(start, end);
10030 }else if(d.createTextRange){
10031 var range = d.createTextRange();
10032 range.moveStart("character", start);
10033 range.moveEnd("character", v.length-end);
10040 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
10041 * @param {Mixed} value The value to set
10043 setValue : function(v){
10046 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10052 processValue : function(value){
10053 if(this.stripCharsRe){
10054 var newValue = value.replace(this.stripCharsRe, '');
10055 if(newValue !== value){
10056 this.setRawValue(newValue);
10063 preFocus : function(){
10065 if(this.selectOnFocus){
10066 this.inputEl().dom.select();
10069 filterKeys : function(e){
10070 var k = e.getKey();
10071 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
10074 var c = e.getCharCode(), cc = String.fromCharCode(c);
10075 if(Roo.isIE && (e.isSpecialKey() || !cc)){
10078 if(!this.maskRe.test(cc)){
10083 * Clear any invalid styles/messages for this field
10085 clearInvalid : function(){
10087 if(!this.el || this.preventMark){ // not rendered
10092 this.el.removeClass([this.invalidClass, 'is-invalid']);
10094 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10096 var feedback = this.el.select('.form-control-feedback', true).first();
10099 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10104 if(this.indicator){
10105 this.indicator.removeClass('visible');
10106 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10109 this.fireEvent('valid', this);
10113 * Mark this field as valid
10115 markValid : function()
10117 if(!this.el || this.preventMark){ // not rendered...
10121 this.el.removeClass([this.invalidClass, this.validClass]);
10122 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10124 var feedback = this.el.select('.form-control-feedback', true).first();
10127 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10130 if(this.indicator){
10131 this.indicator.removeClass('visible');
10132 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10139 if(this.allowBlank && !this.getRawValue().length){
10142 if (Roo.bootstrap.version == 3) {
10143 this.el.addClass(this.validClass);
10145 this.inputEl().addClass('is-valid');
10148 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10150 var feedback = this.el.select('.form-control-feedback', true).first();
10153 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10154 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10159 this.fireEvent('valid', this);
10163 * Mark this field as invalid
10164 * @param {String} msg The validation message
10166 markInvalid : function(msg)
10168 if(!this.el || this.preventMark){ // not rendered
10172 this.el.removeClass([this.invalidClass, this.validClass]);
10173 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10175 var feedback = this.el.select('.form-control-feedback', true).first();
10178 this.el.select('.form-control-feedback', true).first().removeClass(
10179 [this.invalidFeedbackClass, this.validFeedbackClass]);
10186 if(this.allowBlank && !this.getRawValue().length){
10190 if(this.indicator){
10191 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10192 this.indicator.addClass('visible');
10194 if (Roo.bootstrap.version == 3) {
10195 this.el.addClass(this.invalidClass);
10197 this.inputEl().addClass('is-invalid');
10202 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10204 var feedback = this.el.select('.form-control-feedback', true).first();
10207 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10209 if(this.getValue().length || this.forceFeedback){
10210 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10217 this.fireEvent('invalid', this, msg);
10220 SafariOnKeyDown : function(event)
10222 // this is a workaround for a password hang bug on chrome/ webkit.
10223 if (this.inputEl().dom.type != 'password') {
10227 var isSelectAll = false;
10229 if(this.inputEl().dom.selectionEnd > 0){
10230 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10232 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10233 event.preventDefault();
10238 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10240 event.preventDefault();
10241 // this is very hacky as keydown always get's upper case.
10243 var cc = String.fromCharCode(event.getCharCode());
10244 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
10248 adjustWidth : function(tag, w){
10249 tag = tag.toLowerCase();
10250 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10251 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10252 if(tag == 'input'){
10255 if(tag == 'textarea'){
10258 }else if(Roo.isOpera){
10259 if(tag == 'input'){
10262 if(tag == 'textarea'){
10270 setFieldLabel : function(v)
10272 if(!this.rendered){
10276 if(this.indicatorEl()){
10277 var ar = this.el.select('label > span',true);
10279 if (ar.elements.length) {
10280 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10281 this.fieldLabel = v;
10285 var br = this.el.select('label',true);
10287 if(br.elements.length) {
10288 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10289 this.fieldLabel = v;
10293 Roo.log('Cannot Found any of label > span || label in input');
10297 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10298 this.fieldLabel = v;
10313 * @class Roo.bootstrap.TextArea
10314 * @extends Roo.bootstrap.Input
10315 * Bootstrap TextArea class
10316 * @cfg {Number} cols Specifies the visible width of a text area
10317 * @cfg {Number} rows Specifies the visible number of lines in a text area
10318 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10319 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10320 * @cfg {string} html text
10323 * Create a new TextArea
10324 * @param {Object} config The config object
10327 Roo.bootstrap.TextArea = function(config){
10328 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10332 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
10342 getAutoCreate : function(){
10344 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10350 if(this.inputType != 'hidden'){
10351 cfg.cls = 'form-group' //input-group
10359 value : this.value || '',
10360 html: this.html || '',
10361 cls : 'form-control',
10362 placeholder : this.placeholder || ''
10366 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10367 input.maxLength = this.maxLength;
10371 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10375 input.cols = this.cols;
10378 if (this.readOnly) {
10379 input.readonly = true;
10383 input.name = this.name;
10387 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10391 ['xs','sm','md','lg'].map(function(size){
10392 if (settings[size]) {
10393 cfg.cls += ' col-' + size + '-' + settings[size];
10397 var inputblock = input;
10399 if(this.hasFeedback && !this.allowBlank){
10403 cls: 'glyphicon form-control-feedback'
10407 cls : 'has-feedback',
10416 if (this.before || this.after) {
10419 cls : 'input-group',
10423 inputblock.cn.push({
10425 cls : 'input-group-addon',
10430 inputblock.cn.push(input);
10432 if(this.hasFeedback && !this.allowBlank){
10433 inputblock.cls += ' has-feedback';
10434 inputblock.cn.push(feedback);
10438 inputblock.cn.push({
10440 cls : 'input-group-addon',
10447 if (align ==='left' && this.fieldLabel.length) {
10452 cls : 'control-label',
10453 html : this.fieldLabel
10464 if(this.labelWidth > 12){
10465 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10468 if(this.labelWidth < 13 && this.labelmd == 0){
10469 this.labelmd = this.labelWidth;
10472 if(this.labellg > 0){
10473 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10474 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10477 if(this.labelmd > 0){
10478 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10479 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10482 if(this.labelsm > 0){
10483 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10484 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10487 if(this.labelxs > 0){
10488 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10489 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10492 } else if ( this.fieldLabel.length) {
10497 //cls : 'input-group-addon',
10498 html : this.fieldLabel
10516 if (this.disabled) {
10517 input.disabled=true;
10524 * return the real textarea element.
10526 inputEl: function ()
10528 return this.el.select('textarea.form-control',true).first();
10532 * Clear any invalid styles/messages for this field
10534 clearInvalid : function()
10537 if(!this.el || this.preventMark){ // not rendered
10541 var label = this.el.select('label', true).first();
10542 var icon = this.el.select('i.fa-star', true).first();
10547 this.el.removeClass( this.validClass);
10548 this.inputEl().removeClass('is-invalid');
10550 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10552 var feedback = this.el.select('.form-control-feedback', true).first();
10555 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10560 this.fireEvent('valid', this);
10564 * Mark this field as valid
10566 markValid : function()
10568 if(!this.el || this.preventMark){ // not rendered
10572 this.el.removeClass([this.invalidClass, this.validClass]);
10573 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10575 var feedback = this.el.select('.form-control-feedback', true).first();
10578 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10581 if(this.disabled || this.allowBlank){
10585 var label = this.el.select('label', true).first();
10586 var icon = this.el.select('i.fa-star', true).first();
10591 if (Roo.bootstrap.version == 3) {
10592 this.el.addClass(this.validClass);
10594 this.inputEl().addClass('is-valid');
10598 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10600 var feedback = this.el.select('.form-control-feedback', true).first();
10603 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10604 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10609 this.fireEvent('valid', this);
10613 * Mark this field as invalid
10614 * @param {String} msg The validation message
10616 markInvalid : function(msg)
10618 if(!this.el || this.preventMark){ // not rendered
10622 this.el.removeClass([this.invalidClass, this.validClass]);
10623 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10625 var feedback = this.el.select('.form-control-feedback', true).first();
10628 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10631 if(this.disabled || this.allowBlank){
10635 var label = this.el.select('label', true).first();
10636 var icon = this.el.select('i.fa-star', true).first();
10638 if(!this.getValue().length && label && !icon){
10639 this.el.createChild({
10641 cls : 'text-danger fa fa-lg fa-star',
10642 tooltip : 'This field is required',
10643 style : 'margin-right:5px;'
10647 if (Roo.bootstrap.version == 3) {
10648 this.el.addClass(this.invalidClass);
10650 this.inputEl().addClass('is-invalid');
10653 // fixme ... this may be depricated need to test..
10654 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10656 var feedback = this.el.select('.form-control-feedback', true).first();
10659 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10661 if(this.getValue().length || this.forceFeedback){
10662 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10669 this.fireEvent('invalid', this, msg);
10677 * trigger field - base class for combo..
10682 * @class Roo.bootstrap.TriggerField
10683 * @extends Roo.bootstrap.Input
10684 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10685 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10686 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10687 * for which you can provide a custom implementation. For example:
10689 var trigger = new Roo.bootstrap.TriggerField();
10690 trigger.onTriggerClick = myTriggerFn;
10691 trigger.applyTo('my-field');
10694 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10695 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10696 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10697 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10698 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10701 * Create a new TriggerField.
10702 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10703 * to the base TextField)
10705 Roo.bootstrap.TriggerField = function(config){
10706 this.mimicing = false;
10707 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10710 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10712 * @cfg {String} triggerClass A CSS class to apply to the trigger
10715 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10720 * @cfg {Boolean} removable (true|false) special filter default false
10724 /** @cfg {Boolean} grow @hide */
10725 /** @cfg {Number} growMin @hide */
10726 /** @cfg {Number} growMax @hide */
10732 autoSize: Roo.emptyFn,
10736 deferHeight : true,
10739 actionMode : 'wrap',
10744 getAutoCreate : function(){
10746 var align = this.labelAlign || this.parentLabelAlign();
10751 cls: 'form-group' //input-group
10758 type : this.inputType,
10759 cls : 'form-control',
10760 autocomplete: 'new-password',
10761 placeholder : this.placeholder || ''
10765 input.name = this.name;
10768 input.cls += ' input-' + this.size;
10771 if (this.disabled) {
10772 input.disabled=true;
10775 var inputblock = input;
10777 if(this.hasFeedback && !this.allowBlank){
10781 cls: 'glyphicon form-control-feedback'
10784 if(this.removable && !this.editable && !this.tickable){
10786 cls : 'has-feedback',
10792 cls : 'roo-combo-removable-btn close'
10799 cls : 'has-feedback',
10808 if(this.removable && !this.editable && !this.tickable){
10810 cls : 'roo-removable',
10816 cls : 'roo-combo-removable-btn close'
10823 if (this.before || this.after) {
10826 cls : 'input-group',
10830 inputblock.cn.push({
10832 cls : 'input-group-addon input-group-prepend input-group-text',
10837 inputblock.cn.push(input);
10839 if(this.hasFeedback && !this.allowBlank){
10840 inputblock.cls += ' has-feedback';
10841 inputblock.cn.push(feedback);
10845 inputblock.cn.push({
10847 cls : 'input-group-addon input-group-append input-group-text',
10856 var ibwrap = inputblock;
10861 cls: 'roo-select2-choices',
10865 cls: 'roo-select2-search-field',
10877 cls: 'roo-select2-container input-group',
10882 cls: 'form-hidden-field'
10888 if(!this.multiple && this.showToggleBtn){
10894 if (this.caret != false) {
10897 cls: 'fa fa-' + this.caret
10904 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10906 Roo.bootstrap.version == 3 ? caret : '',
10909 cls: 'combobox-clear',
10923 combobox.cls += ' roo-select2-container-multi';
10927 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10928 tooltip : 'This field is required'
10930 if (Roo.bootstrap.version == 4) {
10933 style : 'display:none'
10938 if (align ==='left' && this.fieldLabel.length) {
10940 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10947 cls : 'control-label',
10948 html : this.fieldLabel
10960 var labelCfg = cfg.cn[1];
10961 var contentCfg = cfg.cn[2];
10963 if(this.indicatorpos == 'right'){
10968 cls : 'control-label',
10972 html : this.fieldLabel
10986 labelCfg = cfg.cn[0];
10987 contentCfg = cfg.cn[1];
10990 if(this.labelWidth > 12){
10991 labelCfg.style = "width: " + this.labelWidth + 'px';
10994 if(this.labelWidth < 13 && this.labelmd == 0){
10995 this.labelmd = this.labelWidth;
10998 if(this.labellg > 0){
10999 labelCfg.cls += ' col-lg-' + this.labellg;
11000 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11003 if(this.labelmd > 0){
11004 labelCfg.cls += ' col-md-' + this.labelmd;
11005 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11008 if(this.labelsm > 0){
11009 labelCfg.cls += ' col-sm-' + this.labelsm;
11010 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11013 if(this.labelxs > 0){
11014 labelCfg.cls += ' col-xs-' + this.labelxs;
11015 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11018 } else if ( this.fieldLabel.length) {
11019 // Roo.log(" label");
11024 //cls : 'input-group-addon',
11025 html : this.fieldLabel
11033 if(this.indicatorpos == 'right'){
11041 html : this.fieldLabel
11055 // Roo.log(" no label && no align");
11062 ['xs','sm','md','lg'].map(function(size){
11063 if (settings[size]) {
11064 cfg.cls += ' col-' + size + '-' + settings[size];
11075 onResize : function(w, h){
11076 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
11077 // if(typeof w == 'number'){
11078 // var x = w - this.trigger.getWidth();
11079 // this.inputEl().setWidth(this.adjustWidth('input', x));
11080 // this.trigger.setStyle('left', x+'px');
11085 adjustSize : Roo.BoxComponent.prototype.adjustSize,
11088 getResizeEl : function(){
11089 return this.inputEl();
11093 getPositionEl : function(){
11094 return this.inputEl();
11098 alignErrorIcon : function(){
11099 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
11103 initEvents : function(){
11107 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
11108 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
11109 if(!this.multiple && this.showToggleBtn){
11110 this.trigger = this.el.select('span.dropdown-toggle',true).first();
11111 if(this.hideTrigger){
11112 this.trigger.setDisplayed(false);
11114 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
11118 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
11121 if(this.removable && !this.editable && !this.tickable){
11122 var close = this.closeTriggerEl();
11125 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
11126 close.on('click', this.removeBtnClick, this, close);
11130 //this.trigger.addClassOnOver('x-form-trigger-over');
11131 //this.trigger.addClassOnClick('x-form-trigger-click');
11134 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
11138 closeTriggerEl : function()
11140 var close = this.el.select('.roo-combo-removable-btn', true).first();
11141 return close ? close : false;
11144 removeBtnClick : function(e, h, el)
11146 e.preventDefault();
11148 if(this.fireEvent("remove", this) !== false){
11150 this.fireEvent("afterremove", this)
11154 createList : function()
11156 this.list = Roo.get(document.body).createChild({
11157 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
11158 cls: 'typeahead typeahead-long dropdown-menu',
11159 style: 'display:none'
11162 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
11167 initTrigger : function(){
11172 onDestroy : function(){
11174 this.trigger.removeAllListeners();
11175 // this.trigger.remove();
11178 // this.wrap.remove();
11180 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
11184 onFocus : function(){
11185 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
11187 if(!this.mimicing){
11188 this.wrap.addClass('x-trigger-wrap-focus');
11189 this.mimicing = true;
11190 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
11191 if(this.monitorTab){
11192 this.el.on("keydown", this.checkTab, this);
11199 checkTab : function(e){
11200 if(e.getKey() == e.TAB){
11201 this.triggerBlur();
11206 onBlur : function(){
11211 mimicBlur : function(e, t){
11213 if(!this.wrap.contains(t) && this.validateBlur()){
11214 this.triggerBlur();
11220 triggerBlur : function(){
11221 this.mimicing = false;
11222 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
11223 if(this.monitorTab){
11224 this.el.un("keydown", this.checkTab, this);
11226 //this.wrap.removeClass('x-trigger-wrap-focus');
11227 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11231 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11232 validateBlur : function(e, t){
11237 onDisable : function(){
11238 this.inputEl().dom.disabled = true;
11239 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11241 // this.wrap.addClass('x-item-disabled');
11246 onEnable : function(){
11247 this.inputEl().dom.disabled = false;
11248 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11250 // this.el.removeClass('x-item-disabled');
11255 onShow : function(){
11256 var ae = this.getActionEl();
11259 ae.dom.style.display = '';
11260 ae.dom.style.visibility = 'visible';
11266 onHide : function(){
11267 var ae = this.getActionEl();
11268 ae.dom.style.display = 'none';
11272 * The function that should handle the trigger's click event. This method does nothing by default until overridden
11273 * by an implementing function.
11275 * @param {EventObject} e
11277 onTriggerClick : Roo.emptyFn
11281 * Ext JS Library 1.1.1
11282 * Copyright(c) 2006-2007, Ext JS, LLC.
11284 * Originally Released Under LGPL - original licence link has changed is not relivant.
11287 * <script type="text/javascript">
11292 * @class Roo.data.SortTypes
11294 * Defines the default sorting (casting?) comparison functions used when sorting data.
11296 Roo.data.SortTypes = {
11298 * Default sort that does nothing
11299 * @param {Mixed} s The value being converted
11300 * @return {Mixed} The comparison value
11302 none : function(s){
11307 * The regular expression used to strip tags
11311 stripTagsRE : /<\/?[^>]+>/gi,
11314 * Strips all HTML tags to sort on text only
11315 * @param {Mixed} s The value being converted
11316 * @return {String} The comparison value
11318 asText : function(s){
11319 return String(s).replace(this.stripTagsRE, "");
11323 * Strips all HTML tags to sort on text only - Case insensitive
11324 * @param {Mixed} s The value being converted
11325 * @return {String} The comparison value
11327 asUCText : function(s){
11328 return String(s).toUpperCase().replace(this.stripTagsRE, "");
11332 * Case insensitive string
11333 * @param {Mixed} s The value being converted
11334 * @return {String} The comparison value
11336 asUCString : function(s) {
11337 return String(s).toUpperCase();
11342 * @param {Mixed} s The value being converted
11343 * @return {Number} The comparison value
11345 asDate : function(s) {
11349 if(s instanceof Date){
11350 return s.getTime();
11352 return Date.parse(String(s));
11357 * @param {Mixed} s The value being converted
11358 * @return {Float} The comparison value
11360 asFloat : function(s) {
11361 var val = parseFloat(String(s).replace(/,/g, ""));
11370 * @param {Mixed} s The value being converted
11371 * @return {Number} The comparison value
11373 asInt : function(s) {
11374 var val = parseInt(String(s).replace(/,/g, ""));
11382 * Ext JS Library 1.1.1
11383 * Copyright(c) 2006-2007, Ext JS, LLC.
11385 * Originally Released Under LGPL - original licence link has changed is not relivant.
11388 * <script type="text/javascript">
11392 * @class Roo.data.Record
11393 * Instances of this class encapsulate both record <em>definition</em> information, and record
11394 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11395 * to access Records cached in an {@link Roo.data.Store} object.<br>
11397 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11398 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11401 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11403 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11404 * {@link #create}. The parameters are the same.
11405 * @param {Array} data An associative Array of data values keyed by the field name.
11406 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11407 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11408 * not specified an integer id is generated.
11410 Roo.data.Record = function(data, id){
11411 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11416 * Generate a constructor for a specific record layout.
11417 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11418 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11419 * Each field definition object may contain the following properties: <ul>
11420 * <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,
11421 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11422 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11423 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11424 * is being used, then this is a string containing the javascript expression to reference the data relative to
11425 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11426 * to the data item relative to the record element. If the mapping expression is the same as the field name,
11427 * this may be omitted.</p></li>
11428 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11429 * <ul><li>auto (Default, implies no conversion)</li>
11434 * <li>date</li></ul></p></li>
11435 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11436 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11437 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11438 * by the Reader into an object that will be stored in the Record. It is passed the
11439 * following parameters:<ul>
11440 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11442 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11444 * <br>usage:<br><pre><code>
11445 var TopicRecord = Roo.data.Record.create(
11446 {name: 'title', mapping: 'topic_title'},
11447 {name: 'author', mapping: 'username'},
11448 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11449 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11450 {name: 'lastPoster', mapping: 'user2'},
11451 {name: 'excerpt', mapping: 'post_text'}
11454 var myNewRecord = new TopicRecord({
11455 title: 'Do my job please',
11458 lastPost: new Date(),
11459 lastPoster: 'Animal',
11460 excerpt: 'No way dude!'
11462 myStore.add(myNewRecord);
11467 Roo.data.Record.create = function(o){
11468 var f = function(){
11469 f.superclass.constructor.apply(this, arguments);
11471 Roo.extend(f, Roo.data.Record);
11472 var p = f.prototype;
11473 p.fields = new Roo.util.MixedCollection(false, function(field){
11476 for(var i = 0, len = o.length; i < len; i++){
11477 p.fields.add(new Roo.data.Field(o[i]));
11479 f.getField = function(name){
11480 return p.fields.get(name);
11485 Roo.data.Record.AUTO_ID = 1000;
11486 Roo.data.Record.EDIT = 'edit';
11487 Roo.data.Record.REJECT = 'reject';
11488 Roo.data.Record.COMMIT = 'commit';
11490 Roo.data.Record.prototype = {
11492 * Readonly flag - true if this record has been modified.
11501 join : function(store){
11502 this.store = store;
11506 * Set the named field to the specified value.
11507 * @param {String} name The name of the field to set.
11508 * @param {Object} value The value to set the field to.
11510 set : function(name, value){
11511 if(this.data[name] == value){
11515 if(!this.modified){
11516 this.modified = {};
11518 if(typeof this.modified[name] == 'undefined'){
11519 this.modified[name] = this.data[name];
11521 this.data[name] = value;
11522 if(!this.editing && this.store){
11523 this.store.afterEdit(this);
11528 * Get the value of the named field.
11529 * @param {String} name The name of the field to get the value of.
11530 * @return {Object} The value of the field.
11532 get : function(name){
11533 return this.data[name];
11537 beginEdit : function(){
11538 this.editing = true;
11539 this.modified = {};
11543 cancelEdit : function(){
11544 this.editing = false;
11545 delete this.modified;
11549 endEdit : function(){
11550 this.editing = false;
11551 if(this.dirty && this.store){
11552 this.store.afterEdit(this);
11557 * Usually called by the {@link Roo.data.Store} which owns the Record.
11558 * Rejects all changes made to the Record since either creation, or the last commit operation.
11559 * Modified fields are reverted to their original values.
11561 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11562 * of reject operations.
11564 reject : function(){
11565 var m = this.modified;
11567 if(typeof m[n] != "function"){
11568 this.data[n] = m[n];
11571 this.dirty = false;
11572 delete this.modified;
11573 this.editing = false;
11575 this.store.afterReject(this);
11580 * Usually called by the {@link Roo.data.Store} which owns the Record.
11581 * Commits all changes made to the Record since either creation, or the last commit operation.
11583 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11584 * of commit operations.
11586 commit : function(){
11587 this.dirty = false;
11588 delete this.modified;
11589 this.editing = false;
11591 this.store.afterCommit(this);
11596 hasError : function(){
11597 return this.error != null;
11601 clearError : function(){
11606 * Creates a copy of this record.
11607 * @param {String} id (optional) A new record id if you don't want to use this record's id
11610 copy : function(newId) {
11611 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11615 * Ext JS Library 1.1.1
11616 * Copyright(c) 2006-2007, Ext JS, LLC.
11618 * Originally Released Under LGPL - original licence link has changed is not relivant.
11621 * <script type="text/javascript">
11627 * @class Roo.data.Store
11628 * @extends Roo.util.Observable
11629 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11630 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11632 * 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
11633 * has no knowledge of the format of the data returned by the Proxy.<br>
11635 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11636 * instances from the data object. These records are cached and made available through accessor functions.
11638 * Creates a new Store.
11639 * @param {Object} config A config object containing the objects needed for the Store to access data,
11640 * and read the data into Records.
11642 Roo.data.Store = function(config){
11643 this.data = new Roo.util.MixedCollection(false);
11644 this.data.getKey = function(o){
11647 this.baseParams = {};
11649 this.paramNames = {
11654 "multisort" : "_multisort"
11657 if(config && config.data){
11658 this.inlineData = config.data;
11659 delete config.data;
11662 Roo.apply(this, config);
11664 if(this.reader){ // reader passed
11665 this.reader = Roo.factory(this.reader, Roo.data);
11666 this.reader.xmodule = this.xmodule || false;
11667 if(!this.recordType){
11668 this.recordType = this.reader.recordType;
11670 if(this.reader.onMetaChange){
11671 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11675 if(this.recordType){
11676 this.fields = this.recordType.prototype.fields;
11678 this.modified = [];
11682 * @event datachanged
11683 * Fires when the data cache has changed, and a widget which is using this Store
11684 * as a Record cache should refresh its view.
11685 * @param {Store} this
11687 datachanged : true,
11689 * @event metachange
11690 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11691 * @param {Store} this
11692 * @param {Object} meta The JSON metadata
11697 * Fires when Records have been added to the Store
11698 * @param {Store} this
11699 * @param {Roo.data.Record[]} records The array of Records added
11700 * @param {Number} index The index at which the record(s) were added
11705 * Fires when a Record has been removed from the Store
11706 * @param {Store} this
11707 * @param {Roo.data.Record} record The Record that was removed
11708 * @param {Number} index The index at which the record was removed
11713 * Fires when a Record has been updated
11714 * @param {Store} this
11715 * @param {Roo.data.Record} record The Record that was updated
11716 * @param {String} operation The update operation being performed. Value may be one of:
11718 Roo.data.Record.EDIT
11719 Roo.data.Record.REJECT
11720 Roo.data.Record.COMMIT
11726 * Fires when the data cache has been cleared.
11727 * @param {Store} this
11731 * @event beforeload
11732 * Fires before a request is made for a new data object. If the beforeload handler returns false
11733 * the load action will be canceled.
11734 * @param {Store} this
11735 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11739 * @event beforeloadadd
11740 * Fires after a new set of Records has been loaded.
11741 * @param {Store} this
11742 * @param {Roo.data.Record[]} records The Records that were loaded
11743 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11745 beforeloadadd : true,
11748 * Fires after a new set of Records has been loaded, before they are added to the store.
11749 * @param {Store} this
11750 * @param {Roo.data.Record[]} records The Records that were loaded
11751 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11752 * @params {Object} return from reader
11756 * @event loadexception
11757 * Fires if an exception occurs in the Proxy during loading.
11758 * Called with the signature of the Proxy's "loadexception" event.
11759 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11762 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11763 * @param {Object} load options
11764 * @param {Object} jsonData from your request (normally this contains the Exception)
11766 loadexception : true
11770 this.proxy = Roo.factory(this.proxy, Roo.data);
11771 this.proxy.xmodule = this.xmodule || false;
11772 this.relayEvents(this.proxy, ["loadexception"]);
11774 this.sortToggle = {};
11775 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11777 Roo.data.Store.superclass.constructor.call(this);
11779 if(this.inlineData){
11780 this.loadData(this.inlineData);
11781 delete this.inlineData;
11785 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11787 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11788 * without a remote query - used by combo/forms at present.
11792 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11795 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11798 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11799 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11802 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11803 * on any HTTP request
11806 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11809 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11813 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11814 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11816 remoteSort : false,
11819 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11820 * loaded or when a record is removed. (defaults to false).
11822 pruneModifiedRecords : false,
11825 lastOptions : null,
11828 * Add Records to the Store and fires the add event.
11829 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11831 add : function(records){
11832 records = [].concat(records);
11833 for(var i = 0, len = records.length; i < len; i++){
11834 records[i].join(this);
11836 var index = this.data.length;
11837 this.data.addAll(records);
11838 this.fireEvent("add", this, records, index);
11842 * Remove a Record from the Store and fires the remove event.
11843 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11845 remove : function(record){
11846 var index = this.data.indexOf(record);
11847 this.data.removeAt(index);
11849 if(this.pruneModifiedRecords){
11850 this.modified.remove(record);
11852 this.fireEvent("remove", this, record, index);
11856 * Remove all Records from the Store and fires the clear event.
11858 removeAll : function(){
11860 if(this.pruneModifiedRecords){
11861 this.modified = [];
11863 this.fireEvent("clear", this);
11867 * Inserts Records to the Store at the given index and fires the add event.
11868 * @param {Number} index The start index at which to insert the passed Records.
11869 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11871 insert : function(index, records){
11872 records = [].concat(records);
11873 for(var i = 0, len = records.length; i < len; i++){
11874 this.data.insert(index, records[i]);
11875 records[i].join(this);
11877 this.fireEvent("add", this, records, index);
11881 * Get the index within the cache of the passed Record.
11882 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11883 * @return {Number} The index of the passed Record. Returns -1 if not found.
11885 indexOf : function(record){
11886 return this.data.indexOf(record);
11890 * Get the index within the cache of the Record with the passed id.
11891 * @param {String} id The id of the Record to find.
11892 * @return {Number} The index of the Record. Returns -1 if not found.
11894 indexOfId : function(id){
11895 return this.data.indexOfKey(id);
11899 * Get the Record with the specified id.
11900 * @param {String} id The id of the Record to find.
11901 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11903 getById : function(id){
11904 return this.data.key(id);
11908 * Get the Record at the specified index.
11909 * @param {Number} index The index of the Record to find.
11910 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11912 getAt : function(index){
11913 return this.data.itemAt(index);
11917 * Returns a range of Records between specified indices.
11918 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11919 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11920 * @return {Roo.data.Record[]} An array of Records
11922 getRange : function(start, end){
11923 return this.data.getRange(start, end);
11927 storeOptions : function(o){
11928 o = Roo.apply({}, o);
11931 this.lastOptions = o;
11935 * Loads the Record cache from the configured Proxy using the configured Reader.
11937 * If using remote paging, then the first load call must specify the <em>start</em>
11938 * and <em>limit</em> properties in the options.params property to establish the initial
11939 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11941 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11942 * and this call will return before the new data has been loaded. Perform any post-processing
11943 * in a callback function, or in a "load" event handler.</strong>
11945 * @param {Object} options An object containing properties which control loading options:<ul>
11946 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11947 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11948 * passed the following arguments:<ul>
11949 * <li>r : Roo.data.Record[]</li>
11950 * <li>options: Options object from the load call</li>
11951 * <li>success: Boolean success indicator</li></ul></li>
11952 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11953 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11956 load : function(options){
11957 options = options || {};
11958 if(this.fireEvent("beforeload", this, options) !== false){
11959 this.storeOptions(options);
11960 var p = Roo.apply(options.params || {}, this.baseParams);
11961 // if meta was not loaded from remote source.. try requesting it.
11962 if (!this.reader.metaFromRemote) {
11963 p._requestMeta = 1;
11965 if(this.sortInfo && this.remoteSort){
11966 var pn = this.paramNames;
11967 p[pn["sort"]] = this.sortInfo.field;
11968 p[pn["dir"]] = this.sortInfo.direction;
11970 if (this.multiSort) {
11971 var pn = this.paramNames;
11972 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11975 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11980 * Reloads the Record cache from the configured Proxy using the configured Reader and
11981 * the options from the last load operation performed.
11982 * @param {Object} options (optional) An object containing properties which may override the options
11983 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11984 * the most recently used options are reused).
11986 reload : function(options){
11987 this.load(Roo.applyIf(options||{}, this.lastOptions));
11991 // Called as a callback by the Reader during a load operation.
11992 loadRecords : function(o, options, success){
11993 if(!o || success === false){
11994 if(success !== false){
11995 this.fireEvent("load", this, [], options, o);
11997 if(options.callback){
11998 options.callback.call(options.scope || this, [], options, false);
12002 // if data returned failure - throw an exception.
12003 if (o.success === false) {
12004 // show a message if no listener is registered.
12005 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
12006 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
12008 // loadmask wil be hooked into this..
12009 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
12012 var r = o.records, t = o.totalRecords || r.length;
12014 this.fireEvent("beforeloadadd", this, r, options, o);
12016 if(!options || options.add !== true){
12017 if(this.pruneModifiedRecords){
12018 this.modified = [];
12020 for(var i = 0, len = r.length; i < len; i++){
12024 this.data = this.snapshot;
12025 delete this.snapshot;
12028 this.data.addAll(r);
12029 this.totalLength = t;
12031 this.fireEvent("datachanged", this);
12033 this.totalLength = Math.max(t, this.data.length+r.length);
12037 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
12039 var e = new Roo.data.Record({});
12041 e.set(this.parent.displayField, this.parent.emptyTitle);
12042 e.set(this.parent.valueField, '');
12047 this.fireEvent("load", this, r, options, o);
12048 if(options.callback){
12049 options.callback.call(options.scope || this, r, options, true);
12055 * Loads data from a passed data block. A Reader which understands the format of the data
12056 * must have been configured in the constructor.
12057 * @param {Object} data The data block from which to read the Records. The format of the data expected
12058 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
12059 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
12061 loadData : function(o, append){
12062 var r = this.reader.readRecords(o);
12063 this.loadRecords(r, {add: append}, true);
12067 * using 'cn' the nested child reader read the child array into it's child stores.
12068 * @param {Object} rec The record with a 'children array
12070 loadDataFromChildren : function(rec)
12072 this.loadData(this.reader.toLoadData(rec));
12077 * Gets the number of cached records.
12079 * <em>If using paging, this may not be the total size of the dataset. If the data object
12080 * used by the Reader contains the dataset size, then the getTotalCount() function returns
12081 * the data set size</em>
12083 getCount : function(){
12084 return this.data.length || 0;
12088 * Gets the total number of records in the dataset as returned by the server.
12090 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
12091 * the dataset size</em>
12093 getTotalCount : function(){
12094 return this.totalLength || 0;
12098 * Returns the sort state of the Store as an object with two properties:
12100 field {String} The name of the field by which the Records are sorted
12101 direction {String} The sort order, "ASC" or "DESC"
12104 getSortState : function(){
12105 return this.sortInfo;
12109 applySort : function(){
12110 if(this.sortInfo && !this.remoteSort){
12111 var s = this.sortInfo, f = s.field;
12112 var st = this.fields.get(f).sortType;
12113 var fn = function(r1, r2){
12114 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
12115 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12117 this.data.sort(s.direction, fn);
12118 if(this.snapshot && this.snapshot != this.data){
12119 this.snapshot.sort(s.direction, fn);
12125 * Sets the default sort column and order to be used by the next load operation.
12126 * @param {String} fieldName The name of the field to sort by.
12127 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12129 setDefaultSort : function(field, dir){
12130 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
12134 * Sort the Records.
12135 * If remote sorting is used, the sort is performed on the server, and the cache is
12136 * reloaded. If local sorting is used, the cache is sorted internally.
12137 * @param {String} fieldName The name of the field to sort by.
12138 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12140 sort : function(fieldName, dir){
12141 var f = this.fields.get(fieldName);
12143 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
12145 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
12146 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
12151 this.sortToggle[f.name] = dir;
12152 this.sortInfo = {field: f.name, direction: dir};
12153 if(!this.remoteSort){
12155 this.fireEvent("datachanged", this);
12157 this.load(this.lastOptions);
12162 * Calls the specified function for each of the Records in the cache.
12163 * @param {Function} fn The function to call. The Record is passed as the first parameter.
12164 * Returning <em>false</em> aborts and exits the iteration.
12165 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
12167 each : function(fn, scope){
12168 this.data.each(fn, scope);
12172 * Gets all records modified since the last commit. Modified records are persisted across load operations
12173 * (e.g., during paging).
12174 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
12176 getModifiedRecords : function(){
12177 return this.modified;
12181 createFilterFn : function(property, value, anyMatch){
12182 if(!value.exec){ // not a regex
12183 value = String(value);
12184 if(value.length == 0){
12187 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
12189 return function(r){
12190 return value.test(r.data[property]);
12195 * Sums the value of <i>property</i> for each record between start and end and returns the result.
12196 * @param {String} property A field on your records
12197 * @param {Number} start The record index to start at (defaults to 0)
12198 * @param {Number} end The last record index to include (defaults to length - 1)
12199 * @return {Number} The sum
12201 sum : function(property, start, end){
12202 var rs = this.data.items, v = 0;
12203 start = start || 0;
12204 end = (end || end === 0) ? end : rs.length-1;
12206 for(var i = start; i <= end; i++){
12207 v += (rs[i].data[property] || 0);
12213 * Filter the records by a specified property.
12214 * @param {String} field A field on your records
12215 * @param {String/RegExp} value Either a string that the field
12216 * should start with or a RegExp to test against the field
12217 * @param {Boolean} anyMatch True to match any part not just the beginning
12219 filter : function(property, value, anyMatch){
12220 var fn = this.createFilterFn(property, value, anyMatch);
12221 return fn ? this.filterBy(fn) : this.clearFilter();
12225 * Filter by a function. The specified function will be called with each
12226 * record in this data source. If the function returns true the record is included,
12227 * otherwise it is filtered.
12228 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12229 * @param {Object} scope (optional) The scope of the function (defaults to this)
12231 filterBy : function(fn, scope){
12232 this.snapshot = this.snapshot || this.data;
12233 this.data = this.queryBy(fn, scope||this);
12234 this.fireEvent("datachanged", this);
12238 * Query the records by a specified property.
12239 * @param {String} field A field on your records
12240 * @param {String/RegExp} value Either a string that the field
12241 * should start with or a RegExp to test against the field
12242 * @param {Boolean} anyMatch True to match any part not just the beginning
12243 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12245 query : function(property, value, anyMatch){
12246 var fn = this.createFilterFn(property, value, anyMatch);
12247 return fn ? this.queryBy(fn) : this.data.clone();
12251 * Query by a function. The specified function will be called with each
12252 * record in this data source. If the function returns true the record is included
12254 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12255 * @param {Object} scope (optional) The scope of the function (defaults to this)
12256 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12258 queryBy : function(fn, scope){
12259 var data = this.snapshot || this.data;
12260 return data.filterBy(fn, scope||this);
12264 * Collects unique values for a particular dataIndex from this store.
12265 * @param {String} dataIndex The property to collect
12266 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12267 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12268 * @return {Array} An array of the unique values
12270 collect : function(dataIndex, allowNull, bypassFilter){
12271 var d = (bypassFilter === true && this.snapshot) ?
12272 this.snapshot.items : this.data.items;
12273 var v, sv, r = [], l = {};
12274 for(var i = 0, len = d.length; i < len; i++){
12275 v = d[i].data[dataIndex];
12277 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12286 * Revert to a view of the Record cache with no filtering applied.
12287 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12289 clearFilter : function(suppressEvent){
12290 if(this.snapshot && this.snapshot != this.data){
12291 this.data = this.snapshot;
12292 delete this.snapshot;
12293 if(suppressEvent !== true){
12294 this.fireEvent("datachanged", this);
12300 afterEdit : function(record){
12301 if(this.modified.indexOf(record) == -1){
12302 this.modified.push(record);
12304 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12308 afterReject : function(record){
12309 this.modified.remove(record);
12310 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12314 afterCommit : function(record){
12315 this.modified.remove(record);
12316 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12320 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12321 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12323 commitChanges : function(){
12324 var m = this.modified.slice(0);
12325 this.modified = [];
12326 for(var i = 0, len = m.length; i < len; i++){
12332 * Cancel outstanding changes on all changed records.
12334 rejectChanges : function(){
12335 var m = this.modified.slice(0);
12336 this.modified = [];
12337 for(var i = 0, len = m.length; i < len; i++){
12342 onMetaChange : function(meta, rtype, o){
12343 this.recordType = rtype;
12344 this.fields = rtype.prototype.fields;
12345 delete this.snapshot;
12346 this.sortInfo = meta.sortInfo || this.sortInfo;
12347 this.modified = [];
12348 this.fireEvent('metachange', this, this.reader.meta);
12351 moveIndex : function(data, type)
12353 var index = this.indexOf(data);
12355 var newIndex = index + type;
12359 this.insert(newIndex, data);
12364 * Ext JS Library 1.1.1
12365 * Copyright(c) 2006-2007, Ext JS, LLC.
12367 * Originally Released Under LGPL - original licence link has changed is not relivant.
12370 * <script type="text/javascript">
12374 * @class Roo.data.SimpleStore
12375 * @extends Roo.data.Store
12376 * Small helper class to make creating Stores from Array data easier.
12377 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12378 * @cfg {Array} fields An array of field definition objects, or field name strings.
12379 * @cfg {Object} an existing reader (eg. copied from another store)
12380 * @cfg {Array} data The multi-dimensional array of data
12382 * @param {Object} config
12384 Roo.data.SimpleStore = function(config)
12386 Roo.data.SimpleStore.superclass.constructor.call(this, {
12388 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
12391 Roo.data.Record.create(config.fields)
12393 proxy : new Roo.data.MemoryProxy(config.data)
12397 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12399 * Ext JS Library 1.1.1
12400 * Copyright(c) 2006-2007, Ext JS, LLC.
12402 * Originally Released Under LGPL - original licence link has changed is not relivant.
12405 * <script type="text/javascript">
12410 * @extends Roo.data.Store
12411 * @class Roo.data.JsonStore
12412 * Small helper class to make creating Stores for JSON data easier. <br/>
12414 var store = new Roo.data.JsonStore({
12415 url: 'get-images.php',
12417 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12420 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12421 * JsonReader and HttpProxy (unless inline data is provided).</b>
12422 * @cfg {Array} fields An array of field definition objects, or field name strings.
12424 * @param {Object} config
12426 Roo.data.JsonStore = function(c){
12427 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12428 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12429 reader: new Roo.data.JsonReader(c, c.fields)
12432 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12434 * Ext JS Library 1.1.1
12435 * Copyright(c) 2006-2007, Ext JS, LLC.
12437 * Originally Released Under LGPL - original licence link has changed is not relivant.
12440 * <script type="text/javascript">
12444 Roo.data.Field = function(config){
12445 if(typeof config == "string"){
12446 config = {name: config};
12448 Roo.apply(this, config);
12451 this.type = "auto";
12454 var st = Roo.data.SortTypes;
12455 // named sortTypes are supported, here we look them up
12456 if(typeof this.sortType == "string"){
12457 this.sortType = st[this.sortType];
12460 // set default sortType for strings and dates
12461 if(!this.sortType){
12464 this.sortType = st.asUCString;
12467 this.sortType = st.asDate;
12470 this.sortType = st.none;
12475 var stripRe = /[\$,%]/g;
12477 // prebuilt conversion function for this field, instead of
12478 // switching every time we're reading a value
12480 var cv, dateFormat = this.dateFormat;
12485 cv = function(v){ return v; };
12488 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12492 return v !== undefined && v !== null && v !== '' ?
12493 parseInt(String(v).replace(stripRe, ""), 10) : '';
12498 return v !== undefined && v !== null && v !== '' ?
12499 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12504 cv = function(v){ return v === true || v === "true" || v == 1; };
12511 if(v instanceof Date){
12515 if(dateFormat == "timestamp"){
12516 return new Date(v*1000);
12518 return Date.parseDate(v, dateFormat);
12520 var parsed = Date.parse(v);
12521 return parsed ? new Date(parsed) : null;
12530 Roo.data.Field.prototype = {
12538 * Ext JS Library 1.1.1
12539 * Copyright(c) 2006-2007, Ext JS, LLC.
12541 * Originally Released Under LGPL - original licence link has changed is not relivant.
12544 * <script type="text/javascript">
12547 // Base class for reading structured data from a data source. This class is intended to be
12548 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12551 * @class Roo.data.DataReader
12552 * Base class for reading structured data from a data source. This class is intended to be
12553 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12556 Roo.data.DataReader = function(meta, recordType){
12560 this.recordType = recordType instanceof Array ?
12561 Roo.data.Record.create(recordType) : recordType;
12564 Roo.data.DataReader.prototype = {
12567 readerType : 'Data',
12569 * Create an empty record
12570 * @param {Object} data (optional) - overlay some values
12571 * @return {Roo.data.Record} record created.
12573 newRow : function(d) {
12575 this.recordType.prototype.fields.each(function(c) {
12577 case 'int' : da[c.name] = 0; break;
12578 case 'date' : da[c.name] = new Date(); break;
12579 case 'float' : da[c.name] = 0.0; break;
12580 case 'boolean' : da[c.name] = false; break;
12581 default : da[c.name] = ""; break;
12585 return new this.recordType(Roo.apply(da, d));
12591 * Ext JS Library 1.1.1
12592 * Copyright(c) 2006-2007, Ext JS, LLC.
12594 * Originally Released Under LGPL - original licence link has changed is not relivant.
12597 * <script type="text/javascript">
12601 * @class Roo.data.DataProxy
12602 * @extends Roo.data.Observable
12603 * This class is an abstract base class for implementations which provide retrieval of
12604 * unformatted data objects.<br>
12606 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12607 * (of the appropriate type which knows how to parse the data object) to provide a block of
12608 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12610 * Custom implementations must implement the load method as described in
12611 * {@link Roo.data.HttpProxy#load}.
12613 Roo.data.DataProxy = function(){
12616 * @event beforeload
12617 * Fires before a network request is made to retrieve a data object.
12618 * @param {Object} This DataProxy object.
12619 * @param {Object} params The params parameter to the load function.
12624 * Fires before the load method's callback is called.
12625 * @param {Object} This DataProxy object.
12626 * @param {Object} o The data object.
12627 * @param {Object} arg The callback argument object passed to the load function.
12631 * @event loadexception
12632 * Fires if an Exception occurs during data retrieval.
12633 * @param {Object} This DataProxy object.
12634 * @param {Object} o The data object.
12635 * @param {Object} arg The callback argument object passed to the load function.
12636 * @param {Object} e The Exception.
12638 loadexception : true
12640 Roo.data.DataProxy.superclass.constructor.call(this);
12643 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12646 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12650 * Ext JS Library 1.1.1
12651 * Copyright(c) 2006-2007, Ext JS, LLC.
12653 * Originally Released Under LGPL - original licence link has changed is not relivant.
12656 * <script type="text/javascript">
12659 * @class Roo.data.MemoryProxy
12660 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12661 * to the Reader when its load method is called.
12663 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12665 Roo.data.MemoryProxy = function(data){
12669 Roo.data.MemoryProxy.superclass.constructor.call(this);
12673 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12676 * Load data from the requested source (in this case an in-memory
12677 * data object passed to the constructor), read the data object into
12678 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12679 * process that block using the passed callback.
12680 * @param {Object} params This parameter is not used by the MemoryProxy class.
12681 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12682 * object into a block of Roo.data.Records.
12683 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12684 * The function must be passed <ul>
12685 * <li>The Record block object</li>
12686 * <li>The "arg" argument from the load function</li>
12687 * <li>A boolean success indicator</li>
12689 * @param {Object} scope The scope in which to call the callback
12690 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12692 load : function(params, reader, callback, scope, arg){
12693 params = params || {};
12696 result = reader.readRecords(params.data ? params.data :this.data);
12698 this.fireEvent("loadexception", this, arg, null, e);
12699 callback.call(scope, null, arg, false);
12702 callback.call(scope, result, arg, true);
12706 update : function(params, records){
12711 * Ext JS Library 1.1.1
12712 * Copyright(c) 2006-2007, Ext JS, LLC.
12714 * Originally Released Under LGPL - original licence link has changed is not relivant.
12717 * <script type="text/javascript">
12720 * @class Roo.data.HttpProxy
12721 * @extends Roo.data.DataProxy
12722 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12723 * configured to reference a certain URL.<br><br>
12725 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12726 * from which the running page was served.<br><br>
12728 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12730 * Be aware that to enable the browser to parse an XML document, the server must set
12731 * the Content-Type header in the HTTP response to "text/xml".
12733 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12734 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12735 * will be used to make the request.
12737 Roo.data.HttpProxy = function(conn){
12738 Roo.data.HttpProxy.superclass.constructor.call(this);
12739 // is conn a conn config or a real conn?
12741 this.useAjax = !conn || !conn.events;
12745 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12746 // thse are take from connection...
12749 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12752 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12753 * extra parameters to each request made by this object. (defaults to undefined)
12756 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12757 * to each request made by this object. (defaults to undefined)
12760 * @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)
12763 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12766 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12772 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12776 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12777 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12778 * a finer-grained basis than the DataProxy events.
12780 getConnection : function(){
12781 return this.useAjax ? Roo.Ajax : this.conn;
12785 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12786 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12787 * process that block using the passed callback.
12788 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12789 * for the request to the remote server.
12790 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12791 * object into a block of Roo.data.Records.
12792 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12793 * The function must be passed <ul>
12794 * <li>The Record block object</li>
12795 * <li>The "arg" argument from the load function</li>
12796 * <li>A boolean success indicator</li>
12798 * @param {Object} scope The scope in which to call the callback
12799 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12801 load : function(params, reader, callback, scope, arg){
12802 if(this.fireEvent("beforeload", this, params) !== false){
12804 params : params || {},
12806 callback : callback,
12811 callback : this.loadResponse,
12815 Roo.applyIf(o, this.conn);
12816 if(this.activeRequest){
12817 Roo.Ajax.abort(this.activeRequest);
12819 this.activeRequest = Roo.Ajax.request(o);
12821 this.conn.request(o);
12824 callback.call(scope||this, null, arg, false);
12829 loadResponse : function(o, success, response){
12830 delete this.activeRequest;
12832 this.fireEvent("loadexception", this, o, response);
12833 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12838 result = o.reader.read(response);
12840 this.fireEvent("loadexception", this, o, response, e);
12841 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12845 this.fireEvent("load", this, o, o.request.arg);
12846 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12850 update : function(dataSet){
12855 updateResponse : function(dataSet){
12860 * Ext JS Library 1.1.1
12861 * Copyright(c) 2006-2007, Ext JS, LLC.
12863 * Originally Released Under LGPL - original licence link has changed is not relivant.
12866 * <script type="text/javascript">
12870 * @class Roo.data.ScriptTagProxy
12871 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12872 * other than the originating domain of the running page.<br><br>
12874 * <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
12875 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12877 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12878 * source code that is used as the source inside a <script> tag.<br><br>
12880 * In order for the browser to process the returned data, the server must wrap the data object
12881 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12882 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12883 * depending on whether the callback name was passed:
12886 boolean scriptTag = false;
12887 String cb = request.getParameter("callback");
12890 response.setContentType("text/javascript");
12892 response.setContentType("application/x-json");
12894 Writer out = response.getWriter();
12896 out.write(cb + "(");
12898 out.print(dataBlock.toJsonString());
12905 * @param {Object} config A configuration object.
12907 Roo.data.ScriptTagProxy = function(config){
12908 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12909 Roo.apply(this, config);
12910 this.head = document.getElementsByTagName("head")[0];
12913 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12915 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12917 * @cfg {String} url The URL from which to request the data object.
12920 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12924 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12925 * the server the name of the callback function set up by the load call to process the returned data object.
12926 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12927 * javascript output which calls this named function passing the data object as its only parameter.
12929 callbackParam : "callback",
12931 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12932 * name to the request.
12937 * Load data from the configured URL, read the data object into
12938 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12939 * process that block using the passed callback.
12940 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12941 * for the request to the remote server.
12942 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12943 * object into a block of Roo.data.Records.
12944 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12945 * The function must be passed <ul>
12946 * <li>The Record block object</li>
12947 * <li>The "arg" argument from the load function</li>
12948 * <li>A boolean success indicator</li>
12950 * @param {Object} scope The scope in which to call the callback
12951 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12953 load : function(params, reader, callback, scope, arg){
12954 if(this.fireEvent("beforeload", this, params) !== false){
12956 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12958 var url = this.url;
12959 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12961 url += "&_dc=" + (new Date().getTime());
12963 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12966 cb : "stcCallback"+transId,
12967 scriptId : "stcScript"+transId,
12971 callback : callback,
12977 window[trans.cb] = function(o){
12978 conn.handleResponse(o, trans);
12981 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12983 if(this.autoAbort !== false){
12987 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12989 var script = document.createElement("script");
12990 script.setAttribute("src", url);
12991 script.setAttribute("type", "text/javascript");
12992 script.setAttribute("id", trans.scriptId);
12993 this.head.appendChild(script);
12995 this.trans = trans;
12997 callback.call(scope||this, null, arg, false);
13002 isLoading : function(){
13003 return this.trans ? true : false;
13007 * Abort the current server request.
13009 abort : function(){
13010 if(this.isLoading()){
13011 this.destroyTrans(this.trans);
13016 destroyTrans : function(trans, isLoaded){
13017 this.head.removeChild(document.getElementById(trans.scriptId));
13018 clearTimeout(trans.timeoutId);
13020 window[trans.cb] = undefined;
13022 delete window[trans.cb];
13025 // if hasn't been loaded, wait for load to remove it to prevent script error
13026 window[trans.cb] = function(){
13027 window[trans.cb] = undefined;
13029 delete window[trans.cb];
13036 handleResponse : function(o, trans){
13037 this.trans = false;
13038 this.destroyTrans(trans, true);
13041 result = trans.reader.readRecords(o);
13043 this.fireEvent("loadexception", this, o, trans.arg, e);
13044 trans.callback.call(trans.scope||window, null, trans.arg, false);
13047 this.fireEvent("load", this, o, trans.arg);
13048 trans.callback.call(trans.scope||window, result, trans.arg, true);
13052 handleFailure : function(trans){
13053 this.trans = false;
13054 this.destroyTrans(trans, false);
13055 this.fireEvent("loadexception", this, null, trans.arg);
13056 trans.callback.call(trans.scope||window, null, trans.arg, false);
13060 * Ext JS Library 1.1.1
13061 * Copyright(c) 2006-2007, Ext JS, LLC.
13063 * Originally Released Under LGPL - original licence link has changed is not relivant.
13066 * <script type="text/javascript">
13070 * @class Roo.data.JsonReader
13071 * @extends Roo.data.DataReader
13072 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
13073 * based on mappings in a provided Roo.data.Record constructor.
13075 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
13076 * in the reply previously.
13081 var RecordDef = Roo.data.Record.create([
13082 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
13083 {name: 'occupation'} // This field will use "occupation" as the mapping.
13085 var myReader = new Roo.data.JsonReader({
13086 totalProperty: "results", // The property which contains the total dataset size (optional)
13087 root: "rows", // The property which contains an Array of row objects
13088 id: "id" // The property within each row object that provides an ID for the record (optional)
13092 * This would consume a JSON file like this:
13094 { 'results': 2, 'rows': [
13095 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
13096 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
13099 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
13100 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
13101 * paged from the remote server.
13102 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
13103 * @cfg {String} root name of the property which contains the Array of row objects.
13104 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13105 * @cfg {Array} fields Array of field definition objects
13107 * Create a new JsonReader
13108 * @param {Object} meta Metadata configuration options
13109 * @param {Object} recordType Either an Array of field definition objects,
13110 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
13112 Roo.data.JsonReader = function(meta, recordType){
13115 // set some defaults:
13116 Roo.applyIf(meta, {
13117 totalProperty: 'total',
13118 successProperty : 'success',
13123 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13125 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
13127 readerType : 'Json',
13130 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
13131 * Used by Store query builder to append _requestMeta to params.
13134 metaFromRemote : false,
13136 * This method is only used by a DataProxy which has retrieved data from a remote server.
13137 * @param {Object} response The XHR object which contains the JSON data in its responseText.
13138 * @return {Object} data A data block which is used by an Roo.data.Store object as
13139 * a cache of Roo.data.Records.
13141 read : function(response){
13142 var json = response.responseText;
13144 var o = /* eval:var:o */ eval("("+json+")");
13146 throw {message: "JsonReader.read: Json object not found"};
13152 this.metaFromRemote = true;
13153 this.meta = o.metaData;
13154 this.recordType = Roo.data.Record.create(o.metaData.fields);
13155 this.onMetaChange(this.meta, this.recordType, o);
13157 return this.readRecords(o);
13160 // private function a store will implement
13161 onMetaChange : function(meta, recordType, o){
13168 simpleAccess: function(obj, subsc) {
13175 getJsonAccessor: function(){
13177 return function(expr) {
13179 return(re.test(expr))
13180 ? new Function("obj", "return obj." + expr)
13185 return Roo.emptyFn;
13190 * Create a data block containing Roo.data.Records from an XML document.
13191 * @param {Object} o An object which contains an Array of row objects in the property specified
13192 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
13193 * which contains the total size of the dataset.
13194 * @return {Object} data A data block which is used by an Roo.data.Store object as
13195 * a cache of Roo.data.Records.
13197 readRecords : function(o){
13199 * After any data loads, the raw JSON data is available for further custom processing.
13203 var s = this.meta, Record = this.recordType,
13204 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
13206 // Generate extraction functions for the totalProperty, the root, the id, and for each field
13208 if(s.totalProperty) {
13209 this.getTotal = this.getJsonAccessor(s.totalProperty);
13211 if(s.successProperty) {
13212 this.getSuccess = this.getJsonAccessor(s.successProperty);
13214 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
13216 var g = this.getJsonAccessor(s.id);
13217 this.getId = function(rec) {
13219 return (r === undefined || r === "") ? null : r;
13222 this.getId = function(){return null;};
13225 for(var jj = 0; jj < fl; jj++){
13227 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
13228 this.ef[jj] = this.getJsonAccessor(map);
13232 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
13233 if(s.totalProperty){
13234 var vt = parseInt(this.getTotal(o), 10);
13239 if(s.successProperty){
13240 var vs = this.getSuccess(o);
13241 if(vs === false || vs === 'false'){
13246 for(var i = 0; i < c; i++){
13249 var id = this.getId(n);
13250 for(var j = 0; j < fl; j++){
13252 var v = this.ef[j](n);
13254 Roo.log('missing convert for ' + f.name);
13258 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13260 var record = new Record(values, id);
13262 records[i] = record;
13268 totalRecords : totalRecords
13271 // used when loading children.. @see loadDataFromChildren
13272 toLoadData: function(rec)
13274 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13275 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13276 return { data : data, total : data.length };
13281 * Ext JS Library 1.1.1
13282 * Copyright(c) 2006-2007, Ext JS, LLC.
13284 * Originally Released Under LGPL - original licence link has changed is not relivant.
13287 * <script type="text/javascript">
13291 * @class Roo.data.ArrayReader
13292 * @extends Roo.data.DataReader
13293 * Data reader class to create an Array of Roo.data.Record objects from an Array.
13294 * Each element of that Array represents a row of data fields. The
13295 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13296 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13300 var RecordDef = Roo.data.Record.create([
13301 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
13302 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
13304 var myReader = new Roo.data.ArrayReader({
13305 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
13309 * This would consume an Array like this:
13311 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13315 * Create a new JsonReader
13316 * @param {Object} meta Metadata configuration options.
13317 * @param {Object|Array} recordType Either an Array of field definition objects
13319 * @cfg {Array} fields Array of field definition objects
13320 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13321 * as specified to {@link Roo.data.Record#create},
13322 * or an {@link Roo.data.Record} object
13325 * created using {@link Roo.data.Record#create}.
13327 Roo.data.ArrayReader = function(meta, recordType)
13329 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13332 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13335 * Create a data block containing Roo.data.Records from an XML document.
13336 * @param {Object} o An Array of row objects which represents the dataset.
13337 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13338 * a cache of Roo.data.Records.
13340 readRecords : function(o)
13342 var sid = this.meta ? this.meta.id : null;
13343 var recordType = this.recordType, fields = recordType.prototype.fields;
13346 for(var i = 0; i < root.length; i++){
13349 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13350 for(var j = 0, jlen = fields.length; j < jlen; j++){
13351 var f = fields.items[j];
13352 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13353 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13355 values[f.name] = v;
13357 var record = new recordType(values, id);
13359 records[records.length] = record;
13363 totalRecords : records.length
13366 // used when loading children.. @see loadDataFromChildren
13367 toLoadData: function(rec)
13369 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13370 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13381 * @class Roo.bootstrap.ComboBox
13382 * @extends Roo.bootstrap.TriggerField
13383 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13384 * @cfg {Boolean} append (true|false) default false
13385 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13386 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13387 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13388 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13389 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13390 * @cfg {Boolean} animate default true
13391 * @cfg {Boolean} emptyResultText only for touch device
13392 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13393 * @cfg {String} emptyTitle default ''
13395 * Create a new ComboBox.
13396 * @param {Object} config Configuration options
13398 Roo.bootstrap.ComboBox = function(config){
13399 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13403 * Fires when the dropdown list is expanded
13404 * @param {Roo.bootstrap.ComboBox} combo This combo box
13409 * Fires when the dropdown list is collapsed
13410 * @param {Roo.bootstrap.ComboBox} combo This combo box
13414 * @event beforeselect
13415 * Fires before a list item is selected. Return false to cancel the selection.
13416 * @param {Roo.bootstrap.ComboBox} combo This combo box
13417 * @param {Roo.data.Record} record The data record returned from the underlying store
13418 * @param {Number} index The index of the selected item in the dropdown list
13420 'beforeselect' : true,
13423 * Fires when a list item is selected
13424 * @param {Roo.bootstrap.ComboBox} combo This combo box
13425 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13426 * @param {Number} index The index of the selected item in the dropdown list
13430 * @event beforequery
13431 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13432 * The event object passed has these properties:
13433 * @param {Roo.bootstrap.ComboBox} combo This combo box
13434 * @param {String} query The query
13435 * @param {Boolean} forceAll true to force "all" query
13436 * @param {Boolean} cancel true to cancel the query
13437 * @param {Object} e The query event object
13439 'beforequery': true,
13442 * Fires when the 'add' icon is pressed (add a listener to enable add button)
13443 * @param {Roo.bootstrap.ComboBox} combo This combo box
13448 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13449 * @param {Roo.bootstrap.ComboBox} combo This combo box
13450 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13455 * Fires when the remove value from the combobox array
13456 * @param {Roo.bootstrap.ComboBox} combo This combo box
13460 * @event afterremove
13461 * Fires when the remove value from the combobox array
13462 * @param {Roo.bootstrap.ComboBox} combo This combo box
13464 'afterremove' : true,
13466 * @event specialfilter
13467 * Fires when specialfilter
13468 * @param {Roo.bootstrap.ComboBox} combo This combo box
13470 'specialfilter' : true,
13473 * Fires when tick the element
13474 * @param {Roo.bootstrap.ComboBox} combo This combo box
13478 * @event touchviewdisplay
13479 * Fires when touch view require special display (default is using displayField)
13480 * @param {Roo.bootstrap.ComboBox} combo This combo box
13481 * @param {Object} cfg set html .
13483 'touchviewdisplay' : true
13488 this.tickItems = [];
13490 this.selectedIndex = -1;
13491 if(this.mode == 'local'){
13492 if(config.queryDelay === undefined){
13493 this.queryDelay = 10;
13495 if(config.minChars === undefined){
13501 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13504 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13505 * rendering into an Roo.Editor, defaults to false)
13508 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13509 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13512 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13515 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13516 * the dropdown list (defaults to undefined, with no header element)
13520 * @cfg {String/Roo.Template} tpl The template to use to render the output
13524 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13526 listWidth: undefined,
13528 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13529 * mode = 'remote' or 'text' if mode = 'local')
13531 displayField: undefined,
13534 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13535 * mode = 'remote' or 'value' if mode = 'local').
13536 * Note: use of a valueField requires the user make a selection
13537 * in order for a value to be mapped.
13539 valueField: undefined,
13541 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13546 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13547 * field's data value (defaults to the underlying DOM element's name)
13549 hiddenName: undefined,
13551 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13555 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13557 selectedClass: 'active',
13560 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13564 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13565 * anchor positions (defaults to 'tl-bl')
13567 listAlign: 'tl-bl?',
13569 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13573 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13574 * query specified by the allQuery config option (defaults to 'query')
13576 triggerAction: 'query',
13578 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13579 * (defaults to 4, does not apply if editable = false)
13583 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13584 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13588 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13589 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13593 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13594 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13598 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13599 * when editable = true (defaults to false)
13601 selectOnFocus:false,
13603 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13605 queryParam: 'query',
13607 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13608 * when mode = 'remote' (defaults to 'Loading...')
13610 loadingText: 'Loading...',
13612 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13616 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13620 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13621 * traditional select (defaults to true)
13625 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13629 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13633 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13634 * listWidth has a higher value)
13638 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13639 * allow the user to set arbitrary text into the field (defaults to false)
13641 forceSelection:false,
13643 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13644 * if typeAhead = true (defaults to 250)
13646 typeAheadDelay : 250,
13648 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13649 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13651 valueNotFoundText : undefined,
13653 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13655 blockFocus : false,
13658 * @cfg {Boolean} disableClear Disable showing of clear button.
13660 disableClear : false,
13662 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13664 alwaysQuery : false,
13667 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13672 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13674 invalidClass : "has-warning",
13677 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13679 validClass : "has-success",
13682 * @cfg {Boolean} specialFilter (true|false) special filter default false
13684 specialFilter : false,
13687 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13689 mobileTouchView : true,
13692 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13694 useNativeIOS : false,
13697 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13699 mobile_restrict_height : false,
13701 ios_options : false,
13713 btnPosition : 'right',
13714 triggerList : true,
13715 showToggleBtn : true,
13717 emptyResultText: 'Empty',
13718 triggerText : 'Select',
13721 // element that contains real text value.. (when hidden is used..)
13723 getAutoCreate : function()
13728 * Render classic select for iso
13731 if(Roo.isIOS && this.useNativeIOS){
13732 cfg = this.getAutoCreateNativeIOS();
13740 if(Roo.isTouch && this.mobileTouchView){
13741 cfg = this.getAutoCreateTouchView();
13748 if(!this.tickable){
13749 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13754 * ComboBox with tickable selections
13757 var align = this.labelAlign || this.parentLabelAlign();
13760 cls : 'form-group roo-combobox-tickable' //input-group
13763 var btn_text_select = '';
13764 var btn_text_done = '';
13765 var btn_text_cancel = '';
13767 if (this.btn_text_show) {
13768 btn_text_select = 'Select';
13769 btn_text_done = 'Done';
13770 btn_text_cancel = 'Cancel';
13775 cls : 'tickable-buttons',
13780 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13781 //html : this.triggerText
13782 html: btn_text_select
13788 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13790 html: btn_text_done
13796 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13798 html: btn_text_cancel
13804 buttons.cn.unshift({
13806 cls: 'roo-select2-search-field-input'
13812 Roo.each(buttons.cn, function(c){
13814 c.cls += ' btn-' + _this.size;
13817 if (_this.disabled) {
13824 style : 'display: contents',
13829 cls: 'form-hidden-field'
13833 cls: 'roo-select2-choices',
13837 cls: 'roo-select2-search-field',
13848 cls: 'roo-select2-container input-group roo-select2-container-multi',
13854 // cls: 'typeahead typeahead-long dropdown-menu',
13855 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13860 if(this.hasFeedback && !this.allowBlank){
13864 cls: 'glyphicon form-control-feedback'
13867 combobox.cn.push(feedback);
13872 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13873 tooltip : 'This field is required'
13875 if (Roo.bootstrap.version == 4) {
13878 style : 'display:none'
13881 if (align ==='left' && this.fieldLabel.length) {
13883 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13890 cls : 'control-label col-form-label',
13891 html : this.fieldLabel
13903 var labelCfg = cfg.cn[1];
13904 var contentCfg = cfg.cn[2];
13907 if(this.indicatorpos == 'right'){
13913 cls : 'control-label col-form-label',
13917 html : this.fieldLabel
13933 labelCfg = cfg.cn[0];
13934 contentCfg = cfg.cn[1];
13938 if(this.labelWidth > 12){
13939 labelCfg.style = "width: " + this.labelWidth + 'px';
13942 if(this.labelWidth < 13 && this.labelmd == 0){
13943 this.labelmd = this.labelWidth;
13946 if(this.labellg > 0){
13947 labelCfg.cls += ' col-lg-' + this.labellg;
13948 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13951 if(this.labelmd > 0){
13952 labelCfg.cls += ' col-md-' + this.labelmd;
13953 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13956 if(this.labelsm > 0){
13957 labelCfg.cls += ' col-sm-' + this.labelsm;
13958 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13961 if(this.labelxs > 0){
13962 labelCfg.cls += ' col-xs-' + this.labelxs;
13963 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13967 } else if ( this.fieldLabel.length) {
13968 // Roo.log(" label");
13973 //cls : 'input-group-addon',
13974 html : this.fieldLabel
13979 if(this.indicatorpos == 'right'){
13983 //cls : 'input-group-addon',
13984 html : this.fieldLabel
13994 // Roo.log(" no label && no align");
14001 ['xs','sm','md','lg'].map(function(size){
14002 if (settings[size]) {
14003 cfg.cls += ' col-' + size + '-' + settings[size];
14011 _initEventsCalled : false,
14014 initEvents: function()
14016 if (this._initEventsCalled) { // as we call render... prevent looping...
14019 this._initEventsCalled = true;
14022 throw "can not find store for combo";
14025 this.indicator = this.indicatorEl();
14027 this.store = Roo.factory(this.store, Roo.data);
14028 this.store.parent = this;
14030 // if we are building from html. then this element is so complex, that we can not really
14031 // use the rendered HTML.
14032 // so we have to trash and replace the previous code.
14033 if (Roo.XComponent.build_from_html) {
14034 // remove this element....
14035 var e = this.el.dom, k=0;
14036 while (e ) { e = e.previousSibling; ++k;}
14041 this.rendered = false;
14043 this.render(this.parent().getChildContainer(true), k);
14046 if(Roo.isIOS && this.useNativeIOS){
14047 this.initIOSView();
14055 if(Roo.isTouch && this.mobileTouchView){
14056 this.initTouchView();
14061 this.initTickableEvents();
14065 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
14067 if(this.hiddenName){
14069 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14071 this.hiddenField.dom.value =
14072 this.hiddenValue !== undefined ? this.hiddenValue :
14073 this.value !== undefined ? this.value : '';
14075 // prevent input submission
14076 this.el.dom.removeAttribute('name');
14077 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14082 // this.el.dom.setAttribute('autocomplete', 'off');
14085 var cls = 'x-combo-list';
14087 //this.list = new Roo.Layer({
14088 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
14094 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14095 _this.list.setWidth(lw);
14098 this.list.on('mouseover', this.onViewOver, this);
14099 this.list.on('mousemove', this.onViewMove, this);
14100 this.list.on('scroll', this.onViewScroll, this);
14103 this.list.swallowEvent('mousewheel');
14104 this.assetHeight = 0;
14107 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
14108 this.assetHeight += this.header.getHeight();
14111 this.innerList = this.list.createChild({cls:cls+'-inner'});
14112 this.innerList.on('mouseover', this.onViewOver, this);
14113 this.innerList.on('mousemove', this.onViewMove, this);
14114 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14116 if(this.allowBlank && !this.pageSize && !this.disableClear){
14117 this.footer = this.list.createChild({cls:cls+'-ft'});
14118 this.pageTb = new Roo.Toolbar(this.footer);
14122 this.footer = this.list.createChild({cls:cls+'-ft'});
14123 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
14124 {pageSize: this.pageSize});
14128 if (this.pageTb && this.allowBlank && !this.disableClear) {
14130 this.pageTb.add(new Roo.Toolbar.Fill(), {
14131 cls: 'x-btn-icon x-btn-clear',
14133 handler: function()
14136 _this.clearValue();
14137 _this.onSelect(false, -1);
14142 this.assetHeight += this.footer.getHeight();
14147 this.tpl = Roo.bootstrap.version == 4 ?
14148 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
14149 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
14152 this.view = new Roo.View(this.list, this.tpl, {
14153 singleSelect:true, store: this.store, selectedClass: this.selectedClass
14155 //this.view.wrapEl.setDisplayed(false);
14156 this.view.on('click', this.onViewClick, this);
14159 this.store.on('beforeload', this.onBeforeLoad, this);
14160 this.store.on('load', this.onLoad, this);
14161 this.store.on('loadexception', this.onLoadException, this);
14163 if(this.resizable){
14164 this.resizer = new Roo.Resizable(this.list, {
14165 pinned:true, handles:'se'
14167 this.resizer.on('resize', function(r, w, h){
14168 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
14169 this.listWidth = w;
14170 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
14171 this.restrictHeight();
14173 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
14176 if(!this.editable){
14177 this.editable = true;
14178 this.setEditable(false);
14183 if (typeof(this.events.add.listeners) != 'undefined') {
14185 this.addicon = this.wrap.createChild(
14186 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
14188 this.addicon.on('click', function(e) {
14189 this.fireEvent('add', this);
14192 if (typeof(this.events.edit.listeners) != 'undefined') {
14194 this.editicon = this.wrap.createChild(
14195 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
14196 if (this.addicon) {
14197 this.editicon.setStyle('margin-left', '40px');
14199 this.editicon.on('click', function(e) {
14201 // we fire even if inothing is selected..
14202 this.fireEvent('edit', this, this.lastData );
14208 this.keyNav = new Roo.KeyNav(this.inputEl(), {
14209 "up" : function(e){
14210 this.inKeyMode = true;
14214 "down" : function(e){
14215 if(!this.isExpanded()){
14216 this.onTriggerClick();
14218 this.inKeyMode = true;
14223 "enter" : function(e){
14224 // this.onViewClick();
14228 if(this.fireEvent("specialkey", this, e)){
14229 this.onViewClick(false);
14235 "esc" : function(e){
14239 "tab" : function(e){
14242 if(this.fireEvent("specialkey", this, e)){
14243 this.onViewClick(false);
14251 doRelay : function(foo, bar, hname){
14252 if(hname == 'down' || this.scope.isExpanded()){
14253 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14262 this.queryDelay = Math.max(this.queryDelay || 10,
14263 this.mode == 'local' ? 10 : 250);
14266 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14268 if(this.typeAhead){
14269 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14271 if(this.editable !== false){
14272 this.inputEl().on("keyup", this.onKeyUp, this);
14274 if(this.forceSelection){
14275 this.inputEl().on('blur', this.doForce, this);
14279 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14280 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14284 initTickableEvents: function()
14288 if(this.hiddenName){
14290 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14292 this.hiddenField.dom.value =
14293 this.hiddenValue !== undefined ? this.hiddenValue :
14294 this.value !== undefined ? this.value : '';
14296 // prevent input submission
14297 this.el.dom.removeAttribute('name');
14298 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14303 // this.list = this.el.select('ul.dropdown-menu',true).first();
14305 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14306 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14307 if(this.triggerList){
14308 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14311 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14312 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14314 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14315 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14317 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14318 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14320 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14321 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14322 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14325 this.cancelBtn.hide();
14330 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14331 _this.list.setWidth(lw);
14334 this.list.on('mouseover', this.onViewOver, this);
14335 this.list.on('mousemove', this.onViewMove, this);
14337 this.list.on('scroll', this.onViewScroll, this);
14340 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
14341 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14344 this.view = new Roo.View(this.list, this.tpl, {
14349 selectedClass: this.selectedClass
14352 //this.view.wrapEl.setDisplayed(false);
14353 this.view.on('click', this.onViewClick, this);
14357 this.store.on('beforeload', this.onBeforeLoad, this);
14358 this.store.on('load', this.onLoad, this);
14359 this.store.on('loadexception', this.onLoadException, this);
14362 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14363 "up" : function(e){
14364 this.inKeyMode = true;
14368 "down" : function(e){
14369 this.inKeyMode = true;
14373 "enter" : function(e){
14374 if(this.fireEvent("specialkey", this, e)){
14375 this.onViewClick(false);
14381 "esc" : function(e){
14382 this.onTickableFooterButtonClick(e, false, false);
14385 "tab" : function(e){
14386 this.fireEvent("specialkey", this, e);
14388 this.onTickableFooterButtonClick(e, false, false);
14395 doRelay : function(e, fn, key){
14396 if(this.scope.isExpanded()){
14397 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14406 this.queryDelay = Math.max(this.queryDelay || 10,
14407 this.mode == 'local' ? 10 : 250);
14410 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14412 if(this.typeAhead){
14413 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14416 if(this.editable !== false){
14417 this.tickableInputEl().on("keyup", this.onKeyUp, this);
14420 this.indicator = this.indicatorEl();
14422 if(this.indicator){
14423 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14424 this.indicator.hide();
14429 onDestroy : function(){
14431 this.view.setStore(null);
14432 this.view.el.removeAllListeners();
14433 this.view.el.remove();
14434 this.view.purgeListeners();
14437 this.list.dom.innerHTML = '';
14441 this.store.un('beforeload', this.onBeforeLoad, this);
14442 this.store.un('load', this.onLoad, this);
14443 this.store.un('loadexception', this.onLoadException, this);
14445 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14449 fireKey : function(e){
14450 if(e.isNavKeyPress() && !this.list.isVisible()){
14451 this.fireEvent("specialkey", this, e);
14456 onResize: function(w, h){
14457 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14459 // if(typeof w != 'number'){
14460 // // we do not handle it!?!?
14463 // var tw = this.trigger.getWidth();
14464 // // tw += this.addicon ? this.addicon.getWidth() : 0;
14465 // // tw += this.editicon ? this.editicon.getWidth() : 0;
14467 // this.inputEl().setWidth( this.adjustWidth('input', x));
14469 // //this.trigger.setStyle('left', x+'px');
14471 // if(this.list && this.listWidth === undefined){
14472 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14473 // this.list.setWidth(lw);
14474 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14482 * Allow or prevent the user from directly editing the field text. If false is passed,
14483 * the user will only be able to select from the items defined in the dropdown list. This method
14484 * is the runtime equivalent of setting the 'editable' config option at config time.
14485 * @param {Boolean} value True to allow the user to directly edit the field text
14487 setEditable : function(value){
14488 if(value == this.editable){
14491 this.editable = value;
14493 this.inputEl().dom.setAttribute('readOnly', true);
14494 this.inputEl().on('mousedown', this.onTriggerClick, this);
14495 this.inputEl().addClass('x-combo-noedit');
14497 this.inputEl().dom.setAttribute('readOnly', false);
14498 this.inputEl().un('mousedown', this.onTriggerClick, this);
14499 this.inputEl().removeClass('x-combo-noedit');
14505 onBeforeLoad : function(combo,opts){
14506 if(!this.hasFocus){
14510 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14512 this.restrictHeight();
14513 this.selectedIndex = -1;
14517 onLoad : function(){
14519 this.hasQuery = false;
14521 if(!this.hasFocus){
14525 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14526 this.loading.hide();
14529 if(this.store.getCount() > 0){
14532 this.restrictHeight();
14533 if(this.lastQuery == this.allQuery){
14534 if(this.editable && !this.tickable){
14535 this.inputEl().dom.select();
14539 !this.selectByValue(this.value, true) &&
14542 !this.store.lastOptions ||
14543 typeof(this.store.lastOptions.add) == 'undefined' ||
14544 this.store.lastOptions.add != true
14547 this.select(0, true);
14550 if(this.autoFocus){
14553 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14554 this.taTask.delay(this.typeAheadDelay);
14558 this.onEmptyResults();
14564 onLoadException : function()
14566 this.hasQuery = false;
14568 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14569 this.loading.hide();
14572 if(this.tickable && this.editable){
14577 // only causes errors at present
14578 //Roo.log(this.store.reader.jsonData);
14579 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14581 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14587 onTypeAhead : function(){
14588 if(this.store.getCount() > 0){
14589 var r = this.store.getAt(0);
14590 var newValue = r.data[this.displayField];
14591 var len = newValue.length;
14592 var selStart = this.getRawValue().length;
14594 if(selStart != len){
14595 this.setRawValue(newValue);
14596 this.selectText(selStart, newValue.length);
14602 onSelect : function(record, index){
14604 if(this.fireEvent('beforeselect', this, record, index) !== false){
14606 this.setFromData(index > -1 ? record.data : false);
14609 this.fireEvent('select', this, record, index);
14614 * Returns the currently selected field value or empty string if no value is set.
14615 * @return {String} value The selected value
14617 getValue : function()
14619 if(Roo.isIOS && this.useNativeIOS){
14620 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14624 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14627 if(this.valueField){
14628 return typeof this.value != 'undefined' ? this.value : '';
14630 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14634 getRawValue : function()
14636 if(Roo.isIOS && this.useNativeIOS){
14637 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14640 var v = this.inputEl().getValue();
14646 * Clears any text/value currently set in the field
14648 clearValue : function(){
14650 if(this.hiddenField){
14651 this.hiddenField.dom.value = '';
14654 this.setRawValue('');
14655 this.lastSelectionText = '';
14656 this.lastData = false;
14658 var close = this.closeTriggerEl();
14669 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14670 * will be displayed in the field. If the value does not match the data value of an existing item,
14671 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14672 * Otherwise the field will be blank (although the value will still be set).
14673 * @param {String} value The value to match
14675 setValue : function(v)
14677 if(Roo.isIOS && this.useNativeIOS){
14678 this.setIOSValue(v);
14688 if(this.valueField){
14689 var r = this.findRecord(this.valueField, v);
14691 text = r.data[this.displayField];
14692 }else if(this.valueNotFoundText !== undefined){
14693 text = this.valueNotFoundText;
14696 this.lastSelectionText = text;
14697 if(this.hiddenField){
14698 this.hiddenField.dom.value = v;
14700 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14703 var close = this.closeTriggerEl();
14706 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14712 * @property {Object} the last set data for the element
14717 * Sets the value of the field based on a object which is related to the record format for the store.
14718 * @param {Object} value the value to set as. or false on reset?
14720 setFromData : function(o){
14727 var dv = ''; // display value
14728 var vv = ''; // value value..
14730 if (this.displayField) {
14731 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14733 // this is an error condition!!!
14734 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14737 if(this.valueField){
14738 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14741 var close = this.closeTriggerEl();
14744 if(dv.length || vv * 1 > 0){
14746 this.blockFocus=true;
14752 if(this.hiddenField){
14753 this.hiddenField.dom.value = vv;
14755 this.lastSelectionText = dv;
14756 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14760 // no hidden field.. - we store the value in 'value', but still display
14761 // display field!!!!
14762 this.lastSelectionText = dv;
14763 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14770 reset : function(){
14771 // overridden so that last data is reset..
14778 this.setValue(this.originalValue);
14779 //this.clearInvalid();
14780 this.lastData = false;
14782 this.view.clearSelections();
14788 findRecord : function(prop, value){
14790 if(this.store.getCount() > 0){
14791 this.store.each(function(r){
14792 if(r.data[prop] == value){
14802 getName: function()
14804 // returns hidden if it's set..
14805 if (!this.rendered) {return ''};
14806 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14810 onViewMove : function(e, t){
14811 this.inKeyMode = false;
14815 onViewOver : function(e, t){
14816 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14819 var item = this.view.findItemFromChild(t);
14822 var index = this.view.indexOf(item);
14823 this.select(index, false);
14828 onViewClick : function(view, doFocus, el, e)
14830 var index = this.view.getSelectedIndexes()[0];
14832 var r = this.store.getAt(index);
14836 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14843 Roo.each(this.tickItems, function(v,k){
14845 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14847 _this.tickItems.splice(k, 1);
14849 if(typeof(e) == 'undefined' && view == false){
14850 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14862 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14863 this.tickItems.push(r.data);
14866 if(typeof(e) == 'undefined' && view == false){
14867 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14874 this.onSelect(r, index);
14876 if(doFocus !== false && !this.blockFocus){
14877 this.inputEl().focus();
14882 restrictHeight : function(){
14883 //this.innerList.dom.style.height = '';
14884 //var inner = this.innerList.dom;
14885 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14886 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14887 //this.list.beginUpdate();
14888 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14889 this.list.alignTo(this.inputEl(), this.listAlign);
14890 this.list.alignTo(this.inputEl(), this.listAlign);
14891 //this.list.endUpdate();
14895 onEmptyResults : function(){
14897 if(this.tickable && this.editable){
14898 this.hasFocus = false;
14899 this.restrictHeight();
14907 * Returns true if the dropdown list is expanded, else false.
14909 isExpanded : function(){
14910 return this.list.isVisible();
14914 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14915 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14916 * @param {String} value The data value of the item to select
14917 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14918 * selected item if it is not currently in view (defaults to true)
14919 * @return {Boolean} True if the value matched an item in the list, else false
14921 selectByValue : function(v, scrollIntoView){
14922 if(v !== undefined && v !== null){
14923 var r = this.findRecord(this.valueField || this.displayField, v);
14925 this.select(this.store.indexOf(r), scrollIntoView);
14933 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14934 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14935 * @param {Number} index The zero-based index of the list item to select
14936 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14937 * selected item if it is not currently in view (defaults to true)
14939 select : function(index, scrollIntoView){
14940 this.selectedIndex = index;
14941 this.view.select(index);
14942 if(scrollIntoView !== false){
14943 var el = this.view.getNode(index);
14945 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14948 this.list.scrollChildIntoView(el, false);
14954 selectNext : function(){
14955 var ct = this.store.getCount();
14957 if(this.selectedIndex == -1){
14959 }else if(this.selectedIndex < ct-1){
14960 this.select(this.selectedIndex+1);
14966 selectPrev : function(){
14967 var ct = this.store.getCount();
14969 if(this.selectedIndex == -1){
14971 }else if(this.selectedIndex != 0){
14972 this.select(this.selectedIndex-1);
14978 onKeyUp : function(e){
14979 if(this.editable !== false && !e.isSpecialKey()){
14980 this.lastKey = e.getKey();
14981 this.dqTask.delay(this.queryDelay);
14986 validateBlur : function(){
14987 return !this.list || !this.list.isVisible();
14991 initQuery : function(){
14993 var v = this.getRawValue();
14995 if(this.tickable && this.editable){
14996 v = this.tickableInputEl().getValue();
15003 doForce : function(){
15004 if(this.inputEl().dom.value.length > 0){
15005 this.inputEl().dom.value =
15006 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
15012 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
15013 * query allowing the query action to be canceled if needed.
15014 * @param {String} query The SQL query to execute
15015 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
15016 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
15017 * saved in the current store (defaults to false)
15019 doQuery : function(q, forceAll){
15021 if(q === undefined || q === null){
15026 forceAll: forceAll,
15030 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
15035 forceAll = qe.forceAll;
15036 if(forceAll === true || (q.length >= this.minChars)){
15038 this.hasQuery = true;
15040 if(this.lastQuery != q || this.alwaysQuery){
15041 this.lastQuery = q;
15042 if(this.mode == 'local'){
15043 this.selectedIndex = -1;
15045 this.store.clearFilter();
15048 if(this.specialFilter){
15049 this.fireEvent('specialfilter', this);
15054 this.store.filter(this.displayField, q);
15057 this.store.fireEvent("datachanged", this.store);
15064 this.store.baseParams[this.queryParam] = q;
15066 var options = {params : this.getParams(q)};
15069 options.add = true;
15070 options.params.start = this.page * this.pageSize;
15073 this.store.load(options);
15076 * this code will make the page width larger, at the beginning, the list not align correctly,
15077 * we should expand the list on onLoad
15078 * so command out it
15083 this.selectedIndex = -1;
15088 this.loadNext = false;
15092 getParams : function(q){
15094 //p[this.queryParam] = q;
15098 p.limit = this.pageSize;
15104 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
15106 collapse : function(){
15107 if(!this.isExpanded()){
15113 this.hasFocus = false;
15117 this.cancelBtn.hide();
15118 this.trigger.show();
15121 this.tickableInputEl().dom.value = '';
15122 this.tickableInputEl().blur();
15127 Roo.get(document).un('mousedown', this.collapseIf, this);
15128 Roo.get(document).un('mousewheel', this.collapseIf, this);
15129 if (!this.editable) {
15130 Roo.get(document).un('keydown', this.listKeyPress, this);
15132 this.fireEvent('collapse', this);
15138 collapseIf : function(e){
15139 var in_combo = e.within(this.el);
15140 var in_list = e.within(this.list);
15141 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
15143 if (in_combo || in_list || is_list) {
15144 //e.stopPropagation();
15149 this.onTickableFooterButtonClick(e, false, false);
15157 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
15159 expand : function(){
15161 if(this.isExpanded() || !this.hasFocus){
15165 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
15166 this.list.setWidth(lw);
15172 this.restrictHeight();
15176 this.tickItems = Roo.apply([], this.item);
15179 this.cancelBtn.show();
15180 this.trigger.hide();
15183 this.tickableInputEl().focus();
15188 Roo.get(document).on('mousedown', this.collapseIf, this);
15189 Roo.get(document).on('mousewheel', this.collapseIf, this);
15190 if (!this.editable) {
15191 Roo.get(document).on('keydown', this.listKeyPress, this);
15194 this.fireEvent('expand', this);
15198 // Implements the default empty TriggerField.onTriggerClick function
15199 onTriggerClick : function(e)
15201 Roo.log('trigger click');
15203 if(this.disabled || !this.triggerList){
15208 this.loadNext = false;
15210 if(this.isExpanded()){
15212 if (!this.blockFocus) {
15213 this.inputEl().focus();
15217 this.hasFocus = true;
15218 if(this.triggerAction == 'all') {
15219 this.doQuery(this.allQuery, true);
15221 this.doQuery(this.getRawValue());
15223 if (!this.blockFocus) {
15224 this.inputEl().focus();
15229 onTickableTriggerClick : function(e)
15236 this.loadNext = false;
15237 this.hasFocus = true;
15239 if(this.triggerAction == 'all') {
15240 this.doQuery(this.allQuery, true);
15242 this.doQuery(this.getRawValue());
15246 onSearchFieldClick : function(e)
15248 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15249 this.onTickableFooterButtonClick(e, false, false);
15253 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15258 this.loadNext = false;
15259 this.hasFocus = true;
15261 if(this.triggerAction == 'all') {
15262 this.doQuery(this.allQuery, true);
15264 this.doQuery(this.getRawValue());
15268 listKeyPress : function(e)
15270 //Roo.log('listkeypress');
15271 // scroll to first matching element based on key pres..
15272 if (e.isSpecialKey()) {
15275 var k = String.fromCharCode(e.getKey()).toUpperCase();
15278 var csel = this.view.getSelectedNodes();
15279 var cselitem = false;
15281 var ix = this.view.indexOf(csel[0]);
15282 cselitem = this.store.getAt(ix);
15283 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15289 this.store.each(function(v) {
15291 // start at existing selection.
15292 if (cselitem.id == v.id) {
15298 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15299 match = this.store.indexOf(v);
15305 if (match === false) {
15306 return true; // no more action?
15309 this.view.select(match);
15310 var sn = Roo.get(this.view.getSelectedNodes()[0]);
15311 sn.scrollIntoView(sn.dom.parentNode, false);
15314 onViewScroll : function(e, t){
15316 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){
15320 this.hasQuery = true;
15322 this.loading = this.list.select('.loading', true).first();
15324 if(this.loading === null){
15325 this.list.createChild({
15327 cls: 'loading roo-select2-more-results roo-select2-active',
15328 html: 'Loading more results...'
15331 this.loading = this.list.select('.loading', true).first();
15333 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15335 this.loading.hide();
15338 this.loading.show();
15343 this.loadNext = true;
15345 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15350 addItem : function(o)
15352 var dv = ''; // display value
15354 if (this.displayField) {
15355 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15357 // this is an error condition!!!
15358 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
15365 var choice = this.choices.createChild({
15367 cls: 'roo-select2-search-choice',
15376 cls: 'roo-select2-search-choice-close fa fa-times',
15381 }, this.searchField);
15383 var close = choice.select('a.roo-select2-search-choice-close', true).first();
15385 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15393 this.inputEl().dom.value = '';
15398 onRemoveItem : function(e, _self, o)
15400 e.preventDefault();
15402 this.lastItem = Roo.apply([], this.item);
15404 var index = this.item.indexOf(o.data) * 1;
15407 Roo.log('not this item?!');
15411 this.item.splice(index, 1);
15416 this.fireEvent('remove', this, e);
15422 syncValue : function()
15424 if(!this.item.length){
15431 Roo.each(this.item, function(i){
15432 if(_this.valueField){
15433 value.push(i[_this.valueField]);
15440 this.value = value.join(',');
15442 if(this.hiddenField){
15443 this.hiddenField.dom.value = this.value;
15446 this.store.fireEvent("datachanged", this.store);
15451 clearItem : function()
15453 if(!this.multiple){
15459 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15467 if(this.tickable && !Roo.isTouch){
15468 this.view.refresh();
15472 inputEl: function ()
15474 if(Roo.isIOS && this.useNativeIOS){
15475 return this.el.select('select.roo-ios-select', true).first();
15478 if(Roo.isTouch && this.mobileTouchView){
15479 return this.el.select('input.form-control',true).first();
15483 return this.searchField;
15486 return this.el.select('input.form-control',true).first();
15489 onTickableFooterButtonClick : function(e, btn, el)
15491 e.preventDefault();
15493 this.lastItem = Roo.apply([], this.item);
15495 if(btn && btn.name == 'cancel'){
15496 this.tickItems = Roo.apply([], this.item);
15505 Roo.each(this.tickItems, function(o){
15513 validate : function()
15515 if(this.getVisibilityEl().hasClass('hidden')){
15519 var v = this.getRawValue();
15522 v = this.getValue();
15525 if(this.disabled || this.allowBlank || v.length){
15530 this.markInvalid();
15534 tickableInputEl : function()
15536 if(!this.tickable || !this.editable){
15537 return this.inputEl();
15540 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15544 getAutoCreateTouchView : function()
15549 cls: 'form-group' //input-group
15555 type : this.inputType,
15556 cls : 'form-control x-combo-noedit',
15557 autocomplete: 'new-password',
15558 placeholder : this.placeholder || '',
15563 input.name = this.name;
15567 input.cls += ' input-' + this.size;
15570 if (this.disabled) {
15571 input.disabled = true;
15582 inputblock.cls += ' input-group';
15584 inputblock.cn.unshift({
15586 cls : 'input-group-addon input-group-prepend input-group-text',
15591 if(this.removable && !this.multiple){
15592 inputblock.cls += ' roo-removable';
15594 inputblock.cn.push({
15597 cls : 'roo-combo-removable-btn close'
15601 if(this.hasFeedback && !this.allowBlank){
15603 inputblock.cls += ' has-feedback';
15605 inputblock.cn.push({
15607 cls: 'glyphicon form-control-feedback'
15614 inputblock.cls += (this.before) ? '' : ' input-group';
15616 inputblock.cn.push({
15618 cls : 'input-group-addon input-group-append input-group-text',
15624 var ibwrap = inputblock;
15629 cls: 'roo-select2-choices',
15633 cls: 'roo-select2-search-field',
15646 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15651 cls: 'form-hidden-field'
15657 if(!this.multiple && this.showToggleBtn){
15663 if (this.caret != false) {
15666 cls: 'fa fa-' + this.caret
15673 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15675 Roo.bootstrap.version == 3 ? caret : '',
15678 cls: 'combobox-clear',
15692 combobox.cls += ' roo-select2-container-multi';
15695 var align = this.labelAlign || this.parentLabelAlign();
15697 if (align ==='left' && this.fieldLabel.length) {
15702 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15703 tooltip : 'This field is required'
15707 cls : 'control-label col-form-label',
15708 html : this.fieldLabel
15719 var labelCfg = cfg.cn[1];
15720 var contentCfg = cfg.cn[2];
15723 if(this.indicatorpos == 'right'){
15728 cls : 'control-label col-form-label',
15732 html : this.fieldLabel
15736 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15737 tooltip : 'This field is required'
15750 labelCfg = cfg.cn[0];
15751 contentCfg = cfg.cn[1];
15756 if(this.labelWidth > 12){
15757 labelCfg.style = "width: " + this.labelWidth + 'px';
15760 if(this.labelWidth < 13 && this.labelmd == 0){
15761 this.labelmd = this.labelWidth;
15764 if(this.labellg > 0){
15765 labelCfg.cls += ' col-lg-' + this.labellg;
15766 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15769 if(this.labelmd > 0){
15770 labelCfg.cls += ' col-md-' + this.labelmd;
15771 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15774 if(this.labelsm > 0){
15775 labelCfg.cls += ' col-sm-' + this.labelsm;
15776 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15779 if(this.labelxs > 0){
15780 labelCfg.cls += ' col-xs-' + this.labelxs;
15781 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15785 } else if ( this.fieldLabel.length) {
15789 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15790 tooltip : 'This field is required'
15794 cls : 'control-label',
15795 html : this.fieldLabel
15806 if(this.indicatorpos == 'right'){
15810 cls : 'control-label',
15811 html : this.fieldLabel,
15815 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15816 tooltip : 'This field is required'
15833 var settings = this;
15835 ['xs','sm','md','lg'].map(function(size){
15836 if (settings[size]) {
15837 cfg.cls += ' col-' + size + '-' + settings[size];
15844 initTouchView : function()
15846 this.renderTouchView();
15848 this.touchViewEl.on('scroll', function(){
15849 this.el.dom.scrollTop = 0;
15852 this.originalValue = this.getValue();
15854 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15856 this.inputEl().on("click", this.showTouchView, this);
15857 if (this.triggerEl) {
15858 this.triggerEl.on("click", this.showTouchView, this);
15862 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15863 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15865 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15867 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15868 this.store.on('load', this.onTouchViewLoad, this);
15869 this.store.on('loadexception', this.onTouchViewLoadException, this);
15871 if(this.hiddenName){
15873 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15875 this.hiddenField.dom.value =
15876 this.hiddenValue !== undefined ? this.hiddenValue :
15877 this.value !== undefined ? this.value : '';
15879 this.el.dom.removeAttribute('name');
15880 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15884 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15885 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15888 if(this.removable && !this.multiple){
15889 var close = this.closeTriggerEl();
15891 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15892 close.on('click', this.removeBtnClick, this, close);
15896 * fix the bug in Safari iOS8
15898 this.inputEl().on("focus", function(e){
15899 document.activeElement.blur();
15902 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15909 renderTouchView : function()
15911 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15912 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15914 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15915 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15917 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15918 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15919 this.touchViewBodyEl.setStyle('overflow', 'auto');
15921 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15922 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15924 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15925 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15929 showTouchView : function()
15935 this.touchViewHeaderEl.hide();
15937 if(this.modalTitle.length){
15938 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15939 this.touchViewHeaderEl.show();
15942 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15943 this.touchViewEl.show();
15945 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15947 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15948 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15950 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15952 if(this.modalTitle.length){
15953 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15956 this.touchViewBodyEl.setHeight(bodyHeight);
15960 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15962 this.touchViewEl.addClass('in');
15965 if(this._touchViewMask){
15966 Roo.get(document.body).addClass("x-body-masked");
15967 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15968 this._touchViewMask.setStyle('z-index', 10000);
15969 this._touchViewMask.addClass('show');
15972 this.doTouchViewQuery();
15976 hideTouchView : function()
15978 this.touchViewEl.removeClass('in');
15982 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15984 this.touchViewEl.setStyle('display', 'none');
15987 if(this._touchViewMask){
15988 this._touchViewMask.removeClass('show');
15989 Roo.get(document.body).removeClass("x-body-masked");
15993 setTouchViewValue : function()
16000 Roo.each(this.tickItems, function(o){
16005 this.hideTouchView();
16008 doTouchViewQuery : function()
16017 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
16021 if(!this.alwaysQuery || this.mode == 'local'){
16022 this.onTouchViewLoad();
16029 onTouchViewBeforeLoad : function(combo,opts)
16035 onTouchViewLoad : function()
16037 if(this.store.getCount() < 1){
16038 this.onTouchViewEmptyResults();
16042 this.clearTouchView();
16044 var rawValue = this.getRawValue();
16046 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
16048 this.tickItems = [];
16050 this.store.data.each(function(d, rowIndex){
16051 var row = this.touchViewListGroup.createChild(template);
16053 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
16054 row.addClass(d.data.cls);
16057 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16060 html : d.data[this.displayField]
16063 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
16064 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
16067 row.removeClass('selected');
16068 if(!this.multiple && this.valueField &&
16069 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
16072 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16073 row.addClass('selected');
16076 if(this.multiple && this.valueField &&
16077 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
16081 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16082 this.tickItems.push(d.data);
16085 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
16089 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
16091 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16093 if(this.modalTitle.length){
16094 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16097 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
16099 if(this.mobile_restrict_height && listHeight < bodyHeight){
16100 this.touchViewBodyEl.setHeight(listHeight);
16105 if(firstChecked && listHeight > bodyHeight){
16106 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
16111 onTouchViewLoadException : function()
16113 this.hideTouchView();
16116 onTouchViewEmptyResults : function()
16118 this.clearTouchView();
16120 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
16122 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
16126 clearTouchView : function()
16128 this.touchViewListGroup.dom.innerHTML = '';
16131 onTouchViewClick : function(e, el, o)
16133 e.preventDefault();
16136 var rowIndex = o.rowIndex;
16138 var r = this.store.getAt(rowIndex);
16140 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
16142 if(!this.multiple){
16143 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
16144 c.dom.removeAttribute('checked');
16147 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16149 this.setFromData(r.data);
16151 var close = this.closeTriggerEl();
16157 this.hideTouchView();
16159 this.fireEvent('select', this, r, rowIndex);
16164 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
16165 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
16166 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
16170 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16171 this.addItem(r.data);
16172 this.tickItems.push(r.data);
16176 getAutoCreateNativeIOS : function()
16179 cls: 'form-group' //input-group,
16184 cls : 'roo-ios-select'
16188 combobox.name = this.name;
16191 if (this.disabled) {
16192 combobox.disabled = true;
16195 var settings = this;
16197 ['xs','sm','md','lg'].map(function(size){
16198 if (settings[size]) {
16199 cfg.cls += ' col-' + size + '-' + settings[size];
16209 initIOSView : function()
16211 this.store.on('load', this.onIOSViewLoad, this);
16216 onIOSViewLoad : function()
16218 if(this.store.getCount() < 1){
16222 this.clearIOSView();
16224 if(this.allowBlank) {
16226 var default_text = '-- SELECT --';
16228 if(this.placeholder.length){
16229 default_text = this.placeholder;
16232 if(this.emptyTitle.length){
16233 default_text += ' - ' + this.emptyTitle + ' -';
16236 var opt = this.inputEl().createChild({
16239 html : default_text
16243 o[this.valueField] = 0;
16244 o[this.displayField] = default_text;
16246 this.ios_options.push({
16253 this.store.data.each(function(d, rowIndex){
16257 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16258 html = d.data[this.displayField];
16263 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16264 value = d.data[this.valueField];
16273 if(this.value == d.data[this.valueField]){
16274 option['selected'] = true;
16277 var opt = this.inputEl().createChild(option);
16279 this.ios_options.push({
16286 this.inputEl().on('change', function(){
16287 this.fireEvent('select', this);
16292 clearIOSView: function()
16294 this.inputEl().dom.innerHTML = '';
16296 this.ios_options = [];
16299 setIOSValue: function(v)
16303 if(!this.ios_options){
16307 Roo.each(this.ios_options, function(opts){
16309 opts.el.dom.removeAttribute('selected');
16311 if(opts.data[this.valueField] != v){
16315 opts.el.dom.setAttribute('selected', true);
16321 * @cfg {Boolean} grow
16325 * @cfg {Number} growMin
16329 * @cfg {Number} growMax
16338 Roo.apply(Roo.bootstrap.ComboBox, {
16342 cls: 'modal-header',
16364 cls: 'list-group-item',
16368 cls: 'roo-combobox-list-group-item-value'
16372 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16386 listItemCheckbox : {
16388 cls: 'list-group-item',
16392 cls: 'roo-combobox-list-group-item-value'
16396 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16412 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16417 cls: 'modal-footer',
16425 cls: 'col-xs-6 text-left',
16428 cls: 'btn btn-danger roo-touch-view-cancel',
16434 cls: 'col-xs-6 text-right',
16437 cls: 'btn btn-success roo-touch-view-ok',
16448 Roo.apply(Roo.bootstrap.ComboBox, {
16450 touchViewTemplate : {
16452 cls: 'modal fade roo-combobox-touch-view',
16456 cls: 'modal-dialog',
16457 style : 'position:fixed', // we have to fix position....
16461 cls: 'modal-content',
16463 Roo.bootstrap.ComboBox.header,
16464 Roo.bootstrap.ComboBox.body,
16465 Roo.bootstrap.ComboBox.footer
16474 * Ext JS Library 1.1.1
16475 * Copyright(c) 2006-2007, Ext JS, LLC.
16477 * Originally Released Under LGPL - original licence link has changed is not relivant.
16480 * <script type="text/javascript">
16485 * @extends Roo.util.Observable
16486 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16487 * This class also supports single and multi selection modes. <br>
16488 * Create a data model bound view:
16490 var store = new Roo.data.Store(...);
16492 var view = new Roo.View({
16494 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16496 singleSelect: true,
16497 selectedClass: "ydataview-selected",
16501 // listen for node click?
16502 view.on("click", function(vw, index, node, e){
16503 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16507 dataModel.load("foobar.xml");
16509 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16511 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16512 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16514 * Note: old style constructor is still suported (container, template, config)
16517 * Create a new View
16518 * @param {Object} config The config object
16521 Roo.View = function(config, depreciated_tpl, depreciated_config){
16523 this.parent = false;
16525 if (typeof(depreciated_tpl) == 'undefined') {
16526 // new way.. - universal constructor.
16527 Roo.apply(this, config);
16528 this.el = Roo.get(this.el);
16531 this.el = Roo.get(config);
16532 this.tpl = depreciated_tpl;
16533 Roo.apply(this, depreciated_config);
16535 this.wrapEl = this.el.wrap().wrap();
16536 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16539 if(typeof(this.tpl) == "string"){
16540 this.tpl = new Roo.Template(this.tpl);
16542 // support xtype ctors..
16543 this.tpl = new Roo.factory(this.tpl, Roo);
16547 this.tpl.compile();
16552 * @event beforeclick
16553 * Fires before a click is processed. Returns false to cancel the default action.
16554 * @param {Roo.View} this
16555 * @param {Number} index The index of the target node
16556 * @param {HTMLElement} node The target node
16557 * @param {Roo.EventObject} e The raw event object
16559 "beforeclick" : true,
16562 * Fires when a template node is clicked.
16563 * @param {Roo.View} this
16564 * @param {Number} index The index of the target node
16565 * @param {HTMLElement} node The target node
16566 * @param {Roo.EventObject} e The raw event object
16571 * Fires when a template node is double clicked.
16572 * @param {Roo.View} this
16573 * @param {Number} index The index of the target node
16574 * @param {HTMLElement} node The target node
16575 * @param {Roo.EventObject} e The raw event object
16579 * @event contextmenu
16580 * Fires when a template node is right clicked.
16581 * @param {Roo.View} this
16582 * @param {Number} index The index of the target node
16583 * @param {HTMLElement} node The target node
16584 * @param {Roo.EventObject} e The raw event object
16586 "contextmenu" : true,
16588 * @event selectionchange
16589 * Fires when the selected nodes change.
16590 * @param {Roo.View} this
16591 * @param {Array} selections Array of the selected nodes
16593 "selectionchange" : true,
16596 * @event beforeselect
16597 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16598 * @param {Roo.View} this
16599 * @param {HTMLElement} node The node to be selected
16600 * @param {Array} selections Array of currently selected nodes
16602 "beforeselect" : true,
16604 * @event preparedata
16605 * Fires on every row to render, to allow you to change the data.
16606 * @param {Roo.View} this
16607 * @param {Object} data to be rendered (change this)
16609 "preparedata" : true
16617 "click": this.onClick,
16618 "dblclick": this.onDblClick,
16619 "contextmenu": this.onContextMenu,
16623 this.selections = [];
16625 this.cmp = new Roo.CompositeElementLite([]);
16627 this.store = Roo.factory(this.store, Roo.data);
16628 this.setStore(this.store, true);
16631 if ( this.footer && this.footer.xtype) {
16633 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16635 this.footer.dataSource = this.store;
16636 this.footer.container = fctr;
16637 this.footer = Roo.factory(this.footer, Roo);
16638 fctr.insertFirst(this.el);
16640 // this is a bit insane - as the paging toolbar seems to detach the el..
16641 // dom.parentNode.parentNode.parentNode
16642 // they get detached?
16646 Roo.View.superclass.constructor.call(this);
16651 Roo.extend(Roo.View, Roo.util.Observable, {
16654 * @cfg {Roo.data.Store} store Data store to load data from.
16659 * @cfg {String|Roo.Element} el The container element.
16664 * @cfg {String|Roo.Template} tpl The template used by this View
16668 * @cfg {String} dataName the named area of the template to use as the data area
16669 * Works with domtemplates roo-name="name"
16673 * @cfg {String} selectedClass The css class to add to selected nodes
16675 selectedClass : "x-view-selected",
16677 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16682 * @cfg {String} text to display on mask (default Loading)
16686 * @cfg {Boolean} multiSelect Allow multiple selection
16688 multiSelect : false,
16690 * @cfg {Boolean} singleSelect Allow single selection
16692 singleSelect: false,
16695 * @cfg {Boolean} toggleSelect - selecting
16697 toggleSelect : false,
16700 * @cfg {Boolean} tickable - selecting
16705 * Returns the element this view is bound to.
16706 * @return {Roo.Element}
16708 getEl : function(){
16709 return this.wrapEl;
16715 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16717 refresh : function(){
16718 //Roo.log('refresh');
16721 // if we are using something like 'domtemplate', then
16722 // the what gets used is:
16723 // t.applySubtemplate(NAME, data, wrapping data..)
16724 // the outer template then get' applied with
16725 // the store 'extra data'
16726 // and the body get's added to the
16727 // roo-name="data" node?
16728 // <span class='roo-tpl-{name}'></span> ?????
16732 this.clearSelections();
16733 this.el.update("");
16735 var records = this.store.getRange();
16736 if(records.length < 1) {
16738 // is this valid?? = should it render a template??
16740 this.el.update(this.emptyText);
16744 if (this.dataName) {
16745 this.el.update(t.apply(this.store.meta)); //????
16746 el = this.el.child('.roo-tpl-' + this.dataName);
16749 for(var i = 0, len = records.length; i < len; i++){
16750 var data = this.prepareData(records[i].data, i, records[i]);
16751 this.fireEvent("preparedata", this, data, i, records[i]);
16753 var d = Roo.apply({}, data);
16756 Roo.apply(d, {'roo-id' : Roo.id()});
16760 Roo.each(this.parent.item, function(item){
16761 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16764 Roo.apply(d, {'roo-data-checked' : 'checked'});
16768 html[html.length] = Roo.util.Format.trim(
16770 t.applySubtemplate(this.dataName, d, this.store.meta) :
16777 el.update(html.join(""));
16778 this.nodes = el.dom.childNodes;
16779 this.updateIndexes(0);
16784 * Function to override to reformat the data that is sent to
16785 * the template for each node.
16786 * DEPRICATED - use the preparedata event handler.
16787 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16788 * a JSON object for an UpdateManager bound view).
16790 prepareData : function(data, index, record)
16792 this.fireEvent("preparedata", this, data, index, record);
16796 onUpdate : function(ds, record){
16797 // Roo.log('on update');
16798 this.clearSelections();
16799 var index = this.store.indexOf(record);
16800 var n = this.nodes[index];
16801 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16802 n.parentNode.removeChild(n);
16803 this.updateIndexes(index, index);
16809 onAdd : function(ds, records, index)
16811 //Roo.log(['on Add', ds, records, index] );
16812 this.clearSelections();
16813 if(this.nodes.length == 0){
16817 var n = this.nodes[index];
16818 for(var i = 0, len = records.length; i < len; i++){
16819 var d = this.prepareData(records[i].data, i, records[i]);
16821 this.tpl.insertBefore(n, d);
16824 this.tpl.append(this.el, d);
16827 this.updateIndexes(index);
16830 onRemove : function(ds, record, index){
16831 // Roo.log('onRemove');
16832 this.clearSelections();
16833 var el = this.dataName ?
16834 this.el.child('.roo-tpl-' + this.dataName) :
16837 el.dom.removeChild(this.nodes[index]);
16838 this.updateIndexes(index);
16842 * Refresh an individual node.
16843 * @param {Number} index
16845 refreshNode : function(index){
16846 this.onUpdate(this.store, this.store.getAt(index));
16849 updateIndexes : function(startIndex, endIndex){
16850 var ns = this.nodes;
16851 startIndex = startIndex || 0;
16852 endIndex = endIndex || ns.length - 1;
16853 for(var i = startIndex; i <= endIndex; i++){
16854 ns[i].nodeIndex = i;
16859 * Changes the data store this view uses and refresh the view.
16860 * @param {Store} store
16862 setStore : function(store, initial){
16863 if(!initial && this.store){
16864 this.store.un("datachanged", this.refresh);
16865 this.store.un("add", this.onAdd);
16866 this.store.un("remove", this.onRemove);
16867 this.store.un("update", this.onUpdate);
16868 this.store.un("clear", this.refresh);
16869 this.store.un("beforeload", this.onBeforeLoad);
16870 this.store.un("load", this.onLoad);
16871 this.store.un("loadexception", this.onLoad);
16875 store.on("datachanged", this.refresh, this);
16876 store.on("add", this.onAdd, this);
16877 store.on("remove", this.onRemove, this);
16878 store.on("update", this.onUpdate, this);
16879 store.on("clear", this.refresh, this);
16880 store.on("beforeload", this.onBeforeLoad, this);
16881 store.on("load", this.onLoad, this);
16882 store.on("loadexception", this.onLoad, this);
16890 * onbeforeLoad - masks the loading area.
16893 onBeforeLoad : function(store,opts)
16895 //Roo.log('onBeforeLoad');
16897 this.el.update("");
16899 this.el.mask(this.mask ? this.mask : "Loading" );
16901 onLoad : function ()
16908 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16909 * @param {HTMLElement} node
16910 * @return {HTMLElement} The template node
16912 findItemFromChild : function(node){
16913 var el = this.dataName ?
16914 this.el.child('.roo-tpl-' + this.dataName,true) :
16917 if(!node || node.parentNode == el){
16920 var p = node.parentNode;
16921 while(p && p != el){
16922 if(p.parentNode == el){
16931 onClick : function(e){
16932 var item = this.findItemFromChild(e.getTarget());
16934 var index = this.indexOf(item);
16935 if(this.onItemClick(item, index, e) !== false){
16936 this.fireEvent("click", this, index, item, e);
16939 this.clearSelections();
16944 onContextMenu : function(e){
16945 var item = this.findItemFromChild(e.getTarget());
16947 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16952 onDblClick : function(e){
16953 var item = this.findItemFromChild(e.getTarget());
16955 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16959 onItemClick : function(item, index, e)
16961 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16964 if (this.toggleSelect) {
16965 var m = this.isSelected(item) ? 'unselect' : 'select';
16968 _t[m](item, true, false);
16971 if(this.multiSelect || this.singleSelect){
16972 if(this.multiSelect && e.shiftKey && this.lastSelection){
16973 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16975 this.select(item, this.multiSelect && e.ctrlKey);
16976 this.lastSelection = item;
16979 if(!this.tickable){
16980 e.preventDefault();
16988 * Get the number of selected nodes.
16991 getSelectionCount : function(){
16992 return this.selections.length;
16996 * Get the currently selected nodes.
16997 * @return {Array} An array of HTMLElements
16999 getSelectedNodes : function(){
17000 return this.selections;
17004 * Get the indexes of the selected nodes.
17007 getSelectedIndexes : function(){
17008 var indexes = [], s = this.selections;
17009 for(var i = 0, len = s.length; i < len; i++){
17010 indexes.push(s[i].nodeIndex);
17016 * Clear all selections
17017 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
17019 clearSelections : function(suppressEvent){
17020 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
17021 this.cmp.elements = this.selections;
17022 this.cmp.removeClass(this.selectedClass);
17023 this.selections = [];
17024 if(!suppressEvent){
17025 this.fireEvent("selectionchange", this, this.selections);
17031 * Returns true if the passed node is selected
17032 * @param {HTMLElement/Number} node The node or node index
17033 * @return {Boolean}
17035 isSelected : function(node){
17036 var s = this.selections;
17040 node = this.getNode(node);
17041 return s.indexOf(node) !== -1;
17046 * @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
17047 * @param {Boolean} keepExisting (optional) true to keep existing selections
17048 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17050 select : function(nodeInfo, keepExisting, suppressEvent){
17051 if(nodeInfo instanceof Array){
17053 this.clearSelections(true);
17055 for(var i = 0, len = nodeInfo.length; i < len; i++){
17056 this.select(nodeInfo[i], true, true);
17060 var node = this.getNode(nodeInfo);
17061 if(!node || this.isSelected(node)){
17062 return; // already selected.
17065 this.clearSelections(true);
17068 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
17069 Roo.fly(node).addClass(this.selectedClass);
17070 this.selections.push(node);
17071 if(!suppressEvent){
17072 this.fireEvent("selectionchange", this, this.selections);
17080 * @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
17081 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
17082 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17084 unselect : function(nodeInfo, keepExisting, suppressEvent)
17086 if(nodeInfo instanceof Array){
17087 Roo.each(this.selections, function(s) {
17088 this.unselect(s, nodeInfo);
17092 var node = this.getNode(nodeInfo);
17093 if(!node || !this.isSelected(node)){
17094 //Roo.log("not selected");
17095 return; // not selected.
17099 Roo.each(this.selections, function(s) {
17101 Roo.fly(node).removeClass(this.selectedClass);
17108 this.selections= ns;
17109 this.fireEvent("selectionchange", this, this.selections);
17113 * Gets a template node.
17114 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17115 * @return {HTMLElement} The node or null if it wasn't found
17117 getNode : function(nodeInfo){
17118 if(typeof nodeInfo == "string"){
17119 return document.getElementById(nodeInfo);
17120 }else if(typeof nodeInfo == "number"){
17121 return this.nodes[nodeInfo];
17127 * Gets a range template nodes.
17128 * @param {Number} startIndex
17129 * @param {Number} endIndex
17130 * @return {Array} An array of nodes
17132 getNodes : function(start, end){
17133 var ns = this.nodes;
17134 start = start || 0;
17135 end = typeof end == "undefined" ? ns.length - 1 : end;
17138 for(var i = start; i <= end; i++){
17142 for(var i = start; i >= end; i--){
17150 * Finds the index of the passed node
17151 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17152 * @return {Number} The index of the node or -1
17154 indexOf : function(node){
17155 node = this.getNode(node);
17156 if(typeof node.nodeIndex == "number"){
17157 return node.nodeIndex;
17159 var ns = this.nodes;
17160 for(var i = 0, len = ns.length; i < len; i++){
17171 * based on jquery fullcalendar
17175 Roo.bootstrap = Roo.bootstrap || {};
17177 * @class Roo.bootstrap.Calendar
17178 * @extends Roo.bootstrap.Component
17179 * Bootstrap Calendar class
17180 * @cfg {Boolean} loadMask (true|false) default false
17181 * @cfg {Object} header generate the user specific header of the calendar, default false
17184 * Create a new Container
17185 * @param {Object} config The config object
17190 Roo.bootstrap.Calendar = function(config){
17191 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
17195 * Fires when a date is selected
17196 * @param {DatePicker} this
17197 * @param {Date} date The selected date
17201 * @event monthchange
17202 * Fires when the displayed month changes
17203 * @param {DatePicker} this
17204 * @param {Date} date The selected month
17206 'monthchange': true,
17208 * @event evententer
17209 * Fires when mouse over an event
17210 * @param {Calendar} this
17211 * @param {event} Event
17213 'evententer': true,
17215 * @event eventleave
17216 * Fires when the mouse leaves an
17217 * @param {Calendar} this
17220 'eventleave': true,
17222 * @event eventclick
17223 * Fires when the mouse click an
17224 * @param {Calendar} this
17233 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
17236 * @cfg {Number} startDay
17237 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17245 getAutoCreate : function(){
17248 var fc_button = function(name, corner, style, content ) {
17249 return Roo.apply({},{
17251 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
17253 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17256 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17267 style : 'width:100%',
17274 cls : 'fc-header-left',
17276 fc_button('prev', 'left', 'arrow', '‹' ),
17277 fc_button('next', 'right', 'arrow', '›' ),
17278 { tag: 'span', cls: 'fc-header-space' },
17279 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
17287 cls : 'fc-header-center',
17291 cls: 'fc-header-title',
17294 html : 'month / year'
17302 cls : 'fc-header-right',
17304 /* fc_button('month', 'left', '', 'month' ),
17305 fc_button('week', '', '', 'week' ),
17306 fc_button('day', 'right', '', 'day' )
17318 header = this.header;
17321 var cal_heads = function() {
17323 // fixme - handle this.
17325 for (var i =0; i < Date.dayNames.length; i++) {
17326 var d = Date.dayNames[i];
17329 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17330 html : d.substring(0,3)
17334 ret[0].cls += ' fc-first';
17335 ret[6].cls += ' fc-last';
17338 var cal_cell = function(n) {
17341 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17346 cls: 'fc-day-number',
17350 cls: 'fc-day-content',
17354 style: 'position: relative;' // height: 17px;
17366 var cal_rows = function() {
17369 for (var r = 0; r < 6; r++) {
17376 for (var i =0; i < Date.dayNames.length; i++) {
17377 var d = Date.dayNames[i];
17378 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17381 row.cn[0].cls+=' fc-first';
17382 row.cn[0].cn[0].style = 'min-height:90px';
17383 row.cn[6].cls+=' fc-last';
17387 ret[0].cls += ' fc-first';
17388 ret[4].cls += ' fc-prev-last';
17389 ret[5].cls += ' fc-last';
17396 cls: 'fc-border-separate',
17397 style : 'width:100%',
17405 cls : 'fc-first fc-last',
17423 cls : 'fc-content',
17424 style : "position: relative;",
17427 cls : 'fc-view fc-view-month fc-grid',
17428 style : 'position: relative',
17429 unselectable : 'on',
17432 cls : 'fc-event-container',
17433 style : 'position:absolute;z-index:8;top:0;left:0;'
17451 initEvents : function()
17454 throw "can not find store for calendar";
17460 style: "text-align:center",
17464 style: "background-color:white;width:50%;margin:250 auto",
17468 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17479 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17481 var size = this.el.select('.fc-content', true).first().getSize();
17482 this.maskEl.setSize(size.width, size.height);
17483 this.maskEl.enableDisplayMode("block");
17484 if(!this.loadMask){
17485 this.maskEl.hide();
17488 this.store = Roo.factory(this.store, Roo.data);
17489 this.store.on('load', this.onLoad, this);
17490 this.store.on('beforeload', this.onBeforeLoad, this);
17494 this.cells = this.el.select('.fc-day',true);
17495 //Roo.log(this.cells);
17496 this.textNodes = this.el.query('.fc-day-number');
17497 this.cells.addClassOnOver('fc-state-hover');
17499 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17500 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17501 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17502 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17504 this.on('monthchange', this.onMonthChange, this);
17506 this.update(new Date().clearTime());
17509 resize : function() {
17510 var sz = this.el.getSize();
17512 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17513 this.el.select('.fc-day-content div',true).setHeight(34);
17518 showPrevMonth : function(e){
17519 this.update(this.activeDate.add("mo", -1));
17521 showToday : function(e){
17522 this.update(new Date().clearTime());
17525 showNextMonth : function(e){
17526 this.update(this.activeDate.add("mo", 1));
17530 showPrevYear : function(){
17531 this.update(this.activeDate.add("y", -1));
17535 showNextYear : function(){
17536 this.update(this.activeDate.add("y", 1));
17541 update : function(date)
17543 var vd = this.activeDate;
17544 this.activeDate = date;
17545 // if(vd && this.el){
17546 // var t = date.getTime();
17547 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17548 // Roo.log('using add remove');
17550 // this.fireEvent('monthchange', this, date);
17552 // this.cells.removeClass("fc-state-highlight");
17553 // this.cells.each(function(c){
17554 // if(c.dateValue == t){
17555 // c.addClass("fc-state-highlight");
17556 // setTimeout(function(){
17557 // try{c.dom.firstChild.focus();}catch(e){}
17567 var days = date.getDaysInMonth();
17569 var firstOfMonth = date.getFirstDateOfMonth();
17570 var startingPos = firstOfMonth.getDay()-this.startDay;
17572 if(startingPos < this.startDay){
17576 var pm = date.add(Date.MONTH, -1);
17577 var prevStart = pm.getDaysInMonth()-startingPos;
17579 this.cells = this.el.select('.fc-day',true);
17580 this.textNodes = this.el.query('.fc-day-number');
17581 this.cells.addClassOnOver('fc-state-hover');
17583 var cells = this.cells.elements;
17584 var textEls = this.textNodes;
17586 Roo.each(cells, function(cell){
17587 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17590 days += startingPos;
17592 // convert everything to numbers so it's fast
17593 var day = 86400000;
17594 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17597 //Roo.log(prevStart);
17599 var today = new Date().clearTime().getTime();
17600 var sel = date.clearTime().getTime();
17601 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17602 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17603 var ddMatch = this.disabledDatesRE;
17604 var ddText = this.disabledDatesText;
17605 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17606 var ddaysText = this.disabledDaysText;
17607 var format = this.format;
17609 var setCellClass = function(cal, cell){
17613 //Roo.log('set Cell Class');
17615 var t = d.getTime();
17619 cell.dateValue = t;
17621 cell.className += " fc-today";
17622 cell.className += " fc-state-highlight";
17623 cell.title = cal.todayText;
17626 // disable highlight in other month..
17627 //cell.className += " fc-state-highlight";
17632 cell.className = " fc-state-disabled";
17633 cell.title = cal.minText;
17637 cell.className = " fc-state-disabled";
17638 cell.title = cal.maxText;
17642 if(ddays.indexOf(d.getDay()) != -1){
17643 cell.title = ddaysText;
17644 cell.className = " fc-state-disabled";
17647 if(ddMatch && format){
17648 var fvalue = d.dateFormat(format);
17649 if(ddMatch.test(fvalue)){
17650 cell.title = ddText.replace("%0", fvalue);
17651 cell.className = " fc-state-disabled";
17655 if (!cell.initialClassName) {
17656 cell.initialClassName = cell.dom.className;
17659 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17664 for(; i < startingPos; i++) {
17665 textEls[i].innerHTML = (++prevStart);
17666 d.setDate(d.getDate()+1);
17668 cells[i].className = "fc-past fc-other-month";
17669 setCellClass(this, cells[i]);
17674 for(; i < days; i++){
17675 intDay = i - startingPos + 1;
17676 textEls[i].innerHTML = (intDay);
17677 d.setDate(d.getDate()+1);
17679 cells[i].className = ''; // "x-date-active";
17680 setCellClass(this, cells[i]);
17684 for(; i < 42; i++) {
17685 textEls[i].innerHTML = (++extraDays);
17686 d.setDate(d.getDate()+1);
17688 cells[i].className = "fc-future fc-other-month";
17689 setCellClass(this, cells[i]);
17692 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17694 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17696 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17697 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17699 if(totalRows != 6){
17700 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17701 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17704 this.fireEvent('monthchange', this, date);
17708 if(!this.internalRender){
17709 var main = this.el.dom.firstChild;
17710 var w = main.offsetWidth;
17711 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17712 Roo.fly(main).setWidth(w);
17713 this.internalRender = true;
17714 // opera does not respect the auto grow header center column
17715 // then, after it gets a width opera refuses to recalculate
17716 // without a second pass
17717 if(Roo.isOpera && !this.secondPass){
17718 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17719 this.secondPass = true;
17720 this.update.defer(10, this, [date]);
17727 findCell : function(dt) {
17728 dt = dt.clearTime().getTime();
17730 this.cells.each(function(c){
17731 //Roo.log("check " +c.dateValue + '?=' + dt);
17732 if(c.dateValue == dt){
17742 findCells : function(ev) {
17743 var s = ev.start.clone().clearTime().getTime();
17745 var e= ev.end.clone().clearTime().getTime();
17748 this.cells.each(function(c){
17749 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17751 if(c.dateValue > e){
17754 if(c.dateValue < s){
17763 // findBestRow: function(cells)
17767 // for (var i =0 ; i < cells.length;i++) {
17768 // ret = Math.max(cells[i].rows || 0,ret);
17775 addItem : function(ev)
17777 // look for vertical location slot in
17778 var cells = this.findCells(ev);
17780 // ev.row = this.findBestRow(cells);
17782 // work out the location.
17786 for(var i =0; i < cells.length; i++) {
17788 cells[i].row = cells[0].row;
17791 cells[i].row = cells[i].row + 1;
17801 if (crow.start.getY() == cells[i].getY()) {
17803 crow.end = cells[i];
17820 cells[0].events.push(ev);
17822 this.calevents.push(ev);
17825 clearEvents: function() {
17827 if(!this.calevents){
17831 Roo.each(this.cells.elements, function(c){
17837 Roo.each(this.calevents, function(e) {
17838 Roo.each(e.els, function(el) {
17839 el.un('mouseenter' ,this.onEventEnter, this);
17840 el.un('mouseleave' ,this.onEventLeave, this);
17845 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17851 renderEvents: function()
17855 this.cells.each(function(c) {
17864 if(c.row != c.events.length){
17865 r = 4 - (4 - (c.row - c.events.length));
17868 c.events = ev.slice(0, r);
17869 c.more = ev.slice(r);
17871 if(c.more.length && c.more.length == 1){
17872 c.events.push(c.more.pop());
17875 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17879 this.cells.each(function(c) {
17881 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17884 for (var e = 0; e < c.events.length; e++){
17885 var ev = c.events[e];
17886 var rows = ev.rows;
17888 for(var i = 0; i < rows.length; i++) {
17890 // how many rows should it span..
17893 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17894 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17896 unselectable : "on",
17899 cls: 'fc-event-inner',
17903 // cls: 'fc-event-time',
17904 // html : cells.length > 1 ? '' : ev.time
17908 cls: 'fc-event-title',
17909 html : String.format('{0}', ev.title)
17916 cls: 'ui-resizable-handle ui-resizable-e',
17917 html : '  '
17924 cfg.cls += ' fc-event-start';
17926 if ((i+1) == rows.length) {
17927 cfg.cls += ' fc-event-end';
17930 var ctr = _this.el.select('.fc-event-container',true).first();
17931 var cg = ctr.createChild(cfg);
17933 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17934 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17936 var r = (c.more.length) ? 1 : 0;
17937 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17938 cg.setWidth(ebox.right - sbox.x -2);
17940 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17941 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17942 cg.on('click', _this.onEventClick, _this, ev);
17953 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17954 style : 'position: absolute',
17955 unselectable : "on",
17958 cls: 'fc-event-inner',
17962 cls: 'fc-event-title',
17970 cls: 'ui-resizable-handle ui-resizable-e',
17971 html : '  '
17977 var ctr = _this.el.select('.fc-event-container',true).first();
17978 var cg = ctr.createChild(cfg);
17980 var sbox = c.select('.fc-day-content',true).first().getBox();
17981 var ebox = c.select('.fc-day-content',true).first().getBox();
17983 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17984 cg.setWidth(ebox.right - sbox.x -2);
17986 cg.on('click', _this.onMoreEventClick, _this, c.more);
17996 onEventEnter: function (e, el,event,d) {
17997 this.fireEvent('evententer', this, el, event);
18000 onEventLeave: function (e, el,event,d) {
18001 this.fireEvent('eventleave', this, el, event);
18004 onEventClick: function (e, el,event,d) {
18005 this.fireEvent('eventclick', this, el, event);
18008 onMonthChange: function () {
18012 onMoreEventClick: function(e, el, more)
18016 this.calpopover.placement = 'right';
18017 this.calpopover.setTitle('More');
18019 this.calpopover.setContent('');
18021 var ctr = this.calpopover.el.select('.popover-content', true).first();
18023 Roo.each(more, function(m){
18025 cls : 'fc-event-hori fc-event-draggable',
18028 var cg = ctr.createChild(cfg);
18030 cg.on('click', _this.onEventClick, _this, m);
18033 this.calpopover.show(el);
18038 onLoad: function ()
18040 this.calevents = [];
18043 if(this.store.getCount() > 0){
18044 this.store.data.each(function(d){
18047 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
18048 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
18049 time : d.data.start_time,
18050 title : d.data.title,
18051 description : d.data.description,
18052 venue : d.data.venue
18057 this.renderEvents();
18059 if(this.calevents.length && this.loadMask){
18060 this.maskEl.hide();
18064 onBeforeLoad: function()
18066 this.clearEvents();
18068 this.maskEl.show();
18082 * @class Roo.bootstrap.Popover
18083 * @extends Roo.bootstrap.Component
18084 * Bootstrap Popover class
18085 * @cfg {String} html contents of the popover (or false to use children..)
18086 * @cfg {String} title of popover (or false to hide)
18087 * @cfg {String} placement how it is placed
18088 * @cfg {String} trigger click || hover (or false to trigger manually)
18089 * @cfg {String} over what (parent or false to trigger manually.)
18090 * @cfg {Number} delay - delay before showing
18093 * Create a new Popover
18094 * @param {Object} config The config object
18097 Roo.bootstrap.Popover = function(config){
18098 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
18104 * After the popover show
18106 * @param {Roo.bootstrap.Popover} this
18111 * After the popover hide
18113 * @param {Roo.bootstrap.Popover} this
18119 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
18121 title: 'Fill in a title',
18124 placement : 'right',
18125 trigger : 'hover', // hover
18131 can_build_overlaid : false,
18133 getChildContainer : function()
18135 return this.el.select('.popover-content',true).first();
18138 getAutoCreate : function(){
18141 cls : 'popover roo-dynamic',
18142 style: 'display:block',
18148 cls : 'popover-inner',
18152 cls: 'popover-title popover-header',
18156 cls : 'popover-content popover-body',
18167 setTitle: function(str)
18170 this.el.select('.popover-title',true).first().dom.innerHTML = str;
18172 setContent: function(str)
18175 this.el.select('.popover-content',true).first().dom.innerHTML = str;
18177 // as it get's added to the bottom of the page.
18178 onRender : function(ct, position)
18180 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18182 var cfg = Roo.apply({}, this.getAutoCreate());
18186 cfg.cls += ' ' + this.cls;
18189 cfg.style = this.style;
18191 //Roo.log("adding to ");
18192 this.el = Roo.get(document.body).createChild(cfg, position);
18193 // Roo.log(this.el);
18198 initEvents : function()
18200 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
18201 this.el.enableDisplayMode('block');
18203 if (this.over === false) {
18206 if (this.triggers === false) {
18209 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18210 var triggers = this.trigger ? this.trigger.split(' ') : [];
18211 Roo.each(triggers, function(trigger) {
18213 if (trigger == 'click') {
18214 on_el.on('click', this.toggle, this);
18215 } else if (trigger != 'manual') {
18216 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
18217 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
18219 on_el.on(eventIn ,this.enter, this);
18220 on_el.on(eventOut, this.leave, this);
18231 toggle : function () {
18232 this.hoverState == 'in' ? this.leave() : this.enter();
18235 enter : function () {
18237 clearTimeout(this.timeout);
18239 this.hoverState = 'in';
18241 if (!this.delay || !this.delay.show) {
18246 this.timeout = setTimeout(function () {
18247 if (_t.hoverState == 'in') {
18250 }, this.delay.show)
18253 leave : function() {
18254 clearTimeout(this.timeout);
18256 this.hoverState = 'out';
18258 if (!this.delay || !this.delay.hide) {
18263 this.timeout = setTimeout(function () {
18264 if (_t.hoverState == 'out') {
18267 }, this.delay.hide)
18270 show : function (on_el)
18273 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18277 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18278 if (this.html !== false) {
18279 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18281 this.el.removeClass([
18282 'fade','top','bottom', 'left', 'right','in',
18283 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18285 if (!this.title.length) {
18286 this.el.select('.popover-title',true).hide();
18289 var placement = typeof this.placement == 'function' ?
18290 this.placement.call(this, this.el, on_el) :
18293 var autoToken = /\s?auto?\s?/i;
18294 var autoPlace = autoToken.test(placement);
18296 placement = placement.replace(autoToken, '') || 'top';
18300 //this.el.setXY([0,0]);
18302 this.el.dom.style.display='block';
18303 this.el.addClass(placement);
18305 //this.el.appendTo(on_el);
18307 var p = this.getPosition();
18308 var box = this.el.getBox();
18313 var align = Roo.bootstrap.Popover.alignment[placement];
18316 this.el.alignTo(on_el, align[0],align[1]);
18317 //var arrow = this.el.select('.arrow',true).first();
18318 //arrow.set(align[2],
18320 this.el.addClass('in');
18323 if (this.el.hasClass('fade')) {
18327 this.hoverState = 'in';
18329 this.fireEvent('show', this);
18334 this.el.setXY([0,0]);
18335 this.el.removeClass('in');
18337 this.hoverState = null;
18339 this.fireEvent('hide', this);
18344 Roo.bootstrap.Popover.alignment = {
18345 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18346 'right' : ['l-r', [10,0], 'left bs-popover-left'],
18347 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18348 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18359 * @class Roo.bootstrap.Progress
18360 * @extends Roo.bootstrap.Component
18361 * Bootstrap Progress class
18362 * @cfg {Boolean} striped striped of the progress bar
18363 * @cfg {Boolean} active animated of the progress bar
18367 * Create a new Progress
18368 * @param {Object} config The config object
18371 Roo.bootstrap.Progress = function(config){
18372 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18375 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
18380 getAutoCreate : function(){
18388 cfg.cls += ' progress-striped';
18392 cfg.cls += ' active';
18411 * @class Roo.bootstrap.ProgressBar
18412 * @extends Roo.bootstrap.Component
18413 * Bootstrap ProgressBar class
18414 * @cfg {Number} aria_valuenow aria-value now
18415 * @cfg {Number} aria_valuemin aria-value min
18416 * @cfg {Number} aria_valuemax aria-value max
18417 * @cfg {String} label label for the progress bar
18418 * @cfg {String} panel (success | info | warning | danger )
18419 * @cfg {String} role role of the progress bar
18420 * @cfg {String} sr_only text
18424 * Create a new ProgressBar
18425 * @param {Object} config The config object
18428 Roo.bootstrap.ProgressBar = function(config){
18429 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18432 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
18436 aria_valuemax : 100,
18442 getAutoCreate : function()
18447 cls: 'progress-bar',
18448 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18460 cfg.role = this.role;
18463 if(this.aria_valuenow){
18464 cfg['aria-valuenow'] = this.aria_valuenow;
18467 if(this.aria_valuemin){
18468 cfg['aria-valuemin'] = this.aria_valuemin;
18471 if(this.aria_valuemax){
18472 cfg['aria-valuemax'] = this.aria_valuemax;
18475 if(this.label && !this.sr_only){
18476 cfg.html = this.label;
18480 cfg.cls += ' progress-bar-' + this.panel;
18486 update : function(aria_valuenow)
18488 this.aria_valuenow = aria_valuenow;
18490 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18505 * @class Roo.bootstrap.TabGroup
18506 * @extends Roo.bootstrap.Column
18507 * Bootstrap Column class
18508 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18509 * @cfg {Boolean} carousel true to make the group behave like a carousel
18510 * @cfg {Boolean} bullets show bullets for the panels
18511 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18512 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18513 * @cfg {Boolean} showarrow (true|false) show arrow default true
18516 * Create a new TabGroup
18517 * @param {Object} config The config object
18520 Roo.bootstrap.TabGroup = function(config){
18521 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18523 this.navId = Roo.id();
18526 Roo.bootstrap.TabGroup.register(this);
18530 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18533 transition : false,
18538 slideOnTouch : false,
18541 getAutoCreate : function()
18543 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18545 cfg.cls += ' tab-content';
18547 if (this.carousel) {
18548 cfg.cls += ' carousel slide';
18551 cls : 'carousel-inner',
18555 if(this.bullets && !Roo.isTouch){
18558 cls : 'carousel-bullets',
18562 if(this.bullets_cls){
18563 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18570 cfg.cn[0].cn.push(bullets);
18573 if(this.showarrow){
18574 cfg.cn[0].cn.push({
18576 class : 'carousel-arrow',
18580 class : 'carousel-prev',
18584 class : 'fa fa-chevron-left'
18590 class : 'carousel-next',
18594 class : 'fa fa-chevron-right'
18607 initEvents: function()
18609 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18610 // this.el.on("touchstart", this.onTouchStart, this);
18613 if(this.autoslide){
18616 this.slideFn = window.setInterval(function() {
18617 _this.showPanelNext();
18621 if(this.showarrow){
18622 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18623 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18629 // onTouchStart : function(e, el, o)
18631 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18635 // this.showPanelNext();
18639 getChildContainer : function()
18641 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18645 * register a Navigation item
18646 * @param {Roo.bootstrap.NavItem} the navitem to add
18648 register : function(item)
18650 this.tabs.push( item);
18651 item.navId = this.navId; // not really needed..
18656 getActivePanel : function()
18659 Roo.each(this.tabs, function(t) {
18669 getPanelByName : function(n)
18672 Roo.each(this.tabs, function(t) {
18673 if (t.tabId == n) {
18681 indexOfPanel : function(p)
18684 Roo.each(this.tabs, function(t,i) {
18685 if (t.tabId == p.tabId) {
18694 * show a specific panel
18695 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18696 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18698 showPanel : function (pan)
18700 if(this.transition || typeof(pan) == 'undefined'){
18701 Roo.log("waiting for the transitionend");
18705 if (typeof(pan) == 'number') {
18706 pan = this.tabs[pan];
18709 if (typeof(pan) == 'string') {
18710 pan = this.getPanelByName(pan);
18713 var cur = this.getActivePanel();
18716 Roo.log('pan or acitve pan is undefined');
18720 if (pan.tabId == this.getActivePanel().tabId) {
18724 if (false === cur.fireEvent('beforedeactivate')) {
18728 if(this.bullets > 0 && !Roo.isTouch){
18729 this.setActiveBullet(this.indexOfPanel(pan));
18732 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18734 //class="carousel-item carousel-item-next carousel-item-left"
18736 this.transition = true;
18737 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18738 var lr = dir == 'next' ? 'left' : 'right';
18739 pan.el.addClass(dir); // or prev
18740 pan.el.addClass('carousel-item-' + dir); // or prev
18741 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18742 cur.el.addClass(lr); // or right
18743 pan.el.addClass(lr);
18744 cur.el.addClass('carousel-item-' +lr); // or right
18745 pan.el.addClass('carousel-item-' +lr);
18749 cur.el.on('transitionend', function() {
18750 Roo.log("trans end?");
18752 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18753 pan.setActive(true);
18755 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18756 cur.setActive(false);
18758 _this.transition = false;
18760 }, this, { single: true } );
18765 cur.setActive(false);
18766 pan.setActive(true);
18771 showPanelNext : function()
18773 var i = this.indexOfPanel(this.getActivePanel());
18775 if (i >= this.tabs.length - 1 && !this.autoslide) {
18779 if (i >= this.tabs.length - 1 && this.autoslide) {
18783 this.showPanel(this.tabs[i+1]);
18786 showPanelPrev : function()
18788 var i = this.indexOfPanel(this.getActivePanel());
18790 if (i < 1 && !this.autoslide) {
18794 if (i < 1 && this.autoslide) {
18795 i = this.tabs.length;
18798 this.showPanel(this.tabs[i-1]);
18802 addBullet: function()
18804 if(!this.bullets || Roo.isTouch){
18807 var ctr = this.el.select('.carousel-bullets',true).first();
18808 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18809 var bullet = ctr.createChild({
18810 cls : 'bullet bullet-' + i
18811 },ctr.dom.lastChild);
18816 bullet.on('click', (function(e, el, o, ii, t){
18818 e.preventDefault();
18820 this.showPanel(ii);
18822 if(this.autoslide && this.slideFn){
18823 clearInterval(this.slideFn);
18824 this.slideFn = window.setInterval(function() {
18825 _this.showPanelNext();
18829 }).createDelegate(this, [i, bullet], true));
18834 setActiveBullet : function(i)
18840 Roo.each(this.el.select('.bullet', true).elements, function(el){
18841 el.removeClass('selected');
18844 var bullet = this.el.select('.bullet-' + i, true).first();
18850 bullet.addClass('selected');
18861 Roo.apply(Roo.bootstrap.TabGroup, {
18865 * register a Navigation Group
18866 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18868 register : function(navgrp)
18870 this.groups[navgrp.navId] = navgrp;
18874 * fetch a Navigation Group based on the navigation ID
18875 * if one does not exist , it will get created.
18876 * @param {string} the navgroup to add
18877 * @returns {Roo.bootstrap.NavGroup} the navgroup
18879 get: function(navId) {
18880 if (typeof(this.groups[navId]) == 'undefined') {
18881 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18883 return this.groups[navId] ;
18898 * @class Roo.bootstrap.TabPanel
18899 * @extends Roo.bootstrap.Component
18900 * Bootstrap TabPanel class
18901 * @cfg {Boolean} active panel active
18902 * @cfg {String} html panel content
18903 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18904 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18905 * @cfg {String} href click to link..
18909 * Create a new TabPanel
18910 * @param {Object} config The config object
18913 Roo.bootstrap.TabPanel = function(config){
18914 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18918 * Fires when the active status changes
18919 * @param {Roo.bootstrap.TabPanel} this
18920 * @param {Boolean} state the new state
18925 * @event beforedeactivate
18926 * Fires before a tab is de-activated - can be used to do validation on a form.
18927 * @param {Roo.bootstrap.TabPanel} this
18928 * @return {Boolean} false if there is an error
18931 'beforedeactivate': true
18934 this.tabId = this.tabId || Roo.id();
18938 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18946 getAutoCreate : function(){
18951 // item is needed for carousel - not sure if it has any effect otherwise
18952 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18953 html: this.html || ''
18957 cfg.cls += ' active';
18961 cfg.tabId = this.tabId;
18969 initEvents: function()
18971 var p = this.parent();
18973 this.navId = this.navId || p.navId;
18975 if (typeof(this.navId) != 'undefined') {
18976 // not really needed.. but just in case.. parent should be a NavGroup.
18977 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18981 var i = tg.tabs.length - 1;
18983 if(this.active && tg.bullets > 0 && i < tg.bullets){
18984 tg.setActiveBullet(i);
18988 this.el.on('click', this.onClick, this);
18991 this.el.on("touchstart", this.onTouchStart, this);
18992 this.el.on("touchmove", this.onTouchMove, this);
18993 this.el.on("touchend", this.onTouchEnd, this);
18998 onRender : function(ct, position)
19000 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
19003 setActive : function(state)
19005 Roo.log("panel - set active " + this.tabId + "=" + state);
19007 this.active = state;
19009 this.el.removeClass('active');
19011 } else if (!this.el.hasClass('active')) {
19012 this.el.addClass('active');
19015 this.fireEvent('changed', this, state);
19018 onClick : function(e)
19020 e.preventDefault();
19022 if(!this.href.length){
19026 window.location.href = this.href;
19035 onTouchStart : function(e)
19037 this.swiping = false;
19039 this.startX = e.browserEvent.touches[0].clientX;
19040 this.startY = e.browserEvent.touches[0].clientY;
19043 onTouchMove : function(e)
19045 this.swiping = true;
19047 this.endX = e.browserEvent.touches[0].clientX;
19048 this.endY = e.browserEvent.touches[0].clientY;
19051 onTouchEnd : function(e)
19058 var tabGroup = this.parent();
19060 if(this.endX > this.startX){ // swiping right
19061 tabGroup.showPanelPrev();
19065 if(this.startX > this.endX){ // swiping left
19066 tabGroup.showPanelNext();
19085 * @class Roo.bootstrap.DateField
19086 * @extends Roo.bootstrap.Input
19087 * Bootstrap DateField class
19088 * @cfg {Number} weekStart default 0
19089 * @cfg {String} viewMode default empty, (months|years)
19090 * @cfg {String} minViewMode default empty, (months|years)
19091 * @cfg {Number} startDate default -Infinity
19092 * @cfg {Number} endDate default Infinity
19093 * @cfg {Boolean} todayHighlight default false
19094 * @cfg {Boolean} todayBtn default false
19095 * @cfg {Boolean} calendarWeeks default false
19096 * @cfg {Object} daysOfWeekDisabled default empty
19097 * @cfg {Boolean} singleMode default false (true | false)
19099 * @cfg {Boolean} keyboardNavigation default true
19100 * @cfg {String} language default en
19103 * Create a new DateField
19104 * @param {Object} config The config object
19107 Roo.bootstrap.DateField = function(config){
19108 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
19112 * Fires when this field show.
19113 * @param {Roo.bootstrap.DateField} this
19114 * @param {Mixed} date The date value
19119 * Fires when this field hide.
19120 * @param {Roo.bootstrap.DateField} this
19121 * @param {Mixed} date The date value
19126 * Fires when select a date.
19127 * @param {Roo.bootstrap.DateField} this
19128 * @param {Mixed} date The date value
19132 * @event beforeselect
19133 * Fires when before select a date.
19134 * @param {Roo.bootstrap.DateField} this
19135 * @param {Mixed} date The date value
19137 beforeselect : true
19141 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
19144 * @cfg {String} format
19145 * The default date format string which can be overriden for localization support. The format must be
19146 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
19150 * @cfg {String} altFormats
19151 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
19152 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
19154 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
19162 todayHighlight : false,
19168 keyboardNavigation: true,
19170 calendarWeeks: false,
19172 startDate: -Infinity,
19176 daysOfWeekDisabled: [],
19180 singleMode : false,
19182 UTCDate: function()
19184 return new Date(Date.UTC.apply(Date, arguments));
19187 UTCToday: function()
19189 var today = new Date();
19190 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
19193 getDate: function() {
19194 var d = this.getUTCDate();
19195 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
19198 getUTCDate: function() {
19202 setDate: function(d) {
19203 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
19206 setUTCDate: function(d) {
19208 this.setValue(this.formatDate(this.date));
19211 onRender: function(ct, position)
19214 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
19216 this.language = this.language || 'en';
19217 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
19218 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
19220 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
19221 this.format = this.format || 'm/d/y';
19222 this.isInline = false;
19223 this.isInput = true;
19224 this.component = this.el.select('.add-on', true).first() || false;
19225 this.component = (this.component && this.component.length === 0) ? false : this.component;
19226 this.hasInput = this.component && this.inputEl().length;
19228 if (typeof(this.minViewMode === 'string')) {
19229 switch (this.minViewMode) {
19231 this.minViewMode = 1;
19234 this.minViewMode = 2;
19237 this.minViewMode = 0;
19242 if (typeof(this.viewMode === 'string')) {
19243 switch (this.viewMode) {
19256 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19258 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19260 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19262 this.picker().on('mousedown', this.onMousedown, this);
19263 this.picker().on('click', this.onClick, this);
19265 this.picker().addClass('datepicker-dropdown');
19267 this.startViewMode = this.viewMode;
19269 if(this.singleMode){
19270 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19271 v.setVisibilityMode(Roo.Element.DISPLAY);
19275 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19276 v.setStyle('width', '189px');
19280 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19281 if(!this.calendarWeeks){
19286 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19287 v.attr('colspan', function(i, val){
19288 return parseInt(val) + 1;
19293 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19295 this.setStartDate(this.startDate);
19296 this.setEndDate(this.endDate);
19298 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19305 if(this.isInline) {
19310 picker : function()
19312 return this.pickerEl;
19313 // return this.el.select('.datepicker', true).first();
19316 fillDow: function()
19318 var dowCnt = this.weekStart;
19327 if(this.calendarWeeks){
19335 while (dowCnt < this.weekStart + 7) {
19339 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19343 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19346 fillMonths: function()
19349 var months = this.picker().select('>.datepicker-months td', true).first();
19351 months.dom.innerHTML = '';
19357 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19360 months.createChild(month);
19367 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;
19369 if (this.date < this.startDate) {
19370 this.viewDate = new Date(this.startDate);
19371 } else if (this.date > this.endDate) {
19372 this.viewDate = new Date(this.endDate);
19374 this.viewDate = new Date(this.date);
19382 var d = new Date(this.viewDate),
19383 year = d.getUTCFullYear(),
19384 month = d.getUTCMonth(),
19385 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19386 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19387 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19388 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19389 currentDate = this.date && this.date.valueOf(),
19390 today = this.UTCToday();
19392 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19394 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19396 // this.picker.select('>tfoot th.today').
19397 // .text(dates[this.language].today)
19398 // .toggle(this.todayBtn !== false);
19400 this.updateNavArrows();
19403 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19405 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19407 prevMonth.setUTCDate(day);
19409 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19411 var nextMonth = new Date(prevMonth);
19413 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19415 nextMonth = nextMonth.valueOf();
19417 var fillMonths = false;
19419 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19421 while(prevMonth.valueOf() <= nextMonth) {
19424 if (prevMonth.getUTCDay() === this.weekStart) {
19426 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19434 if(this.calendarWeeks){
19435 // ISO 8601: First week contains first thursday.
19436 // ISO also states week starts on Monday, but we can be more abstract here.
19438 // Start of current week: based on weekstart/current date
19439 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19440 // Thursday of this week
19441 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19442 // First Thursday of year, year from thursday
19443 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19444 // Calendar week: ms between thursdays, div ms per day, div 7 days
19445 calWeek = (th - yth) / 864e5 / 7 + 1;
19447 fillMonths.cn.push({
19455 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19457 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19460 if (this.todayHighlight &&
19461 prevMonth.getUTCFullYear() == today.getFullYear() &&
19462 prevMonth.getUTCMonth() == today.getMonth() &&
19463 prevMonth.getUTCDate() == today.getDate()) {
19464 clsName += ' today';
19467 if (currentDate && prevMonth.valueOf() === currentDate) {
19468 clsName += ' active';
19471 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19472 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19473 clsName += ' disabled';
19476 fillMonths.cn.push({
19478 cls: 'day ' + clsName,
19479 html: prevMonth.getDate()
19482 prevMonth.setDate(prevMonth.getDate()+1);
19485 var currentYear = this.date && this.date.getUTCFullYear();
19486 var currentMonth = this.date && this.date.getUTCMonth();
19488 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19490 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19491 v.removeClass('active');
19493 if(currentYear === year && k === currentMonth){
19494 v.addClass('active');
19497 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19498 v.addClass('disabled');
19504 year = parseInt(year/10, 10) * 10;
19506 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19508 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19511 for (var i = -1; i < 11; i++) {
19512 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19514 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19522 showMode: function(dir)
19525 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19528 Roo.each(this.picker().select('>div',true).elements, function(v){
19529 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19532 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19537 if(this.isInline) {
19541 this.picker().removeClass(['bottom', 'top']);
19543 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19545 * place to the top of element!
19549 this.picker().addClass('top');
19550 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19555 this.picker().addClass('bottom');
19557 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19560 parseDate : function(value)
19562 if(!value || value instanceof Date){
19565 var v = Date.parseDate(value, this.format);
19566 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19567 v = Date.parseDate(value, 'Y-m-d');
19569 if(!v && this.altFormats){
19570 if(!this.altFormatsArray){
19571 this.altFormatsArray = this.altFormats.split("|");
19573 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19574 v = Date.parseDate(value, this.altFormatsArray[i]);
19580 formatDate : function(date, fmt)
19582 return (!date || !(date instanceof Date)) ?
19583 date : date.dateFormat(fmt || this.format);
19586 onFocus : function()
19588 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19592 onBlur : function()
19594 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19596 var d = this.inputEl().getValue();
19603 showPopup : function()
19605 this.picker().show();
19609 this.fireEvent('showpopup', this, this.date);
19612 hidePopup : function()
19614 if(this.isInline) {
19617 this.picker().hide();
19618 this.viewMode = this.startViewMode;
19621 this.fireEvent('hidepopup', this, this.date);
19625 onMousedown: function(e)
19627 e.stopPropagation();
19628 e.preventDefault();
19633 Roo.bootstrap.DateField.superclass.keyup.call(this);
19637 setValue: function(v)
19639 if(this.fireEvent('beforeselect', this, v) !== false){
19640 var d = new Date(this.parseDate(v) ).clearTime();
19642 if(isNaN(d.getTime())){
19643 this.date = this.viewDate = '';
19644 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19648 v = this.formatDate(d);
19650 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19652 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19656 this.fireEvent('select', this, this.date);
19660 getValue: function()
19662 return this.formatDate(this.date);
19665 fireKey: function(e)
19667 if (!this.picker().isVisible()){
19668 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19674 var dateChanged = false,
19676 newDate, newViewDate;
19681 e.preventDefault();
19685 if (!this.keyboardNavigation) {
19688 dir = e.keyCode == 37 ? -1 : 1;
19691 newDate = this.moveYear(this.date, dir);
19692 newViewDate = this.moveYear(this.viewDate, dir);
19693 } else if (e.shiftKey){
19694 newDate = this.moveMonth(this.date, dir);
19695 newViewDate = this.moveMonth(this.viewDate, dir);
19697 newDate = new Date(this.date);
19698 newDate.setUTCDate(this.date.getUTCDate() + dir);
19699 newViewDate = new Date(this.viewDate);
19700 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19702 if (this.dateWithinRange(newDate)){
19703 this.date = newDate;
19704 this.viewDate = newViewDate;
19705 this.setValue(this.formatDate(this.date));
19707 e.preventDefault();
19708 dateChanged = true;
19713 if (!this.keyboardNavigation) {
19716 dir = e.keyCode == 38 ? -1 : 1;
19718 newDate = this.moveYear(this.date, dir);
19719 newViewDate = this.moveYear(this.viewDate, dir);
19720 } else if (e.shiftKey){
19721 newDate = this.moveMonth(this.date, dir);
19722 newViewDate = this.moveMonth(this.viewDate, dir);
19724 newDate = new Date(this.date);
19725 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19726 newViewDate = new Date(this.viewDate);
19727 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19729 if (this.dateWithinRange(newDate)){
19730 this.date = newDate;
19731 this.viewDate = newViewDate;
19732 this.setValue(this.formatDate(this.date));
19734 e.preventDefault();
19735 dateChanged = true;
19739 this.setValue(this.formatDate(this.date));
19741 e.preventDefault();
19744 this.setValue(this.formatDate(this.date));
19758 onClick: function(e)
19760 e.stopPropagation();
19761 e.preventDefault();
19763 var target = e.getTarget();
19765 if(target.nodeName.toLowerCase() === 'i'){
19766 target = Roo.get(target).dom.parentNode;
19769 var nodeName = target.nodeName;
19770 var className = target.className;
19771 var html = target.innerHTML;
19772 //Roo.log(nodeName);
19774 switch(nodeName.toLowerCase()) {
19776 switch(className) {
19782 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19783 switch(this.viewMode){
19785 this.viewDate = this.moveMonth(this.viewDate, dir);
19789 this.viewDate = this.moveYear(this.viewDate, dir);
19795 var date = new Date();
19796 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19798 this.setValue(this.formatDate(this.date));
19805 if (className.indexOf('disabled') < 0) {
19806 this.viewDate.setUTCDate(1);
19807 if (className.indexOf('month') > -1) {
19808 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19810 var year = parseInt(html, 10) || 0;
19811 this.viewDate.setUTCFullYear(year);
19815 if(this.singleMode){
19816 this.setValue(this.formatDate(this.viewDate));
19827 //Roo.log(className);
19828 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19829 var day = parseInt(html, 10) || 1;
19830 var year = this.viewDate.getUTCFullYear(),
19831 month = this.viewDate.getUTCMonth();
19833 if (className.indexOf('old') > -1) {
19840 } else if (className.indexOf('new') > -1) {
19848 //Roo.log([year,month,day]);
19849 this.date = this.UTCDate(year, month, day,0,0,0,0);
19850 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19852 //Roo.log(this.formatDate(this.date));
19853 this.setValue(this.formatDate(this.date));
19860 setStartDate: function(startDate)
19862 this.startDate = startDate || -Infinity;
19863 if (this.startDate !== -Infinity) {
19864 this.startDate = this.parseDate(this.startDate);
19867 this.updateNavArrows();
19870 setEndDate: function(endDate)
19872 this.endDate = endDate || Infinity;
19873 if (this.endDate !== Infinity) {
19874 this.endDate = this.parseDate(this.endDate);
19877 this.updateNavArrows();
19880 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19882 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19883 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19884 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19886 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19887 return parseInt(d, 10);
19890 this.updateNavArrows();
19893 updateNavArrows: function()
19895 if(this.singleMode){
19899 var d = new Date(this.viewDate),
19900 year = d.getUTCFullYear(),
19901 month = d.getUTCMonth();
19903 Roo.each(this.picker().select('.prev', true).elements, function(v){
19905 switch (this.viewMode) {
19908 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19914 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19921 Roo.each(this.picker().select('.next', true).elements, function(v){
19923 switch (this.viewMode) {
19926 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19932 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19940 moveMonth: function(date, dir)
19945 var new_date = new Date(date.valueOf()),
19946 day = new_date.getUTCDate(),
19947 month = new_date.getUTCMonth(),
19948 mag = Math.abs(dir),
19950 dir = dir > 0 ? 1 : -1;
19953 // If going back one month, make sure month is not current month
19954 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19956 return new_date.getUTCMonth() == month;
19958 // If going forward one month, make sure month is as expected
19959 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19961 return new_date.getUTCMonth() != new_month;
19963 new_month = month + dir;
19964 new_date.setUTCMonth(new_month);
19965 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19966 if (new_month < 0 || new_month > 11) {
19967 new_month = (new_month + 12) % 12;
19970 // For magnitudes >1, move one month at a time...
19971 for (var i=0; i<mag; i++) {
19972 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19973 new_date = this.moveMonth(new_date, dir);
19975 // ...then reset the day, keeping it in the new month
19976 new_month = new_date.getUTCMonth();
19977 new_date.setUTCDate(day);
19979 return new_month != new_date.getUTCMonth();
19982 // Common date-resetting loop -- if date is beyond end of month, make it
19985 new_date.setUTCDate(--day);
19986 new_date.setUTCMonth(new_month);
19991 moveYear: function(date, dir)
19993 return this.moveMonth(date, dir*12);
19996 dateWithinRange: function(date)
19998 return date >= this.startDate && date <= this.endDate;
20004 this.picker().remove();
20007 validateValue : function(value)
20009 if(this.getVisibilityEl().hasClass('hidden')){
20013 if(value.length < 1) {
20014 if(this.allowBlank){
20020 if(value.length < this.minLength){
20023 if(value.length > this.maxLength){
20027 var vt = Roo.form.VTypes;
20028 if(!vt[this.vtype](value, this)){
20032 if(typeof this.validator == "function"){
20033 var msg = this.validator(value);
20039 if(this.regex && !this.regex.test(value)){
20043 if(typeof(this.parseDate(value)) == 'undefined'){
20047 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
20051 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
20061 this.date = this.viewDate = '';
20063 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20068 Roo.apply(Roo.bootstrap.DateField, {
20079 html: '<i class="fa fa-arrow-left"/>'
20089 html: '<i class="fa fa-arrow-right"/>'
20131 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
20132 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
20133 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
20134 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20135 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
20148 navFnc: 'FullYear',
20153 navFnc: 'FullYear',
20158 Roo.apply(Roo.bootstrap.DateField, {
20162 cls: 'datepicker dropdown-menu roo-dynamic',
20166 cls: 'datepicker-days',
20170 cls: 'table-condensed',
20172 Roo.bootstrap.DateField.head,
20176 Roo.bootstrap.DateField.footer
20183 cls: 'datepicker-months',
20187 cls: 'table-condensed',
20189 Roo.bootstrap.DateField.head,
20190 Roo.bootstrap.DateField.content,
20191 Roo.bootstrap.DateField.footer
20198 cls: 'datepicker-years',
20202 cls: 'table-condensed',
20204 Roo.bootstrap.DateField.head,
20205 Roo.bootstrap.DateField.content,
20206 Roo.bootstrap.DateField.footer
20225 * @class Roo.bootstrap.TimeField
20226 * @extends Roo.bootstrap.Input
20227 * Bootstrap DateField class
20231 * Create a new TimeField
20232 * @param {Object} config The config object
20235 Roo.bootstrap.TimeField = function(config){
20236 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20240 * Fires when this field show.
20241 * @param {Roo.bootstrap.DateField} thisthis
20242 * @param {Mixed} date The date value
20247 * Fires when this field hide.
20248 * @param {Roo.bootstrap.DateField} this
20249 * @param {Mixed} date The date value
20254 * Fires when select a date.
20255 * @param {Roo.bootstrap.DateField} this
20256 * @param {Mixed} date The date value
20262 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
20265 * @cfg {String} format
20266 * The default time format string which can be overriden for localization support. The format must be
20267 * valid according to {@link Date#parseDate} (defaults to 'H:i').
20271 onRender: function(ct, position)
20274 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20276 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20278 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20280 this.pop = this.picker().select('>.datepicker-time',true).first();
20281 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20283 this.picker().on('mousedown', this.onMousedown, this);
20284 this.picker().on('click', this.onClick, this);
20286 this.picker().addClass('datepicker-dropdown');
20291 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20292 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20293 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20294 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20295 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20296 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20300 fireKey: function(e){
20301 if (!this.picker().isVisible()){
20302 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20308 e.preventDefault();
20316 this.onTogglePeriod();
20319 this.onIncrementMinutes();
20322 this.onDecrementMinutes();
20331 onClick: function(e) {
20332 e.stopPropagation();
20333 e.preventDefault();
20336 picker : function()
20338 return this.el.select('.datepicker', true).first();
20341 fillTime: function()
20343 var time = this.pop.select('tbody', true).first();
20345 time.dom.innerHTML = '';
20360 cls: 'hours-up glyphicon glyphicon-chevron-up'
20380 cls: 'minutes-up glyphicon glyphicon-chevron-up'
20401 cls: 'timepicker-hour',
20416 cls: 'timepicker-minute',
20431 cls: 'btn btn-primary period',
20453 cls: 'hours-down glyphicon glyphicon-chevron-down'
20473 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20491 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20498 var hours = this.time.getHours();
20499 var minutes = this.time.getMinutes();
20512 hours = hours - 12;
20516 hours = '0' + hours;
20520 minutes = '0' + minutes;
20523 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20524 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20525 this.pop.select('button', true).first().dom.innerHTML = period;
20531 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20533 var cls = ['bottom'];
20535 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20542 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20547 this.picker().addClass(cls.join('-'));
20551 Roo.each(cls, function(c){
20553 _this.picker().setTop(_this.inputEl().getHeight());
20557 _this.picker().setTop(0 - _this.picker().getHeight());
20562 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20566 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20573 onFocus : function()
20575 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20579 onBlur : function()
20581 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20587 this.picker().show();
20592 this.fireEvent('show', this, this.date);
20597 this.picker().hide();
20600 this.fireEvent('hide', this, this.date);
20603 setTime : function()
20606 this.setValue(this.time.format(this.format));
20608 this.fireEvent('select', this, this.date);
20613 onMousedown: function(e){
20614 e.stopPropagation();
20615 e.preventDefault();
20618 onIncrementHours: function()
20620 Roo.log('onIncrementHours');
20621 this.time = this.time.add(Date.HOUR, 1);
20626 onDecrementHours: function()
20628 Roo.log('onDecrementHours');
20629 this.time = this.time.add(Date.HOUR, -1);
20633 onIncrementMinutes: function()
20635 Roo.log('onIncrementMinutes');
20636 this.time = this.time.add(Date.MINUTE, 1);
20640 onDecrementMinutes: function()
20642 Roo.log('onDecrementMinutes');
20643 this.time = this.time.add(Date.MINUTE, -1);
20647 onTogglePeriod: function()
20649 Roo.log('onTogglePeriod');
20650 this.time = this.time.add(Date.HOUR, 12);
20657 Roo.apply(Roo.bootstrap.TimeField, {
20687 cls: 'btn btn-info ok',
20699 Roo.apply(Roo.bootstrap.TimeField, {
20703 cls: 'datepicker dropdown-menu',
20707 cls: 'datepicker-time',
20711 cls: 'table-condensed',
20713 Roo.bootstrap.TimeField.content,
20714 Roo.bootstrap.TimeField.footer
20733 * @class Roo.bootstrap.MonthField
20734 * @extends Roo.bootstrap.Input
20735 * Bootstrap MonthField class
20737 * @cfg {String} language default en
20740 * Create a new MonthField
20741 * @param {Object} config The config object
20744 Roo.bootstrap.MonthField = function(config){
20745 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20750 * Fires when this field show.
20751 * @param {Roo.bootstrap.MonthField} this
20752 * @param {Mixed} date The date value
20757 * Fires when this field hide.
20758 * @param {Roo.bootstrap.MonthField} this
20759 * @param {Mixed} date The date value
20764 * Fires when select a date.
20765 * @param {Roo.bootstrap.MonthField} this
20766 * @param {String} oldvalue The old value
20767 * @param {String} newvalue The new value
20773 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20775 onRender: function(ct, position)
20778 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20780 this.language = this.language || 'en';
20781 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20782 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20784 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20785 this.isInline = false;
20786 this.isInput = true;
20787 this.component = this.el.select('.add-on', true).first() || false;
20788 this.component = (this.component && this.component.length === 0) ? false : this.component;
20789 this.hasInput = this.component && this.inputEL().length;
20791 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20793 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20795 this.picker().on('mousedown', this.onMousedown, this);
20796 this.picker().on('click', this.onClick, this);
20798 this.picker().addClass('datepicker-dropdown');
20800 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20801 v.setStyle('width', '189px');
20808 if(this.isInline) {
20814 setValue: function(v, suppressEvent)
20816 var o = this.getValue();
20818 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20822 if(suppressEvent !== true){
20823 this.fireEvent('select', this, o, v);
20828 getValue: function()
20833 onClick: function(e)
20835 e.stopPropagation();
20836 e.preventDefault();
20838 var target = e.getTarget();
20840 if(target.nodeName.toLowerCase() === 'i'){
20841 target = Roo.get(target).dom.parentNode;
20844 var nodeName = target.nodeName;
20845 var className = target.className;
20846 var html = target.innerHTML;
20848 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20852 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20854 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20860 picker : function()
20862 return this.pickerEl;
20865 fillMonths: function()
20868 var months = this.picker().select('>.datepicker-months td', true).first();
20870 months.dom.innerHTML = '';
20876 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20879 months.createChild(month);
20888 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20889 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20892 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20893 e.removeClass('active');
20895 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20896 e.addClass('active');
20903 if(this.isInline) {
20907 this.picker().removeClass(['bottom', 'top']);
20909 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20911 * place to the top of element!
20915 this.picker().addClass('top');
20916 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20921 this.picker().addClass('bottom');
20923 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20926 onFocus : function()
20928 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20932 onBlur : function()
20934 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20936 var d = this.inputEl().getValue();
20945 this.picker().show();
20946 this.picker().select('>.datepicker-months', true).first().show();
20950 this.fireEvent('show', this, this.date);
20955 if(this.isInline) {
20958 this.picker().hide();
20959 this.fireEvent('hide', this, this.date);
20963 onMousedown: function(e)
20965 e.stopPropagation();
20966 e.preventDefault();
20971 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20975 fireKey: function(e)
20977 if (!this.picker().isVisible()){
20978 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20989 e.preventDefault();
20993 dir = e.keyCode == 37 ? -1 : 1;
20995 this.vIndex = this.vIndex + dir;
20997 if(this.vIndex < 0){
21001 if(this.vIndex > 11){
21005 if(isNaN(this.vIndex)){
21009 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21015 dir = e.keyCode == 38 ? -1 : 1;
21017 this.vIndex = this.vIndex + dir * 4;
21019 if(this.vIndex < 0){
21023 if(this.vIndex > 11){
21027 if(isNaN(this.vIndex)){
21031 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21036 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21037 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21041 e.preventDefault();
21044 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21045 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21061 this.picker().remove();
21066 Roo.apply(Roo.bootstrap.MonthField, {
21085 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21086 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
21091 Roo.apply(Roo.bootstrap.MonthField, {
21095 cls: 'datepicker dropdown-menu roo-dynamic',
21099 cls: 'datepicker-months',
21103 cls: 'table-condensed',
21105 Roo.bootstrap.DateField.content
21125 * @class Roo.bootstrap.CheckBox
21126 * @extends Roo.bootstrap.Input
21127 * Bootstrap CheckBox class
21129 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
21130 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
21131 * @cfg {String} boxLabel The text that appears beside the checkbox
21132 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
21133 * @cfg {Boolean} checked initnal the element
21134 * @cfg {Boolean} inline inline the element (default false)
21135 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
21136 * @cfg {String} tooltip label tooltip
21139 * Create a new CheckBox
21140 * @param {Object} config The config object
21143 Roo.bootstrap.CheckBox = function(config){
21144 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
21149 * Fires when the element is checked or unchecked.
21150 * @param {Roo.bootstrap.CheckBox} this This input
21151 * @param {Boolean} checked The new checked value
21156 * Fires when the element is click.
21157 * @param {Roo.bootstrap.CheckBox} this This input
21164 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
21166 inputType: 'checkbox',
21175 // checkbox success does not make any sense really..
21180 getAutoCreate : function()
21182 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
21188 cfg.cls = 'form-group ' + this.inputType; //input-group
21191 cfg.cls += ' ' + this.inputType + '-inline';
21197 type : this.inputType,
21198 value : this.inputValue,
21199 cls : 'roo-' + this.inputType, //'form-box',
21200 placeholder : this.placeholder || ''
21204 if(this.inputType != 'radio'){
21208 cls : 'roo-hidden-value',
21209 value : this.checked ? this.inputValue : this.valueOff
21214 if (this.weight) { // Validity check?
21215 cfg.cls += " " + this.inputType + "-" + this.weight;
21218 if (this.disabled) {
21219 input.disabled=true;
21223 input.checked = this.checked;
21228 input.name = this.name;
21230 if(this.inputType != 'radio'){
21231 hidden.name = this.name;
21232 input.name = '_hidden_' + this.name;
21237 input.cls += ' input-' + this.size;
21242 ['xs','sm','md','lg'].map(function(size){
21243 if (settings[size]) {
21244 cfg.cls += ' col-' + size + '-' + settings[size];
21248 var inputblock = input;
21250 if (this.before || this.after) {
21253 cls : 'input-group',
21258 inputblock.cn.push({
21260 cls : 'input-group-addon',
21265 inputblock.cn.push(input);
21267 if(this.inputType != 'radio'){
21268 inputblock.cn.push(hidden);
21272 inputblock.cn.push({
21274 cls : 'input-group-addon',
21280 var boxLabelCfg = false;
21286 //'for': id, // box label is handled by onclick - so no for...
21288 html: this.boxLabel
21291 boxLabelCfg.tooltip = this.tooltip;
21297 if (align ==='left' && this.fieldLabel.length) {
21298 // Roo.log("left and has label");
21303 cls : 'control-label',
21304 html : this.fieldLabel
21315 cfg.cn[1].cn.push(boxLabelCfg);
21318 if(this.labelWidth > 12){
21319 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21322 if(this.labelWidth < 13 && this.labelmd == 0){
21323 this.labelmd = this.labelWidth;
21326 if(this.labellg > 0){
21327 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21328 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21331 if(this.labelmd > 0){
21332 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21333 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21336 if(this.labelsm > 0){
21337 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21338 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21341 if(this.labelxs > 0){
21342 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21343 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21346 } else if ( this.fieldLabel.length) {
21347 // Roo.log(" label");
21351 tag: this.boxLabel ? 'span' : 'label',
21353 cls: 'control-label box-input-label',
21354 //cls : 'input-group-addon',
21355 html : this.fieldLabel
21362 cfg.cn.push(boxLabelCfg);
21367 // Roo.log(" no label && no align");
21368 cfg.cn = [ inputblock ] ;
21370 cfg.cn.push(boxLabelCfg);
21378 if(this.inputType != 'radio'){
21379 cfg.cn.push(hidden);
21387 * return the real input element.
21389 inputEl: function ()
21391 return this.el.select('input.roo-' + this.inputType,true).first();
21393 hiddenEl: function ()
21395 return this.el.select('input.roo-hidden-value',true).first();
21398 labelEl: function()
21400 return this.el.select('label.control-label',true).first();
21402 /* depricated... */
21406 return this.labelEl();
21409 boxLabelEl: function()
21411 return this.el.select('label.box-label',true).first();
21414 initEvents : function()
21416 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21418 this.inputEl().on('click', this.onClick, this);
21420 if (this.boxLabel) {
21421 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
21424 this.startValue = this.getValue();
21427 Roo.bootstrap.CheckBox.register(this);
21431 onClick : function(e)
21433 if(this.fireEvent('click', this, e) !== false){
21434 this.setChecked(!this.checked);
21439 setChecked : function(state,suppressEvent)
21441 this.startValue = this.getValue();
21443 if(this.inputType == 'radio'){
21445 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21446 e.dom.checked = false;
21449 this.inputEl().dom.checked = true;
21451 this.inputEl().dom.value = this.inputValue;
21453 if(suppressEvent !== true){
21454 this.fireEvent('check', this, true);
21462 this.checked = state;
21464 this.inputEl().dom.checked = state;
21467 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21469 if(suppressEvent !== true){
21470 this.fireEvent('check', this, state);
21476 getValue : function()
21478 if(this.inputType == 'radio'){
21479 return this.getGroupValue();
21482 return this.hiddenEl().dom.value;
21486 getGroupValue : function()
21488 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21492 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21495 setValue : function(v,suppressEvent)
21497 if(this.inputType == 'radio'){
21498 this.setGroupValue(v, suppressEvent);
21502 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21507 setGroupValue : function(v, suppressEvent)
21509 this.startValue = this.getValue();
21511 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21512 e.dom.checked = false;
21514 if(e.dom.value == v){
21515 e.dom.checked = true;
21519 if(suppressEvent !== true){
21520 this.fireEvent('check', this, true);
21528 validate : function()
21530 if(this.getVisibilityEl().hasClass('hidden')){
21536 (this.inputType == 'radio' && this.validateRadio()) ||
21537 (this.inputType == 'checkbox' && this.validateCheckbox())
21543 this.markInvalid();
21547 validateRadio : function()
21549 if(this.getVisibilityEl().hasClass('hidden')){
21553 if(this.allowBlank){
21559 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21560 if(!e.dom.checked){
21572 validateCheckbox : function()
21575 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21576 //return (this.getValue() == this.inputValue) ? true : false;
21579 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21587 for(var i in group){
21588 if(group[i].el.isVisible(true)){
21596 for(var i in group){
21601 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21608 * Mark this field as valid
21610 markValid : function()
21614 this.fireEvent('valid', this);
21616 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21619 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21626 if(this.inputType == 'radio'){
21627 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21628 var fg = e.findParent('.form-group', false, true);
21629 if (Roo.bootstrap.version == 3) {
21630 fg.removeClass([_this.invalidClass, _this.validClass]);
21631 fg.addClass(_this.validClass);
21633 fg.removeClass(['is-valid', 'is-invalid']);
21634 fg.addClass('is-valid');
21642 var fg = this.el.findParent('.form-group', false, true);
21643 if (Roo.bootstrap.version == 3) {
21644 fg.removeClass([this.invalidClass, this.validClass]);
21645 fg.addClass(this.validClass);
21647 fg.removeClass(['is-valid', 'is-invalid']);
21648 fg.addClass('is-valid');
21653 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21659 for(var i in group){
21660 var fg = group[i].el.findParent('.form-group', false, true);
21661 if (Roo.bootstrap.version == 3) {
21662 fg.removeClass([this.invalidClass, this.validClass]);
21663 fg.addClass(this.validClass);
21665 fg.removeClass(['is-valid', 'is-invalid']);
21666 fg.addClass('is-valid');
21672 * Mark this field as invalid
21673 * @param {String} msg The validation message
21675 markInvalid : function(msg)
21677 if(this.allowBlank){
21683 this.fireEvent('invalid', this, msg);
21685 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21688 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21692 label.markInvalid();
21695 if(this.inputType == 'radio'){
21697 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21698 var fg = e.findParent('.form-group', false, true);
21699 if (Roo.bootstrap.version == 3) {
21700 fg.removeClass([_this.invalidClass, _this.validClass]);
21701 fg.addClass(_this.invalidClass);
21703 fg.removeClass(['is-invalid', 'is-valid']);
21704 fg.addClass('is-invalid');
21712 var fg = this.el.findParent('.form-group', false, true);
21713 if (Roo.bootstrap.version == 3) {
21714 fg.removeClass([_this.invalidClass, _this.validClass]);
21715 fg.addClass(_this.invalidClass);
21717 fg.removeClass(['is-invalid', 'is-valid']);
21718 fg.addClass('is-invalid');
21723 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21729 for(var i in group){
21730 var fg = group[i].el.findParent('.form-group', false, true);
21731 if (Roo.bootstrap.version == 3) {
21732 fg.removeClass([_this.invalidClass, _this.validClass]);
21733 fg.addClass(_this.invalidClass);
21735 fg.removeClass(['is-invalid', 'is-valid']);
21736 fg.addClass('is-invalid');
21742 clearInvalid : function()
21744 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21746 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21748 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21750 if (label && label.iconEl) {
21751 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21752 label.iconEl.removeClass(['is-invalid', 'is-valid']);
21756 disable : function()
21758 if(this.inputType != 'radio'){
21759 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21766 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21767 _this.getActionEl().addClass(this.disabledClass);
21768 e.dom.disabled = true;
21772 this.disabled = true;
21773 this.fireEvent("disable", this);
21777 enable : function()
21779 if(this.inputType != 'radio'){
21780 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21787 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21788 _this.getActionEl().removeClass(this.disabledClass);
21789 e.dom.disabled = false;
21793 this.disabled = false;
21794 this.fireEvent("enable", this);
21798 setBoxLabel : function(v)
21803 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21809 Roo.apply(Roo.bootstrap.CheckBox, {
21814 * register a CheckBox Group
21815 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21817 register : function(checkbox)
21819 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21820 this.groups[checkbox.groupId] = {};
21823 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21827 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21831 * fetch a CheckBox Group based on the group ID
21832 * @param {string} the group ID
21833 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21835 get: function(groupId) {
21836 if (typeof(this.groups[groupId]) == 'undefined') {
21840 return this.groups[groupId] ;
21853 * @class Roo.bootstrap.Radio
21854 * @extends Roo.bootstrap.Component
21855 * Bootstrap Radio class
21856 * @cfg {String} boxLabel - the label associated
21857 * @cfg {String} value - the value of radio
21860 * Create a new Radio
21861 * @param {Object} config The config object
21863 Roo.bootstrap.Radio = function(config){
21864 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21868 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21874 getAutoCreate : function()
21878 cls : 'form-group radio',
21883 html : this.boxLabel
21891 initEvents : function()
21893 this.parent().register(this);
21895 this.el.on('click', this.onClick, this);
21899 onClick : function(e)
21901 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21902 this.setChecked(true);
21906 setChecked : function(state, suppressEvent)
21908 this.parent().setValue(this.value, suppressEvent);
21912 setBoxLabel : function(v)
21917 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21932 * @class Roo.bootstrap.SecurePass
21933 * @extends Roo.bootstrap.Input
21934 * Bootstrap SecurePass class
21938 * Create a new SecurePass
21939 * @param {Object} config The config object
21942 Roo.bootstrap.SecurePass = function (config) {
21943 // these go here, so the translation tool can replace them..
21945 PwdEmpty: "Please type a password, and then retype it to confirm.",
21946 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21947 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21948 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21949 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21950 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21951 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21952 TooWeak: "Your password is Too Weak."
21954 this.meterLabel = "Password strength:";
21955 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21956 this.meterClass = [
21957 "roo-password-meter-tooweak",
21958 "roo-password-meter-weak",
21959 "roo-password-meter-medium",
21960 "roo-password-meter-strong",
21961 "roo-password-meter-grey"
21966 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21969 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21971 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21973 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21974 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21975 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21976 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21977 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21978 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21979 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21989 * @cfg {String/Object} Label for the strength meter (defaults to
21990 * 'Password strength:')
21995 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21996 * ['Weak', 'Medium', 'Strong'])
21999 pwdStrengths: false,
22012 initEvents: function ()
22014 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
22016 if (this.el.is('input[type=password]') && Roo.isSafari) {
22017 this.el.on('keydown', this.SafariOnKeyDown, this);
22020 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
22023 onRender: function (ct, position)
22025 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
22026 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
22027 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
22029 this.trigger.createChild({
22034 cls: 'roo-password-meter-grey col-xs-12',
22037 //width: this.meterWidth + 'px'
22041 cls: 'roo-password-meter-text'
22047 if (this.hideTrigger) {
22048 this.trigger.setDisplayed(false);
22050 this.setSize(this.width || '', this.height || '');
22053 onDestroy: function ()
22055 if (this.trigger) {
22056 this.trigger.removeAllListeners();
22057 this.trigger.remove();
22060 this.wrap.remove();
22062 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
22065 checkStrength: function ()
22067 var pwd = this.inputEl().getValue();
22068 if (pwd == this._lastPwd) {
22073 if (this.ClientSideStrongPassword(pwd)) {
22075 } else if (this.ClientSideMediumPassword(pwd)) {
22077 } else if (this.ClientSideWeakPassword(pwd)) {
22083 Roo.log('strength1: ' + strength);
22085 //var pm = this.trigger.child('div/div/div').dom;
22086 var pm = this.trigger.child('div/div');
22087 pm.removeClass(this.meterClass);
22088 pm.addClass(this.meterClass[strength]);
22091 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22093 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
22095 this._lastPwd = pwd;
22099 Roo.bootstrap.SecurePass.superclass.reset.call(this);
22101 this._lastPwd = '';
22103 var pm = this.trigger.child('div/div');
22104 pm.removeClass(this.meterClass);
22105 pm.addClass('roo-password-meter-grey');
22108 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22111 this.inputEl().dom.type='password';
22114 validateValue: function (value)
22117 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
22120 if (value.length == 0) {
22121 if (this.allowBlank) {
22122 this.clearInvalid();
22126 this.markInvalid(this.errors.PwdEmpty);
22127 this.errorMsg = this.errors.PwdEmpty;
22135 if ('[\x21-\x7e]*'.match(value)) {
22136 this.markInvalid(this.errors.PwdBadChar);
22137 this.errorMsg = this.errors.PwdBadChar;
22140 if (value.length < 6) {
22141 this.markInvalid(this.errors.PwdShort);
22142 this.errorMsg = this.errors.PwdShort;
22145 if (value.length > 16) {
22146 this.markInvalid(this.errors.PwdLong);
22147 this.errorMsg = this.errors.PwdLong;
22151 if (this.ClientSideStrongPassword(value)) {
22153 } else if (this.ClientSideMediumPassword(value)) {
22155 } else if (this.ClientSideWeakPassword(value)) {
22162 if (strength < 2) {
22163 //this.markInvalid(this.errors.TooWeak);
22164 this.errorMsg = this.errors.TooWeak;
22169 console.log('strength2: ' + strength);
22171 //var pm = this.trigger.child('div/div/div').dom;
22173 var pm = this.trigger.child('div/div');
22174 pm.removeClass(this.meterClass);
22175 pm.addClass(this.meterClass[strength]);
22177 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22179 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
22181 this.errorMsg = '';
22185 CharacterSetChecks: function (type)
22188 this.fResult = false;
22191 isctype: function (character, type)
22194 case this.kCapitalLetter:
22195 if (character >= 'A' && character <= 'Z') {
22200 case this.kSmallLetter:
22201 if (character >= 'a' && character <= 'z') {
22207 if (character >= '0' && character <= '9') {
22212 case this.kPunctuation:
22213 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
22224 IsLongEnough: function (pwd, size)
22226 return !(pwd == null || isNaN(size) || pwd.length < size);
22229 SpansEnoughCharacterSets: function (word, nb)
22231 if (!this.IsLongEnough(word, nb))
22236 var characterSetChecks = new Array(
22237 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22238 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22241 for (var index = 0; index < word.length; ++index) {
22242 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22243 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22244 characterSetChecks[nCharSet].fResult = true;
22251 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22252 if (characterSetChecks[nCharSet].fResult) {
22257 if (nCharSets < nb) {
22263 ClientSideStrongPassword: function (pwd)
22265 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22268 ClientSideMediumPassword: function (pwd)
22270 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22273 ClientSideWeakPassword: function (pwd)
22275 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22278 })//<script type="text/javascript">
22281 * Based Ext JS Library 1.1.1
22282 * Copyright(c) 2006-2007, Ext JS, LLC.
22288 * @class Roo.HtmlEditorCore
22289 * @extends Roo.Component
22290 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22292 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22295 Roo.HtmlEditorCore = function(config){
22298 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22303 * @event initialize
22304 * Fires when the editor is fully initialized (including the iframe)
22305 * @param {Roo.HtmlEditorCore} this
22310 * Fires when the editor is first receives the focus. Any insertion must wait
22311 * until after this event.
22312 * @param {Roo.HtmlEditorCore} this
22316 * @event beforesync
22317 * Fires before the textarea is updated with content from the editor iframe. Return false
22318 * to cancel the sync.
22319 * @param {Roo.HtmlEditorCore} this
22320 * @param {String} html
22324 * @event beforepush
22325 * Fires before the iframe editor is updated with content from the textarea. Return false
22326 * to cancel the push.
22327 * @param {Roo.HtmlEditorCore} this
22328 * @param {String} html
22333 * Fires when the textarea is updated with content from the editor iframe.
22334 * @param {Roo.HtmlEditorCore} this
22335 * @param {String} html
22340 * Fires when the iframe editor is updated with content from the textarea.
22341 * @param {Roo.HtmlEditorCore} this
22342 * @param {String} html
22347 * @event editorevent
22348 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22349 * @param {Roo.HtmlEditorCore} this
22355 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22357 // defaults : white / black...
22358 this.applyBlacklists();
22365 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
22369 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
22375 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22380 * @cfg {Number} height (in pixels)
22384 * @cfg {Number} width (in pixels)
22389 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22392 stylesheets: false,
22397 // private properties
22398 validationEvent : false,
22400 initialized : false,
22402 sourceEditMode : false,
22403 onFocus : Roo.emptyFn,
22405 hideMode:'offsets',
22409 // blacklist + whitelisted elements..
22416 * Protected method that will not generally be called directly. It
22417 * is called when the editor initializes the iframe with HTML contents. Override this method if you
22418 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22420 getDocMarkup : function(){
22424 // inherit styels from page...??
22425 if (this.stylesheets === false) {
22427 Roo.get(document.head).select('style').each(function(node) {
22428 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22431 Roo.get(document.head).select('link').each(function(node) {
22432 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22435 } else if (!this.stylesheets.length) {
22437 st = '<style type="text/css">' +
22438 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22441 st = '<style type="text/css">' +
22446 st += '<style type="text/css">' +
22447 'IMG { cursor: pointer } ' +
22450 var cls = 'roo-htmleditor-body';
22452 if(this.bodyCls.length){
22453 cls += ' ' + this.bodyCls;
22456 return '<html><head>' + st +
22457 //<style type="text/css">' +
22458 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22460 ' </head><body class="' + cls + '"></body></html>';
22464 onRender : function(ct, position)
22467 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22468 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22471 this.el.dom.style.border = '0 none';
22472 this.el.dom.setAttribute('tabIndex', -1);
22473 this.el.addClass('x-hidden hide');
22477 if(Roo.isIE){ // fix IE 1px bogus margin
22478 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22482 this.frameId = Roo.id();
22486 var iframe = this.owner.wrap.createChild({
22488 cls: 'form-control', // bootstrap..
22490 name: this.frameId,
22491 frameBorder : 'no',
22492 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
22497 this.iframe = iframe.dom;
22499 this.assignDocWin();
22501 this.doc.designMode = 'on';
22504 this.doc.write(this.getDocMarkup());
22508 var task = { // must defer to wait for browser to be ready
22510 //console.log("run task?" + this.doc.readyState);
22511 this.assignDocWin();
22512 if(this.doc.body || this.doc.readyState == 'complete'){
22514 this.doc.designMode="on";
22518 Roo.TaskMgr.stop(task);
22519 this.initEditor.defer(10, this);
22526 Roo.TaskMgr.start(task);
22531 onResize : function(w, h)
22533 Roo.log('resize: ' +w + ',' + h );
22534 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22538 if(typeof w == 'number'){
22540 this.iframe.style.width = w + 'px';
22542 if(typeof h == 'number'){
22544 this.iframe.style.height = h + 'px';
22546 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22553 * Toggles the editor between standard and source edit mode.
22554 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22556 toggleSourceEdit : function(sourceEditMode){
22558 this.sourceEditMode = sourceEditMode === true;
22560 if(this.sourceEditMode){
22562 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22565 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22566 //this.iframe.className = '';
22569 //this.setSize(this.owner.wrap.getSize());
22570 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22577 * Protected method that will not generally be called directly. If you need/want
22578 * custom HTML cleanup, this is the method you should override.
22579 * @param {String} html The HTML to be cleaned
22580 * return {String} The cleaned HTML
22582 cleanHtml : function(html){
22583 html = String(html);
22584 if(html.length > 5){
22585 if(Roo.isSafari){ // strip safari nonsense
22586 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22589 if(html == ' '){
22596 * HTML Editor -> Textarea
22597 * Protected method that will not generally be called directly. Syncs the contents
22598 * of the editor iframe with the textarea.
22600 syncValue : function(){
22601 if(this.initialized){
22602 var bd = (this.doc.body || this.doc.documentElement);
22603 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22604 var html = bd.innerHTML;
22606 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22607 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22609 html = '<div style="'+m[0]+'">' + html + '</div>';
22612 html = this.cleanHtml(html);
22613 // fix up the special chars.. normaly like back quotes in word...
22614 // however we do not want to do this with chinese..
22615 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
22617 var cc = match.charCodeAt();
22619 // Get the character value, handling surrogate pairs
22620 if (match.length == 2) {
22621 // It's a surrogate pair, calculate the Unicode code point
22622 var high = match.charCodeAt(0) - 0xD800;
22623 var low = match.charCodeAt(1) - 0xDC00;
22624 cc = (high * 0x400) + low + 0x10000;
22626 (cc >= 0x4E00 && cc < 0xA000 ) ||
22627 (cc >= 0x3400 && cc < 0x4E00 ) ||
22628 (cc >= 0xf900 && cc < 0xfb00 )
22633 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
22634 return "&#" + cc + ";";
22641 if(this.owner.fireEvent('beforesync', this, html) !== false){
22642 this.el.dom.value = html;
22643 this.owner.fireEvent('sync', this, html);
22649 * Protected method that will not generally be called directly. Pushes the value of the textarea
22650 * into the iframe editor.
22652 pushValue : function(){
22653 if(this.initialized){
22654 var v = this.el.dom.value.trim();
22656 // if(v.length < 1){
22660 if(this.owner.fireEvent('beforepush', this, v) !== false){
22661 var d = (this.doc.body || this.doc.documentElement);
22663 this.cleanUpPaste();
22664 this.el.dom.value = d.innerHTML;
22665 this.owner.fireEvent('push', this, v);
22671 deferFocus : function(){
22672 this.focus.defer(10, this);
22676 focus : function(){
22677 if(this.win && !this.sourceEditMode){
22684 assignDocWin: function()
22686 var iframe = this.iframe;
22689 this.doc = iframe.contentWindow.document;
22690 this.win = iframe.contentWindow;
22692 // if (!Roo.get(this.frameId)) {
22695 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22696 // this.win = Roo.get(this.frameId).dom.contentWindow;
22698 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22702 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22703 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22708 initEditor : function(){
22709 //console.log("INIT EDITOR");
22710 this.assignDocWin();
22714 this.doc.designMode="on";
22716 this.doc.write(this.getDocMarkup());
22719 var dbody = (this.doc.body || this.doc.documentElement);
22720 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22721 // this copies styles from the containing element into thsi one..
22722 // not sure why we need all of this..
22723 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22725 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22726 //ss['background-attachment'] = 'fixed'; // w3c
22727 dbody.bgProperties = 'fixed'; // ie
22728 //Roo.DomHelper.applyStyles(dbody, ss);
22729 Roo.EventManager.on(this.doc, {
22730 //'mousedown': this.onEditorEvent,
22731 'mouseup': this.onEditorEvent,
22732 'dblclick': this.onEditorEvent,
22733 'click': this.onEditorEvent,
22734 'keyup': this.onEditorEvent,
22739 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22741 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22742 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22744 this.initialized = true;
22746 this.owner.fireEvent('initialize', this);
22751 onDestroy : function(){
22757 //for (var i =0; i < this.toolbars.length;i++) {
22758 // // fixme - ask toolbars for heights?
22759 // this.toolbars[i].onDestroy();
22762 //this.wrap.dom.innerHTML = '';
22763 //this.wrap.remove();
22768 onFirstFocus : function(){
22770 this.assignDocWin();
22773 this.activated = true;
22776 if(Roo.isGecko){ // prevent silly gecko errors
22778 var s = this.win.getSelection();
22779 if(!s.focusNode || s.focusNode.nodeType != 3){
22780 var r = s.getRangeAt(0);
22781 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22786 this.execCmd('useCSS', true);
22787 this.execCmd('styleWithCSS', false);
22790 this.owner.fireEvent('activate', this);
22794 adjustFont: function(btn){
22795 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22796 //if(Roo.isSafari){ // safari
22799 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22800 if(Roo.isSafari){ // safari
22801 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22802 v = (v < 10) ? 10 : v;
22803 v = (v > 48) ? 48 : v;
22804 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22809 v = Math.max(1, v+adjust);
22811 this.execCmd('FontSize', v );
22814 onEditorEvent : function(e)
22816 this.owner.fireEvent('editorevent', this, e);
22817 // this.updateToolbar();
22818 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22821 insertTag : function(tg)
22823 // could be a bit smarter... -> wrap the current selected tRoo..
22824 if (tg.toLowerCase() == 'span' ||
22825 tg.toLowerCase() == 'code' ||
22826 tg.toLowerCase() == 'sup' ||
22827 tg.toLowerCase() == 'sub'
22830 range = this.createRange(this.getSelection());
22831 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22832 wrappingNode.appendChild(range.extractContents());
22833 range.insertNode(wrappingNode);
22840 this.execCmd("formatblock", tg);
22844 insertText : function(txt)
22848 var range = this.createRange();
22849 range.deleteContents();
22850 //alert(Sender.getAttribute('label'));
22852 range.insertNode(this.doc.createTextNode(txt));
22858 * Executes a Midas editor command on the editor document and performs necessary focus and
22859 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22860 * @param {String} cmd The Midas command
22861 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22863 relayCmd : function(cmd, value){
22865 this.execCmd(cmd, value);
22866 this.owner.fireEvent('editorevent', this);
22867 //this.updateToolbar();
22868 this.owner.deferFocus();
22872 * Executes a Midas editor command directly on the editor document.
22873 * For visual commands, you should use {@link #relayCmd} instead.
22874 * <b>This should only be called after the editor is initialized.</b>
22875 * @param {String} cmd The Midas command
22876 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22878 execCmd : function(cmd, value){
22879 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22886 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22888 * @param {String} text | dom node..
22890 insertAtCursor : function(text)
22893 if(!this.activated){
22899 var r = this.doc.selection.createRange();
22910 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22914 // from jquery ui (MIT licenced)
22916 var win = this.win;
22918 if (win.getSelection && win.getSelection().getRangeAt) {
22919 range = win.getSelection().getRangeAt(0);
22920 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22921 range.insertNode(node);
22922 } else if (win.document.selection && win.document.selection.createRange) {
22923 // no firefox support
22924 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22925 win.document.selection.createRange().pasteHTML(txt);
22927 // no firefox support
22928 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22929 this.execCmd('InsertHTML', txt);
22938 mozKeyPress : function(e){
22940 var c = e.getCharCode(), cmd;
22943 c = String.fromCharCode(c).toLowerCase();
22957 this.cleanUpPaste.defer(100, this);
22965 e.preventDefault();
22973 fixKeys : function(){ // load time branching for fastest keydown performance
22975 return function(e){
22976 var k = e.getKey(), r;
22979 r = this.doc.selection.createRange();
22982 r.pasteHTML('    ');
22989 r = this.doc.selection.createRange();
22991 var target = r.parentElement();
22992 if(!target || target.tagName.toLowerCase() != 'li'){
22994 r.pasteHTML('<br />');
23000 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23001 this.cleanUpPaste.defer(100, this);
23007 }else if(Roo.isOpera){
23008 return function(e){
23009 var k = e.getKey();
23013 this.execCmd('InsertHTML','    ');
23016 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23017 this.cleanUpPaste.defer(100, this);
23022 }else if(Roo.isSafari){
23023 return function(e){
23024 var k = e.getKey();
23028 this.execCmd('InsertText','\t');
23032 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23033 this.cleanUpPaste.defer(100, this);
23041 getAllAncestors: function()
23043 var p = this.getSelectedNode();
23046 a.push(p); // push blank onto stack..
23047 p = this.getParentElement();
23051 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
23055 a.push(this.doc.body);
23059 lastSelNode : false,
23062 getSelection : function()
23064 this.assignDocWin();
23065 return Roo.isIE ? this.doc.selection : this.win.getSelection();
23068 getSelectedNode: function()
23070 // this may only work on Gecko!!!
23072 // should we cache this!!!!
23077 var range = this.createRange(this.getSelection()).cloneRange();
23080 var parent = range.parentElement();
23082 var testRange = range.duplicate();
23083 testRange.moveToElementText(parent);
23084 if (testRange.inRange(range)) {
23087 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
23090 parent = parent.parentElement;
23095 // is ancestor a text element.
23096 var ac = range.commonAncestorContainer;
23097 if (ac.nodeType == 3) {
23098 ac = ac.parentNode;
23101 var ar = ac.childNodes;
23104 var other_nodes = [];
23105 var has_other_nodes = false;
23106 for (var i=0;i<ar.length;i++) {
23107 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
23110 // fullly contained node.
23112 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
23117 // probably selected..
23118 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
23119 other_nodes.push(ar[i]);
23123 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
23128 has_other_nodes = true;
23130 if (!nodes.length && other_nodes.length) {
23131 nodes= other_nodes;
23133 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
23139 createRange: function(sel)
23141 // this has strange effects when using with
23142 // top toolbar - not sure if it's a great idea.
23143 //this.editor.contentWindow.focus();
23144 if (typeof sel != "undefined") {
23146 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
23148 return this.doc.createRange();
23151 return this.doc.createRange();
23154 getParentElement: function()
23157 this.assignDocWin();
23158 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
23160 var range = this.createRange(sel);
23163 var p = range.commonAncestorContainer;
23164 while (p.nodeType == 3) { // text node
23175 * Range intersection.. the hard stuff...
23179 * [ -- selected range --- ]
23183 * if end is before start or hits it. fail.
23184 * if start is after end or hits it fail.
23186 * if either hits (but other is outside. - then it's not
23192 // @see http://www.thismuchiknow.co.uk/?p=64.
23193 rangeIntersectsNode : function(range, node)
23195 var nodeRange = node.ownerDocument.createRange();
23197 nodeRange.selectNode(node);
23199 nodeRange.selectNodeContents(node);
23202 var rangeStartRange = range.cloneRange();
23203 rangeStartRange.collapse(true);
23205 var rangeEndRange = range.cloneRange();
23206 rangeEndRange.collapse(false);
23208 var nodeStartRange = nodeRange.cloneRange();
23209 nodeStartRange.collapse(true);
23211 var nodeEndRange = nodeRange.cloneRange();
23212 nodeEndRange.collapse(false);
23214 return rangeStartRange.compareBoundaryPoints(
23215 Range.START_TO_START, nodeEndRange) == -1 &&
23216 rangeEndRange.compareBoundaryPoints(
23217 Range.START_TO_START, nodeStartRange) == 1;
23221 rangeCompareNode : function(range, node)
23223 var nodeRange = node.ownerDocument.createRange();
23225 nodeRange.selectNode(node);
23227 nodeRange.selectNodeContents(node);
23231 range.collapse(true);
23233 nodeRange.collapse(true);
23235 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23236 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
23238 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23240 var nodeIsBefore = ss == 1;
23241 var nodeIsAfter = ee == -1;
23243 if (nodeIsBefore && nodeIsAfter) {
23246 if (!nodeIsBefore && nodeIsAfter) {
23247 return 1; //right trailed.
23250 if (nodeIsBefore && !nodeIsAfter) {
23251 return 2; // left trailed.
23257 // private? - in a new class?
23258 cleanUpPaste : function()
23260 // cleans up the whole document..
23261 Roo.log('cleanuppaste');
23263 this.cleanUpChildren(this.doc.body);
23264 var clean = this.cleanWordChars(this.doc.body.innerHTML);
23265 if (clean != this.doc.body.innerHTML) {
23266 this.doc.body.innerHTML = clean;
23271 cleanWordChars : function(input) {// change the chars to hex code
23272 var he = Roo.HtmlEditorCore;
23274 var output = input;
23275 Roo.each(he.swapCodes, function(sw) {
23276 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23278 output = output.replace(swapper, sw[1]);
23285 cleanUpChildren : function (n)
23287 if (!n.childNodes.length) {
23290 for (var i = n.childNodes.length-1; i > -1 ; i--) {
23291 this.cleanUpChild(n.childNodes[i]);
23298 cleanUpChild : function (node)
23301 //console.log(node);
23302 if (node.nodeName == "#text") {
23303 // clean up silly Windows -- stuff?
23306 if (node.nodeName == "#comment") {
23307 node.parentNode.removeChild(node);
23308 // clean up silly Windows -- stuff?
23311 var lcname = node.tagName.toLowerCase();
23312 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23313 // whitelist of tags..
23315 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23317 node.parentNode.removeChild(node);
23322 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23324 // spans with no attributes - just remove them..
23325 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
23326 remove_keep_children = true;
23329 // remove <a name=....> as rendering on yahoo mailer is borked with this.
23330 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23332 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23333 // remove_keep_children = true;
23336 if (remove_keep_children) {
23337 this.cleanUpChildren(node);
23338 // inserts everything just before this node...
23339 while (node.childNodes.length) {
23340 var cn = node.childNodes[0];
23341 node.removeChild(cn);
23342 node.parentNode.insertBefore(cn, node);
23344 node.parentNode.removeChild(node);
23348 if (!node.attributes || !node.attributes.length) {
23353 this.cleanUpChildren(node);
23357 function cleanAttr(n,v)
23360 if (v.match(/^\./) || v.match(/^\//)) {
23363 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23366 if (v.match(/^#/)) {
23369 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23370 node.removeAttribute(n);
23374 var cwhite = this.cwhite;
23375 var cblack = this.cblack;
23377 function cleanStyle(n,v)
23379 if (v.match(/expression/)) { //XSS?? should we even bother..
23380 node.removeAttribute(n);
23384 var parts = v.split(/;/);
23387 Roo.each(parts, function(p) {
23388 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23392 var l = p.split(':').shift().replace(/\s+/g,'');
23393 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23395 if ( cwhite.length && cblack.indexOf(l) > -1) {
23396 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23397 //node.removeAttribute(n);
23401 // only allow 'c whitelisted system attributes'
23402 if ( cwhite.length && cwhite.indexOf(l) < 0) {
23403 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23404 //node.removeAttribute(n);
23414 if (clean.length) {
23415 node.setAttribute(n, clean.join(';'));
23417 node.removeAttribute(n);
23423 for (var i = node.attributes.length-1; i > -1 ; i--) {
23424 var a = node.attributes[i];
23427 if (a.name.toLowerCase().substr(0,2)=='on') {
23428 node.removeAttribute(a.name);
23431 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23432 node.removeAttribute(a.name);
23435 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23436 cleanAttr(a.name,a.value); // fixme..
23439 if (a.name == 'style') {
23440 cleanStyle(a.name,a.value);
23443 /// clean up MS crap..
23444 // tecnically this should be a list of valid class'es..
23447 if (a.name == 'class') {
23448 if (a.value.match(/^Mso/)) {
23449 node.removeAttribute('class');
23452 if (a.value.match(/^body$/)) {
23453 node.removeAttribute('class');
23464 this.cleanUpChildren(node);
23470 * Clean up MS wordisms...
23472 cleanWord : function(node)
23475 this.cleanWord(this.doc.body);
23480 node.nodeName == 'SPAN' &&
23481 !node.hasAttributes() &&
23482 node.childNodes.length == 1 &&
23483 node.firstChild.nodeName == "#text"
23485 var textNode = node.firstChild;
23486 node.removeChild(textNode);
23487 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
23488 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
23490 node.parentNode.insertBefore(textNode, node);
23491 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
23492 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
23494 node.parentNode.removeChild(node);
23497 if (node.nodeName == "#text") {
23498 // clean up silly Windows -- stuff?
23501 if (node.nodeName == "#comment") {
23502 node.parentNode.removeChild(node);
23503 // clean up silly Windows -- stuff?
23507 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23508 node.parentNode.removeChild(node);
23511 //Roo.log(node.tagName);
23512 // remove - but keep children..
23513 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
23514 //Roo.log('-- removed');
23515 while (node.childNodes.length) {
23516 var cn = node.childNodes[0];
23517 node.removeChild(cn);
23518 node.parentNode.insertBefore(cn, node);
23519 // move node to parent - and clean it..
23520 this.cleanWord(cn);
23522 node.parentNode.removeChild(node);
23523 /// no need to iterate chidlren = it's got none..
23524 //this.iterateChildren(node, this.cleanWord);
23528 if (node.className.length) {
23530 var cn = node.className.split(/\W+/);
23532 Roo.each(cn, function(cls) {
23533 if (cls.match(/Mso[a-zA-Z]+/)) {
23538 node.className = cna.length ? cna.join(' ') : '';
23540 node.removeAttribute("class");
23544 if (node.hasAttribute("lang")) {
23545 node.removeAttribute("lang");
23548 if (node.hasAttribute("style")) {
23550 var styles = node.getAttribute("style").split(";");
23552 Roo.each(styles, function(s) {
23553 if (!s.match(/:/)) {
23556 var kv = s.split(":");
23557 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23560 // what ever is left... we allow.
23563 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23564 if (!nstyle.length) {
23565 node.removeAttribute('style');
23568 this.iterateChildren(node, this.cleanWord);
23574 * iterateChildren of a Node, calling fn each time, using this as the scole..
23575 * @param {DomNode} node node to iterate children of.
23576 * @param {Function} fn method of this class to call on each item.
23578 iterateChildren : function(node, fn)
23580 if (!node.childNodes.length) {
23583 for (var i = node.childNodes.length-1; i > -1 ; i--) {
23584 fn.call(this, node.childNodes[i])
23590 * cleanTableWidths.
23592 * Quite often pasting from word etc.. results in tables with column and widths.
23593 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23596 cleanTableWidths : function(node)
23601 this.cleanTableWidths(this.doc.body);
23606 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23609 Roo.log(node.tagName);
23610 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23611 this.iterateChildren(node, this.cleanTableWidths);
23614 if (node.hasAttribute('width')) {
23615 node.removeAttribute('width');
23619 if (node.hasAttribute("style")) {
23622 var styles = node.getAttribute("style").split(";");
23624 Roo.each(styles, function(s) {
23625 if (!s.match(/:/)) {
23628 var kv = s.split(":");
23629 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23632 // what ever is left... we allow.
23635 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23636 if (!nstyle.length) {
23637 node.removeAttribute('style');
23641 this.iterateChildren(node, this.cleanTableWidths);
23649 domToHTML : function(currentElement, depth, nopadtext) {
23651 depth = depth || 0;
23652 nopadtext = nopadtext || false;
23654 if (!currentElement) {
23655 return this.domToHTML(this.doc.body);
23658 //Roo.log(currentElement);
23660 var allText = false;
23661 var nodeName = currentElement.nodeName;
23662 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23664 if (nodeName == '#text') {
23666 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23671 if (nodeName != 'BODY') {
23674 // Prints the node tagName, such as <A>, <IMG>, etc
23677 for(i = 0; i < currentElement.attributes.length;i++) {
23679 var aname = currentElement.attributes.item(i).name;
23680 if (!currentElement.attributes.item(i).value.length) {
23683 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23686 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23695 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23698 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23703 // Traverse the tree
23705 var currentElementChild = currentElement.childNodes.item(i);
23706 var allText = true;
23707 var innerHTML = '';
23709 while (currentElementChild) {
23710 // Formatting code (indent the tree so it looks nice on the screen)
23711 var nopad = nopadtext;
23712 if (lastnode == 'SPAN') {
23716 if (currentElementChild.nodeName == '#text') {
23717 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23718 toadd = nopadtext ? toadd : toadd.trim();
23719 if (!nopad && toadd.length > 80) {
23720 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23722 innerHTML += toadd;
23725 currentElementChild = currentElement.childNodes.item(i);
23731 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23733 // Recursively traverse the tree structure of the child node
23734 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23735 lastnode = currentElementChild.nodeName;
23737 currentElementChild=currentElement.childNodes.item(i);
23743 // The remaining code is mostly for formatting the tree
23744 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23749 ret+= "</"+tagName+">";
23755 applyBlacklists : function()
23757 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23758 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23762 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23763 if (b.indexOf(tag) > -1) {
23766 this.white.push(tag);
23770 Roo.each(w, function(tag) {
23771 if (b.indexOf(tag) > -1) {
23774 if (this.white.indexOf(tag) > -1) {
23777 this.white.push(tag);
23782 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23783 if (w.indexOf(tag) > -1) {
23786 this.black.push(tag);
23790 Roo.each(b, function(tag) {
23791 if (w.indexOf(tag) > -1) {
23794 if (this.black.indexOf(tag) > -1) {
23797 this.black.push(tag);
23802 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23803 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23807 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23808 if (b.indexOf(tag) > -1) {
23811 this.cwhite.push(tag);
23815 Roo.each(w, function(tag) {
23816 if (b.indexOf(tag) > -1) {
23819 if (this.cwhite.indexOf(tag) > -1) {
23822 this.cwhite.push(tag);
23827 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23828 if (w.indexOf(tag) > -1) {
23831 this.cblack.push(tag);
23835 Roo.each(b, function(tag) {
23836 if (w.indexOf(tag) > -1) {
23839 if (this.cblack.indexOf(tag) > -1) {
23842 this.cblack.push(tag);
23847 setStylesheets : function(stylesheets)
23849 if(typeof(stylesheets) == 'string'){
23850 Roo.get(this.iframe.contentDocument.head).createChild({
23852 rel : 'stylesheet',
23861 Roo.each(stylesheets, function(s) {
23866 Roo.get(_this.iframe.contentDocument.head).createChild({
23868 rel : 'stylesheet',
23877 removeStylesheets : function()
23881 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23886 setStyle : function(style)
23888 Roo.get(this.iframe.contentDocument.head).createChild({
23897 // hide stuff that is not compatible
23911 * @event specialkey
23915 * @cfg {String} fieldClass @hide
23918 * @cfg {String} focusClass @hide
23921 * @cfg {String} autoCreate @hide
23924 * @cfg {String} inputType @hide
23927 * @cfg {String} invalidClass @hide
23930 * @cfg {String} invalidText @hide
23933 * @cfg {String} msgFx @hide
23936 * @cfg {String} validateOnBlur @hide
23940 Roo.HtmlEditorCore.white = [
23941 'area', 'br', 'img', 'input', 'hr', 'wbr',
23943 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23944 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23945 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23946 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23947 'table', 'ul', 'xmp',
23949 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23952 'dir', 'menu', 'ol', 'ul', 'dl',
23958 Roo.HtmlEditorCore.black = [
23959 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23961 'base', 'basefont', 'bgsound', 'blink', 'body',
23962 'frame', 'frameset', 'head', 'html', 'ilayer',
23963 'iframe', 'layer', 'link', 'meta', 'object',
23964 'script', 'style' ,'title', 'xml' // clean later..
23966 Roo.HtmlEditorCore.clean = [
23967 'script', 'style', 'title', 'xml'
23969 Roo.HtmlEditorCore.remove = [
23974 Roo.HtmlEditorCore.ablack = [
23978 Roo.HtmlEditorCore.aclean = [
23979 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23983 Roo.HtmlEditorCore.pwhite= [
23984 'http', 'https', 'mailto'
23987 // white listed style attributes.
23988 Roo.HtmlEditorCore.cwhite= [
23989 // 'text-align', /// default is to allow most things..
23995 // black listed style attributes.
23996 Roo.HtmlEditorCore.cblack= [
23997 // 'font-size' -- this can be set by the project
24001 Roo.HtmlEditorCore.swapCodes =[
24020 * @class Roo.bootstrap.HtmlEditor
24021 * @extends Roo.bootstrap.TextArea
24022 * Bootstrap HtmlEditor class
24025 * Create a new HtmlEditor
24026 * @param {Object} config The config object
24029 Roo.bootstrap.HtmlEditor = function(config){
24030 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
24031 if (!this.toolbars) {
24032 this.toolbars = [];
24035 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
24038 * @event initialize
24039 * Fires when the editor is fully initialized (including the iframe)
24040 * @param {HtmlEditor} this
24045 * Fires when the editor is first receives the focus. Any insertion must wait
24046 * until after this event.
24047 * @param {HtmlEditor} this
24051 * @event beforesync
24052 * Fires before the textarea is updated with content from the editor iframe. Return false
24053 * to cancel the sync.
24054 * @param {HtmlEditor} this
24055 * @param {String} html
24059 * @event beforepush
24060 * Fires before the iframe editor is updated with content from the textarea. Return false
24061 * to cancel the push.
24062 * @param {HtmlEditor} this
24063 * @param {String} html
24068 * Fires when the textarea is updated with content from the editor iframe.
24069 * @param {HtmlEditor} this
24070 * @param {String} html
24075 * Fires when the iframe editor is updated with content from the textarea.
24076 * @param {HtmlEditor} this
24077 * @param {String} html
24081 * @event editmodechange
24082 * Fires when the editor switches edit modes
24083 * @param {HtmlEditor} this
24084 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24086 editmodechange: true,
24088 * @event editorevent
24089 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24090 * @param {HtmlEditor} this
24094 * @event firstfocus
24095 * Fires when on first focus - needed by toolbars..
24096 * @param {HtmlEditor} this
24101 * Auto save the htmlEditor value as a file into Events
24102 * @param {HtmlEditor} this
24106 * @event savedpreview
24107 * preview the saved version of htmlEditor
24108 * @param {HtmlEditor} this
24115 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
24119 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24124 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
24129 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24134 * @cfg {Number} height (in pixels)
24138 * @cfg {Number} width (in pixels)
24143 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24146 stylesheets: false,
24151 // private properties
24152 validationEvent : false,
24154 initialized : false,
24157 onFocus : Roo.emptyFn,
24159 hideMode:'offsets',
24161 tbContainer : false,
24165 toolbarContainer :function() {
24166 return this.wrap.select('.x-html-editor-tb',true).first();
24170 * Protected method that will not generally be called directly. It
24171 * is called when the editor creates its toolbar. Override this method if you need to
24172 * add custom toolbar buttons.
24173 * @param {HtmlEditor} editor
24175 createToolbar : function(){
24176 Roo.log('renewing');
24177 Roo.log("create toolbars");
24179 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
24180 this.toolbars[0].render(this.toolbarContainer());
24184 // if (!editor.toolbars || !editor.toolbars.length) {
24185 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
24188 // for (var i =0 ; i < editor.toolbars.length;i++) {
24189 // editor.toolbars[i] = Roo.factory(
24190 // typeof(editor.toolbars[i]) == 'string' ?
24191 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
24192 // Roo.bootstrap.HtmlEditor);
24193 // editor.toolbars[i].init(editor);
24199 onRender : function(ct, position)
24201 // Roo.log("Call onRender: " + this.xtype);
24203 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
24205 this.wrap = this.inputEl().wrap({
24206 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24209 this.editorcore.onRender(ct, position);
24211 if (this.resizable) {
24212 this.resizeEl = new Roo.Resizable(this.wrap, {
24216 minHeight : this.height,
24217 height: this.height,
24218 handles : this.resizable,
24221 resize : function(r, w, h) {
24222 _t.onResize(w,h); // -something
24228 this.createToolbar(this);
24231 if(!this.width && this.resizable){
24232 this.setSize(this.wrap.getSize());
24234 if (this.resizeEl) {
24235 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24236 // should trigger onReize..
24242 onResize : function(w, h)
24244 Roo.log('resize: ' +w + ',' + h );
24245 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
24249 if(this.inputEl() ){
24250 if(typeof w == 'number'){
24251 var aw = w - this.wrap.getFrameWidth('lr');
24252 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24255 if(typeof h == 'number'){
24256 var tbh = -11; // fixme it needs to tool bar size!
24257 for (var i =0; i < this.toolbars.length;i++) {
24258 // fixme - ask toolbars for heights?
24259 tbh += this.toolbars[i].el.getHeight();
24260 //if (this.toolbars[i].footer) {
24261 // tbh += this.toolbars[i].footer.el.getHeight();
24269 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24270 ah -= 5; // knock a few pixes off for look..
24271 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24275 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24276 this.editorcore.onResize(ew,eh);
24281 * Toggles the editor between standard and source edit mode.
24282 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24284 toggleSourceEdit : function(sourceEditMode)
24286 this.editorcore.toggleSourceEdit(sourceEditMode);
24288 if(this.editorcore.sourceEditMode){
24289 Roo.log('editor - showing textarea');
24292 // Roo.log(this.syncValue());
24294 this.inputEl().removeClass(['hide', 'x-hidden']);
24295 this.inputEl().dom.removeAttribute('tabIndex');
24296 this.inputEl().focus();
24298 Roo.log('editor - hiding textarea');
24300 // Roo.log(this.pushValue());
24303 this.inputEl().addClass(['hide', 'x-hidden']);
24304 this.inputEl().dom.setAttribute('tabIndex', -1);
24305 //this.deferFocus();
24308 if(this.resizable){
24309 this.setSize(this.wrap.getSize());
24312 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24315 // private (for BoxComponent)
24316 adjustSize : Roo.BoxComponent.prototype.adjustSize,
24318 // private (for BoxComponent)
24319 getResizeEl : function(){
24323 // private (for BoxComponent)
24324 getPositionEl : function(){
24329 initEvents : function(){
24330 this.originalValue = this.getValue();
24334 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24337 // markInvalid : Roo.emptyFn,
24339 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24342 // clearInvalid : Roo.emptyFn,
24344 setValue : function(v){
24345 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24346 this.editorcore.pushValue();
24351 deferFocus : function(){
24352 this.focus.defer(10, this);
24356 focus : function(){
24357 this.editorcore.focus();
24363 onDestroy : function(){
24369 for (var i =0; i < this.toolbars.length;i++) {
24370 // fixme - ask toolbars for heights?
24371 this.toolbars[i].onDestroy();
24374 this.wrap.dom.innerHTML = '';
24375 this.wrap.remove();
24380 onFirstFocus : function(){
24381 //Roo.log("onFirstFocus");
24382 this.editorcore.onFirstFocus();
24383 for (var i =0; i < this.toolbars.length;i++) {
24384 this.toolbars[i].onFirstFocus();
24390 syncValue : function()
24392 this.editorcore.syncValue();
24395 pushValue : function()
24397 this.editorcore.pushValue();
24401 // hide stuff that is not compatible
24415 * @event specialkey
24419 * @cfg {String} fieldClass @hide
24422 * @cfg {String} focusClass @hide
24425 * @cfg {String} autoCreate @hide
24428 * @cfg {String} inputType @hide
24432 * @cfg {String} invalidText @hide
24435 * @cfg {String} msgFx @hide
24438 * @cfg {String} validateOnBlur @hide
24447 Roo.namespace('Roo.bootstrap.htmleditor');
24449 * @class Roo.bootstrap.HtmlEditorToolbar1
24455 new Roo.bootstrap.HtmlEditor({
24458 new Roo.bootstrap.HtmlEditorToolbar1({
24459 disable : { fonts: 1 , format: 1, ..., ... , ...],
24465 * @cfg {Object} disable List of elements to disable..
24466 * @cfg {Array} btns List of additional buttons.
24470 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24473 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24476 Roo.apply(this, config);
24478 // default disabled, based on 'good practice'..
24479 this.disable = this.disable || {};
24480 Roo.applyIf(this.disable, {
24483 specialElements : true
24485 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24487 this.editor = config.editor;
24488 this.editorcore = config.editor.editorcore;
24490 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24492 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24493 // dont call parent... till later.
24495 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
24500 editorcore : false,
24505 "h1","h2","h3","h4","h5","h6",
24507 "abbr", "acronym", "address", "cite", "samp", "var",
24511 onRender : function(ct, position)
24513 // Roo.log("Call onRender: " + this.xtype);
24515 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24517 this.el.dom.style.marginBottom = '0';
24519 var editorcore = this.editorcore;
24520 var editor= this.editor;
24523 var btn = function(id,cmd , toggle, handler, html){
24525 var event = toggle ? 'toggle' : 'click';
24530 xns: Roo.bootstrap,
24534 enableToggle:toggle !== false,
24536 pressed : toggle ? false : null,
24539 a.listeners[toggle ? 'toggle' : 'click'] = function() {
24540 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
24546 // var cb_box = function...
24551 xns: Roo.bootstrap,
24556 xns: Roo.bootstrap,
24560 Roo.each(this.formats, function(f) {
24561 style.menu.items.push({
24563 xns: Roo.bootstrap,
24564 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24569 editorcore.insertTag(this.tagname);
24576 children.push(style);
24578 btn('bold',false,true);
24579 btn('italic',false,true);
24580 btn('align-left', 'justifyleft',true);
24581 btn('align-center', 'justifycenter',true);
24582 btn('align-right' , 'justifyright',true);
24583 btn('link', false, false, function(btn) {
24584 //Roo.log("create link?");
24585 var url = prompt(this.createLinkText, this.defaultLinkValue);
24586 if(url && url != 'http:/'+'/'){
24587 this.editorcore.relayCmd('createlink', url);
24590 btn('list','insertunorderedlist',true);
24591 btn('pencil', false,true, function(btn){
24593 this.toggleSourceEdit(btn.pressed);
24596 if (this.editor.btns.length > 0) {
24597 for (var i = 0; i<this.editor.btns.length; i++) {
24598 children.push(this.editor.btns[i]);
24606 xns: Roo.bootstrap,
24611 xns: Roo.bootstrap,
24616 cog.menu.items.push({
24618 xns: Roo.bootstrap,
24619 html : Clean styles,
24624 editorcore.insertTag(this.tagname);
24633 this.xtype = 'NavSimplebar';
24635 for(var i=0;i< children.length;i++) {
24637 this.buttons.add(this.addxtypeChild(children[i]));
24641 editor.on('editorevent', this.updateToolbar, this);
24643 onBtnClick : function(id)
24645 this.editorcore.relayCmd(id);
24646 this.editorcore.focus();
24650 * Protected method that will not generally be called directly. It triggers
24651 * a toolbar update by reading the markup state of the current selection in the editor.
24653 updateToolbar: function(){
24655 if(!this.editorcore.activated){
24656 this.editor.onFirstFocus(); // is this neeed?
24660 var btns = this.buttons;
24661 var doc = this.editorcore.doc;
24662 btns.get('bold').setActive(doc.queryCommandState('bold'));
24663 btns.get('italic').setActive(doc.queryCommandState('italic'));
24664 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24666 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24667 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24668 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24670 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24671 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24674 var ans = this.editorcore.getAllAncestors();
24675 if (this.formatCombo) {
24678 var store = this.formatCombo.store;
24679 this.formatCombo.setValue("");
24680 for (var i =0; i < ans.length;i++) {
24681 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24683 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24691 // hides menus... - so this cant be on a menu...
24692 Roo.bootstrap.MenuMgr.hideAll();
24694 Roo.bootstrap.MenuMgr.hideAll();
24695 //this.editorsyncValue();
24697 onFirstFocus: function() {
24698 this.buttons.each(function(item){
24702 toggleSourceEdit : function(sourceEditMode){
24705 if(sourceEditMode){
24706 Roo.log("disabling buttons");
24707 this.buttons.each( function(item){
24708 if(item.cmd != 'pencil'){
24714 Roo.log("enabling buttons");
24715 if(this.editorcore.initialized){
24716 this.buttons.each( function(item){
24722 Roo.log("calling toggole on editor");
24723 // tell the editor that it's been pressed..
24724 this.editor.toggleSourceEdit(sourceEditMode);
24734 * @class Roo.bootstrap.Table.AbstractSelectionModel
24735 * @extends Roo.util.Observable
24736 * Abstract base class for grid SelectionModels. It provides the interface that should be
24737 * implemented by descendant classes. This class should not be directly instantiated.
24740 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24741 this.locked = false;
24742 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24746 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24747 /** @ignore Called by the grid automatically. Do not call directly. */
24748 init : function(grid){
24754 * Locks the selections.
24757 this.locked = true;
24761 * Unlocks the selections.
24763 unlock : function(){
24764 this.locked = false;
24768 * Returns true if the selections are locked.
24769 * @return {Boolean}
24771 isLocked : function(){
24772 return this.locked;
24776 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24777 * @class Roo.bootstrap.Table.RowSelectionModel
24778 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24779 * It supports multiple selections and keyboard selection/navigation.
24781 * @param {Object} config
24784 Roo.bootstrap.Table.RowSelectionModel = function(config){
24785 Roo.apply(this, config);
24786 this.selections = new Roo.util.MixedCollection(false, function(o){
24791 this.lastActive = false;
24795 * @event selectionchange
24796 * Fires when the selection changes
24797 * @param {SelectionModel} this
24799 "selectionchange" : true,
24801 * @event afterselectionchange
24802 * Fires after the selection changes (eg. by key press or clicking)
24803 * @param {SelectionModel} this
24805 "afterselectionchange" : true,
24807 * @event beforerowselect
24808 * Fires when a row is selected being selected, return false to cancel.
24809 * @param {SelectionModel} this
24810 * @param {Number} rowIndex The selected index
24811 * @param {Boolean} keepExisting False if other selections will be cleared
24813 "beforerowselect" : true,
24816 * Fires when a row is selected.
24817 * @param {SelectionModel} this
24818 * @param {Number} rowIndex The selected index
24819 * @param {Roo.data.Record} r The record
24821 "rowselect" : true,
24823 * @event rowdeselect
24824 * Fires when a row is deselected.
24825 * @param {SelectionModel} this
24826 * @param {Number} rowIndex The selected index
24828 "rowdeselect" : true
24830 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24831 this.locked = false;
24834 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24836 * @cfg {Boolean} singleSelect
24837 * True to allow selection of only one row at a time (defaults to false)
24839 singleSelect : false,
24842 initEvents : function()
24845 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24846 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24847 //}else{ // allow click to work like normal
24848 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24850 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24851 this.grid.on("rowclick", this.handleMouseDown, this);
24853 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24854 "up" : function(e){
24856 this.selectPrevious(e.shiftKey);
24857 }else if(this.last !== false && this.lastActive !== false){
24858 var last = this.last;
24859 this.selectRange(this.last, this.lastActive-1);
24860 this.grid.getView().focusRow(this.lastActive);
24861 if(last !== false){
24865 this.selectFirstRow();
24867 this.fireEvent("afterselectionchange", this);
24869 "down" : function(e){
24871 this.selectNext(e.shiftKey);
24872 }else if(this.last !== false && this.lastActive !== false){
24873 var last = this.last;
24874 this.selectRange(this.last, this.lastActive+1);
24875 this.grid.getView().focusRow(this.lastActive);
24876 if(last !== false){
24880 this.selectFirstRow();
24882 this.fireEvent("afterselectionchange", this);
24886 this.grid.store.on('load', function(){
24887 this.selections.clear();
24890 var view = this.grid.view;
24891 view.on("refresh", this.onRefresh, this);
24892 view.on("rowupdated", this.onRowUpdated, this);
24893 view.on("rowremoved", this.onRemove, this);
24898 onRefresh : function()
24900 var ds = this.grid.store, i, v = this.grid.view;
24901 var s = this.selections;
24902 s.each(function(r){
24903 if((i = ds.indexOfId(r.id)) != -1){
24912 onRemove : function(v, index, r){
24913 this.selections.remove(r);
24917 onRowUpdated : function(v, index, r){
24918 if(this.isSelected(r)){
24919 v.onRowSelect(index);
24925 * @param {Array} records The records to select
24926 * @param {Boolean} keepExisting (optional) True to keep existing selections
24928 selectRecords : function(records, keepExisting)
24931 this.clearSelections();
24933 var ds = this.grid.store;
24934 for(var i = 0, len = records.length; i < len; i++){
24935 this.selectRow(ds.indexOf(records[i]), true);
24940 * Gets the number of selected rows.
24943 getCount : function(){
24944 return this.selections.length;
24948 * Selects the first row in the grid.
24950 selectFirstRow : function(){
24955 * Select the last row.
24956 * @param {Boolean} keepExisting (optional) True to keep existing selections
24958 selectLastRow : function(keepExisting){
24959 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24960 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24964 * Selects the row immediately following the last selected row.
24965 * @param {Boolean} keepExisting (optional) True to keep existing selections
24967 selectNext : function(keepExisting)
24969 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24970 this.selectRow(this.last+1, keepExisting);
24971 this.grid.getView().focusRow(this.last);
24976 * Selects the row that precedes the last selected row.
24977 * @param {Boolean} keepExisting (optional) True to keep existing selections
24979 selectPrevious : function(keepExisting){
24981 this.selectRow(this.last-1, keepExisting);
24982 this.grid.getView().focusRow(this.last);
24987 * Returns the selected records
24988 * @return {Array} Array of selected records
24990 getSelections : function(){
24991 return [].concat(this.selections.items);
24995 * Returns the first selected record.
24998 getSelected : function(){
24999 return this.selections.itemAt(0);
25004 * Clears all selections.
25006 clearSelections : function(fast)
25012 var ds = this.grid.store;
25013 var s = this.selections;
25014 s.each(function(r){
25015 this.deselectRow(ds.indexOfId(r.id));
25019 this.selections.clear();
25026 * Selects all rows.
25028 selectAll : function(){
25032 this.selections.clear();
25033 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
25034 this.selectRow(i, true);
25039 * Returns True if there is a selection.
25040 * @return {Boolean}
25042 hasSelection : function(){
25043 return this.selections.length > 0;
25047 * Returns True if the specified row is selected.
25048 * @param {Number/Record} record The record or index of the record to check
25049 * @return {Boolean}
25051 isSelected : function(index){
25052 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
25053 return (r && this.selections.key(r.id) ? true : false);
25057 * Returns True if the specified record id is selected.
25058 * @param {String} id The id of record to check
25059 * @return {Boolean}
25061 isIdSelected : function(id){
25062 return (this.selections.key(id) ? true : false);
25067 handleMouseDBClick : function(e, t){
25071 handleMouseDown : function(e, t)
25073 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
25074 if(this.isLocked() || rowIndex < 0 ){
25077 if(e.shiftKey && this.last !== false){
25078 var last = this.last;
25079 this.selectRange(last, rowIndex, e.ctrlKey);
25080 this.last = last; // reset the last
25084 var isSelected = this.isSelected(rowIndex);
25085 //Roo.log("select row:" + rowIndex);
25087 this.deselectRow(rowIndex);
25089 this.selectRow(rowIndex, true);
25093 if(e.button !== 0 && isSelected){
25094 alert('rowIndex 2: ' + rowIndex);
25095 view.focusRow(rowIndex);
25096 }else if(e.ctrlKey && isSelected){
25097 this.deselectRow(rowIndex);
25098 }else if(!isSelected){
25099 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
25100 view.focusRow(rowIndex);
25104 this.fireEvent("afterselectionchange", this);
25107 handleDragableRowClick : function(grid, rowIndex, e)
25109 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
25110 this.selectRow(rowIndex, false);
25111 grid.view.focusRow(rowIndex);
25112 this.fireEvent("afterselectionchange", this);
25117 * Selects multiple rows.
25118 * @param {Array} rows Array of the indexes of the row to select
25119 * @param {Boolean} keepExisting (optional) True to keep existing selections
25121 selectRows : function(rows, keepExisting){
25123 this.clearSelections();
25125 for(var i = 0, len = rows.length; i < len; i++){
25126 this.selectRow(rows[i], true);
25131 * Selects a range of rows. All rows in between startRow and endRow are also selected.
25132 * @param {Number} startRow The index of the first row in the range
25133 * @param {Number} endRow The index of the last row in the range
25134 * @param {Boolean} keepExisting (optional) True to retain existing selections
25136 selectRange : function(startRow, endRow, keepExisting){
25141 this.clearSelections();
25143 if(startRow <= endRow){
25144 for(var i = startRow; i <= endRow; i++){
25145 this.selectRow(i, true);
25148 for(var i = startRow; i >= endRow; i--){
25149 this.selectRow(i, true);
25155 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
25156 * @param {Number} startRow The index of the first row in the range
25157 * @param {Number} endRow The index of the last row in the range
25159 deselectRange : function(startRow, endRow, preventViewNotify){
25163 for(var i = startRow; i <= endRow; i++){
25164 this.deselectRow(i, preventViewNotify);
25170 * @param {Number} row The index of the row to select
25171 * @param {Boolean} keepExisting (optional) True to keep existing selections
25173 selectRow : function(index, keepExisting, preventViewNotify)
25175 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
25178 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
25179 if(!keepExisting || this.singleSelect){
25180 this.clearSelections();
25183 var r = this.grid.store.getAt(index);
25184 //console.log('selectRow - record id :' + r.id);
25186 this.selections.add(r);
25187 this.last = this.lastActive = index;
25188 if(!preventViewNotify){
25189 var proxy = new Roo.Element(
25190 this.grid.getRowDom(index)
25192 proxy.addClass('bg-info info');
25194 this.fireEvent("rowselect", this, index, r);
25195 this.fireEvent("selectionchange", this);
25201 * @param {Number} row The index of the row to deselect
25203 deselectRow : function(index, preventViewNotify)
25208 if(this.last == index){
25211 if(this.lastActive == index){
25212 this.lastActive = false;
25215 var r = this.grid.store.getAt(index);
25220 this.selections.remove(r);
25221 //.console.log('deselectRow - record id :' + r.id);
25222 if(!preventViewNotify){
25224 var proxy = new Roo.Element(
25225 this.grid.getRowDom(index)
25227 proxy.removeClass('bg-info info');
25229 this.fireEvent("rowdeselect", this, index);
25230 this.fireEvent("selectionchange", this);
25234 restoreLast : function(){
25236 this.last = this._last;
25241 acceptsNav : function(row, col, cm){
25242 return !cm.isHidden(col) && cm.isCellEditable(col, row);
25246 onEditorKey : function(field, e){
25247 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25252 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25254 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25256 }else if(k == e.ENTER && !e.ctrlKey){
25260 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25262 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25264 }else if(k == e.ESC){
25268 g.startEditing(newCell[0], newCell[1]);
25274 * Ext JS Library 1.1.1
25275 * Copyright(c) 2006-2007, Ext JS, LLC.
25277 * Originally Released Under LGPL - original licence link has changed is not relivant.
25280 * <script type="text/javascript">
25284 * @class Roo.bootstrap.PagingToolbar
25285 * @extends Roo.bootstrap.NavSimplebar
25286 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25288 * Create a new PagingToolbar
25289 * @param {Object} config The config object
25290 * @param {Roo.data.Store} store
25292 Roo.bootstrap.PagingToolbar = function(config)
25294 // old args format still supported... - xtype is prefered..
25295 // created from xtype...
25297 this.ds = config.dataSource;
25299 if (config.store && !this.ds) {
25300 this.store= Roo.factory(config.store, Roo.data);
25301 this.ds = this.store;
25302 this.ds.xmodule = this.xmodule || false;
25305 this.toolbarItems = [];
25306 if (config.items) {
25307 this.toolbarItems = config.items;
25310 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25315 this.bind(this.ds);
25318 if (Roo.bootstrap.version == 4) {
25319 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25321 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25326 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25328 * @cfg {Roo.data.Store} dataSource
25329 * The underlying data store providing the paged data
25332 * @cfg {String/HTMLElement/Element} container
25333 * container The id or element that will contain the toolbar
25336 * @cfg {Boolean} displayInfo
25337 * True to display the displayMsg (defaults to false)
25340 * @cfg {Number} pageSize
25341 * The number of records to display per page (defaults to 20)
25345 * @cfg {String} displayMsg
25346 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25348 displayMsg : 'Displaying {0} - {1} of {2}',
25350 * @cfg {String} emptyMsg
25351 * The message to display when no records are found (defaults to "No data to display")
25353 emptyMsg : 'No data to display',
25355 * Customizable piece of the default paging text (defaults to "Page")
25358 beforePageText : "Page",
25360 * Customizable piece of the default paging text (defaults to "of %0")
25363 afterPageText : "of {0}",
25365 * Customizable piece of the default paging text (defaults to "First Page")
25368 firstText : "First Page",
25370 * Customizable piece of the default paging text (defaults to "Previous Page")
25373 prevText : "Previous Page",
25375 * Customizable piece of the default paging text (defaults to "Next Page")
25378 nextText : "Next Page",
25380 * Customizable piece of the default paging text (defaults to "Last Page")
25383 lastText : "Last Page",
25385 * Customizable piece of the default paging text (defaults to "Refresh")
25388 refreshText : "Refresh",
25392 onRender : function(ct, position)
25394 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25395 this.navgroup.parentId = this.id;
25396 this.navgroup.onRender(this.el, null);
25397 // add the buttons to the navgroup
25399 if(this.displayInfo){
25400 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25401 this.displayEl = this.el.select('.x-paging-info', true).first();
25402 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25403 // this.displayEl = navel.el.select('span',true).first();
25409 Roo.each(_this.buttons, function(e){ // this might need to use render????
25410 Roo.factory(e).render(_this.el);
25414 Roo.each(_this.toolbarItems, function(e) {
25415 _this.navgroup.addItem(e);
25419 this.first = this.navgroup.addItem({
25420 tooltip: this.firstText,
25421 cls: "prev btn-outline-secondary",
25422 html : ' <i class="fa fa-step-backward"></i>',
25424 preventDefault: true,
25425 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25428 this.prev = this.navgroup.addItem({
25429 tooltip: this.prevText,
25430 cls: "prev btn-outline-secondary",
25431 html : ' <i class="fa fa-backward"></i>',
25433 preventDefault: true,
25434 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
25436 //this.addSeparator();
25439 var field = this.navgroup.addItem( {
25441 cls : 'x-paging-position btn-outline-secondary',
25443 html : this.beforePageText +
25444 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25445 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
25448 this.field = field.el.select('input', true).first();
25449 this.field.on("keydown", this.onPagingKeydown, this);
25450 this.field.on("focus", function(){this.dom.select();});
25453 this.afterTextEl = field.el.select('.x-paging-after',true).first();
25454 //this.field.setHeight(18);
25455 //this.addSeparator();
25456 this.next = this.navgroup.addItem({
25457 tooltip: this.nextText,
25458 cls: "next btn-outline-secondary",
25459 html : ' <i class="fa fa-forward"></i>',
25461 preventDefault: true,
25462 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
25464 this.last = this.navgroup.addItem({
25465 tooltip: this.lastText,
25466 html : ' <i class="fa fa-step-forward"></i>',
25467 cls: "next btn-outline-secondary",
25469 preventDefault: true,
25470 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
25472 //this.addSeparator();
25473 this.loading = this.navgroup.addItem({
25474 tooltip: this.refreshText,
25475 cls: "btn-outline-secondary",
25476 html : ' <i class="fa fa-refresh"></i>',
25477 preventDefault: true,
25478 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25484 updateInfo : function(){
25485 if(this.displayEl){
25486 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25487 var msg = count == 0 ?
25491 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
25493 this.displayEl.update(msg);
25498 onLoad : function(ds, r, o)
25500 this.cursor = o.params.start ? o.params.start : 0;
25502 var d = this.getPageData(),
25507 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25508 this.field.dom.value = ap;
25509 this.first.setDisabled(ap == 1);
25510 this.prev.setDisabled(ap == 1);
25511 this.next.setDisabled(ap == ps);
25512 this.last.setDisabled(ap == ps);
25513 this.loading.enable();
25518 getPageData : function(){
25519 var total = this.ds.getTotalCount();
25522 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25523 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25528 onLoadError : function(){
25529 this.loading.enable();
25533 onPagingKeydown : function(e){
25534 var k = e.getKey();
25535 var d = this.getPageData();
25537 var v = this.field.dom.value, pageNum;
25538 if(!v || isNaN(pageNum = parseInt(v, 10))){
25539 this.field.dom.value = d.activePage;
25542 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25543 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25546 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))
25548 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25549 this.field.dom.value = pageNum;
25550 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25553 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25555 var v = this.field.dom.value, pageNum;
25556 var increment = (e.shiftKey) ? 10 : 1;
25557 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25560 if(!v || isNaN(pageNum = parseInt(v, 10))) {
25561 this.field.dom.value = d.activePage;
25564 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25566 this.field.dom.value = parseInt(v, 10) + increment;
25567 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25568 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25575 beforeLoad : function(){
25577 this.loading.disable();
25582 onClick : function(which){
25591 ds.load({params:{start: 0, limit: this.pageSize}});
25594 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25597 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25600 var total = ds.getTotalCount();
25601 var extra = total % this.pageSize;
25602 var lastStart = extra ? (total - extra) : total-this.pageSize;
25603 ds.load({params:{start: lastStart, limit: this.pageSize}});
25606 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25612 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25613 * @param {Roo.data.Store} store The data store to unbind
25615 unbind : function(ds){
25616 ds.un("beforeload", this.beforeLoad, this);
25617 ds.un("load", this.onLoad, this);
25618 ds.un("loadexception", this.onLoadError, this);
25619 ds.un("remove", this.updateInfo, this);
25620 ds.un("add", this.updateInfo, this);
25621 this.ds = undefined;
25625 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25626 * @param {Roo.data.Store} store The data store to bind
25628 bind : function(ds){
25629 ds.on("beforeload", this.beforeLoad, this);
25630 ds.on("load", this.onLoad, this);
25631 ds.on("loadexception", this.onLoadError, this);
25632 ds.on("remove", this.updateInfo, this);
25633 ds.on("add", this.updateInfo, this);
25644 * @class Roo.bootstrap.MessageBar
25645 * @extends Roo.bootstrap.Component
25646 * Bootstrap MessageBar class
25647 * @cfg {String} html contents of the MessageBar
25648 * @cfg {String} weight (info | success | warning | danger) default info
25649 * @cfg {String} beforeClass insert the bar before the given class
25650 * @cfg {Boolean} closable (true | false) default false
25651 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25654 * Create a new Element
25655 * @param {Object} config The config object
25658 Roo.bootstrap.MessageBar = function(config){
25659 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25662 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25668 beforeClass: 'bootstrap-sticky-wrap',
25670 getAutoCreate : function(){
25674 cls: 'alert alert-dismissable alert-' + this.weight,
25679 html: this.html || ''
25685 cfg.cls += ' alert-messages-fixed';
25699 onRender : function(ct, position)
25701 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25704 var cfg = Roo.apply({}, this.getAutoCreate());
25708 cfg.cls += ' ' + this.cls;
25711 cfg.style = this.style;
25713 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25715 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25718 this.el.select('>button.close').on('click', this.hide, this);
25724 if (!this.rendered) {
25730 this.fireEvent('show', this);
25736 if (!this.rendered) {
25742 this.fireEvent('hide', this);
25745 update : function()
25747 // var e = this.el.dom.firstChild;
25749 // if(this.closable){
25750 // e = e.nextSibling;
25753 // e.data = this.html || '';
25755 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25771 * @class Roo.bootstrap.Graph
25772 * @extends Roo.bootstrap.Component
25773 * Bootstrap Graph class
25777 @cfg {String} graphtype bar | vbar | pie
25778 @cfg {number} g_x coodinator | centre x (pie)
25779 @cfg {number} g_y coodinator | centre y (pie)
25780 @cfg {number} g_r radius (pie)
25781 @cfg {number} g_height height of the chart (respected by all elements in the set)
25782 @cfg {number} g_width width of the chart (respected by all elements in the set)
25783 @cfg {Object} title The title of the chart
25786 -opts (object) options for the chart
25788 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25789 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25791 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.
25792 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25794 o stretch (boolean)
25796 -opts (object) options for the pie
25799 o startAngle (number)
25800 o endAngle (number)
25804 * Create a new Input
25805 * @param {Object} config The config object
25808 Roo.bootstrap.Graph = function(config){
25809 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25815 * The img click event for the img.
25816 * @param {Roo.EventObject} e
25822 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25833 //g_colors: this.colors,
25840 getAutoCreate : function(){
25851 onRender : function(ct,position){
25854 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25856 if (typeof(Raphael) == 'undefined') {
25857 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25861 this.raphael = Raphael(this.el.dom);
25863 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25864 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25865 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25866 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25868 r.text(160, 10, "Single Series Chart").attr(txtattr);
25869 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25870 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25871 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25873 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25874 r.barchart(330, 10, 300, 220, data1);
25875 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25876 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25879 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25880 // r.barchart(30, 30, 560, 250, xdata, {
25881 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25882 // axis : "0 0 1 1",
25883 // axisxlabels : xdata
25884 // //yvalues : cols,
25887 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25889 // this.load(null,xdata,{
25890 // axis : "0 0 1 1",
25891 // axisxlabels : xdata
25896 load : function(graphtype,xdata,opts)
25898 this.raphael.clear();
25900 graphtype = this.graphtype;
25905 var r = this.raphael,
25906 fin = function () {
25907 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25909 fout = function () {
25910 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25912 pfin = function() {
25913 this.sector.stop();
25914 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25917 this.label[0].stop();
25918 this.label[0].attr({ r: 7.5 });
25919 this.label[1].attr({ "font-weight": 800 });
25922 pfout = function() {
25923 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25926 this.label[0].animate({ r: 5 }, 500, "bounce");
25927 this.label[1].attr({ "font-weight": 400 });
25933 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25936 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25939 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25940 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25942 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25949 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25954 setTitle: function(o)
25959 initEvents: function() {
25962 this.el.on('click', this.onClick, this);
25966 onClick : function(e)
25968 Roo.log('img onclick');
25969 this.fireEvent('click', this, e);
25981 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25984 * @class Roo.bootstrap.dash.NumberBox
25985 * @extends Roo.bootstrap.Component
25986 * Bootstrap NumberBox class
25987 * @cfg {String} headline Box headline
25988 * @cfg {String} content Box content
25989 * @cfg {String} icon Box icon
25990 * @cfg {String} footer Footer text
25991 * @cfg {String} fhref Footer href
25994 * Create a new NumberBox
25995 * @param {Object} config The config object
25999 Roo.bootstrap.dash.NumberBox = function(config){
26000 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
26004 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
26013 getAutoCreate : function(){
26017 cls : 'small-box ',
26025 cls : 'roo-headline',
26026 html : this.headline
26030 cls : 'roo-content',
26031 html : this.content
26045 cls : 'ion ' + this.icon
26054 cls : 'small-box-footer',
26055 href : this.fhref || '#',
26059 cfg.cn.push(footer);
26066 onRender : function(ct,position){
26067 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
26074 setHeadline: function (value)
26076 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
26079 setFooter: function (value, href)
26081 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
26084 this.el.select('a.small-box-footer',true).first().attr('href', href);
26089 setContent: function (value)
26091 this.el.select('.roo-content',true).first().dom.innerHTML = value;
26094 initEvents: function()
26108 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26111 * @class Roo.bootstrap.dash.TabBox
26112 * @extends Roo.bootstrap.Component
26113 * Bootstrap TabBox class
26114 * @cfg {String} title Title of the TabBox
26115 * @cfg {String} icon Icon of the TabBox
26116 * @cfg {Boolean} showtabs (true|false) show the tabs default true
26117 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
26120 * Create a new TabBox
26121 * @param {Object} config The config object
26125 Roo.bootstrap.dash.TabBox = function(config){
26126 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
26131 * When a pane is added
26132 * @param {Roo.bootstrap.dash.TabPane} pane
26136 * @event activatepane
26137 * When a pane is activated
26138 * @param {Roo.bootstrap.dash.TabPane} pane
26140 "activatepane" : true
26148 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
26153 tabScrollable : false,
26155 getChildContainer : function()
26157 return this.el.select('.tab-content', true).first();
26160 getAutoCreate : function(){
26164 cls: 'pull-left header',
26172 cls: 'fa ' + this.icon
26178 cls: 'nav nav-tabs pull-right',
26184 if(this.tabScrollable){
26191 cls: 'nav nav-tabs pull-right',
26202 cls: 'nav-tabs-custom',
26207 cls: 'tab-content no-padding',
26215 initEvents : function()
26217 //Roo.log('add add pane handler');
26218 this.on('addpane', this.onAddPane, this);
26221 * Updates the box title
26222 * @param {String} html to set the title to.
26224 setTitle : function(value)
26226 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
26228 onAddPane : function(pane)
26230 this.panes.push(pane);
26231 //Roo.log('addpane');
26233 // tabs are rendere left to right..
26234 if(!this.showtabs){
26238 var ctr = this.el.select('.nav-tabs', true).first();
26241 var existing = ctr.select('.nav-tab',true);
26242 var qty = existing.getCount();;
26245 var tab = ctr.createChild({
26247 cls : 'nav-tab' + (qty ? '' : ' active'),
26255 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26258 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26260 pane.el.addClass('active');
26265 onTabClick : function(ev,un,ob,pane)
26267 //Roo.log('tab - prev default');
26268 ev.preventDefault();
26271 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26272 pane.tab.addClass('active');
26273 //Roo.log(pane.title);
26274 this.getChildContainer().select('.tab-pane',true).removeClass('active');
26275 // technically we should have a deactivate event.. but maybe add later.
26276 // and it should not de-activate the selected tab...
26277 this.fireEvent('activatepane', pane);
26278 pane.el.addClass('active');
26279 pane.fireEvent('activate');
26284 getActivePane : function()
26287 Roo.each(this.panes, function(p) {
26288 if(p.el.hasClass('active')){
26309 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26311 * @class Roo.bootstrap.TabPane
26312 * @extends Roo.bootstrap.Component
26313 * Bootstrap TabPane class
26314 * @cfg {Boolean} active (false | true) Default false
26315 * @cfg {String} title title of panel
26319 * Create a new TabPane
26320 * @param {Object} config The config object
26323 Roo.bootstrap.dash.TabPane = function(config){
26324 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26330 * When a pane is activated
26331 * @param {Roo.bootstrap.dash.TabPane} pane
26338 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
26343 // the tabBox that this is attached to.
26346 getAutoCreate : function()
26354 cfg.cls += ' active';
26359 initEvents : function()
26361 //Roo.log('trigger add pane handler');
26362 this.parent().fireEvent('addpane', this)
26366 * Updates the tab title
26367 * @param {String} html to set the title to.
26369 setTitle: function(str)
26375 this.tab.select('a', true).first().dom.innerHTML = str;
26392 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26395 * @class Roo.bootstrap.menu.Menu
26396 * @extends Roo.bootstrap.Component
26397 * Bootstrap Menu class - container for Menu
26398 * @cfg {String} html Text of the menu
26399 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26400 * @cfg {String} icon Font awesome icon
26401 * @cfg {String} pos Menu align to (top | bottom) default bottom
26405 * Create a new Menu
26406 * @param {Object} config The config object
26410 Roo.bootstrap.menu.Menu = function(config){
26411 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26415 * @event beforeshow
26416 * Fires before this menu is displayed
26417 * @param {Roo.bootstrap.menu.Menu} this
26421 * @event beforehide
26422 * Fires before this menu is hidden
26423 * @param {Roo.bootstrap.menu.Menu} this
26428 * Fires after this menu is displayed
26429 * @param {Roo.bootstrap.menu.Menu} this
26434 * Fires after this menu is hidden
26435 * @param {Roo.bootstrap.menu.Menu} this
26440 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26441 * @param {Roo.bootstrap.menu.Menu} this
26442 * @param {Roo.EventObject} e
26449 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
26453 weight : 'default',
26458 getChildContainer : function() {
26459 if(this.isSubMenu){
26463 return this.el.select('ul.dropdown-menu', true).first();
26466 getAutoCreate : function()
26471 cls : 'roo-menu-text',
26479 cls : 'fa ' + this.icon
26490 cls : 'dropdown-button btn btn-' + this.weight,
26495 cls : 'dropdown-toggle btn btn-' + this.weight,
26505 cls : 'dropdown-menu'
26511 if(this.pos == 'top'){
26512 cfg.cls += ' dropup';
26515 if(this.isSubMenu){
26518 cls : 'dropdown-menu'
26525 onRender : function(ct, position)
26527 this.isSubMenu = ct.hasClass('dropdown-submenu');
26529 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26532 initEvents : function()
26534 if(this.isSubMenu){
26538 this.hidden = true;
26540 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26541 this.triggerEl.on('click', this.onTriggerPress, this);
26543 this.buttonEl = this.el.select('button.dropdown-button', true).first();
26544 this.buttonEl.on('click', this.onClick, this);
26550 if(this.isSubMenu){
26554 return this.el.select('ul.dropdown-menu', true).first();
26557 onClick : function(e)
26559 this.fireEvent("click", this, e);
26562 onTriggerPress : function(e)
26564 if (this.isVisible()) {
26571 isVisible : function(){
26572 return !this.hidden;
26577 this.fireEvent("beforeshow", this);
26579 this.hidden = false;
26580 this.el.addClass('open');
26582 Roo.get(document).on("mouseup", this.onMouseUp, this);
26584 this.fireEvent("show", this);
26591 this.fireEvent("beforehide", this);
26593 this.hidden = true;
26594 this.el.removeClass('open');
26596 Roo.get(document).un("mouseup", this.onMouseUp);
26598 this.fireEvent("hide", this);
26601 onMouseUp : function()
26615 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26618 * @class Roo.bootstrap.menu.Item
26619 * @extends Roo.bootstrap.Component
26620 * Bootstrap MenuItem class
26621 * @cfg {Boolean} submenu (true | false) default false
26622 * @cfg {String} html text of the item
26623 * @cfg {String} href the link
26624 * @cfg {Boolean} disable (true | false) default false
26625 * @cfg {Boolean} preventDefault (true | false) default true
26626 * @cfg {String} icon Font awesome icon
26627 * @cfg {String} pos Submenu align to (left | right) default right
26631 * Create a new Item
26632 * @param {Object} config The config object
26636 Roo.bootstrap.menu.Item = function(config){
26637 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26641 * Fires when the mouse is hovering over this menu
26642 * @param {Roo.bootstrap.menu.Item} this
26643 * @param {Roo.EventObject} e
26648 * Fires when the mouse exits this menu
26649 * @param {Roo.bootstrap.menu.Item} this
26650 * @param {Roo.EventObject} e
26656 * The raw click event for the entire grid.
26657 * @param {Roo.EventObject} e
26663 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26668 preventDefault: true,
26673 getAutoCreate : function()
26678 cls : 'roo-menu-item-text',
26686 cls : 'fa ' + this.icon
26695 href : this.href || '#',
26702 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26706 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26708 if(this.pos == 'left'){
26709 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26716 initEvents : function()
26718 this.el.on('mouseover', this.onMouseOver, this);
26719 this.el.on('mouseout', this.onMouseOut, this);
26721 this.el.select('a', true).first().on('click', this.onClick, this);
26725 onClick : function(e)
26727 if(this.preventDefault){
26728 e.preventDefault();
26731 this.fireEvent("click", this, e);
26734 onMouseOver : function(e)
26736 if(this.submenu && this.pos == 'left'){
26737 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26740 this.fireEvent("mouseover", this, e);
26743 onMouseOut : function(e)
26745 this.fireEvent("mouseout", this, e);
26757 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26760 * @class Roo.bootstrap.menu.Separator
26761 * @extends Roo.bootstrap.Component
26762 * Bootstrap Separator class
26765 * Create a new Separator
26766 * @param {Object} config The config object
26770 Roo.bootstrap.menu.Separator = function(config){
26771 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26774 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26776 getAutoCreate : function(){
26797 * @class Roo.bootstrap.Tooltip
26798 * Bootstrap Tooltip class
26799 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26800 * to determine which dom element triggers the tooltip.
26802 * It needs to add support for additional attributes like tooltip-position
26805 * Create a new Toolti
26806 * @param {Object} config The config object
26809 Roo.bootstrap.Tooltip = function(config){
26810 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26812 this.alignment = Roo.bootstrap.Tooltip.alignment;
26814 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26815 this.alignment = config.alignment;
26820 Roo.apply(Roo.bootstrap.Tooltip, {
26822 * @function init initialize tooltip monitoring.
26826 currentTip : false,
26827 currentRegion : false,
26833 Roo.get(document).on('mouseover', this.enter ,this);
26834 Roo.get(document).on('mouseout', this.leave, this);
26837 this.currentTip = new Roo.bootstrap.Tooltip();
26840 enter : function(ev)
26842 var dom = ev.getTarget();
26844 //Roo.log(['enter',dom]);
26845 var el = Roo.fly(dom);
26846 if (this.currentEl) {
26848 //Roo.log(this.currentEl);
26849 //Roo.log(this.currentEl.contains(dom));
26850 if (this.currentEl == el) {
26853 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26859 if (this.currentTip.el) {
26860 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26864 if(!el || el.dom == document){
26870 // you can not look for children, as if el is the body.. then everythign is the child..
26871 if (!el.attr('tooltip')) { //
26872 if (!el.select("[tooltip]").elements.length) {
26875 // is the mouse over this child...?
26876 bindEl = el.select("[tooltip]").first();
26877 var xy = ev.getXY();
26878 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26879 //Roo.log("not in region.");
26882 //Roo.log("child element over..");
26885 this.currentEl = bindEl;
26886 this.currentTip.bind(bindEl);
26887 this.currentRegion = Roo.lib.Region.getRegion(dom);
26888 this.currentTip.enter();
26891 leave : function(ev)
26893 var dom = ev.getTarget();
26894 //Roo.log(['leave',dom]);
26895 if (!this.currentEl) {
26900 if (dom != this.currentEl.dom) {
26903 var xy = ev.getXY();
26904 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26907 // only activate leave if mouse cursor is outside... bounding box..
26912 if (this.currentTip) {
26913 this.currentTip.leave();
26915 //Roo.log('clear currentEl');
26916 this.currentEl = false;
26921 'left' : ['r-l', [-2,0], 'right'],
26922 'right' : ['l-r', [2,0], 'left'],
26923 'bottom' : ['t-b', [0,2], 'top'],
26924 'top' : [ 'b-t', [0,-2], 'bottom']
26930 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26935 delay : null, // can be { show : 300 , hide: 500}
26939 hoverState : null, //???
26941 placement : 'bottom',
26945 getAutoCreate : function(){
26952 cls : 'tooltip-arrow'
26955 cls : 'tooltip-inner'
26962 bind : function(el)
26968 enter : function () {
26970 if (this.timeout != null) {
26971 clearTimeout(this.timeout);
26974 this.hoverState = 'in';
26975 //Roo.log("enter - show");
26976 if (!this.delay || !this.delay.show) {
26981 this.timeout = setTimeout(function () {
26982 if (_t.hoverState == 'in') {
26985 }, this.delay.show);
26989 clearTimeout(this.timeout);
26991 this.hoverState = 'out';
26992 if (!this.delay || !this.delay.hide) {
26998 this.timeout = setTimeout(function () {
26999 //Roo.log("leave - timeout");
27001 if (_t.hoverState == 'out') {
27003 Roo.bootstrap.Tooltip.currentEl = false;
27008 show : function (msg)
27011 this.render(document.body);
27014 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
27016 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
27018 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
27020 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
27022 var placement = typeof this.placement == 'function' ?
27023 this.placement.call(this, this.el, on_el) :
27026 var autoToken = /\s?auto?\s?/i;
27027 var autoPlace = autoToken.test(placement);
27029 placement = placement.replace(autoToken, '') || 'top';
27033 //this.el.setXY([0,0]);
27035 //this.el.dom.style.display='block';
27037 //this.el.appendTo(on_el);
27039 var p = this.getPosition();
27040 var box = this.el.getBox();
27046 var align = this.alignment[placement];
27048 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
27050 if(placement == 'top' || placement == 'bottom'){
27052 placement = 'right';
27055 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
27056 placement = 'left';
27059 var scroll = Roo.select('body', true).first().getScroll();
27061 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
27065 align = this.alignment[placement];
27068 this.el.alignTo(this.bindEl, align[0],align[1]);
27069 //var arrow = this.el.select('.arrow',true).first();
27070 //arrow.set(align[2],
27072 this.el.addClass(placement);
27074 this.el.addClass('in fade');
27076 this.hoverState = null;
27078 if (this.el.hasClass('fade')) {
27089 //this.el.setXY([0,0]);
27090 this.el.removeClass('in');
27106 * @class Roo.bootstrap.LocationPicker
27107 * @extends Roo.bootstrap.Component
27108 * Bootstrap LocationPicker class
27109 * @cfg {Number} latitude Position when init default 0
27110 * @cfg {Number} longitude Position when init default 0
27111 * @cfg {Number} zoom default 15
27112 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
27113 * @cfg {Boolean} mapTypeControl default false
27114 * @cfg {Boolean} disableDoubleClickZoom default false
27115 * @cfg {Boolean} scrollwheel default true
27116 * @cfg {Boolean} streetViewControl default false
27117 * @cfg {Number} radius default 0
27118 * @cfg {String} locationName
27119 * @cfg {Boolean} draggable default true
27120 * @cfg {Boolean} enableAutocomplete default false
27121 * @cfg {Boolean} enableReverseGeocode default true
27122 * @cfg {String} markerTitle
27125 * Create a new LocationPicker
27126 * @param {Object} config The config object
27130 Roo.bootstrap.LocationPicker = function(config){
27132 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
27137 * Fires when the picker initialized.
27138 * @param {Roo.bootstrap.LocationPicker} this
27139 * @param {Google Location} location
27143 * @event positionchanged
27144 * Fires when the picker position changed.
27145 * @param {Roo.bootstrap.LocationPicker} this
27146 * @param {Google Location} location
27148 positionchanged : true,
27151 * Fires when the map resize.
27152 * @param {Roo.bootstrap.LocationPicker} this
27157 * Fires when the map show.
27158 * @param {Roo.bootstrap.LocationPicker} this
27163 * Fires when the map hide.
27164 * @param {Roo.bootstrap.LocationPicker} this
27169 * Fires when click the map.
27170 * @param {Roo.bootstrap.LocationPicker} this
27171 * @param {Map event} e
27175 * @event mapRightClick
27176 * Fires when right click the map.
27177 * @param {Roo.bootstrap.LocationPicker} this
27178 * @param {Map event} e
27180 mapRightClick : true,
27182 * @event markerClick
27183 * Fires when click the marker.
27184 * @param {Roo.bootstrap.LocationPicker} this
27185 * @param {Map event} e
27187 markerClick : true,
27189 * @event markerRightClick
27190 * Fires when right click the marker.
27191 * @param {Roo.bootstrap.LocationPicker} this
27192 * @param {Map event} e
27194 markerRightClick : true,
27196 * @event OverlayViewDraw
27197 * Fires when OverlayView Draw
27198 * @param {Roo.bootstrap.LocationPicker} this
27200 OverlayViewDraw : true,
27202 * @event OverlayViewOnAdd
27203 * Fires when OverlayView Draw
27204 * @param {Roo.bootstrap.LocationPicker} this
27206 OverlayViewOnAdd : true,
27208 * @event OverlayViewOnRemove
27209 * Fires when OverlayView Draw
27210 * @param {Roo.bootstrap.LocationPicker} this
27212 OverlayViewOnRemove : true,
27214 * @event OverlayViewShow
27215 * Fires when OverlayView Draw
27216 * @param {Roo.bootstrap.LocationPicker} this
27217 * @param {Pixel} cpx
27219 OverlayViewShow : true,
27221 * @event OverlayViewHide
27222 * Fires when OverlayView Draw
27223 * @param {Roo.bootstrap.LocationPicker} this
27225 OverlayViewHide : true,
27227 * @event loadexception
27228 * Fires when load google lib failed.
27229 * @param {Roo.bootstrap.LocationPicker} this
27231 loadexception : true
27236 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
27238 gMapContext: false,
27244 mapTypeControl: false,
27245 disableDoubleClickZoom: false,
27247 streetViewControl: false,
27251 enableAutocomplete: false,
27252 enableReverseGeocode: true,
27255 getAutoCreate: function()
27260 cls: 'roo-location-picker'
27266 initEvents: function(ct, position)
27268 if(!this.el.getWidth() || this.isApplied()){
27272 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27277 initial: function()
27279 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27280 this.fireEvent('loadexception', this);
27284 if(!this.mapTypeId){
27285 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27288 this.gMapContext = this.GMapContext();
27290 this.initOverlayView();
27292 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27296 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27297 _this.setPosition(_this.gMapContext.marker.position);
27300 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27301 _this.fireEvent('mapClick', this, event);
27305 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27306 _this.fireEvent('mapRightClick', this, event);
27310 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27311 _this.fireEvent('markerClick', this, event);
27315 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27316 _this.fireEvent('markerRightClick', this, event);
27320 this.setPosition(this.gMapContext.location);
27322 this.fireEvent('initial', this, this.gMapContext.location);
27325 initOverlayView: function()
27329 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27333 _this.fireEvent('OverlayViewDraw', _this);
27338 _this.fireEvent('OverlayViewOnAdd', _this);
27341 onRemove: function()
27343 _this.fireEvent('OverlayViewOnRemove', _this);
27346 show: function(cpx)
27348 _this.fireEvent('OverlayViewShow', _this, cpx);
27353 _this.fireEvent('OverlayViewHide', _this);
27359 fromLatLngToContainerPixel: function(event)
27361 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27364 isApplied: function()
27366 return this.getGmapContext() == false ? false : true;
27369 getGmapContext: function()
27371 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27374 GMapContext: function()
27376 var position = new google.maps.LatLng(this.latitude, this.longitude);
27378 var _map = new google.maps.Map(this.el.dom, {
27381 mapTypeId: this.mapTypeId,
27382 mapTypeControl: this.mapTypeControl,
27383 disableDoubleClickZoom: this.disableDoubleClickZoom,
27384 scrollwheel: this.scrollwheel,
27385 streetViewControl: this.streetViewControl,
27386 locationName: this.locationName,
27387 draggable: this.draggable,
27388 enableAutocomplete: this.enableAutocomplete,
27389 enableReverseGeocode: this.enableReverseGeocode
27392 var _marker = new google.maps.Marker({
27393 position: position,
27395 title: this.markerTitle,
27396 draggable: this.draggable
27403 location: position,
27404 radius: this.radius,
27405 locationName: this.locationName,
27406 addressComponents: {
27407 formatted_address: null,
27408 addressLine1: null,
27409 addressLine2: null,
27411 streetNumber: null,
27415 stateOrProvince: null
27418 domContainer: this.el.dom,
27419 geodecoder: new google.maps.Geocoder()
27423 drawCircle: function(center, radius, options)
27425 if (this.gMapContext.circle != null) {
27426 this.gMapContext.circle.setMap(null);
27430 options = Roo.apply({}, options, {
27431 strokeColor: "#0000FF",
27432 strokeOpacity: .35,
27434 fillColor: "#0000FF",
27438 options.map = this.gMapContext.map;
27439 options.radius = radius;
27440 options.center = center;
27441 this.gMapContext.circle = new google.maps.Circle(options);
27442 return this.gMapContext.circle;
27448 setPosition: function(location)
27450 this.gMapContext.location = location;
27451 this.gMapContext.marker.setPosition(location);
27452 this.gMapContext.map.panTo(location);
27453 this.drawCircle(location, this.gMapContext.radius, {});
27457 if (this.gMapContext.settings.enableReverseGeocode) {
27458 this.gMapContext.geodecoder.geocode({
27459 latLng: this.gMapContext.location
27460 }, function(results, status) {
27462 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27463 _this.gMapContext.locationName = results[0].formatted_address;
27464 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27466 _this.fireEvent('positionchanged', this, location);
27473 this.fireEvent('positionchanged', this, location);
27478 google.maps.event.trigger(this.gMapContext.map, "resize");
27480 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27482 this.fireEvent('resize', this);
27485 setPositionByLatLng: function(latitude, longitude)
27487 this.setPosition(new google.maps.LatLng(latitude, longitude));
27490 getCurrentPosition: function()
27493 latitude: this.gMapContext.location.lat(),
27494 longitude: this.gMapContext.location.lng()
27498 getAddressName: function()
27500 return this.gMapContext.locationName;
27503 getAddressComponents: function()
27505 return this.gMapContext.addressComponents;
27508 address_component_from_google_geocode: function(address_components)
27512 for (var i = 0; i < address_components.length; i++) {
27513 var component = address_components[i];
27514 if (component.types.indexOf("postal_code") >= 0) {
27515 result.postalCode = component.short_name;
27516 } else if (component.types.indexOf("street_number") >= 0) {
27517 result.streetNumber = component.short_name;
27518 } else if (component.types.indexOf("route") >= 0) {
27519 result.streetName = component.short_name;
27520 } else if (component.types.indexOf("neighborhood") >= 0) {
27521 result.city = component.short_name;
27522 } else if (component.types.indexOf("locality") >= 0) {
27523 result.city = component.short_name;
27524 } else if (component.types.indexOf("sublocality") >= 0) {
27525 result.district = component.short_name;
27526 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27527 result.stateOrProvince = component.short_name;
27528 } else if (component.types.indexOf("country") >= 0) {
27529 result.country = component.short_name;
27533 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27534 result.addressLine2 = "";
27538 setZoomLevel: function(zoom)
27540 this.gMapContext.map.setZoom(zoom);
27553 this.fireEvent('show', this);
27564 this.fireEvent('hide', this);
27569 Roo.apply(Roo.bootstrap.LocationPicker, {
27571 OverlayView : function(map, options)
27573 options = options || {};
27580 * @class Roo.bootstrap.Alert
27581 * @extends Roo.bootstrap.Component
27582 * Bootstrap Alert class - shows an alert area box
27584 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27585 Enter a valid email address
27588 * @cfg {String} title The title of alert
27589 * @cfg {String} html The content of alert
27590 * @cfg {String} weight ( success | info | warning | danger )
27591 * @cfg {String} faicon font-awesomeicon
27594 * Create a new alert
27595 * @param {Object} config The config object
27599 Roo.bootstrap.Alert = function(config){
27600 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27604 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27611 getAutoCreate : function()
27620 cls : 'roo-alert-icon'
27625 cls : 'roo-alert-title',
27630 cls : 'roo-alert-text',
27637 cfg.cn[0].cls += ' fa ' + this.faicon;
27641 cfg.cls += ' alert-' + this.weight;
27647 initEvents: function()
27649 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27652 setTitle : function(str)
27654 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27657 setText : function(str)
27659 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27662 setWeight : function(weight)
27665 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27668 this.weight = weight;
27670 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27673 setIcon : function(icon)
27676 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27679 this.faicon = icon;
27681 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27702 * @class Roo.bootstrap.UploadCropbox
27703 * @extends Roo.bootstrap.Component
27704 * Bootstrap UploadCropbox class
27705 * @cfg {String} emptyText show when image has been loaded
27706 * @cfg {String} rotateNotify show when image too small to rotate
27707 * @cfg {Number} errorTimeout default 3000
27708 * @cfg {Number} minWidth default 300
27709 * @cfg {Number} minHeight default 300
27710 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27711 * @cfg {Boolean} isDocument (true|false) default false
27712 * @cfg {String} url action url
27713 * @cfg {String} paramName default 'imageUpload'
27714 * @cfg {String} method default POST
27715 * @cfg {Boolean} loadMask (true|false) default true
27716 * @cfg {Boolean} loadingText default 'Loading...'
27719 * Create a new UploadCropbox
27720 * @param {Object} config The config object
27723 Roo.bootstrap.UploadCropbox = function(config){
27724 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27728 * @event beforeselectfile
27729 * Fire before select file
27730 * @param {Roo.bootstrap.UploadCropbox} this
27732 "beforeselectfile" : true,
27735 * Fire after initEvent
27736 * @param {Roo.bootstrap.UploadCropbox} this
27741 * Fire after initEvent
27742 * @param {Roo.bootstrap.UploadCropbox} this
27743 * @param {String} data
27748 * Fire when preparing the file data
27749 * @param {Roo.bootstrap.UploadCropbox} this
27750 * @param {Object} file
27755 * Fire when get exception
27756 * @param {Roo.bootstrap.UploadCropbox} this
27757 * @param {XMLHttpRequest} xhr
27759 "exception" : true,
27761 * @event beforeloadcanvas
27762 * Fire before load the canvas
27763 * @param {Roo.bootstrap.UploadCropbox} this
27764 * @param {String} src
27766 "beforeloadcanvas" : true,
27769 * Fire when trash image
27770 * @param {Roo.bootstrap.UploadCropbox} this
27775 * Fire when download the image
27776 * @param {Roo.bootstrap.UploadCropbox} this
27780 * @event footerbuttonclick
27781 * Fire when footerbuttonclick
27782 * @param {Roo.bootstrap.UploadCropbox} this
27783 * @param {String} type
27785 "footerbuttonclick" : true,
27789 * @param {Roo.bootstrap.UploadCropbox} this
27794 * Fire when rotate the image
27795 * @param {Roo.bootstrap.UploadCropbox} this
27796 * @param {String} pos
27801 * Fire when inspect the file
27802 * @param {Roo.bootstrap.UploadCropbox} this
27803 * @param {Object} file
27808 * Fire when xhr upload the file
27809 * @param {Roo.bootstrap.UploadCropbox} this
27810 * @param {Object} data
27815 * Fire when arrange the file data
27816 * @param {Roo.bootstrap.UploadCropbox} this
27817 * @param {Object} formData
27822 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27825 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27827 emptyText : 'Click to upload image',
27828 rotateNotify : 'Image is too small to rotate',
27829 errorTimeout : 3000,
27843 cropType : 'image/jpeg',
27845 canvasLoaded : false,
27846 isDocument : false,
27848 paramName : 'imageUpload',
27850 loadingText : 'Loading...',
27853 getAutoCreate : function()
27857 cls : 'roo-upload-cropbox',
27861 cls : 'roo-upload-cropbox-selector',
27866 cls : 'roo-upload-cropbox-body',
27867 style : 'cursor:pointer',
27871 cls : 'roo-upload-cropbox-preview'
27875 cls : 'roo-upload-cropbox-thumb'
27879 cls : 'roo-upload-cropbox-empty-notify',
27880 html : this.emptyText
27884 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27885 html : this.rotateNotify
27891 cls : 'roo-upload-cropbox-footer',
27894 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27904 onRender : function(ct, position)
27906 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27908 if (this.buttons.length) {
27910 Roo.each(this.buttons, function(bb) {
27912 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27914 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27920 this.maskEl = this.el;
27924 initEvents : function()
27926 this.urlAPI = (window.createObjectURL && window) ||
27927 (window.URL && URL.revokeObjectURL && URL) ||
27928 (window.webkitURL && webkitURL);
27930 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27931 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27933 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27934 this.selectorEl.hide();
27936 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27937 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27939 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27940 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27941 this.thumbEl.hide();
27943 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27944 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27946 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27947 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27948 this.errorEl.hide();
27950 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27951 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27952 this.footerEl.hide();
27954 this.setThumbBoxSize();
27960 this.fireEvent('initial', this);
27967 window.addEventListener("resize", function() { _this.resize(); } );
27969 this.bodyEl.on('click', this.beforeSelectFile, this);
27972 this.bodyEl.on('touchstart', this.onTouchStart, this);
27973 this.bodyEl.on('touchmove', this.onTouchMove, this);
27974 this.bodyEl.on('touchend', this.onTouchEnd, this);
27978 this.bodyEl.on('mousedown', this.onMouseDown, this);
27979 this.bodyEl.on('mousemove', this.onMouseMove, this);
27980 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27981 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27982 Roo.get(document).on('mouseup', this.onMouseUp, this);
27985 this.selectorEl.on('change', this.onFileSelected, this);
27991 this.baseScale = 1;
27993 this.baseRotate = 1;
27994 this.dragable = false;
27995 this.pinching = false;
27998 this.cropData = false;
27999 this.notifyEl.dom.innerHTML = this.emptyText;
28001 this.selectorEl.dom.value = '';
28005 resize : function()
28007 if(this.fireEvent('resize', this) != false){
28008 this.setThumbBoxPosition();
28009 this.setCanvasPosition();
28013 onFooterButtonClick : function(e, el, o, type)
28016 case 'rotate-left' :
28017 this.onRotateLeft(e);
28019 case 'rotate-right' :
28020 this.onRotateRight(e);
28023 this.beforeSelectFile(e);
28038 this.fireEvent('footerbuttonclick', this, type);
28041 beforeSelectFile : function(e)
28043 e.preventDefault();
28045 if(this.fireEvent('beforeselectfile', this) != false){
28046 this.selectorEl.dom.click();
28050 onFileSelected : function(e)
28052 e.preventDefault();
28054 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28058 var file = this.selectorEl.dom.files[0];
28060 if(this.fireEvent('inspect', this, file) != false){
28061 this.prepare(file);
28066 trash : function(e)
28068 this.fireEvent('trash', this);
28071 download : function(e)
28073 this.fireEvent('download', this);
28076 loadCanvas : function(src)
28078 if(this.fireEvent('beforeloadcanvas', this, src) != false){
28082 this.imageEl = document.createElement('img');
28086 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
28088 this.imageEl.src = src;
28092 onLoadCanvas : function()
28094 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
28095 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
28097 this.bodyEl.un('click', this.beforeSelectFile, this);
28099 this.notifyEl.hide();
28100 this.thumbEl.show();
28101 this.footerEl.show();
28103 this.baseRotateLevel();
28105 if(this.isDocument){
28106 this.setThumbBoxSize();
28109 this.setThumbBoxPosition();
28111 this.baseScaleLevel();
28117 this.canvasLoaded = true;
28120 this.maskEl.unmask();
28125 setCanvasPosition : function()
28127 if(!this.canvasEl){
28131 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
28132 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
28134 this.previewEl.setLeft(pw);
28135 this.previewEl.setTop(ph);
28139 onMouseDown : function(e)
28143 this.dragable = true;
28144 this.pinching = false;
28146 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
28147 this.dragable = false;
28151 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28152 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28156 onMouseMove : function(e)
28160 if(!this.canvasLoaded){
28164 if (!this.dragable){
28168 var minX = Math.ceil(this.thumbEl.getLeft(true));
28169 var minY = Math.ceil(this.thumbEl.getTop(true));
28171 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
28172 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
28174 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28175 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28177 x = x - this.mouseX;
28178 y = y - this.mouseY;
28180 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
28181 var bgY = Math.ceil(y + this.previewEl.getTop(true));
28183 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
28184 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
28186 this.previewEl.setLeft(bgX);
28187 this.previewEl.setTop(bgY);
28189 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28190 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28193 onMouseUp : function(e)
28197 this.dragable = false;
28200 onMouseWheel : function(e)
28204 this.startScale = this.scale;
28206 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
28208 if(!this.zoomable()){
28209 this.scale = this.startScale;
28218 zoomable : function()
28220 var minScale = this.thumbEl.getWidth() / this.minWidth;
28222 if(this.minWidth < this.minHeight){
28223 minScale = this.thumbEl.getHeight() / this.minHeight;
28226 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
28227 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
28231 (this.rotate == 0 || this.rotate == 180) &&
28233 width > this.imageEl.OriginWidth ||
28234 height > this.imageEl.OriginHeight ||
28235 (width < this.minWidth && height < this.minHeight)
28243 (this.rotate == 90 || this.rotate == 270) &&
28245 width > this.imageEl.OriginWidth ||
28246 height > this.imageEl.OriginHeight ||
28247 (width < this.minHeight && height < this.minWidth)
28254 !this.isDocument &&
28255 (this.rotate == 0 || this.rotate == 180) &&
28257 width < this.minWidth ||
28258 width > this.imageEl.OriginWidth ||
28259 height < this.minHeight ||
28260 height > this.imageEl.OriginHeight
28267 !this.isDocument &&
28268 (this.rotate == 90 || this.rotate == 270) &&
28270 width < this.minHeight ||
28271 width > this.imageEl.OriginWidth ||
28272 height < this.minWidth ||
28273 height > this.imageEl.OriginHeight
28283 onRotateLeft : function(e)
28285 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28287 var minScale = this.thumbEl.getWidth() / this.minWidth;
28289 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28290 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28292 this.startScale = this.scale;
28294 while (this.getScaleLevel() < minScale){
28296 this.scale = this.scale + 1;
28298 if(!this.zoomable()){
28303 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28304 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28309 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28316 this.scale = this.startScale;
28318 this.onRotateFail();
28323 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28325 if(this.isDocument){
28326 this.setThumbBoxSize();
28327 this.setThumbBoxPosition();
28328 this.setCanvasPosition();
28333 this.fireEvent('rotate', this, 'left');
28337 onRotateRight : function(e)
28339 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28341 var minScale = this.thumbEl.getWidth() / this.minWidth;
28343 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28344 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28346 this.startScale = this.scale;
28348 while (this.getScaleLevel() < minScale){
28350 this.scale = this.scale + 1;
28352 if(!this.zoomable()){
28357 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28358 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28363 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28370 this.scale = this.startScale;
28372 this.onRotateFail();
28377 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28379 if(this.isDocument){
28380 this.setThumbBoxSize();
28381 this.setThumbBoxPosition();
28382 this.setCanvasPosition();
28387 this.fireEvent('rotate', this, 'right');
28390 onRotateFail : function()
28392 this.errorEl.show(true);
28396 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28401 this.previewEl.dom.innerHTML = '';
28403 var canvasEl = document.createElement("canvas");
28405 var contextEl = canvasEl.getContext("2d");
28407 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28408 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28409 var center = this.imageEl.OriginWidth / 2;
28411 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28412 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28413 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28414 center = this.imageEl.OriginHeight / 2;
28417 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28419 contextEl.translate(center, center);
28420 contextEl.rotate(this.rotate * Math.PI / 180);
28422 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28424 this.canvasEl = document.createElement("canvas");
28426 this.contextEl = this.canvasEl.getContext("2d");
28428 switch (this.rotate) {
28431 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28432 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28434 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28439 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28440 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28442 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28443 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);
28447 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28452 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28453 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28455 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28456 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);
28460 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);
28465 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28466 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28468 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28469 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28473 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);
28480 this.previewEl.appendChild(this.canvasEl);
28482 this.setCanvasPosition();
28487 if(!this.canvasLoaded){
28491 var imageCanvas = document.createElement("canvas");
28493 var imageContext = imageCanvas.getContext("2d");
28495 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28496 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28498 var center = imageCanvas.width / 2;
28500 imageContext.translate(center, center);
28502 imageContext.rotate(this.rotate * Math.PI / 180);
28504 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28506 var canvas = document.createElement("canvas");
28508 var context = canvas.getContext("2d");
28510 canvas.width = this.minWidth;
28511 canvas.height = this.minHeight;
28513 switch (this.rotate) {
28516 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28517 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28519 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28520 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28522 var targetWidth = this.minWidth - 2 * x;
28523 var targetHeight = this.minHeight - 2 * y;
28527 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28528 scale = targetWidth / width;
28531 if(x > 0 && y == 0){
28532 scale = targetHeight / height;
28535 if(x > 0 && y > 0){
28536 scale = targetWidth / width;
28538 if(width < height){
28539 scale = targetHeight / height;
28543 context.scale(scale, scale);
28545 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28546 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28548 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28549 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28551 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28556 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28557 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28559 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28560 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28562 var targetWidth = this.minWidth - 2 * x;
28563 var targetHeight = this.minHeight - 2 * y;
28567 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28568 scale = targetWidth / width;
28571 if(x > 0 && y == 0){
28572 scale = targetHeight / height;
28575 if(x > 0 && y > 0){
28576 scale = targetWidth / width;
28578 if(width < height){
28579 scale = targetHeight / height;
28583 context.scale(scale, scale);
28585 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28586 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28588 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28589 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28591 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28593 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28598 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28599 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28601 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28602 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28604 var targetWidth = this.minWidth - 2 * x;
28605 var targetHeight = this.minHeight - 2 * y;
28609 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28610 scale = targetWidth / width;
28613 if(x > 0 && y == 0){
28614 scale = targetHeight / height;
28617 if(x > 0 && y > 0){
28618 scale = targetWidth / width;
28620 if(width < height){
28621 scale = targetHeight / height;
28625 context.scale(scale, scale);
28627 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28628 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28630 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28631 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28633 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28634 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28636 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28641 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28642 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28644 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28645 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28647 var targetWidth = this.minWidth - 2 * x;
28648 var targetHeight = this.minHeight - 2 * y;
28652 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28653 scale = targetWidth / width;
28656 if(x > 0 && y == 0){
28657 scale = targetHeight / height;
28660 if(x > 0 && y > 0){
28661 scale = targetWidth / width;
28663 if(width < height){
28664 scale = targetHeight / height;
28668 context.scale(scale, scale);
28670 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28671 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28673 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28674 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28676 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28678 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28685 this.cropData = canvas.toDataURL(this.cropType);
28687 if(this.fireEvent('crop', this, this.cropData) !== false){
28688 this.process(this.file, this.cropData);
28695 setThumbBoxSize : function()
28699 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28700 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28701 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28703 this.minWidth = width;
28704 this.minHeight = height;
28706 if(this.rotate == 90 || this.rotate == 270){
28707 this.minWidth = height;
28708 this.minHeight = width;
28713 width = Math.ceil(this.minWidth * height / this.minHeight);
28715 if(this.minWidth > this.minHeight){
28717 height = Math.ceil(this.minHeight * width / this.minWidth);
28720 this.thumbEl.setStyle({
28721 width : width + 'px',
28722 height : height + 'px'
28729 setThumbBoxPosition : function()
28731 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28732 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28734 this.thumbEl.setLeft(x);
28735 this.thumbEl.setTop(y);
28739 baseRotateLevel : function()
28741 this.baseRotate = 1;
28744 typeof(this.exif) != 'undefined' &&
28745 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28746 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28748 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28751 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28755 baseScaleLevel : function()
28759 if(this.isDocument){
28761 if(this.baseRotate == 6 || this.baseRotate == 8){
28763 height = this.thumbEl.getHeight();
28764 this.baseScale = height / this.imageEl.OriginWidth;
28766 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28767 width = this.thumbEl.getWidth();
28768 this.baseScale = width / this.imageEl.OriginHeight;
28774 height = this.thumbEl.getHeight();
28775 this.baseScale = height / this.imageEl.OriginHeight;
28777 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28778 width = this.thumbEl.getWidth();
28779 this.baseScale = width / this.imageEl.OriginWidth;
28785 if(this.baseRotate == 6 || this.baseRotate == 8){
28787 width = this.thumbEl.getHeight();
28788 this.baseScale = width / this.imageEl.OriginHeight;
28790 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28791 height = this.thumbEl.getWidth();
28792 this.baseScale = height / this.imageEl.OriginHeight;
28795 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28796 height = this.thumbEl.getWidth();
28797 this.baseScale = height / this.imageEl.OriginHeight;
28799 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28800 width = this.thumbEl.getHeight();
28801 this.baseScale = width / this.imageEl.OriginWidth;
28808 width = this.thumbEl.getWidth();
28809 this.baseScale = width / this.imageEl.OriginWidth;
28811 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28812 height = this.thumbEl.getHeight();
28813 this.baseScale = height / this.imageEl.OriginHeight;
28816 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28818 height = this.thumbEl.getHeight();
28819 this.baseScale = height / this.imageEl.OriginHeight;
28821 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28822 width = this.thumbEl.getWidth();
28823 this.baseScale = width / this.imageEl.OriginWidth;
28831 getScaleLevel : function()
28833 return this.baseScale * Math.pow(1.1, this.scale);
28836 onTouchStart : function(e)
28838 if(!this.canvasLoaded){
28839 this.beforeSelectFile(e);
28843 var touches = e.browserEvent.touches;
28849 if(touches.length == 1){
28850 this.onMouseDown(e);
28854 if(touches.length != 2){
28860 for(var i = 0, finger; finger = touches[i]; i++){
28861 coords.push(finger.pageX, finger.pageY);
28864 var x = Math.pow(coords[0] - coords[2], 2);
28865 var y = Math.pow(coords[1] - coords[3], 2);
28867 this.startDistance = Math.sqrt(x + y);
28869 this.startScale = this.scale;
28871 this.pinching = true;
28872 this.dragable = false;
28876 onTouchMove : function(e)
28878 if(!this.pinching && !this.dragable){
28882 var touches = e.browserEvent.touches;
28889 this.onMouseMove(e);
28895 for(var i = 0, finger; finger = touches[i]; i++){
28896 coords.push(finger.pageX, finger.pageY);
28899 var x = Math.pow(coords[0] - coords[2], 2);
28900 var y = Math.pow(coords[1] - coords[3], 2);
28902 this.endDistance = Math.sqrt(x + y);
28904 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28906 if(!this.zoomable()){
28907 this.scale = this.startScale;
28915 onTouchEnd : function(e)
28917 this.pinching = false;
28918 this.dragable = false;
28922 process : function(file, crop)
28925 this.maskEl.mask(this.loadingText);
28928 this.xhr = new XMLHttpRequest();
28930 file.xhr = this.xhr;
28932 this.xhr.open(this.method, this.url, true);
28935 "Accept": "application/json",
28936 "Cache-Control": "no-cache",
28937 "X-Requested-With": "XMLHttpRequest"
28940 for (var headerName in headers) {
28941 var headerValue = headers[headerName];
28943 this.xhr.setRequestHeader(headerName, headerValue);
28949 this.xhr.onload = function()
28951 _this.xhrOnLoad(_this.xhr);
28954 this.xhr.onerror = function()
28956 _this.xhrOnError(_this.xhr);
28959 var formData = new FormData();
28961 formData.append('returnHTML', 'NO');
28964 formData.append('crop', crop);
28967 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28968 formData.append(this.paramName, file, file.name);
28971 if(typeof(file.filename) != 'undefined'){
28972 formData.append('filename', file.filename);
28975 if(typeof(file.mimetype) != 'undefined'){
28976 formData.append('mimetype', file.mimetype);
28979 if(this.fireEvent('arrange', this, formData) != false){
28980 this.xhr.send(formData);
28984 xhrOnLoad : function(xhr)
28987 this.maskEl.unmask();
28990 if (xhr.readyState !== 4) {
28991 this.fireEvent('exception', this, xhr);
28995 var response = Roo.decode(xhr.responseText);
28997 if(!response.success){
28998 this.fireEvent('exception', this, xhr);
29002 var response = Roo.decode(xhr.responseText);
29004 this.fireEvent('upload', this, response);
29008 xhrOnError : function()
29011 this.maskEl.unmask();
29014 Roo.log('xhr on error');
29016 var response = Roo.decode(xhr.responseText);
29022 prepare : function(file)
29025 this.maskEl.mask(this.loadingText);
29031 if(typeof(file) === 'string'){
29032 this.loadCanvas(file);
29036 if(!file || !this.urlAPI){
29041 this.cropType = file.type;
29045 if(this.fireEvent('prepare', this, this.file) != false){
29047 var reader = new FileReader();
29049 reader.onload = function (e) {
29050 if (e.target.error) {
29051 Roo.log(e.target.error);
29055 var buffer = e.target.result,
29056 dataView = new DataView(buffer),
29058 maxOffset = dataView.byteLength - 4,
29062 if (dataView.getUint16(0) === 0xffd8) {
29063 while (offset < maxOffset) {
29064 markerBytes = dataView.getUint16(offset);
29066 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
29067 markerLength = dataView.getUint16(offset + 2) + 2;
29068 if (offset + markerLength > dataView.byteLength) {
29069 Roo.log('Invalid meta data: Invalid segment size.');
29073 if(markerBytes == 0xffe1){
29074 _this.parseExifData(
29081 offset += markerLength;
29091 var url = _this.urlAPI.createObjectURL(_this.file);
29093 _this.loadCanvas(url);
29098 reader.readAsArrayBuffer(this.file);
29104 parseExifData : function(dataView, offset, length)
29106 var tiffOffset = offset + 10,
29110 if (dataView.getUint32(offset + 4) !== 0x45786966) {
29111 // No Exif data, might be XMP data instead
29115 // Check for the ASCII code for "Exif" (0x45786966):
29116 if (dataView.getUint32(offset + 4) !== 0x45786966) {
29117 // No Exif data, might be XMP data instead
29120 if (tiffOffset + 8 > dataView.byteLength) {
29121 Roo.log('Invalid Exif data: Invalid segment size.');
29124 // Check for the two null bytes:
29125 if (dataView.getUint16(offset + 8) !== 0x0000) {
29126 Roo.log('Invalid Exif data: Missing byte alignment offset.');
29129 // Check the byte alignment:
29130 switch (dataView.getUint16(tiffOffset)) {
29132 littleEndian = true;
29135 littleEndian = false;
29138 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
29141 // Check for the TIFF tag marker (0x002A):
29142 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
29143 Roo.log('Invalid Exif data: Missing TIFF marker.');
29146 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
29147 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
29149 this.parseExifTags(
29152 tiffOffset + dirOffset,
29157 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
29162 if (dirOffset + 6 > dataView.byteLength) {
29163 Roo.log('Invalid Exif data: Invalid directory offset.');
29166 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
29167 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
29168 if (dirEndOffset + 4 > dataView.byteLength) {
29169 Roo.log('Invalid Exif data: Invalid directory size.');
29172 for (i = 0; i < tagsNumber; i += 1) {
29176 dirOffset + 2 + 12 * i, // tag offset
29180 // Return the offset to the next directory:
29181 return dataView.getUint32(dirEndOffset, littleEndian);
29184 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
29186 var tag = dataView.getUint16(offset, littleEndian);
29188 this.exif[tag] = this.getExifValue(
29192 dataView.getUint16(offset + 2, littleEndian), // tag type
29193 dataView.getUint32(offset + 4, littleEndian), // tag length
29198 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
29200 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
29209 Roo.log('Invalid Exif data: Invalid tag type.');
29213 tagSize = tagType.size * length;
29214 // Determine if the value is contained in the dataOffset bytes,
29215 // or if the value at the dataOffset is a pointer to the actual data:
29216 dataOffset = tagSize > 4 ?
29217 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
29218 if (dataOffset + tagSize > dataView.byteLength) {
29219 Roo.log('Invalid Exif data: Invalid data offset.');
29222 if (length === 1) {
29223 return tagType.getValue(dataView, dataOffset, littleEndian);
29226 for (i = 0; i < length; i += 1) {
29227 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
29230 if (tagType.ascii) {
29232 // Concatenate the chars:
29233 for (i = 0; i < values.length; i += 1) {
29235 // Ignore the terminating NULL byte(s):
29236 if (c === '\u0000') {
29248 Roo.apply(Roo.bootstrap.UploadCropbox, {
29250 'Orientation': 0x0112
29254 1: 0, //'top-left',
29256 3: 180, //'bottom-right',
29257 // 4: 'bottom-left',
29259 6: 90, //'right-top',
29260 // 7: 'right-bottom',
29261 8: 270 //'left-bottom'
29265 // byte, 8-bit unsigned int:
29267 getValue: function (dataView, dataOffset) {
29268 return dataView.getUint8(dataOffset);
29272 // ascii, 8-bit byte:
29274 getValue: function (dataView, dataOffset) {
29275 return String.fromCharCode(dataView.getUint8(dataOffset));
29280 // short, 16 bit int:
29282 getValue: function (dataView, dataOffset, littleEndian) {
29283 return dataView.getUint16(dataOffset, littleEndian);
29287 // long, 32 bit int:
29289 getValue: function (dataView, dataOffset, littleEndian) {
29290 return dataView.getUint32(dataOffset, littleEndian);
29294 // rational = two long values, first is numerator, second is denominator:
29296 getValue: function (dataView, dataOffset, littleEndian) {
29297 return dataView.getUint32(dataOffset, littleEndian) /
29298 dataView.getUint32(dataOffset + 4, littleEndian);
29302 // slong, 32 bit signed int:
29304 getValue: function (dataView, dataOffset, littleEndian) {
29305 return dataView.getInt32(dataOffset, littleEndian);
29309 // srational, two slongs, first is numerator, second is denominator:
29311 getValue: function (dataView, dataOffset, littleEndian) {
29312 return dataView.getInt32(dataOffset, littleEndian) /
29313 dataView.getInt32(dataOffset + 4, littleEndian);
29323 cls : 'btn-group roo-upload-cropbox-rotate-left',
29324 action : 'rotate-left',
29328 cls : 'btn btn-default',
29329 html : '<i class="fa fa-undo"></i>'
29335 cls : 'btn-group roo-upload-cropbox-picture',
29336 action : 'picture',
29340 cls : 'btn btn-default',
29341 html : '<i class="fa fa-picture-o"></i>'
29347 cls : 'btn-group roo-upload-cropbox-rotate-right',
29348 action : 'rotate-right',
29352 cls : 'btn btn-default',
29353 html : '<i class="fa fa-repeat"></i>'
29361 cls : 'btn-group roo-upload-cropbox-rotate-left',
29362 action : 'rotate-left',
29366 cls : 'btn btn-default',
29367 html : '<i class="fa fa-undo"></i>'
29373 cls : 'btn-group roo-upload-cropbox-download',
29374 action : 'download',
29378 cls : 'btn btn-default',
29379 html : '<i class="fa fa-download"></i>'
29385 cls : 'btn-group roo-upload-cropbox-crop',
29390 cls : 'btn btn-default',
29391 html : '<i class="fa fa-crop"></i>'
29397 cls : 'btn-group roo-upload-cropbox-trash',
29402 cls : 'btn btn-default',
29403 html : '<i class="fa fa-trash"></i>'
29409 cls : 'btn-group roo-upload-cropbox-rotate-right',
29410 action : 'rotate-right',
29414 cls : 'btn btn-default',
29415 html : '<i class="fa fa-repeat"></i>'
29423 cls : 'btn-group roo-upload-cropbox-rotate-left',
29424 action : 'rotate-left',
29428 cls : 'btn btn-default',
29429 html : '<i class="fa fa-undo"></i>'
29435 cls : 'btn-group roo-upload-cropbox-rotate-right',
29436 action : 'rotate-right',
29440 cls : 'btn btn-default',
29441 html : '<i class="fa fa-repeat"></i>'
29454 * @class Roo.bootstrap.DocumentManager
29455 * @extends Roo.bootstrap.Component
29456 * Bootstrap DocumentManager class
29457 * @cfg {String} paramName default 'imageUpload'
29458 * @cfg {String} toolTipName default 'filename'
29459 * @cfg {String} method default POST
29460 * @cfg {String} url action url
29461 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29462 * @cfg {Boolean} multiple multiple upload default true
29463 * @cfg {Number} thumbSize default 300
29464 * @cfg {String} fieldLabel
29465 * @cfg {Number} labelWidth default 4
29466 * @cfg {String} labelAlign (left|top) default left
29467 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29468 * @cfg {Number} labellg set the width of label (1-12)
29469 * @cfg {Number} labelmd set the width of label (1-12)
29470 * @cfg {Number} labelsm set the width of label (1-12)
29471 * @cfg {Number} labelxs set the width of label (1-12)
29474 * Create a new DocumentManager
29475 * @param {Object} config The config object
29478 Roo.bootstrap.DocumentManager = function(config){
29479 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29482 this.delegates = [];
29487 * Fire when initial the DocumentManager
29488 * @param {Roo.bootstrap.DocumentManager} this
29493 * inspect selected file
29494 * @param {Roo.bootstrap.DocumentManager} this
29495 * @param {File} file
29500 * Fire when xhr load exception
29501 * @param {Roo.bootstrap.DocumentManager} this
29502 * @param {XMLHttpRequest} xhr
29504 "exception" : true,
29506 * @event afterupload
29507 * Fire when xhr load exception
29508 * @param {Roo.bootstrap.DocumentManager} this
29509 * @param {XMLHttpRequest} xhr
29511 "afterupload" : true,
29514 * prepare the form data
29515 * @param {Roo.bootstrap.DocumentManager} this
29516 * @param {Object} formData
29521 * Fire when remove the file
29522 * @param {Roo.bootstrap.DocumentManager} this
29523 * @param {Object} file
29528 * Fire after refresh the file
29529 * @param {Roo.bootstrap.DocumentManager} this
29534 * Fire after click the image
29535 * @param {Roo.bootstrap.DocumentManager} this
29536 * @param {Object} file
29541 * Fire when upload a image and editable set to true
29542 * @param {Roo.bootstrap.DocumentManager} this
29543 * @param {Object} file
29547 * @event beforeselectfile
29548 * Fire before select file
29549 * @param {Roo.bootstrap.DocumentManager} this
29551 "beforeselectfile" : true,
29554 * Fire before process file
29555 * @param {Roo.bootstrap.DocumentManager} this
29556 * @param {Object} file
29560 * @event previewrendered
29561 * Fire when preview rendered
29562 * @param {Roo.bootstrap.DocumentManager} this
29563 * @param {Object} file
29565 "previewrendered" : true,
29568 "previewResize" : true
29573 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
29582 paramName : 'imageUpload',
29583 toolTipName : 'filename',
29586 labelAlign : 'left',
29596 getAutoCreate : function()
29598 var managerWidget = {
29600 cls : 'roo-document-manager',
29604 cls : 'roo-document-manager-selector',
29609 cls : 'roo-document-manager-uploader',
29613 cls : 'roo-document-manager-upload-btn',
29614 html : '<i class="fa fa-plus"></i>'
29625 cls : 'column col-md-12',
29630 if(this.fieldLabel.length){
29635 cls : 'column col-md-12',
29636 html : this.fieldLabel
29640 cls : 'column col-md-12',
29645 if(this.labelAlign == 'left'){
29650 html : this.fieldLabel
29659 if(this.labelWidth > 12){
29660 content[0].style = "width: " + this.labelWidth + 'px';
29663 if(this.labelWidth < 13 && this.labelmd == 0){
29664 this.labelmd = this.labelWidth;
29667 if(this.labellg > 0){
29668 content[0].cls += ' col-lg-' + this.labellg;
29669 content[1].cls += ' col-lg-' + (12 - this.labellg);
29672 if(this.labelmd > 0){
29673 content[0].cls += ' col-md-' + this.labelmd;
29674 content[1].cls += ' col-md-' + (12 - this.labelmd);
29677 if(this.labelsm > 0){
29678 content[0].cls += ' col-sm-' + this.labelsm;
29679 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29682 if(this.labelxs > 0){
29683 content[0].cls += ' col-xs-' + this.labelxs;
29684 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29692 cls : 'row clearfix',
29700 initEvents : function()
29702 this.managerEl = this.el.select('.roo-document-manager', true).first();
29703 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29705 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29706 this.selectorEl.hide();
29709 this.selectorEl.attr('multiple', 'multiple');
29712 this.selectorEl.on('change', this.onFileSelected, this);
29714 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29715 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29717 this.uploader.on('click', this.onUploaderClick, this);
29719 this.renderProgressDialog();
29723 window.addEventListener("resize", function() { _this.refresh(); } );
29725 this.fireEvent('initial', this);
29728 renderProgressDialog : function()
29732 this.progressDialog = new Roo.bootstrap.Modal({
29733 cls : 'roo-document-manager-progress-dialog',
29734 allow_close : false,
29745 btnclick : function() {
29746 _this.uploadCancel();
29752 this.progressDialog.render(Roo.get(document.body));
29754 this.progress = new Roo.bootstrap.Progress({
29755 cls : 'roo-document-manager-progress',
29760 this.progress.render(this.progressDialog.getChildContainer());
29762 this.progressBar = new Roo.bootstrap.ProgressBar({
29763 cls : 'roo-document-manager-progress-bar',
29766 aria_valuemax : 12,
29770 this.progressBar.render(this.progress.getChildContainer());
29773 onUploaderClick : function(e)
29775 e.preventDefault();
29777 if(this.fireEvent('beforeselectfile', this) != false){
29778 this.selectorEl.dom.click();
29783 onFileSelected : function(e)
29785 e.preventDefault();
29787 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29791 Roo.each(this.selectorEl.dom.files, function(file){
29792 if(this.fireEvent('inspect', this, file) != false){
29793 this.files.push(file);
29803 this.selectorEl.dom.value = '';
29805 if(!this.files || !this.files.length){
29809 if(this.boxes > 0 && this.files.length > this.boxes){
29810 this.files = this.files.slice(0, this.boxes);
29813 this.uploader.show();
29815 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29816 this.uploader.hide();
29825 Roo.each(this.files, function(file){
29827 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29828 var f = this.renderPreview(file);
29833 if(file.type.indexOf('image') != -1){
29834 this.delegates.push(
29836 _this.process(file);
29837 }).createDelegate(this)
29845 _this.process(file);
29846 }).createDelegate(this)
29851 this.files = files;
29853 this.delegates = this.delegates.concat(docs);
29855 if(!this.delegates.length){
29860 this.progressBar.aria_valuemax = this.delegates.length;
29867 arrange : function()
29869 if(!this.delegates.length){
29870 this.progressDialog.hide();
29875 var delegate = this.delegates.shift();
29877 this.progressDialog.show();
29879 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29881 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29886 refresh : function()
29888 this.uploader.show();
29890 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29891 this.uploader.hide();
29894 Roo.isTouch ? this.closable(false) : this.closable(true);
29896 this.fireEvent('refresh', this);
29899 onRemove : function(e, el, o)
29901 e.preventDefault();
29903 this.fireEvent('remove', this, o);
29907 remove : function(o)
29911 Roo.each(this.files, function(file){
29912 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29921 this.files = files;
29928 Roo.each(this.files, function(file){
29933 file.target.remove();
29942 onClick : function(e, el, o)
29944 e.preventDefault();
29946 this.fireEvent('click', this, o);
29950 closable : function(closable)
29952 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29954 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29966 xhrOnLoad : function(xhr)
29968 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29972 if (xhr.readyState !== 4) {
29974 this.fireEvent('exception', this, xhr);
29978 var response = Roo.decode(xhr.responseText);
29980 if(!response.success){
29982 this.fireEvent('exception', this, xhr);
29986 var file = this.renderPreview(response.data);
29988 this.files.push(file);
29992 this.fireEvent('afterupload', this, xhr);
29996 xhrOnError : function(xhr)
29998 Roo.log('xhr on error');
30000 var response = Roo.decode(xhr.responseText);
30007 process : function(file)
30009 if(this.fireEvent('process', this, file) !== false){
30010 if(this.editable && file.type.indexOf('image') != -1){
30011 this.fireEvent('edit', this, file);
30015 this.uploadStart(file, false);
30022 uploadStart : function(file, crop)
30024 this.xhr = new XMLHttpRequest();
30026 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30031 file.xhr = this.xhr;
30033 this.managerEl.createChild({
30035 cls : 'roo-document-manager-loading',
30039 tooltip : file.name,
30040 cls : 'roo-document-manager-thumb',
30041 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30047 this.xhr.open(this.method, this.url, true);
30050 "Accept": "application/json",
30051 "Cache-Control": "no-cache",
30052 "X-Requested-With": "XMLHttpRequest"
30055 for (var headerName in headers) {
30056 var headerValue = headers[headerName];
30058 this.xhr.setRequestHeader(headerName, headerValue);
30064 this.xhr.onload = function()
30066 _this.xhrOnLoad(_this.xhr);
30069 this.xhr.onerror = function()
30071 _this.xhrOnError(_this.xhr);
30074 var formData = new FormData();
30076 formData.append('returnHTML', 'NO');
30079 formData.append('crop', crop);
30082 formData.append(this.paramName, file, file.name);
30089 if(this.fireEvent('prepare', this, formData, options) != false){
30091 if(options.manually){
30095 this.xhr.send(formData);
30099 this.uploadCancel();
30102 uploadCancel : function()
30108 this.delegates = [];
30110 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30117 renderPreview : function(file)
30119 if(typeof(file.target) != 'undefined' && file.target){
30123 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
30125 var previewEl = this.managerEl.createChild({
30127 cls : 'roo-document-manager-preview',
30131 tooltip : file[this.toolTipName],
30132 cls : 'roo-document-manager-thumb',
30133 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
30138 html : '<i class="fa fa-times-circle"></i>'
30143 var close = previewEl.select('button.close', true).first();
30145 close.on('click', this.onRemove, this, file);
30147 file.target = previewEl;
30149 var image = previewEl.select('img', true).first();
30153 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
30155 image.on('click', this.onClick, this, file);
30157 this.fireEvent('previewrendered', this, file);
30163 onPreviewLoad : function(file, image)
30165 if(typeof(file.target) == 'undefined' || !file.target){
30169 var width = image.dom.naturalWidth || image.dom.width;
30170 var height = image.dom.naturalHeight || image.dom.height;
30172 if(!this.previewResize) {
30176 if(width > height){
30177 file.target.addClass('wide');
30181 file.target.addClass('tall');
30186 uploadFromSource : function(file, crop)
30188 this.xhr = new XMLHttpRequest();
30190 this.managerEl.createChild({
30192 cls : 'roo-document-manager-loading',
30196 tooltip : file.name,
30197 cls : 'roo-document-manager-thumb',
30198 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30204 this.xhr.open(this.method, this.url, true);
30207 "Accept": "application/json",
30208 "Cache-Control": "no-cache",
30209 "X-Requested-With": "XMLHttpRequest"
30212 for (var headerName in headers) {
30213 var headerValue = headers[headerName];
30215 this.xhr.setRequestHeader(headerName, headerValue);
30221 this.xhr.onload = function()
30223 _this.xhrOnLoad(_this.xhr);
30226 this.xhr.onerror = function()
30228 _this.xhrOnError(_this.xhr);
30231 var formData = new FormData();
30233 formData.append('returnHTML', 'NO');
30235 formData.append('crop', crop);
30237 if(typeof(file.filename) != 'undefined'){
30238 formData.append('filename', file.filename);
30241 if(typeof(file.mimetype) != 'undefined'){
30242 formData.append('mimetype', file.mimetype);
30247 if(this.fireEvent('prepare', this, formData) != false){
30248 this.xhr.send(formData);
30258 * @class Roo.bootstrap.DocumentViewer
30259 * @extends Roo.bootstrap.Component
30260 * Bootstrap DocumentViewer class
30261 * @cfg {Boolean} showDownload (true|false) show download button (default true)
30262 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30265 * Create a new DocumentViewer
30266 * @param {Object} config The config object
30269 Roo.bootstrap.DocumentViewer = function(config){
30270 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30275 * Fire after initEvent
30276 * @param {Roo.bootstrap.DocumentViewer} this
30282 * @param {Roo.bootstrap.DocumentViewer} this
30287 * Fire after download button
30288 * @param {Roo.bootstrap.DocumentViewer} this
30293 * Fire after trash button
30294 * @param {Roo.bootstrap.DocumentViewer} this
30301 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
30303 showDownload : true,
30307 getAutoCreate : function()
30311 cls : 'roo-document-viewer',
30315 cls : 'roo-document-viewer-body',
30319 cls : 'roo-document-viewer-thumb',
30323 cls : 'roo-document-viewer-image'
30331 cls : 'roo-document-viewer-footer',
30334 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30338 cls : 'btn-group roo-document-viewer-download',
30342 cls : 'btn btn-default',
30343 html : '<i class="fa fa-download"></i>'
30349 cls : 'btn-group roo-document-viewer-trash',
30353 cls : 'btn btn-default',
30354 html : '<i class="fa fa-trash"></i>'
30367 initEvents : function()
30369 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30370 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30372 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30373 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30375 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30376 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30378 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30379 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30381 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30382 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30384 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30385 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30387 this.bodyEl.on('click', this.onClick, this);
30388 this.downloadBtn.on('click', this.onDownload, this);
30389 this.trashBtn.on('click', this.onTrash, this);
30391 this.downloadBtn.hide();
30392 this.trashBtn.hide();
30394 if(this.showDownload){
30395 this.downloadBtn.show();
30398 if(this.showTrash){
30399 this.trashBtn.show();
30402 if(!this.showDownload && !this.showTrash) {
30403 this.footerEl.hide();
30408 initial : function()
30410 this.fireEvent('initial', this);
30414 onClick : function(e)
30416 e.preventDefault();
30418 this.fireEvent('click', this);
30421 onDownload : function(e)
30423 e.preventDefault();
30425 this.fireEvent('download', this);
30428 onTrash : function(e)
30430 e.preventDefault();
30432 this.fireEvent('trash', this);
30444 * @class Roo.bootstrap.NavProgressBar
30445 * @extends Roo.bootstrap.Component
30446 * Bootstrap NavProgressBar class
30449 * Create a new nav progress bar
30450 * @param {Object} config The config object
30453 Roo.bootstrap.NavProgressBar = function(config){
30454 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30456 this.bullets = this.bullets || [];
30458 // Roo.bootstrap.NavProgressBar.register(this);
30462 * Fires when the active item changes
30463 * @param {Roo.bootstrap.NavProgressBar} this
30464 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30465 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
30472 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
30477 getAutoCreate : function()
30479 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30483 cls : 'roo-navigation-bar-group',
30487 cls : 'roo-navigation-top-bar'
30491 cls : 'roo-navigation-bullets-bar',
30495 cls : 'roo-navigation-bar'
30502 cls : 'roo-navigation-bottom-bar'
30512 initEvents: function()
30517 onRender : function(ct, position)
30519 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30521 if(this.bullets.length){
30522 Roo.each(this.bullets, function(b){
30531 addItem : function(cfg)
30533 var item = new Roo.bootstrap.NavProgressItem(cfg);
30535 item.parentId = this.id;
30536 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30539 var top = new Roo.bootstrap.Element({
30541 cls : 'roo-navigation-bar-text'
30544 var bottom = new Roo.bootstrap.Element({
30546 cls : 'roo-navigation-bar-text'
30549 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30550 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30552 var topText = new Roo.bootstrap.Element({
30554 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30557 var bottomText = new Roo.bootstrap.Element({
30559 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30562 topText.onRender(top.el, null);
30563 bottomText.onRender(bottom.el, null);
30566 item.bottomEl = bottom;
30569 this.barItems.push(item);
30574 getActive : function()
30576 var active = false;
30578 Roo.each(this.barItems, function(v){
30580 if (!v.isActive()) {
30592 setActiveItem : function(item)
30596 Roo.each(this.barItems, function(v){
30597 if (v.rid == item.rid) {
30601 if (v.isActive()) {
30602 v.setActive(false);
30607 item.setActive(true);
30609 this.fireEvent('changed', this, item, prev);
30612 getBarItem: function(rid)
30616 Roo.each(this.barItems, function(e) {
30617 if (e.rid != rid) {
30628 indexOfItem : function(item)
30632 Roo.each(this.barItems, function(v, i){
30634 if (v.rid != item.rid) {
30645 setActiveNext : function()
30647 var i = this.indexOfItem(this.getActive());
30649 if (i > this.barItems.length) {
30653 this.setActiveItem(this.barItems[i+1]);
30656 setActivePrev : function()
30658 var i = this.indexOfItem(this.getActive());
30664 this.setActiveItem(this.barItems[i-1]);
30667 format : function()
30669 if(!this.barItems.length){
30673 var width = 100 / this.barItems.length;
30675 Roo.each(this.barItems, function(i){
30676 i.el.setStyle('width', width + '%');
30677 i.topEl.el.setStyle('width', width + '%');
30678 i.bottomEl.el.setStyle('width', width + '%');
30687 * Nav Progress Item
30692 * @class Roo.bootstrap.NavProgressItem
30693 * @extends Roo.bootstrap.Component
30694 * Bootstrap NavProgressItem class
30695 * @cfg {String} rid the reference id
30696 * @cfg {Boolean} active (true|false) Is item active default false
30697 * @cfg {Boolean} disabled (true|false) Is item active default false
30698 * @cfg {String} html
30699 * @cfg {String} position (top|bottom) text position default bottom
30700 * @cfg {String} icon show icon instead of number
30703 * Create a new NavProgressItem
30704 * @param {Object} config The config object
30706 Roo.bootstrap.NavProgressItem = function(config){
30707 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30712 * The raw click event for the entire grid.
30713 * @param {Roo.bootstrap.NavProgressItem} this
30714 * @param {Roo.EventObject} e
30721 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30727 position : 'bottom',
30730 getAutoCreate : function()
30732 var iconCls = 'roo-navigation-bar-item-icon';
30734 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30738 cls: 'roo-navigation-bar-item',
30748 cfg.cls += ' active';
30751 cfg.cls += ' disabled';
30757 disable : function()
30759 this.setDisabled(true);
30762 enable : function()
30764 this.setDisabled(false);
30767 initEvents: function()
30769 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30771 this.iconEl.on('click', this.onClick, this);
30774 onClick : function(e)
30776 e.preventDefault();
30782 if(this.fireEvent('click', this, e) === false){
30786 this.parent().setActiveItem(this);
30789 isActive: function ()
30791 return this.active;
30794 setActive : function(state)
30796 if(this.active == state){
30800 this.active = state;
30803 this.el.addClass('active');
30807 this.el.removeClass('active');
30812 setDisabled : function(state)
30814 if(this.disabled == state){
30818 this.disabled = state;
30821 this.el.addClass('disabled');
30825 this.el.removeClass('disabled');
30828 tooltipEl : function()
30830 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30843 * @class Roo.bootstrap.FieldLabel
30844 * @extends Roo.bootstrap.Component
30845 * Bootstrap FieldLabel class
30846 * @cfg {String} html contents of the element
30847 * @cfg {String} tag tag of the element default label
30848 * @cfg {String} cls class of the element
30849 * @cfg {String} target label target
30850 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30851 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30852 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30853 * @cfg {String} iconTooltip default "This field is required"
30854 * @cfg {String} indicatorpos (left|right) default left
30857 * Create a new FieldLabel
30858 * @param {Object} config The config object
30861 Roo.bootstrap.FieldLabel = function(config){
30862 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30867 * Fires after the field has been marked as invalid.
30868 * @param {Roo.form.FieldLabel} this
30869 * @param {String} msg The validation message
30874 * Fires after the field has been validated with no errors.
30875 * @param {Roo.form.FieldLabel} this
30881 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30888 invalidClass : 'has-warning',
30889 validClass : 'has-success',
30890 iconTooltip : 'This field is required',
30891 indicatorpos : 'left',
30893 getAutoCreate : function(){
30896 if (!this.allowBlank) {
30902 cls : 'roo-bootstrap-field-label ' + this.cls,
30907 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30908 tooltip : this.iconTooltip
30917 if(this.indicatorpos == 'right'){
30920 cls : 'roo-bootstrap-field-label ' + this.cls,
30929 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30930 tooltip : this.iconTooltip
30939 initEvents: function()
30941 Roo.bootstrap.Element.superclass.initEvents.call(this);
30943 this.indicator = this.indicatorEl();
30945 if(this.indicator){
30946 this.indicator.removeClass('visible');
30947 this.indicator.addClass('invisible');
30950 Roo.bootstrap.FieldLabel.register(this);
30953 indicatorEl : function()
30955 var indicator = this.el.select('i.roo-required-indicator',true).first();
30966 * Mark this field as valid
30968 markValid : function()
30970 if(this.indicator){
30971 this.indicator.removeClass('visible');
30972 this.indicator.addClass('invisible');
30974 if (Roo.bootstrap.version == 3) {
30975 this.el.removeClass(this.invalidClass);
30976 this.el.addClass(this.validClass);
30978 this.el.removeClass('is-invalid');
30979 this.el.addClass('is-valid');
30983 this.fireEvent('valid', this);
30987 * Mark this field as invalid
30988 * @param {String} msg The validation message
30990 markInvalid : function(msg)
30992 if(this.indicator){
30993 this.indicator.removeClass('invisible');
30994 this.indicator.addClass('visible');
30996 if (Roo.bootstrap.version == 3) {
30997 this.el.removeClass(this.validClass);
30998 this.el.addClass(this.invalidClass);
31000 this.el.removeClass('is-valid');
31001 this.el.addClass('is-invalid');
31005 this.fireEvent('invalid', this, msg);
31011 Roo.apply(Roo.bootstrap.FieldLabel, {
31016 * register a FieldLabel Group
31017 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
31019 register : function(label)
31021 if(this.groups.hasOwnProperty(label.target)){
31025 this.groups[label.target] = label;
31029 * fetch a FieldLabel Group based on the target
31030 * @param {string} target
31031 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
31033 get: function(target) {
31034 if (typeof(this.groups[target]) == 'undefined') {
31038 return this.groups[target] ;
31047 * page DateSplitField.
31053 * @class Roo.bootstrap.DateSplitField
31054 * @extends Roo.bootstrap.Component
31055 * Bootstrap DateSplitField class
31056 * @cfg {string} fieldLabel - the label associated
31057 * @cfg {Number} labelWidth set the width of label (0-12)
31058 * @cfg {String} labelAlign (top|left)
31059 * @cfg {Boolean} dayAllowBlank (true|false) default false
31060 * @cfg {Boolean} monthAllowBlank (true|false) default false
31061 * @cfg {Boolean} yearAllowBlank (true|false) default false
31062 * @cfg {string} dayPlaceholder
31063 * @cfg {string} monthPlaceholder
31064 * @cfg {string} yearPlaceholder
31065 * @cfg {string} dayFormat default 'd'
31066 * @cfg {string} monthFormat default 'm'
31067 * @cfg {string} yearFormat default 'Y'
31068 * @cfg {Number} labellg set the width of label (1-12)
31069 * @cfg {Number} labelmd set the width of label (1-12)
31070 * @cfg {Number} labelsm set the width of label (1-12)
31071 * @cfg {Number} labelxs set the width of label (1-12)
31075 * Create a new DateSplitField
31076 * @param {Object} config The config object
31079 Roo.bootstrap.DateSplitField = function(config){
31080 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
31086 * getting the data of years
31087 * @param {Roo.bootstrap.DateSplitField} this
31088 * @param {Object} years
31093 * getting the data of days
31094 * @param {Roo.bootstrap.DateSplitField} this
31095 * @param {Object} days
31100 * Fires after the field has been marked as invalid.
31101 * @param {Roo.form.Field} this
31102 * @param {String} msg The validation message
31107 * Fires after the field has been validated with no errors.
31108 * @param {Roo.form.Field} this
31114 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
31117 labelAlign : 'top',
31119 dayAllowBlank : false,
31120 monthAllowBlank : false,
31121 yearAllowBlank : false,
31122 dayPlaceholder : '',
31123 monthPlaceholder : '',
31124 yearPlaceholder : '',
31128 isFormField : true,
31134 getAutoCreate : function()
31138 cls : 'row roo-date-split-field-group',
31143 cls : 'form-hidden-field roo-date-split-field-group-value',
31149 var labelCls = 'col-md-12';
31150 var contentCls = 'col-md-4';
31152 if(this.fieldLabel){
31156 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
31160 html : this.fieldLabel
31165 if(this.labelAlign == 'left'){
31167 if(this.labelWidth > 12){
31168 label.style = "width: " + this.labelWidth + 'px';
31171 if(this.labelWidth < 13 && this.labelmd == 0){
31172 this.labelmd = this.labelWidth;
31175 if(this.labellg > 0){
31176 labelCls = ' col-lg-' + this.labellg;
31177 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
31180 if(this.labelmd > 0){
31181 labelCls = ' col-md-' + this.labelmd;
31182 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
31185 if(this.labelsm > 0){
31186 labelCls = ' col-sm-' + this.labelsm;
31187 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
31190 if(this.labelxs > 0){
31191 labelCls = ' col-xs-' + this.labelxs;
31192 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
31196 label.cls += ' ' + labelCls;
31198 cfg.cn.push(label);
31201 Roo.each(['day', 'month', 'year'], function(t){
31204 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
31211 inputEl: function ()
31213 return this.el.select('.roo-date-split-field-group-value', true).first();
31216 onRender : function(ct, position)
31220 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31222 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
31224 this.dayField = new Roo.bootstrap.ComboBox({
31225 allowBlank : this.dayAllowBlank,
31226 alwaysQuery : true,
31227 displayField : 'value',
31230 forceSelection : true,
31232 placeholder : this.dayPlaceholder,
31233 selectOnFocus : true,
31234 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31235 triggerAction : 'all',
31237 valueField : 'value',
31238 store : new Roo.data.SimpleStore({
31239 data : (function() {
31241 _this.fireEvent('days', _this, days);
31244 fields : [ 'value' ]
31247 select : function (_self, record, index)
31249 _this.setValue(_this.getValue());
31254 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31256 this.monthField = new Roo.bootstrap.MonthField({
31257 after : '<i class=\"fa fa-calendar\"></i>',
31258 allowBlank : this.monthAllowBlank,
31259 placeholder : this.monthPlaceholder,
31262 render : function (_self)
31264 this.el.select('span.input-group-addon', true).first().on('click', function(e){
31265 e.preventDefault();
31269 select : function (_self, oldvalue, newvalue)
31271 _this.setValue(_this.getValue());
31276 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31278 this.yearField = new Roo.bootstrap.ComboBox({
31279 allowBlank : this.yearAllowBlank,
31280 alwaysQuery : true,
31281 displayField : 'value',
31284 forceSelection : true,
31286 placeholder : this.yearPlaceholder,
31287 selectOnFocus : true,
31288 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31289 triggerAction : 'all',
31291 valueField : 'value',
31292 store : new Roo.data.SimpleStore({
31293 data : (function() {
31295 _this.fireEvent('years', _this, years);
31298 fields : [ 'value' ]
31301 select : function (_self, record, index)
31303 _this.setValue(_this.getValue());
31308 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31311 setValue : function(v, format)
31313 this.inputEl.dom.value = v;
31315 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31317 var d = Date.parseDate(v, f);
31324 this.setDay(d.format(this.dayFormat));
31325 this.setMonth(d.format(this.monthFormat));
31326 this.setYear(d.format(this.yearFormat));
31333 setDay : function(v)
31335 this.dayField.setValue(v);
31336 this.inputEl.dom.value = this.getValue();
31341 setMonth : function(v)
31343 this.monthField.setValue(v, true);
31344 this.inputEl.dom.value = this.getValue();
31349 setYear : function(v)
31351 this.yearField.setValue(v);
31352 this.inputEl.dom.value = this.getValue();
31357 getDay : function()
31359 return this.dayField.getValue();
31362 getMonth : function()
31364 return this.monthField.getValue();
31367 getYear : function()
31369 return this.yearField.getValue();
31372 getValue : function()
31374 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31376 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31386 this.inputEl.dom.value = '';
31391 validate : function()
31393 var d = this.dayField.validate();
31394 var m = this.monthField.validate();
31395 var y = this.yearField.validate();
31400 (!this.dayAllowBlank && !d) ||
31401 (!this.monthAllowBlank && !m) ||
31402 (!this.yearAllowBlank && !y)
31407 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31416 this.markInvalid();
31421 markValid : function()
31424 var label = this.el.select('label', true).first();
31425 var icon = this.el.select('i.fa-star', true).first();
31431 this.fireEvent('valid', this);
31435 * Mark this field as invalid
31436 * @param {String} msg The validation message
31438 markInvalid : function(msg)
31441 var label = this.el.select('label', true).first();
31442 var icon = this.el.select('i.fa-star', true).first();
31444 if(label && !icon){
31445 this.el.select('.roo-date-split-field-label', true).createChild({
31447 cls : 'text-danger fa fa-lg fa-star',
31448 tooltip : 'This field is required',
31449 style : 'margin-right:5px;'
31453 this.fireEvent('invalid', this, msg);
31456 clearInvalid : function()
31458 var label = this.el.select('label', true).first();
31459 var icon = this.el.select('i.fa-star', true).first();
31465 this.fireEvent('valid', this);
31468 getName: function()
31478 * http://masonry.desandro.com
31480 * The idea is to render all the bricks based on vertical width...
31482 * The original code extends 'outlayer' - we might need to use that....
31488 * @class Roo.bootstrap.LayoutMasonry
31489 * @extends Roo.bootstrap.Component
31490 * Bootstrap Layout Masonry class
31493 * Create a new Element
31494 * @param {Object} config The config object
31497 Roo.bootstrap.LayoutMasonry = function(config){
31499 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31503 Roo.bootstrap.LayoutMasonry.register(this);
31509 * Fire after layout the items
31510 * @param {Roo.bootstrap.LayoutMasonry} this
31511 * @param {Roo.EventObject} e
31518 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
31521 * @cfg {Boolean} isLayoutInstant = no animation?
31523 isLayoutInstant : false, // needed?
31526 * @cfg {Number} boxWidth width of the columns
31531 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
31536 * @cfg {Number} padWidth padding below box..
31541 * @cfg {Number} gutter gutter width..
31546 * @cfg {Number} maxCols maximum number of columns
31552 * @cfg {Boolean} isAutoInitial defalut true
31554 isAutoInitial : true,
31559 * @cfg {Boolean} isHorizontal defalut false
31561 isHorizontal : false,
31563 currentSize : null,
31569 bricks: null, //CompositeElement
31573 _isLayoutInited : false,
31575 // isAlternative : false, // only use for vertical layout...
31578 * @cfg {Number} alternativePadWidth padding below box..
31580 alternativePadWidth : 50,
31582 selectedBrick : [],
31584 getAutoCreate : function(){
31586 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31590 cls: 'blog-masonary-wrapper ' + this.cls,
31592 cls : 'mas-boxes masonary'
31599 getChildContainer: function( )
31601 if (this.boxesEl) {
31602 return this.boxesEl;
31605 this.boxesEl = this.el.select('.mas-boxes').first();
31607 return this.boxesEl;
31611 initEvents : function()
31615 if(this.isAutoInitial){
31616 Roo.log('hook children rendered');
31617 this.on('childrenrendered', function() {
31618 Roo.log('children rendered');
31624 initial : function()
31626 this.selectedBrick = [];
31628 this.currentSize = this.el.getBox(true);
31630 Roo.EventManager.onWindowResize(this.resize, this);
31632 if(!this.isAutoInitial){
31640 //this.layout.defer(500,this);
31644 resize : function()
31646 var cs = this.el.getBox(true);
31649 this.currentSize.width == cs.width &&
31650 this.currentSize.x == cs.x &&
31651 this.currentSize.height == cs.height &&
31652 this.currentSize.y == cs.y
31654 Roo.log("no change in with or X or Y");
31658 this.currentSize = cs;
31664 layout : function()
31666 this._resetLayout();
31668 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31670 this.layoutItems( isInstant );
31672 this._isLayoutInited = true;
31674 this.fireEvent('layout', this);
31678 _resetLayout : function()
31680 if(this.isHorizontal){
31681 this.horizontalMeasureColumns();
31685 this.verticalMeasureColumns();
31689 verticalMeasureColumns : function()
31691 this.getContainerWidth();
31693 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31694 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31698 var boxWidth = this.boxWidth + this.padWidth;
31700 if(this.containerWidth < this.boxWidth){
31701 boxWidth = this.containerWidth
31704 var containerWidth = this.containerWidth;
31706 var cols = Math.floor(containerWidth / boxWidth);
31708 this.cols = Math.max( cols, 1 );
31710 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31712 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31714 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31716 this.colWidth = boxWidth + avail - this.padWidth;
31718 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31719 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31722 horizontalMeasureColumns : function()
31724 this.getContainerWidth();
31726 var boxWidth = this.boxWidth;
31728 if(this.containerWidth < boxWidth){
31729 boxWidth = this.containerWidth;
31732 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31734 this.el.setHeight(boxWidth);
31738 getContainerWidth : function()
31740 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31743 layoutItems : function( isInstant )
31745 Roo.log(this.bricks);
31747 var items = Roo.apply([], this.bricks);
31749 if(this.isHorizontal){
31750 this._horizontalLayoutItems( items , isInstant );
31754 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31755 // this._verticalAlternativeLayoutItems( items , isInstant );
31759 this._verticalLayoutItems( items , isInstant );
31763 _verticalLayoutItems : function ( items , isInstant)
31765 if ( !items || !items.length ) {
31770 ['xs', 'xs', 'xs', 'tall'],
31771 ['xs', 'xs', 'tall'],
31772 ['xs', 'xs', 'sm'],
31773 ['xs', 'xs', 'xs'],
31779 ['sm', 'xs', 'xs'],
31783 ['tall', 'xs', 'xs', 'xs'],
31784 ['tall', 'xs', 'xs'],
31796 Roo.each(items, function(item, k){
31798 switch (item.size) {
31799 // these layouts take up a full box,
31810 boxes.push([item]);
31833 var filterPattern = function(box, length)
31841 var pattern = box.slice(0, length);
31845 Roo.each(pattern, function(i){
31846 format.push(i.size);
31849 Roo.each(standard, function(s){
31851 if(String(s) != String(format)){
31860 if(!match && length == 1){
31865 filterPattern(box, length - 1);
31869 queue.push(pattern);
31871 box = box.slice(length, box.length);
31873 filterPattern(box, 4);
31879 Roo.each(boxes, function(box, k){
31885 if(box.length == 1){
31890 filterPattern(box, 4);
31894 this._processVerticalLayoutQueue( queue, isInstant );
31898 // _verticalAlternativeLayoutItems : function( items , isInstant )
31900 // if ( !items || !items.length ) {
31904 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31908 _horizontalLayoutItems : function ( items , isInstant)
31910 if ( !items || !items.length || items.length < 3) {
31916 var eItems = items.slice(0, 3);
31918 items = items.slice(3, items.length);
31921 ['xs', 'xs', 'xs', 'wide'],
31922 ['xs', 'xs', 'wide'],
31923 ['xs', 'xs', 'sm'],
31924 ['xs', 'xs', 'xs'],
31930 ['sm', 'xs', 'xs'],
31934 ['wide', 'xs', 'xs', 'xs'],
31935 ['wide', 'xs', 'xs'],
31948 Roo.each(items, function(item, k){
31950 switch (item.size) {
31961 boxes.push([item]);
31985 var filterPattern = function(box, length)
31993 var pattern = box.slice(0, length);
31997 Roo.each(pattern, function(i){
31998 format.push(i.size);
32001 Roo.each(standard, function(s){
32003 if(String(s) != String(format)){
32012 if(!match && length == 1){
32017 filterPattern(box, length - 1);
32021 queue.push(pattern);
32023 box = box.slice(length, box.length);
32025 filterPattern(box, 4);
32031 Roo.each(boxes, function(box, k){
32037 if(box.length == 1){
32042 filterPattern(box, 4);
32049 var pos = this.el.getBox(true);
32053 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32055 var hit_end = false;
32057 Roo.each(queue, function(box){
32061 Roo.each(box, function(b){
32063 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32073 Roo.each(box, function(b){
32075 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32078 mx = Math.max(mx, b.x);
32082 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
32086 Roo.each(box, function(b){
32088 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32102 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
32105 /** Sets position of item in DOM
32106 * @param {Element} item
32107 * @param {Number} x - horizontal position
32108 * @param {Number} y - vertical position
32109 * @param {Boolean} isInstant - disables transitions
32111 _processVerticalLayoutQueue : function( queue, isInstant )
32113 var pos = this.el.getBox(true);
32118 for (var i = 0; i < this.cols; i++){
32122 Roo.each(queue, function(box, k){
32124 var col = k % this.cols;
32126 Roo.each(box, function(b,kk){
32128 b.el.position('absolute');
32130 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32131 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32133 if(b.size == 'md-left' || b.size == 'md-right'){
32134 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32135 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32138 b.el.setWidth(width);
32139 b.el.setHeight(height);
32141 b.el.select('iframe',true).setSize(width,height);
32145 for (var i = 0; i < this.cols; i++){
32147 if(maxY[i] < maxY[col]){
32152 col = Math.min(col, i);
32156 x = pos.x + col * (this.colWidth + this.padWidth);
32160 var positions = [];
32162 switch (box.length){
32164 positions = this.getVerticalOneBoxColPositions(x, y, box);
32167 positions = this.getVerticalTwoBoxColPositions(x, y, box);
32170 positions = this.getVerticalThreeBoxColPositions(x, y, box);
32173 positions = this.getVerticalFourBoxColPositions(x, y, box);
32179 Roo.each(box, function(b,kk){
32181 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32183 var sz = b.el.getSize();
32185 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
32193 for (var i = 0; i < this.cols; i++){
32194 mY = Math.max(mY, maxY[i]);
32197 this.el.setHeight(mY - pos.y);
32201 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
32203 // var pos = this.el.getBox(true);
32206 // var maxX = pos.right;
32208 // var maxHeight = 0;
32210 // Roo.each(items, function(item, k){
32214 // item.el.position('absolute');
32216 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
32218 // item.el.setWidth(width);
32220 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
32222 // item.el.setHeight(height);
32225 // item.el.setXY([x, y], isInstant ? false : true);
32227 // item.el.setXY([maxX - width, y], isInstant ? false : true);
32230 // y = y + height + this.alternativePadWidth;
32232 // maxHeight = maxHeight + height + this.alternativePadWidth;
32236 // this.el.setHeight(maxHeight);
32240 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32242 var pos = this.el.getBox(true);
32247 var maxX = pos.right;
32249 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32251 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32253 Roo.each(queue, function(box, k){
32255 Roo.each(box, function(b, kk){
32257 b.el.position('absolute');
32259 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32260 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32262 if(b.size == 'md-left' || b.size == 'md-right'){
32263 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32264 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32267 b.el.setWidth(width);
32268 b.el.setHeight(height);
32276 var positions = [];
32278 switch (box.length){
32280 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32283 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32286 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32289 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32295 Roo.each(box, function(b,kk){
32297 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32299 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32307 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32309 Roo.each(eItems, function(b,k){
32311 b.size = (k == 0) ? 'sm' : 'xs';
32312 b.x = (k == 0) ? 2 : 1;
32313 b.y = (k == 0) ? 2 : 1;
32315 b.el.position('absolute');
32317 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32319 b.el.setWidth(width);
32321 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32323 b.el.setHeight(height);
32327 var positions = [];
32330 x : maxX - this.unitWidth * 2 - this.gutter,
32335 x : maxX - this.unitWidth,
32336 y : minY + (this.unitWidth + this.gutter) * 2
32340 x : maxX - this.unitWidth * 3 - this.gutter * 2,
32344 Roo.each(eItems, function(b,k){
32346 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32352 getVerticalOneBoxColPositions : function(x, y, box)
32356 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32358 if(box[0].size == 'md-left'){
32362 if(box[0].size == 'md-right'){
32367 x : x + (this.unitWidth + this.gutter) * rand,
32374 getVerticalTwoBoxColPositions : function(x, y, box)
32378 if(box[0].size == 'xs'){
32382 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32386 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32400 x : x + (this.unitWidth + this.gutter) * 2,
32401 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32408 getVerticalThreeBoxColPositions : function(x, y, box)
32412 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32420 x : x + (this.unitWidth + this.gutter) * 1,
32425 x : x + (this.unitWidth + this.gutter) * 2,
32433 if(box[0].size == 'xs' && box[1].size == 'xs'){
32442 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32446 x : x + (this.unitWidth + this.gutter) * 1,
32460 x : x + (this.unitWidth + this.gutter) * 2,
32465 x : x + (this.unitWidth + this.gutter) * 2,
32466 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32473 getVerticalFourBoxColPositions : function(x, y, box)
32477 if(box[0].size == 'xs'){
32486 y : y + (this.unitHeight + this.gutter) * 1
32491 y : y + (this.unitHeight + this.gutter) * 2
32495 x : x + (this.unitWidth + this.gutter) * 1,
32509 x : x + (this.unitWidth + this.gutter) * 2,
32514 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32515 y : y + (this.unitHeight + this.gutter) * 1
32519 x : x + (this.unitWidth + this.gutter) * 2,
32520 y : y + (this.unitWidth + this.gutter) * 2
32527 getHorizontalOneBoxColPositions : function(maxX, minY, box)
32531 if(box[0].size == 'md-left'){
32533 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32540 if(box[0].size == 'md-right'){
32542 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32543 y : minY + (this.unitWidth + this.gutter) * 1
32549 var rand = Math.floor(Math.random() * (4 - box[0].y));
32552 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32553 y : minY + (this.unitWidth + this.gutter) * rand
32560 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32564 if(box[0].size == 'xs'){
32567 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32572 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32573 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32581 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32586 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32587 y : minY + (this.unitWidth + this.gutter) * 2
32594 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32598 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32601 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32606 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32607 y : minY + (this.unitWidth + this.gutter) * 1
32611 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32612 y : minY + (this.unitWidth + this.gutter) * 2
32619 if(box[0].size == 'xs' && box[1].size == 'xs'){
32622 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32627 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32632 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32633 y : minY + (this.unitWidth + this.gutter) * 1
32641 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32646 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32647 y : minY + (this.unitWidth + this.gutter) * 2
32651 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32652 y : minY + (this.unitWidth + this.gutter) * 2
32659 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32663 if(box[0].size == 'xs'){
32666 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32671 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32676 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),
32681 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32682 y : minY + (this.unitWidth + this.gutter) * 1
32690 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32695 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32696 y : minY + (this.unitWidth + this.gutter) * 2
32700 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32701 y : minY + (this.unitWidth + this.gutter) * 2
32705 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),
32706 y : minY + (this.unitWidth + this.gutter) * 2
32714 * remove a Masonry Brick
32715 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32717 removeBrick : function(brick_id)
32723 for (var i = 0; i<this.bricks.length; i++) {
32724 if (this.bricks[i].id == brick_id) {
32725 this.bricks.splice(i,1);
32726 this.el.dom.removeChild(Roo.get(brick_id).dom);
32733 * adds a Masonry Brick
32734 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32736 addBrick : function(cfg)
32738 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32739 //this.register(cn);
32740 cn.parentId = this.id;
32741 cn.render(this.el);
32746 * register a Masonry Brick
32747 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32750 register : function(brick)
32752 this.bricks.push(brick);
32753 brick.masonryId = this.id;
32757 * clear all the Masonry Brick
32759 clearAll : function()
32762 //this.getChildContainer().dom.innerHTML = "";
32763 this.el.dom.innerHTML = '';
32766 getSelected : function()
32768 if (!this.selectedBrick) {
32772 return this.selectedBrick;
32776 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32780 * register a Masonry Layout
32781 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32784 register : function(layout)
32786 this.groups[layout.id] = layout;
32789 * fetch a Masonry Layout based on the masonry layout ID
32790 * @param {string} the masonry layout to add
32791 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32794 get: function(layout_id) {
32795 if (typeof(this.groups[layout_id]) == 'undefined') {
32798 return this.groups[layout_id] ;
32810 * http://masonry.desandro.com
32812 * The idea is to render all the bricks based on vertical width...
32814 * The original code extends 'outlayer' - we might need to use that....
32820 * @class Roo.bootstrap.LayoutMasonryAuto
32821 * @extends Roo.bootstrap.Component
32822 * Bootstrap Layout Masonry class
32825 * Create a new Element
32826 * @param {Object} config The config object
32829 Roo.bootstrap.LayoutMasonryAuto = function(config){
32830 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32833 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32836 * @cfg {Boolean} isFitWidth - resize the width..
32838 isFitWidth : false, // options..
32840 * @cfg {Boolean} isOriginLeft = left align?
32842 isOriginLeft : true,
32844 * @cfg {Boolean} isOriginTop = top align?
32846 isOriginTop : false,
32848 * @cfg {Boolean} isLayoutInstant = no animation?
32850 isLayoutInstant : false, // needed?
32852 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32854 isResizingContainer : true,
32856 * @cfg {Number} columnWidth width of the columns
32862 * @cfg {Number} maxCols maximum number of columns
32867 * @cfg {Number} padHeight padding below box..
32873 * @cfg {Boolean} isAutoInitial defalut true
32876 isAutoInitial : true,
32882 initialColumnWidth : 0,
32883 currentSize : null,
32885 colYs : null, // array.
32892 bricks: null, //CompositeElement
32893 cols : 0, // array?
32894 // element : null, // wrapped now this.el
32895 _isLayoutInited : null,
32898 getAutoCreate : function(){
32902 cls: 'blog-masonary-wrapper ' + this.cls,
32904 cls : 'mas-boxes masonary'
32911 getChildContainer: function( )
32913 if (this.boxesEl) {
32914 return this.boxesEl;
32917 this.boxesEl = this.el.select('.mas-boxes').first();
32919 return this.boxesEl;
32923 initEvents : function()
32927 if(this.isAutoInitial){
32928 Roo.log('hook children rendered');
32929 this.on('childrenrendered', function() {
32930 Roo.log('children rendered');
32937 initial : function()
32939 this.reloadItems();
32941 this.currentSize = this.el.getBox(true);
32943 /// was window resize... - let's see if this works..
32944 Roo.EventManager.onWindowResize(this.resize, this);
32946 if(!this.isAutoInitial){
32951 this.layout.defer(500,this);
32954 reloadItems: function()
32956 this.bricks = this.el.select('.masonry-brick', true);
32958 this.bricks.each(function(b) {
32959 //Roo.log(b.getSize());
32960 if (!b.attr('originalwidth')) {
32961 b.attr('originalwidth', b.getSize().width);
32966 Roo.log(this.bricks.elements.length);
32969 resize : function()
32972 var cs = this.el.getBox(true);
32974 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32975 Roo.log("no change in with or X");
32978 this.currentSize = cs;
32982 layout : function()
32985 this._resetLayout();
32986 //this._manageStamps();
32988 // don't animate first layout
32989 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32990 this.layoutItems( isInstant );
32992 // flag for initalized
32993 this._isLayoutInited = true;
32996 layoutItems : function( isInstant )
32998 //var items = this._getItemsForLayout( this.items );
32999 // original code supports filtering layout items.. we just ignore it..
33001 this._layoutItems( this.bricks , isInstant );
33003 this._postLayout();
33005 _layoutItems : function ( items , isInstant)
33007 //this.fireEvent( 'layout', this, items );
33010 if ( !items || !items.elements.length ) {
33011 // no items, emit event with empty array
33016 items.each(function(item) {
33017 Roo.log("layout item");
33019 // get x/y object from method
33020 var position = this._getItemLayoutPosition( item );
33022 position.item = item;
33023 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
33024 queue.push( position );
33027 this._processLayoutQueue( queue );
33029 /** Sets position of item in DOM
33030 * @param {Element} item
33031 * @param {Number} x - horizontal position
33032 * @param {Number} y - vertical position
33033 * @param {Boolean} isInstant - disables transitions
33035 _processLayoutQueue : function( queue )
33037 for ( var i=0, len = queue.length; i < len; i++ ) {
33038 var obj = queue[i];
33039 obj.item.position('absolute');
33040 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
33046 * Any logic you want to do after each layout,
33047 * i.e. size the container
33049 _postLayout : function()
33051 this.resizeContainer();
33054 resizeContainer : function()
33056 if ( !this.isResizingContainer ) {
33059 var size = this._getContainerSize();
33061 this.el.setSize(size.width,size.height);
33062 this.boxesEl.setSize(size.width,size.height);
33068 _resetLayout : function()
33070 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
33071 this.colWidth = this.el.getWidth();
33072 //this.gutter = this.el.getWidth();
33074 this.measureColumns();
33080 this.colYs.push( 0 );
33086 measureColumns : function()
33088 this.getContainerWidth();
33089 // if columnWidth is 0, default to outerWidth of first item
33090 if ( !this.columnWidth ) {
33091 var firstItem = this.bricks.first();
33092 Roo.log(firstItem);
33093 this.columnWidth = this.containerWidth;
33094 if (firstItem && firstItem.attr('originalwidth') ) {
33095 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
33097 // columnWidth fall back to item of first element
33098 Roo.log("set column width?");
33099 this.initialColumnWidth = this.columnWidth ;
33101 // if first elem has no width, default to size of container
33106 if (this.initialColumnWidth) {
33107 this.columnWidth = this.initialColumnWidth;
33112 // column width is fixed at the top - however if container width get's smaller we should
33115 // this bit calcs how man columns..
33117 var columnWidth = this.columnWidth += this.gutter;
33119 // calculate columns
33120 var containerWidth = this.containerWidth + this.gutter;
33122 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
33123 // fix rounding errors, typically with gutters
33124 var excess = columnWidth - containerWidth % columnWidth;
33127 // if overshoot is less than a pixel, round up, otherwise floor it
33128 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
33129 cols = Math[ mathMethod ]( cols );
33130 this.cols = Math.max( cols, 1 );
33131 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33133 // padding positioning..
33134 var totalColWidth = this.cols * this.columnWidth;
33135 var padavail = this.containerWidth - totalColWidth;
33136 // so for 2 columns - we need 3 'pads'
33138 var padNeeded = (1+this.cols) * this.padWidth;
33140 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
33142 this.columnWidth += padExtra
33143 //this.padWidth = Math.floor(padavail / ( this.cols));
33145 // adjust colum width so that padding is fixed??
33147 // we have 3 columns ... total = width * 3
33148 // we have X left over... that should be used by
33150 //if (this.expandC) {
33158 getContainerWidth : function()
33160 /* // container is parent if fit width
33161 var container = this.isFitWidth ? this.element.parentNode : this.element;
33162 // check that this.size and size are there
33163 // IE8 triggers resize on body size change, so they might not be
33165 var size = getSize( container ); //FIXME
33166 this.containerWidth = size && size.innerWidth; //FIXME
33169 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33173 _getItemLayoutPosition : function( item ) // what is item?
33175 // we resize the item to our columnWidth..
33177 item.setWidth(this.columnWidth);
33178 item.autoBoxAdjust = false;
33180 var sz = item.getSize();
33182 // how many columns does this brick span
33183 var remainder = this.containerWidth % this.columnWidth;
33185 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
33186 // round if off by 1 pixel, otherwise use ceil
33187 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
33188 colSpan = Math.min( colSpan, this.cols );
33190 // normally this should be '1' as we dont' currently allow multi width columns..
33192 var colGroup = this._getColGroup( colSpan );
33193 // get the minimum Y value from the columns
33194 var minimumY = Math.min.apply( Math, colGroup );
33195 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
33197 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
33199 // position the brick
33201 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
33202 y: this.currentSize.y + minimumY + this.padHeight
33206 // apply setHeight to necessary columns
33207 var setHeight = minimumY + sz.height + this.padHeight;
33208 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
33210 var setSpan = this.cols + 1 - colGroup.length;
33211 for ( var i = 0; i < setSpan; i++ ) {
33212 this.colYs[ shortColIndex + i ] = setHeight ;
33219 * @param {Number} colSpan - number of columns the element spans
33220 * @returns {Array} colGroup
33222 _getColGroup : function( colSpan )
33224 if ( colSpan < 2 ) {
33225 // if brick spans only one column, use all the column Ys
33230 // how many different places could this brick fit horizontally
33231 var groupCount = this.cols + 1 - colSpan;
33232 // for each group potential horizontal position
33233 for ( var i = 0; i < groupCount; i++ ) {
33234 // make an array of colY values for that one group
33235 var groupColYs = this.colYs.slice( i, i + colSpan );
33236 // and get the max value of the array
33237 colGroup[i] = Math.max.apply( Math, groupColYs );
33242 _manageStamp : function( stamp )
33244 var stampSize = stamp.getSize();
33245 var offset = stamp.getBox();
33246 // get the columns that this stamp affects
33247 var firstX = this.isOriginLeft ? offset.x : offset.right;
33248 var lastX = firstX + stampSize.width;
33249 var firstCol = Math.floor( firstX / this.columnWidth );
33250 firstCol = Math.max( 0, firstCol );
33252 var lastCol = Math.floor( lastX / this.columnWidth );
33253 // lastCol should not go over if multiple of columnWidth #425
33254 lastCol -= lastX % this.columnWidth ? 0 : 1;
33255 lastCol = Math.min( this.cols - 1, lastCol );
33257 // set colYs to bottom of the stamp
33258 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33261 for ( var i = firstCol; i <= lastCol; i++ ) {
33262 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33267 _getContainerSize : function()
33269 this.maxY = Math.max.apply( Math, this.colYs );
33274 if ( this.isFitWidth ) {
33275 size.width = this._getContainerFitWidth();
33281 _getContainerFitWidth : function()
33283 var unusedCols = 0;
33284 // count unused columns
33287 if ( this.colYs[i] !== 0 ) {
33292 // fit container to columns that have been used
33293 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33296 needsResizeLayout : function()
33298 var previousWidth = this.containerWidth;
33299 this.getContainerWidth();
33300 return previousWidth !== this.containerWidth;
33315 * @class Roo.bootstrap.MasonryBrick
33316 * @extends Roo.bootstrap.Component
33317 * Bootstrap MasonryBrick class
33320 * Create a new MasonryBrick
33321 * @param {Object} config The config object
33324 Roo.bootstrap.MasonryBrick = function(config){
33326 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33328 Roo.bootstrap.MasonryBrick.register(this);
33334 * When a MasonryBrick is clcik
33335 * @param {Roo.bootstrap.MasonryBrick} this
33336 * @param {Roo.EventObject} e
33342 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
33345 * @cfg {String} title
33349 * @cfg {String} html
33353 * @cfg {String} bgimage
33357 * @cfg {String} videourl
33361 * @cfg {String} cls
33365 * @cfg {String} href
33369 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33374 * @cfg {String} placetitle (center|bottom)
33379 * @cfg {Boolean} isFitContainer defalut true
33381 isFitContainer : true,
33384 * @cfg {Boolean} preventDefault defalut false
33386 preventDefault : false,
33389 * @cfg {Boolean} inverse defalut false
33391 maskInverse : false,
33393 getAutoCreate : function()
33395 if(!this.isFitContainer){
33396 return this.getSplitAutoCreate();
33399 var cls = 'masonry-brick masonry-brick-full';
33401 if(this.href.length){
33402 cls += ' masonry-brick-link';
33405 if(this.bgimage.length){
33406 cls += ' masonry-brick-image';
33409 if(this.maskInverse){
33410 cls += ' mask-inverse';
33413 if(!this.html.length && !this.maskInverse && !this.videourl.length){
33414 cls += ' enable-mask';
33418 cls += ' masonry-' + this.size + '-brick';
33421 if(this.placetitle.length){
33423 switch (this.placetitle) {
33425 cls += ' masonry-center-title';
33428 cls += ' masonry-bottom-title';
33435 if(!this.html.length && !this.bgimage.length){
33436 cls += ' masonry-center-title';
33439 if(!this.html.length && this.bgimage.length){
33440 cls += ' masonry-bottom-title';
33445 cls += ' ' + this.cls;
33449 tag: (this.href.length) ? 'a' : 'div',
33454 cls: 'masonry-brick-mask'
33458 cls: 'masonry-brick-paragraph',
33464 if(this.href.length){
33465 cfg.href = this.href;
33468 var cn = cfg.cn[1].cn;
33470 if(this.title.length){
33473 cls: 'masonry-brick-title',
33478 if(this.html.length){
33481 cls: 'masonry-brick-text',
33486 if (!this.title.length && !this.html.length) {
33487 cfg.cn[1].cls += ' hide';
33490 if(this.bgimage.length){
33493 cls: 'masonry-brick-image-view',
33498 if(this.videourl.length){
33499 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33500 // youtube support only?
33503 cls: 'masonry-brick-image-view',
33506 allowfullscreen : true
33514 getSplitAutoCreate : function()
33516 var cls = 'masonry-brick masonry-brick-split';
33518 if(this.href.length){
33519 cls += ' masonry-brick-link';
33522 if(this.bgimage.length){
33523 cls += ' masonry-brick-image';
33527 cls += ' masonry-' + this.size + '-brick';
33530 switch (this.placetitle) {
33532 cls += ' masonry-center-title';
33535 cls += ' masonry-bottom-title';
33538 if(!this.bgimage.length){
33539 cls += ' masonry-center-title';
33542 if(this.bgimage.length){
33543 cls += ' masonry-bottom-title';
33549 cls += ' ' + this.cls;
33553 tag: (this.href.length) ? 'a' : 'div',
33558 cls: 'masonry-brick-split-head',
33562 cls: 'masonry-brick-paragraph',
33569 cls: 'masonry-brick-split-body',
33575 if(this.href.length){
33576 cfg.href = this.href;
33579 if(this.title.length){
33580 cfg.cn[0].cn[0].cn.push({
33582 cls: 'masonry-brick-title',
33587 if(this.html.length){
33588 cfg.cn[1].cn.push({
33590 cls: 'masonry-brick-text',
33595 if(this.bgimage.length){
33596 cfg.cn[0].cn.push({
33598 cls: 'masonry-brick-image-view',
33603 if(this.videourl.length){
33604 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33605 // youtube support only?
33606 cfg.cn[0].cn.cn.push({
33608 cls: 'masonry-brick-image-view',
33611 allowfullscreen : true
33618 initEvents: function()
33620 switch (this.size) {
33653 this.el.on('touchstart', this.onTouchStart, this);
33654 this.el.on('touchmove', this.onTouchMove, this);
33655 this.el.on('touchend', this.onTouchEnd, this);
33656 this.el.on('contextmenu', this.onContextMenu, this);
33658 this.el.on('mouseenter' ,this.enter, this);
33659 this.el.on('mouseleave', this.leave, this);
33660 this.el.on('click', this.onClick, this);
33663 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33664 this.parent().bricks.push(this);
33669 onClick: function(e, el)
33671 var time = this.endTimer - this.startTimer;
33672 // Roo.log(e.preventDefault());
33675 e.preventDefault();
33680 if(!this.preventDefault){
33684 e.preventDefault();
33686 if (this.activeClass != '') {
33687 this.selectBrick();
33690 this.fireEvent('click', this, e);
33693 enter: function(e, el)
33695 e.preventDefault();
33697 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33701 if(this.bgimage.length && this.html.length){
33702 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33706 leave: function(e, el)
33708 e.preventDefault();
33710 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33714 if(this.bgimage.length && this.html.length){
33715 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33719 onTouchStart: function(e, el)
33721 // e.preventDefault();
33723 this.touchmoved = false;
33725 if(!this.isFitContainer){
33729 if(!this.bgimage.length || !this.html.length){
33733 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33735 this.timer = new Date().getTime();
33739 onTouchMove: function(e, el)
33741 this.touchmoved = true;
33744 onContextMenu : function(e,el)
33746 e.preventDefault();
33747 e.stopPropagation();
33751 onTouchEnd: function(e, el)
33753 // e.preventDefault();
33755 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33762 if(!this.bgimage.length || !this.html.length){
33764 if(this.href.length){
33765 window.location.href = this.href;
33771 if(!this.isFitContainer){
33775 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33777 window.location.href = this.href;
33780 //selection on single brick only
33781 selectBrick : function() {
33783 if (!this.parentId) {
33787 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33788 var index = m.selectedBrick.indexOf(this.id);
33791 m.selectedBrick.splice(index,1);
33792 this.el.removeClass(this.activeClass);
33796 for(var i = 0; i < m.selectedBrick.length; i++) {
33797 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33798 b.el.removeClass(b.activeClass);
33801 m.selectedBrick = [];
33803 m.selectedBrick.push(this.id);
33804 this.el.addClass(this.activeClass);
33808 isSelected : function(){
33809 return this.el.hasClass(this.activeClass);
33814 Roo.apply(Roo.bootstrap.MasonryBrick, {
33817 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33819 * register a Masonry Brick
33820 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33823 register : function(brick)
33825 //this.groups[brick.id] = brick;
33826 this.groups.add(brick.id, brick);
33829 * fetch a masonry brick based on the masonry brick ID
33830 * @param {string} the masonry brick to add
33831 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33834 get: function(brick_id)
33836 // if (typeof(this.groups[brick_id]) == 'undefined') {
33839 // return this.groups[brick_id] ;
33841 if(this.groups.key(brick_id)) {
33842 return this.groups.key(brick_id);
33860 * @class Roo.bootstrap.Brick
33861 * @extends Roo.bootstrap.Component
33862 * Bootstrap Brick class
33865 * Create a new Brick
33866 * @param {Object} config The config object
33869 Roo.bootstrap.Brick = function(config){
33870 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33876 * When a Brick is click
33877 * @param {Roo.bootstrap.Brick} this
33878 * @param {Roo.EventObject} e
33884 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33887 * @cfg {String} title
33891 * @cfg {String} html
33895 * @cfg {String} bgimage
33899 * @cfg {String} cls
33903 * @cfg {String} href
33907 * @cfg {String} video
33911 * @cfg {Boolean} square
33915 getAutoCreate : function()
33917 var cls = 'roo-brick';
33919 if(this.href.length){
33920 cls += ' roo-brick-link';
33923 if(this.bgimage.length){
33924 cls += ' roo-brick-image';
33927 if(!this.html.length && !this.bgimage.length){
33928 cls += ' roo-brick-center-title';
33931 if(!this.html.length && this.bgimage.length){
33932 cls += ' roo-brick-bottom-title';
33936 cls += ' ' + this.cls;
33940 tag: (this.href.length) ? 'a' : 'div',
33945 cls: 'roo-brick-paragraph',
33951 if(this.href.length){
33952 cfg.href = this.href;
33955 var cn = cfg.cn[0].cn;
33957 if(this.title.length){
33960 cls: 'roo-brick-title',
33965 if(this.html.length){
33968 cls: 'roo-brick-text',
33975 if(this.bgimage.length){
33978 cls: 'roo-brick-image-view',
33986 initEvents: function()
33988 if(this.title.length || this.html.length){
33989 this.el.on('mouseenter' ,this.enter, this);
33990 this.el.on('mouseleave', this.leave, this);
33993 Roo.EventManager.onWindowResize(this.resize, this);
33995 if(this.bgimage.length){
33996 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33997 this.imageEl.on('load', this.onImageLoad, this);
34004 onImageLoad : function()
34009 resize : function()
34011 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
34013 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
34015 if(this.bgimage.length){
34016 var image = this.el.select('.roo-brick-image-view', true).first();
34018 image.setWidth(paragraph.getWidth());
34021 image.setHeight(paragraph.getWidth());
34024 this.el.setHeight(image.getHeight());
34025 paragraph.setHeight(image.getHeight());
34031 enter: function(e, el)
34033 e.preventDefault();
34035 if(this.bgimage.length){
34036 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
34037 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
34041 leave: function(e, el)
34043 e.preventDefault();
34045 if(this.bgimage.length){
34046 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
34047 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
34062 * @class Roo.bootstrap.NumberField
34063 * @extends Roo.bootstrap.Input
34064 * Bootstrap NumberField class
34070 * Create a new NumberField
34071 * @param {Object} config The config object
34074 Roo.bootstrap.NumberField = function(config){
34075 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
34078 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
34081 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
34083 allowDecimals : true,
34085 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
34087 decimalSeparator : ".",
34089 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
34091 decimalPrecision : 2,
34093 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
34095 allowNegative : true,
34098 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
34102 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
34104 minValue : Number.NEGATIVE_INFINITY,
34106 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
34108 maxValue : Number.MAX_VALUE,
34110 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
34112 minText : "The minimum value for this field is {0}",
34114 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
34116 maxText : "The maximum value for this field is {0}",
34118 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
34119 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
34121 nanText : "{0} is not a valid number",
34123 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
34125 thousandsDelimiter : false,
34127 * @cfg {String} valueAlign alignment of value
34129 valueAlign : "left",
34131 getAutoCreate : function()
34133 var hiddenInput = {
34137 cls: 'hidden-number-input'
34141 hiddenInput.name = this.name;
34146 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
34148 this.name = hiddenInput.name;
34150 if(cfg.cn.length > 0) {
34151 cfg.cn.push(hiddenInput);
34158 initEvents : function()
34160 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
34162 var allowed = "0123456789";
34164 if(this.allowDecimals){
34165 allowed += this.decimalSeparator;
34168 if(this.allowNegative){
34172 if(this.thousandsDelimiter) {
34176 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
34178 var keyPress = function(e){
34180 var k = e.getKey();
34182 var c = e.getCharCode();
34185 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
34186 allowed.indexOf(String.fromCharCode(c)) === -1
34192 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
34196 if(allowed.indexOf(String.fromCharCode(c)) === -1){
34201 this.el.on("keypress", keyPress, this);
34204 validateValue : function(value)
34207 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
34211 var num = this.parseValue(value);
34214 this.markInvalid(String.format(this.nanText, value));
34218 if(num < this.minValue){
34219 this.markInvalid(String.format(this.minText, this.minValue));
34223 if(num > this.maxValue){
34224 this.markInvalid(String.format(this.maxText, this.maxValue));
34231 getValue : function()
34233 var v = this.hiddenEl().getValue();
34235 return this.fixPrecision(this.parseValue(v));
34238 parseValue : function(value)
34240 if(this.thousandsDelimiter) {
34242 r = new RegExp(",", "g");
34243 value = value.replace(r, "");
34246 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34247 return isNaN(value) ? '' : value;
34250 fixPrecision : function(value)
34252 if(this.thousandsDelimiter) {
34254 r = new RegExp(",", "g");
34255 value = value.replace(r, "");
34258 var nan = isNaN(value);
34260 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34261 return nan ? '' : value;
34263 return parseFloat(value).toFixed(this.decimalPrecision);
34266 setValue : function(v)
34268 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34274 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34276 this.inputEl().dom.value = (v == '') ? '' :
34277 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34279 if(!this.allowZero && v === '0') {
34280 this.hiddenEl().dom.value = '';
34281 this.inputEl().dom.value = '';
34288 decimalPrecisionFcn : function(v)
34290 return Math.floor(v);
34293 beforeBlur : function()
34295 var v = this.parseValue(this.getRawValue());
34297 if(v || v === 0 || v === ''){
34302 hiddenEl : function()
34304 return this.el.select('input.hidden-number-input',true).first();
34316 * @class Roo.bootstrap.DocumentSlider
34317 * @extends Roo.bootstrap.Component
34318 * Bootstrap DocumentSlider class
34321 * Create a new DocumentViewer
34322 * @param {Object} config The config object
34325 Roo.bootstrap.DocumentSlider = function(config){
34326 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34333 * Fire after initEvent
34334 * @param {Roo.bootstrap.DocumentSlider} this
34339 * Fire after update
34340 * @param {Roo.bootstrap.DocumentSlider} this
34346 * @param {Roo.bootstrap.DocumentSlider} this
34352 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
34358 getAutoCreate : function()
34362 cls : 'roo-document-slider',
34366 cls : 'roo-document-slider-header',
34370 cls : 'roo-document-slider-header-title'
34376 cls : 'roo-document-slider-body',
34380 cls : 'roo-document-slider-prev',
34384 cls : 'fa fa-chevron-left'
34390 cls : 'roo-document-slider-thumb',
34394 cls : 'roo-document-slider-image'
34400 cls : 'roo-document-slider-next',
34404 cls : 'fa fa-chevron-right'
34416 initEvents : function()
34418 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34419 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34421 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34422 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34424 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34425 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34427 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34428 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34430 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34431 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34433 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34434 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34436 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34437 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34439 this.thumbEl.on('click', this.onClick, this);
34441 this.prevIndicator.on('click', this.prev, this);
34443 this.nextIndicator.on('click', this.next, this);
34447 initial : function()
34449 if(this.files.length){
34450 this.indicator = 1;
34454 this.fireEvent('initial', this);
34457 update : function()
34459 this.imageEl.attr('src', this.files[this.indicator - 1]);
34461 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34463 this.prevIndicator.show();
34465 if(this.indicator == 1){
34466 this.prevIndicator.hide();
34469 this.nextIndicator.show();
34471 if(this.indicator == this.files.length){
34472 this.nextIndicator.hide();
34475 this.thumbEl.scrollTo('top');
34477 this.fireEvent('update', this);
34480 onClick : function(e)
34482 e.preventDefault();
34484 this.fireEvent('click', this);
34489 e.preventDefault();
34491 this.indicator = Math.max(1, this.indicator - 1);
34498 e.preventDefault();
34500 this.indicator = Math.min(this.files.length, this.indicator + 1);
34514 * @class Roo.bootstrap.RadioSet
34515 * @extends Roo.bootstrap.Input
34516 * Bootstrap RadioSet class
34517 * @cfg {String} indicatorpos (left|right) default left
34518 * @cfg {Boolean} inline (true|false) inline the element (default true)
34519 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34521 * Create a new RadioSet
34522 * @param {Object} config The config object
34525 Roo.bootstrap.RadioSet = function(config){
34527 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34531 Roo.bootstrap.RadioSet.register(this);
34536 * Fires when the element is checked or unchecked.
34537 * @param {Roo.bootstrap.RadioSet} this This radio
34538 * @param {Roo.bootstrap.Radio} item The checked item
34543 * Fires when the element is click.
34544 * @param {Roo.bootstrap.RadioSet} this This radio set
34545 * @param {Roo.bootstrap.Radio} item The checked item
34546 * @param {Roo.EventObject} e The event object
34553 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
34561 indicatorpos : 'left',
34563 getAutoCreate : function()
34567 cls : 'roo-radio-set-label',
34571 html : this.fieldLabel
34575 if (Roo.bootstrap.version == 3) {
34578 if(this.indicatorpos == 'left'){
34581 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34582 tooltip : 'This field is required'
34587 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34588 tooltip : 'This field is required'
34594 cls : 'roo-radio-set-items'
34597 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34599 if (align === 'left' && this.fieldLabel.length) {
34602 cls : "roo-radio-set-right",
34608 if(this.labelWidth > 12){
34609 label.style = "width: " + this.labelWidth + 'px';
34612 if(this.labelWidth < 13 && this.labelmd == 0){
34613 this.labelmd = this.labelWidth;
34616 if(this.labellg > 0){
34617 label.cls += ' col-lg-' + this.labellg;
34618 items.cls += ' col-lg-' + (12 - this.labellg);
34621 if(this.labelmd > 0){
34622 label.cls += ' col-md-' + this.labelmd;
34623 items.cls += ' col-md-' + (12 - this.labelmd);
34626 if(this.labelsm > 0){
34627 label.cls += ' col-sm-' + this.labelsm;
34628 items.cls += ' col-sm-' + (12 - this.labelsm);
34631 if(this.labelxs > 0){
34632 label.cls += ' col-xs-' + this.labelxs;
34633 items.cls += ' col-xs-' + (12 - this.labelxs);
34639 cls : 'roo-radio-set',
34643 cls : 'roo-radio-set-input',
34646 value : this.value ? this.value : ''
34653 if(this.weight.length){
34654 cfg.cls += ' roo-radio-' + this.weight;
34658 cfg.cls += ' roo-radio-set-inline';
34662 ['xs','sm','md','lg'].map(function(size){
34663 if (settings[size]) {
34664 cfg.cls += ' col-' + size + '-' + settings[size];
34672 initEvents : function()
34674 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34675 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34677 if(!this.fieldLabel.length){
34678 this.labelEl.hide();
34681 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34682 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34684 this.indicator = this.indicatorEl();
34686 if(this.indicator){
34687 this.indicator.addClass('invisible');
34690 this.originalValue = this.getValue();
34694 inputEl: function ()
34696 return this.el.select('.roo-radio-set-input', true).first();
34699 getChildContainer : function()
34701 return this.itemsEl;
34704 register : function(item)
34706 this.radioes.push(item);
34710 validate : function()
34712 if(this.getVisibilityEl().hasClass('hidden')){
34718 Roo.each(this.radioes, function(i){
34727 if(this.allowBlank) {
34731 if(this.disabled || valid){
34736 this.markInvalid();
34741 markValid : function()
34743 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34744 this.indicatorEl().removeClass('visible');
34745 this.indicatorEl().addClass('invisible');
34749 if (Roo.bootstrap.version == 3) {
34750 this.el.removeClass([this.invalidClass, this.validClass]);
34751 this.el.addClass(this.validClass);
34753 this.el.removeClass(['is-invalid','is-valid']);
34754 this.el.addClass(['is-valid']);
34756 this.fireEvent('valid', this);
34759 markInvalid : function(msg)
34761 if(this.allowBlank || this.disabled){
34765 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34766 this.indicatorEl().removeClass('invisible');
34767 this.indicatorEl().addClass('visible');
34769 if (Roo.bootstrap.version == 3) {
34770 this.el.removeClass([this.invalidClass, this.validClass]);
34771 this.el.addClass(this.invalidClass);
34773 this.el.removeClass(['is-invalid','is-valid']);
34774 this.el.addClass(['is-invalid']);
34777 this.fireEvent('invalid', this, msg);
34781 setValue : function(v, suppressEvent)
34783 if(this.value === v){
34790 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34793 Roo.each(this.radioes, function(i){
34795 i.el.removeClass('checked');
34798 Roo.each(this.radioes, function(i){
34800 if(i.value === v || i.value.toString() === v.toString()){
34802 i.el.addClass('checked');
34804 if(suppressEvent !== true){
34805 this.fireEvent('check', this, i);
34816 clearInvalid : function(){
34818 if(!this.el || this.preventMark){
34822 this.el.removeClass([this.invalidClass]);
34824 this.fireEvent('valid', this);
34829 Roo.apply(Roo.bootstrap.RadioSet, {
34833 register : function(set)
34835 this.groups[set.name] = set;
34838 get: function(name)
34840 if (typeof(this.groups[name]) == 'undefined') {
34844 return this.groups[name] ;
34850 * Ext JS Library 1.1.1
34851 * Copyright(c) 2006-2007, Ext JS, LLC.
34853 * Originally Released Under LGPL - original licence link has changed is not relivant.
34856 * <script type="text/javascript">
34861 * @class Roo.bootstrap.SplitBar
34862 * @extends Roo.util.Observable
34863 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34867 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34868 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34869 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34870 split.minSize = 100;
34871 split.maxSize = 600;
34872 split.animate = true;
34873 split.on('moved', splitterMoved);
34876 * Create a new SplitBar
34877 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34878 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34879 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34880 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34881 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34882 position of the SplitBar).
34884 Roo.bootstrap.SplitBar = function(cfg){
34889 // dragElement : elm
34890 // resizingElement: el,
34892 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34893 // placement : Roo.bootstrap.SplitBar.LEFT ,
34894 // existingProxy ???
34897 this.el = Roo.get(cfg.dragElement, true);
34898 this.el.dom.unselectable = "on";
34900 this.resizingEl = Roo.get(cfg.resizingElement, true);
34904 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34905 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34908 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34911 * The minimum size of the resizing element. (Defaults to 0)
34917 * The maximum size of the resizing element. (Defaults to 2000)
34920 this.maxSize = 2000;
34923 * Whether to animate the transition to the new size
34926 this.animate = false;
34929 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34932 this.useShim = false;
34937 if(!cfg.existingProxy){
34939 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34941 this.proxy = Roo.get(cfg.existingProxy).dom;
34944 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34947 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34950 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34953 this.dragSpecs = {};
34956 * @private The adapter to use to positon and resize elements
34958 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34959 this.adapter.init(this);
34961 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34963 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34964 this.el.addClass("roo-splitbar-h");
34967 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34968 this.el.addClass("roo-splitbar-v");
34974 * Fires when the splitter is moved (alias for {@link #event-moved})
34975 * @param {Roo.bootstrap.SplitBar} this
34976 * @param {Number} newSize the new width or height
34981 * Fires when the splitter is moved
34982 * @param {Roo.bootstrap.SplitBar} this
34983 * @param {Number} newSize the new width or height
34987 * @event beforeresize
34988 * Fires before the splitter is dragged
34989 * @param {Roo.bootstrap.SplitBar} this
34991 "beforeresize" : true,
34993 "beforeapply" : true
34996 Roo.util.Observable.call(this);
34999 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
35000 onStartProxyDrag : function(x, y){
35001 this.fireEvent("beforeresize", this);
35003 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
35005 o.enableDisplayMode("block");
35006 // all splitbars share the same overlay
35007 Roo.bootstrap.SplitBar.prototype.overlay = o;
35009 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
35010 this.overlay.show();
35011 Roo.get(this.proxy).setDisplayed("block");
35012 var size = this.adapter.getElementSize(this);
35013 this.activeMinSize = this.getMinimumSize();;
35014 this.activeMaxSize = this.getMaximumSize();;
35015 var c1 = size - this.activeMinSize;
35016 var c2 = Math.max(this.activeMaxSize - size, 0);
35017 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35018 this.dd.resetConstraints();
35019 this.dd.setXConstraint(
35020 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
35021 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
35023 this.dd.setYConstraint(0, 0);
35025 this.dd.resetConstraints();
35026 this.dd.setXConstraint(0, 0);
35027 this.dd.setYConstraint(
35028 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
35029 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
35032 this.dragSpecs.startSize = size;
35033 this.dragSpecs.startPoint = [x, y];
35034 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
35038 * @private Called after the drag operation by the DDProxy
35040 onEndProxyDrag : function(e){
35041 Roo.get(this.proxy).setDisplayed(false);
35042 var endPoint = Roo.lib.Event.getXY(e);
35044 this.overlay.hide();
35047 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35048 newSize = this.dragSpecs.startSize +
35049 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
35050 endPoint[0] - this.dragSpecs.startPoint[0] :
35051 this.dragSpecs.startPoint[0] - endPoint[0]
35054 newSize = this.dragSpecs.startSize +
35055 (this.placement == Roo.bootstrap.SplitBar.TOP ?
35056 endPoint[1] - this.dragSpecs.startPoint[1] :
35057 this.dragSpecs.startPoint[1] - endPoint[1]
35060 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
35061 if(newSize != this.dragSpecs.startSize){
35062 if(this.fireEvent('beforeapply', this, newSize) !== false){
35063 this.adapter.setElementSize(this, newSize);
35064 this.fireEvent("moved", this, newSize);
35065 this.fireEvent("resize", this, newSize);
35071 * Get the adapter this SplitBar uses
35072 * @return The adapter object
35074 getAdapter : function(){
35075 return this.adapter;
35079 * Set the adapter this SplitBar uses
35080 * @param {Object} adapter A SplitBar adapter object
35082 setAdapter : function(adapter){
35083 this.adapter = adapter;
35084 this.adapter.init(this);
35088 * Gets the minimum size for the resizing element
35089 * @return {Number} The minimum size
35091 getMinimumSize : function(){
35092 return this.minSize;
35096 * Sets the minimum size for the resizing element
35097 * @param {Number} minSize The minimum size
35099 setMinimumSize : function(minSize){
35100 this.minSize = minSize;
35104 * Gets the maximum size for the resizing element
35105 * @return {Number} The maximum size
35107 getMaximumSize : function(){
35108 return this.maxSize;
35112 * Sets the maximum size for the resizing element
35113 * @param {Number} maxSize The maximum size
35115 setMaximumSize : function(maxSize){
35116 this.maxSize = maxSize;
35120 * Sets the initialize size for the resizing element
35121 * @param {Number} size The initial size
35123 setCurrentSize : function(size){
35124 var oldAnimate = this.animate;
35125 this.animate = false;
35126 this.adapter.setElementSize(this, size);
35127 this.animate = oldAnimate;
35131 * Destroy this splitbar.
35132 * @param {Boolean} removeEl True to remove the element
35134 destroy : function(removeEl){
35136 this.shim.remove();
35139 this.proxy.parentNode.removeChild(this.proxy);
35147 * @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.
35149 Roo.bootstrap.SplitBar.createProxy = function(dir){
35150 var proxy = new Roo.Element(document.createElement("div"));
35151 proxy.unselectable();
35152 var cls = 'roo-splitbar-proxy';
35153 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
35154 document.body.appendChild(proxy.dom);
35159 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
35160 * Default Adapter. It assumes the splitter and resizing element are not positioned
35161 * elements and only gets/sets the width of the element. Generally used for table based layouts.
35163 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
35166 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
35167 // do nothing for now
35168 init : function(s){
35172 * Called before drag operations to get the current size of the resizing element.
35173 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35175 getElementSize : function(s){
35176 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35177 return s.resizingEl.getWidth();
35179 return s.resizingEl.getHeight();
35184 * Called after drag operations to set the size of the resizing element.
35185 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35186 * @param {Number} newSize The new size to set
35187 * @param {Function} onComplete A function to be invoked when resizing is complete
35189 setElementSize : function(s, newSize, onComplete){
35190 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35192 s.resizingEl.setWidth(newSize);
35194 onComplete(s, newSize);
35197 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
35202 s.resizingEl.setHeight(newSize);
35204 onComplete(s, newSize);
35207 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
35214 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
35215 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
35216 * Adapter that moves the splitter element to align with the resized sizing element.
35217 * Used with an absolute positioned SplitBar.
35218 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
35219 * document.body, make sure you assign an id to the body element.
35221 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
35222 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35223 this.container = Roo.get(container);
35226 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
35227 init : function(s){
35228 this.basic.init(s);
35231 getElementSize : function(s){
35232 return this.basic.getElementSize(s);
35235 setElementSize : function(s, newSize, onComplete){
35236 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
35239 moveSplitter : function(s){
35240 var yes = Roo.bootstrap.SplitBar;
35241 switch(s.placement){
35243 s.el.setX(s.resizingEl.getRight());
35246 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35249 s.el.setY(s.resizingEl.getBottom());
35252 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35259 * Orientation constant - Create a vertical SplitBar
35263 Roo.bootstrap.SplitBar.VERTICAL = 1;
35266 * Orientation constant - Create a horizontal SplitBar
35270 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35273 * Placement constant - The resizing element is to the left of the splitter element
35277 Roo.bootstrap.SplitBar.LEFT = 1;
35280 * Placement constant - The resizing element is to the right of the splitter element
35284 Roo.bootstrap.SplitBar.RIGHT = 2;
35287 * Placement constant - The resizing element is positioned above the splitter element
35291 Roo.bootstrap.SplitBar.TOP = 3;
35294 * Placement constant - The resizing element is positioned under splitter element
35298 Roo.bootstrap.SplitBar.BOTTOM = 4;
35299 Roo.namespace("Roo.bootstrap.layout");/*
35301 * Ext JS Library 1.1.1
35302 * Copyright(c) 2006-2007, Ext JS, LLC.
35304 * Originally Released Under LGPL - original licence link has changed is not relivant.
35307 * <script type="text/javascript">
35311 * @class Roo.bootstrap.layout.Manager
35312 * @extends Roo.bootstrap.Component
35313 * Base class for layout managers.
35315 Roo.bootstrap.layout.Manager = function(config)
35317 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35323 /** false to disable window resize monitoring @type Boolean */
35324 this.monitorWindowResize = true;
35329 * Fires when a layout is performed.
35330 * @param {Roo.LayoutManager} this
35334 * @event regionresized
35335 * Fires when the user resizes a region.
35336 * @param {Roo.LayoutRegion} region The resized region
35337 * @param {Number} newSize The new size (width for east/west, height for north/south)
35339 "regionresized" : true,
35341 * @event regioncollapsed
35342 * Fires when a region is collapsed.
35343 * @param {Roo.LayoutRegion} region The collapsed region
35345 "regioncollapsed" : true,
35347 * @event regionexpanded
35348 * Fires when a region is expanded.
35349 * @param {Roo.LayoutRegion} region The expanded region
35351 "regionexpanded" : true
35353 this.updating = false;
35356 this.el = Roo.get(config.el);
35362 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35367 monitorWindowResize : true,
35373 onRender : function(ct, position)
35376 this.el = Roo.get(ct);
35379 //this.fireEvent('render',this);
35383 initEvents: function()
35387 // ie scrollbar fix
35388 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35389 document.body.scroll = "no";
35390 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35391 this.el.position('relative');
35393 this.id = this.el.id;
35394 this.el.addClass("roo-layout-container");
35395 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35396 if(this.el.dom != document.body ) {
35397 this.el.on('resize', this.layout,this);
35398 this.el.on('show', this.layout,this);
35404 * Returns true if this layout is currently being updated
35405 * @return {Boolean}
35407 isUpdating : function(){
35408 return this.updating;
35412 * Suspend the LayoutManager from doing auto-layouts while
35413 * making multiple add or remove calls
35415 beginUpdate : function(){
35416 this.updating = true;
35420 * Restore auto-layouts and optionally disable the manager from performing a layout
35421 * @param {Boolean} noLayout true to disable a layout update
35423 endUpdate : function(noLayout){
35424 this.updating = false;
35430 layout: function(){
35434 onRegionResized : function(region, newSize){
35435 this.fireEvent("regionresized", region, newSize);
35439 onRegionCollapsed : function(region){
35440 this.fireEvent("regioncollapsed", region);
35443 onRegionExpanded : function(region){
35444 this.fireEvent("regionexpanded", region);
35448 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35449 * performs box-model adjustments.
35450 * @return {Object} The size as an object {width: (the width), height: (the height)}
35452 getViewSize : function()
35455 if(this.el.dom != document.body){
35456 size = this.el.getSize();
35458 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35460 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35461 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35466 * Returns the Element this layout is bound to.
35467 * @return {Roo.Element}
35469 getEl : function(){
35474 * Returns the specified region.
35475 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35476 * @return {Roo.LayoutRegion}
35478 getRegion : function(target){
35479 return this.regions[target.toLowerCase()];
35482 onWindowResize : function(){
35483 if(this.monitorWindowResize){
35490 * Ext JS Library 1.1.1
35491 * Copyright(c) 2006-2007, Ext JS, LLC.
35493 * Originally Released Under LGPL - original licence link has changed is not relivant.
35496 * <script type="text/javascript">
35499 * @class Roo.bootstrap.layout.Border
35500 * @extends Roo.bootstrap.layout.Manager
35501 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35502 * please see: examples/bootstrap/nested.html<br><br>
35504 <b>The container the layout is rendered into can be either the body element or any other element.
35505 If it is not the body element, the container needs to either be an absolute positioned element,
35506 or you will need to add "position:relative" to the css of the container. You will also need to specify
35507 the container size if it is not the body element.</b>
35510 * Create a new Border
35511 * @param {Object} config Configuration options
35513 Roo.bootstrap.layout.Border = function(config){
35514 config = config || {};
35515 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35519 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35520 if(config[region]){
35521 config[region].region = region;
35522 this.addRegion(config[region]);
35528 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
35530 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35532 parent : false, // this might point to a 'nest' or a ???
35535 * Creates and adds a new region if it doesn't already exist.
35536 * @param {String} target The target region key (north, south, east, west or center).
35537 * @param {Object} config The regions config object
35538 * @return {BorderLayoutRegion} The new region
35540 addRegion : function(config)
35542 if(!this.regions[config.region]){
35543 var r = this.factory(config);
35544 this.bindRegion(r);
35546 return this.regions[config.region];
35550 bindRegion : function(r){
35551 this.regions[r.config.region] = r;
35553 r.on("visibilitychange", this.layout, this);
35554 r.on("paneladded", this.layout, this);
35555 r.on("panelremoved", this.layout, this);
35556 r.on("invalidated", this.layout, this);
35557 r.on("resized", this.onRegionResized, this);
35558 r.on("collapsed", this.onRegionCollapsed, this);
35559 r.on("expanded", this.onRegionExpanded, this);
35563 * Performs a layout update.
35565 layout : function()
35567 if(this.updating) {
35571 // render all the rebions if they have not been done alreayd?
35572 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35573 if(this.regions[region] && !this.regions[region].bodyEl){
35574 this.regions[region].onRender(this.el)
35578 var size = this.getViewSize();
35579 var w = size.width;
35580 var h = size.height;
35585 //var x = 0, y = 0;
35587 var rs = this.regions;
35588 var north = rs["north"];
35589 var south = rs["south"];
35590 var west = rs["west"];
35591 var east = rs["east"];
35592 var center = rs["center"];
35593 //if(this.hideOnLayout){ // not supported anymore
35594 //c.el.setStyle("display", "none");
35596 if(north && north.isVisible()){
35597 var b = north.getBox();
35598 var m = north.getMargins();
35599 b.width = w - (m.left+m.right);
35602 centerY = b.height + b.y + m.bottom;
35603 centerH -= centerY;
35604 north.updateBox(this.safeBox(b));
35606 if(south && south.isVisible()){
35607 var b = south.getBox();
35608 var m = south.getMargins();
35609 b.width = w - (m.left+m.right);
35611 var totalHeight = (b.height + m.top + m.bottom);
35612 b.y = h - totalHeight + m.top;
35613 centerH -= totalHeight;
35614 south.updateBox(this.safeBox(b));
35616 if(west && west.isVisible()){
35617 var b = west.getBox();
35618 var m = west.getMargins();
35619 b.height = centerH - (m.top+m.bottom);
35621 b.y = centerY + m.top;
35622 var totalWidth = (b.width + m.left + m.right);
35623 centerX += totalWidth;
35624 centerW -= totalWidth;
35625 west.updateBox(this.safeBox(b));
35627 if(east && east.isVisible()){
35628 var b = east.getBox();
35629 var m = east.getMargins();
35630 b.height = centerH - (m.top+m.bottom);
35631 var totalWidth = (b.width + m.left + m.right);
35632 b.x = w - totalWidth + m.left;
35633 b.y = centerY + m.top;
35634 centerW -= totalWidth;
35635 east.updateBox(this.safeBox(b));
35638 var m = center.getMargins();
35640 x: centerX + m.left,
35641 y: centerY + m.top,
35642 width: centerW - (m.left+m.right),
35643 height: centerH - (m.top+m.bottom)
35645 //if(this.hideOnLayout){
35646 //center.el.setStyle("display", "block");
35648 center.updateBox(this.safeBox(centerBox));
35651 this.fireEvent("layout", this);
35655 safeBox : function(box){
35656 box.width = Math.max(0, box.width);
35657 box.height = Math.max(0, box.height);
35662 * Adds a ContentPanel (or subclass) to this layout.
35663 * @param {String} target The target region key (north, south, east, west or center).
35664 * @param {Roo.ContentPanel} panel The panel to add
35665 * @return {Roo.ContentPanel} The added panel
35667 add : function(target, panel){
35669 target = target.toLowerCase();
35670 return this.regions[target].add(panel);
35674 * Remove a ContentPanel (or subclass) to this layout.
35675 * @param {String} target The target region key (north, south, east, west or center).
35676 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35677 * @return {Roo.ContentPanel} The removed panel
35679 remove : function(target, panel){
35680 target = target.toLowerCase();
35681 return this.regions[target].remove(panel);
35685 * Searches all regions for a panel with the specified id
35686 * @param {String} panelId
35687 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35689 findPanel : function(panelId){
35690 var rs = this.regions;
35691 for(var target in rs){
35692 if(typeof rs[target] != "function"){
35693 var p = rs[target].getPanel(panelId);
35703 * Searches all regions for a panel with the specified id and activates (shows) it.
35704 * @param {String/ContentPanel} panelId The panels id or the panel itself
35705 * @return {Roo.ContentPanel} The shown panel or null
35707 showPanel : function(panelId) {
35708 var rs = this.regions;
35709 for(var target in rs){
35710 var r = rs[target];
35711 if(typeof r != "function"){
35712 if(r.hasPanel(panelId)){
35713 return r.showPanel(panelId);
35721 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35722 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35725 restoreState : function(provider){
35727 provider = Roo.state.Manager;
35729 var sm = new Roo.LayoutStateManager();
35730 sm.init(this, provider);
35736 * Adds a xtype elements to the layout.
35740 xtype : 'ContentPanel',
35747 xtype : 'NestedLayoutPanel',
35753 items : [ ... list of content panels or nested layout panels.. ]
35757 * @param {Object} cfg Xtype definition of item to add.
35759 addxtype : function(cfg)
35761 // basically accepts a pannel...
35762 // can accept a layout region..!?!?
35763 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35766 // theory? children can only be panels??
35768 //if (!cfg.xtype.match(/Panel$/)) {
35773 if (typeof(cfg.region) == 'undefined') {
35774 Roo.log("Failed to add Panel, region was not set");
35778 var region = cfg.region;
35784 xitems = cfg.items;
35789 if ( region == 'center') {
35790 Roo.log("Center: " + cfg.title);
35796 case 'Content': // ContentPanel (el, cfg)
35797 case 'Scroll': // ContentPanel (el, cfg)
35799 cfg.autoCreate = cfg.autoCreate || true;
35800 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35802 // var el = this.el.createChild();
35803 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35806 this.add(region, ret);
35810 case 'TreePanel': // our new panel!
35811 cfg.el = this.el.createChild();
35812 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35813 this.add(region, ret);
35818 // create a new Layout (which is a Border Layout...
35820 var clayout = cfg.layout;
35821 clayout.el = this.el.createChild();
35822 clayout.items = clayout.items || [];
35826 // replace this exitems with the clayout ones..
35827 xitems = clayout.items;
35829 // force background off if it's in center...
35830 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35831 cfg.background = false;
35833 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35836 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35837 //console.log('adding nested layout panel ' + cfg.toSource());
35838 this.add(region, ret);
35839 nb = {}; /// find first...
35844 // needs grid and region
35846 //var el = this.getRegion(region).el.createChild();
35848 *var el = this.el.createChild();
35849 // create the grid first...
35850 cfg.grid.container = el;
35851 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35854 if (region == 'center' && this.active ) {
35855 cfg.background = false;
35858 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35860 this.add(region, ret);
35862 if (cfg.background) {
35863 // render grid on panel activation (if panel background)
35864 ret.on('activate', function(gp) {
35865 if (!gp.grid.rendered) {
35866 // gp.grid.render(el);
35870 // cfg.grid.render(el);
35876 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35877 // it was the old xcomponent building that caused this before.
35878 // espeically if border is the top element in the tree.
35888 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35890 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35891 this.add(region, ret);
35895 throw "Can not add '" + cfg.xtype + "' to Border";
35901 this.beginUpdate();
35905 Roo.each(xitems, function(i) {
35906 region = nb && i.region ? i.region : false;
35908 var add = ret.addxtype(i);
35911 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35912 if (!i.background) {
35913 abn[region] = nb[region] ;
35920 // make the last non-background panel active..
35921 //if (nb) { Roo.log(abn); }
35924 for(var r in abn) {
35925 region = this.getRegion(r);
35927 // tried using nb[r], but it does not work..
35929 region.showPanel(abn[r]);
35940 factory : function(cfg)
35943 var validRegions = Roo.bootstrap.layout.Border.regions;
35945 var target = cfg.region;
35948 var r = Roo.bootstrap.layout;
35952 return new r.North(cfg);
35954 return new r.South(cfg);
35956 return new r.East(cfg);
35958 return new r.West(cfg);
35960 return new r.Center(cfg);
35962 throw 'Layout region "'+target+'" not supported.';
35969 * Ext JS Library 1.1.1
35970 * Copyright(c) 2006-2007, Ext JS, LLC.
35972 * Originally Released Under LGPL - original licence link has changed is not relivant.
35975 * <script type="text/javascript">
35979 * @class Roo.bootstrap.layout.Basic
35980 * @extends Roo.util.Observable
35981 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35982 * and does not have a titlebar, tabs or any other features. All it does is size and position
35983 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35984 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35985 * @cfg {string} region the region that it inhabits..
35986 * @cfg {bool} skipConfig skip config?
35990 Roo.bootstrap.layout.Basic = function(config){
35992 this.mgr = config.mgr;
35994 this.position = config.region;
35996 var skipConfig = config.skipConfig;
36000 * @scope Roo.BasicLayoutRegion
36004 * @event beforeremove
36005 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
36006 * @param {Roo.LayoutRegion} this
36007 * @param {Roo.ContentPanel} panel The panel
36008 * @param {Object} e The cancel event object
36010 "beforeremove" : true,
36012 * @event invalidated
36013 * Fires when the layout for this region is changed.
36014 * @param {Roo.LayoutRegion} this
36016 "invalidated" : true,
36018 * @event visibilitychange
36019 * Fires when this region is shown or hidden
36020 * @param {Roo.LayoutRegion} this
36021 * @param {Boolean} visibility true or false
36023 "visibilitychange" : true,
36025 * @event paneladded
36026 * Fires when a panel is added.
36027 * @param {Roo.LayoutRegion} this
36028 * @param {Roo.ContentPanel} panel The panel
36030 "paneladded" : true,
36032 * @event panelremoved
36033 * Fires when a panel is removed.
36034 * @param {Roo.LayoutRegion} this
36035 * @param {Roo.ContentPanel} panel The panel
36037 "panelremoved" : true,
36039 * @event beforecollapse
36040 * Fires when this region before collapse.
36041 * @param {Roo.LayoutRegion} this
36043 "beforecollapse" : true,
36046 * Fires when this region is collapsed.
36047 * @param {Roo.LayoutRegion} this
36049 "collapsed" : true,
36052 * Fires when this region is expanded.
36053 * @param {Roo.LayoutRegion} this
36058 * Fires when this region is slid into view.
36059 * @param {Roo.LayoutRegion} this
36061 "slideshow" : true,
36064 * Fires when this region slides out of view.
36065 * @param {Roo.LayoutRegion} this
36067 "slidehide" : true,
36069 * @event panelactivated
36070 * Fires when a panel is activated.
36071 * @param {Roo.LayoutRegion} this
36072 * @param {Roo.ContentPanel} panel The activated panel
36074 "panelactivated" : true,
36077 * Fires when the user resizes this region.
36078 * @param {Roo.LayoutRegion} this
36079 * @param {Number} newSize The new size (width for east/west, height for north/south)
36083 /** A collection of panels in this region. @type Roo.util.MixedCollection */
36084 this.panels = new Roo.util.MixedCollection();
36085 this.panels.getKey = this.getPanelId.createDelegate(this);
36087 this.activePanel = null;
36088 // ensure listeners are added...
36090 if (config.listeners || config.events) {
36091 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
36092 listeners : config.listeners || {},
36093 events : config.events || {}
36097 if(skipConfig !== true){
36098 this.applyConfig(config);
36102 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
36104 getPanelId : function(p){
36108 applyConfig : function(config){
36109 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36110 this.config = config;
36115 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
36116 * the width, for horizontal (north, south) the height.
36117 * @param {Number} newSize The new width or height
36119 resizeTo : function(newSize){
36120 var el = this.el ? this.el :
36121 (this.activePanel ? this.activePanel.getEl() : null);
36123 switch(this.position){
36126 el.setWidth(newSize);
36127 this.fireEvent("resized", this, newSize);
36131 el.setHeight(newSize);
36132 this.fireEvent("resized", this, newSize);
36138 getBox : function(){
36139 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
36142 getMargins : function(){
36143 return this.margins;
36146 updateBox : function(box){
36148 var el = this.activePanel.getEl();
36149 el.dom.style.left = box.x + "px";
36150 el.dom.style.top = box.y + "px";
36151 this.activePanel.setSize(box.width, box.height);
36155 * Returns the container element for this region.
36156 * @return {Roo.Element}
36158 getEl : function(){
36159 return this.activePanel;
36163 * Returns true if this region is currently visible.
36164 * @return {Boolean}
36166 isVisible : function(){
36167 return this.activePanel ? true : false;
36170 setActivePanel : function(panel){
36171 panel = this.getPanel(panel);
36172 if(this.activePanel && this.activePanel != panel){
36173 this.activePanel.setActiveState(false);
36174 this.activePanel.getEl().setLeftTop(-10000,-10000);
36176 this.activePanel = panel;
36177 panel.setActiveState(true);
36179 panel.setSize(this.box.width, this.box.height);
36181 this.fireEvent("panelactivated", this, panel);
36182 this.fireEvent("invalidated");
36186 * Show the specified panel.
36187 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
36188 * @return {Roo.ContentPanel} The shown panel or null
36190 showPanel : function(panel){
36191 panel = this.getPanel(panel);
36193 this.setActivePanel(panel);
36199 * Get the active panel for this region.
36200 * @return {Roo.ContentPanel} The active panel or null
36202 getActivePanel : function(){
36203 return this.activePanel;
36207 * Add the passed ContentPanel(s)
36208 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36209 * @return {Roo.ContentPanel} The panel added (if only one was added)
36211 add : function(panel){
36212 if(arguments.length > 1){
36213 for(var i = 0, len = arguments.length; i < len; i++) {
36214 this.add(arguments[i]);
36218 if(this.hasPanel(panel)){
36219 this.showPanel(panel);
36222 var el = panel.getEl();
36223 if(el.dom.parentNode != this.mgr.el.dom){
36224 this.mgr.el.dom.appendChild(el.dom);
36226 if(panel.setRegion){
36227 panel.setRegion(this);
36229 this.panels.add(panel);
36230 el.setStyle("position", "absolute");
36231 if(!panel.background){
36232 this.setActivePanel(panel);
36233 if(this.config.initialSize && this.panels.getCount()==1){
36234 this.resizeTo(this.config.initialSize);
36237 this.fireEvent("paneladded", this, panel);
36242 * Returns true if the panel is in this region.
36243 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36244 * @return {Boolean}
36246 hasPanel : function(panel){
36247 if(typeof panel == "object"){ // must be panel obj
36248 panel = panel.getId();
36250 return this.getPanel(panel) ? true : false;
36254 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36255 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36256 * @param {Boolean} preservePanel Overrides the config preservePanel option
36257 * @return {Roo.ContentPanel} The panel that was removed
36259 remove : function(panel, preservePanel){
36260 panel = this.getPanel(panel);
36265 this.fireEvent("beforeremove", this, panel, e);
36266 if(e.cancel === true){
36269 var panelId = panel.getId();
36270 this.panels.removeKey(panelId);
36275 * Returns the panel specified or null if it's not in this region.
36276 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36277 * @return {Roo.ContentPanel}
36279 getPanel : function(id){
36280 if(typeof id == "object"){ // must be panel obj
36283 return this.panels.get(id);
36287 * Returns this regions position (north/south/east/west/center).
36290 getPosition: function(){
36291 return this.position;
36295 * Ext JS Library 1.1.1
36296 * Copyright(c) 2006-2007, Ext JS, LLC.
36298 * Originally Released Under LGPL - original licence link has changed is not relivant.
36301 * <script type="text/javascript">
36305 * @class Roo.bootstrap.layout.Region
36306 * @extends Roo.bootstrap.layout.Basic
36307 * This class represents a region in a layout manager.
36309 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36310 * @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})
36311 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
36312 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
36313 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
36314 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
36315 * @cfg {String} title The title for the region (overrides panel titles)
36316 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
36317 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36318 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
36319 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36320 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
36321 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36322 * the space available, similar to FireFox 1.5 tabs (defaults to false)
36323 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
36324 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
36325 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
36327 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
36328 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
36329 * @cfg {Boolean} disableTabTips True to disable tab tooltips
36330 * @cfg {Number} width For East/West panels
36331 * @cfg {Number} height For North/South panels
36332 * @cfg {Boolean} split To show the splitter
36333 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
36335 * @cfg {string} cls Extra CSS classes to add to region
36337 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
36338 * @cfg {string} region the region that it inhabits..
36341 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
36342 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
36344 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
36345 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
36346 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
36348 Roo.bootstrap.layout.Region = function(config)
36350 this.applyConfig(config);
36352 var mgr = config.mgr;
36353 var pos = config.region;
36354 config.skipConfig = true;
36355 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36358 this.onRender(mgr.el);
36361 this.visible = true;
36362 this.collapsed = false;
36363 this.unrendered_panels = [];
36366 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36368 position: '', // set by wrapper (eg. north/south etc..)
36369 unrendered_panels : null, // unrendered panels.
36371 tabPosition : false,
36373 mgr: false, // points to 'Border'
36376 createBody : function(){
36377 /** This region's body element
36378 * @type Roo.Element */
36379 this.bodyEl = this.el.createChild({
36381 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36385 onRender: function(ctr, pos)
36387 var dh = Roo.DomHelper;
36388 /** This region's container element
36389 * @type Roo.Element */
36390 this.el = dh.append(ctr.dom, {
36392 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36394 /** This region's title element
36395 * @type Roo.Element */
36397 this.titleEl = dh.append(this.el.dom, {
36399 unselectable: "on",
36400 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36402 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
36403 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36407 this.titleEl.enableDisplayMode();
36408 /** This region's title text element
36409 * @type HTMLElement */
36410 this.titleTextEl = this.titleEl.dom.firstChild;
36411 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36413 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36414 this.closeBtn.enableDisplayMode();
36415 this.closeBtn.on("click", this.closeClicked, this);
36416 this.closeBtn.hide();
36418 this.createBody(this.config);
36419 if(this.config.hideWhenEmpty){
36421 this.on("paneladded", this.validateVisibility, this);
36422 this.on("panelremoved", this.validateVisibility, this);
36424 if(this.autoScroll){
36425 this.bodyEl.setStyle("overflow", "auto");
36427 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36429 //if(c.titlebar !== false){
36430 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36431 this.titleEl.hide();
36433 this.titleEl.show();
36434 if(this.config.title){
36435 this.titleTextEl.innerHTML = this.config.title;
36439 if(this.config.collapsed){
36440 this.collapse(true);
36442 if(this.config.hidden){
36446 if (this.unrendered_panels && this.unrendered_panels.length) {
36447 for (var i =0;i< this.unrendered_panels.length; i++) {
36448 this.add(this.unrendered_panels[i]);
36450 this.unrendered_panels = null;
36456 applyConfig : function(c)
36459 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36460 var dh = Roo.DomHelper;
36461 if(c.titlebar !== false){
36462 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36463 this.collapseBtn.on("click", this.collapse, this);
36464 this.collapseBtn.enableDisplayMode();
36466 if(c.showPin === true || this.showPin){
36467 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36468 this.stickBtn.enableDisplayMode();
36469 this.stickBtn.on("click", this.expand, this);
36470 this.stickBtn.hide();
36475 /** This region's collapsed element
36476 * @type Roo.Element */
36479 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36480 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36483 if(c.floatable !== false){
36484 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36485 this.collapsedEl.on("click", this.collapseClick, this);
36488 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36489 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36490 id: "message", unselectable: "on", style:{"float":"left"}});
36491 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36493 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36494 this.expandBtn.on("click", this.expand, this);
36498 if(this.collapseBtn){
36499 this.collapseBtn.setVisible(c.collapsible == true);
36502 this.cmargins = c.cmargins || this.cmargins ||
36503 (this.position == "west" || this.position == "east" ?
36504 {top: 0, left: 2, right:2, bottom: 0} :
36505 {top: 2, left: 0, right:0, bottom: 2});
36507 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36510 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36512 this.autoScroll = c.autoScroll || false;
36517 this.duration = c.duration || .30;
36518 this.slideDuration = c.slideDuration || .45;
36523 * Returns true if this region is currently visible.
36524 * @return {Boolean}
36526 isVisible : function(){
36527 return this.visible;
36531 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36532 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
36534 //setCollapsedTitle : function(title){
36535 // title = title || " ";
36536 // if(this.collapsedTitleTextEl){
36537 // this.collapsedTitleTextEl.innerHTML = title;
36541 getBox : function(){
36543 // if(!this.collapsed){
36544 b = this.el.getBox(false, true);
36546 // b = this.collapsedEl.getBox(false, true);
36551 getMargins : function(){
36552 return this.margins;
36553 //return this.collapsed ? this.cmargins : this.margins;
36556 highlight : function(){
36557 this.el.addClass("x-layout-panel-dragover");
36560 unhighlight : function(){
36561 this.el.removeClass("x-layout-panel-dragover");
36564 updateBox : function(box)
36566 if (!this.bodyEl) {
36567 return; // not rendered yet..
36571 if(!this.collapsed){
36572 this.el.dom.style.left = box.x + "px";
36573 this.el.dom.style.top = box.y + "px";
36574 this.updateBody(box.width, box.height);
36576 this.collapsedEl.dom.style.left = box.x + "px";
36577 this.collapsedEl.dom.style.top = box.y + "px";
36578 this.collapsedEl.setSize(box.width, box.height);
36581 this.tabs.autoSizeTabs();
36585 updateBody : function(w, h)
36588 this.el.setWidth(w);
36589 w -= this.el.getBorderWidth("rl");
36590 if(this.config.adjustments){
36591 w += this.config.adjustments[0];
36594 if(h !== null && h > 0){
36595 this.el.setHeight(h);
36596 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36597 h -= this.el.getBorderWidth("tb");
36598 if(this.config.adjustments){
36599 h += this.config.adjustments[1];
36601 this.bodyEl.setHeight(h);
36603 h = this.tabs.syncHeight(h);
36606 if(this.panelSize){
36607 w = w !== null ? w : this.panelSize.width;
36608 h = h !== null ? h : this.panelSize.height;
36610 if(this.activePanel){
36611 var el = this.activePanel.getEl();
36612 w = w !== null ? w : el.getWidth();
36613 h = h !== null ? h : el.getHeight();
36614 this.panelSize = {width: w, height: h};
36615 this.activePanel.setSize(w, h);
36617 if(Roo.isIE && this.tabs){
36618 this.tabs.el.repaint();
36623 * Returns the container element for this region.
36624 * @return {Roo.Element}
36626 getEl : function(){
36631 * Hides this region.
36634 //if(!this.collapsed){
36635 this.el.dom.style.left = "-2000px";
36638 // this.collapsedEl.dom.style.left = "-2000px";
36639 // this.collapsedEl.hide();
36641 this.visible = false;
36642 this.fireEvent("visibilitychange", this, false);
36646 * Shows this region if it was previously hidden.
36649 //if(!this.collapsed){
36652 // this.collapsedEl.show();
36654 this.visible = true;
36655 this.fireEvent("visibilitychange", this, true);
36658 closeClicked : function(){
36659 if(this.activePanel){
36660 this.remove(this.activePanel);
36664 collapseClick : function(e){
36666 e.stopPropagation();
36669 e.stopPropagation();
36675 * Collapses this region.
36676 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36679 collapse : function(skipAnim, skipCheck = false){
36680 if(this.collapsed) {
36684 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36686 this.collapsed = true;
36688 this.split.el.hide();
36690 if(this.config.animate && skipAnim !== true){
36691 this.fireEvent("invalidated", this);
36692 this.animateCollapse();
36694 this.el.setLocation(-20000,-20000);
36696 this.collapsedEl.show();
36697 this.fireEvent("collapsed", this);
36698 this.fireEvent("invalidated", this);
36704 animateCollapse : function(){
36709 * Expands this region if it was previously collapsed.
36710 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36711 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36714 expand : function(e, skipAnim){
36716 e.stopPropagation();
36718 if(!this.collapsed || this.el.hasActiveFx()) {
36722 this.afterSlideIn();
36725 this.collapsed = false;
36726 if(this.config.animate && skipAnim !== true){
36727 this.animateExpand();
36731 this.split.el.show();
36733 this.collapsedEl.setLocation(-2000,-2000);
36734 this.collapsedEl.hide();
36735 this.fireEvent("invalidated", this);
36736 this.fireEvent("expanded", this);
36740 animateExpand : function(){
36744 initTabs : function()
36746 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36748 var ts = new Roo.bootstrap.panel.Tabs({
36749 el: this.bodyEl.dom,
36751 tabPosition: this.tabPosition ? this.tabPosition : 'top',
36752 disableTooltips: this.config.disableTabTips,
36753 toolbar : this.config.toolbar
36756 if(this.config.hideTabs){
36757 ts.stripWrap.setDisplayed(false);
36760 ts.resizeTabs = this.config.resizeTabs === true;
36761 ts.minTabWidth = this.config.minTabWidth || 40;
36762 ts.maxTabWidth = this.config.maxTabWidth || 250;
36763 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36764 ts.monitorResize = false;
36765 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36766 ts.bodyEl.addClass('roo-layout-tabs-body');
36767 this.panels.each(this.initPanelAsTab, this);
36770 initPanelAsTab : function(panel){
36771 var ti = this.tabs.addTab(
36775 this.config.closeOnTab && panel.isClosable(),
36778 if(panel.tabTip !== undefined){
36779 ti.setTooltip(panel.tabTip);
36781 ti.on("activate", function(){
36782 this.setActivePanel(panel);
36785 if(this.config.closeOnTab){
36786 ti.on("beforeclose", function(t, e){
36788 this.remove(panel);
36792 panel.tabItem = ti;
36797 updatePanelTitle : function(panel, title)
36799 if(this.activePanel == panel){
36800 this.updateTitle(title);
36803 var ti = this.tabs.getTab(panel.getEl().id);
36805 if(panel.tabTip !== undefined){
36806 ti.setTooltip(panel.tabTip);
36811 updateTitle : function(title){
36812 if(this.titleTextEl && !this.config.title){
36813 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36817 setActivePanel : function(panel)
36819 panel = this.getPanel(panel);
36820 if(this.activePanel && this.activePanel != panel){
36821 if(this.activePanel.setActiveState(false) === false){
36825 this.activePanel = panel;
36826 panel.setActiveState(true);
36827 if(this.panelSize){
36828 panel.setSize(this.panelSize.width, this.panelSize.height);
36831 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36833 this.updateTitle(panel.getTitle());
36835 this.fireEvent("invalidated", this);
36837 this.fireEvent("panelactivated", this, panel);
36841 * Shows the specified panel.
36842 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36843 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36845 showPanel : function(panel)
36847 panel = this.getPanel(panel);
36850 var tab = this.tabs.getTab(panel.getEl().id);
36851 if(tab.isHidden()){
36852 this.tabs.unhideTab(tab.id);
36856 this.setActivePanel(panel);
36863 * Get the active panel for this region.
36864 * @return {Roo.ContentPanel} The active panel or null
36866 getActivePanel : function(){
36867 return this.activePanel;
36870 validateVisibility : function(){
36871 if(this.panels.getCount() < 1){
36872 this.updateTitle(" ");
36873 this.closeBtn.hide();
36876 if(!this.isVisible()){
36883 * Adds the passed ContentPanel(s) to this region.
36884 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36885 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36887 add : function(panel)
36889 if(arguments.length > 1){
36890 for(var i = 0, len = arguments.length; i < len; i++) {
36891 this.add(arguments[i]);
36896 // if we have not been rendered yet, then we can not really do much of this..
36897 if (!this.bodyEl) {
36898 this.unrendered_panels.push(panel);
36905 if(this.hasPanel(panel)){
36906 this.showPanel(panel);
36909 panel.setRegion(this);
36910 this.panels.add(panel);
36911 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36912 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36913 // and hide them... ???
36914 this.bodyEl.dom.appendChild(panel.getEl().dom);
36915 if(panel.background !== true){
36916 this.setActivePanel(panel);
36918 this.fireEvent("paneladded", this, panel);
36925 this.initPanelAsTab(panel);
36929 if(panel.background !== true){
36930 this.tabs.activate(panel.getEl().id);
36932 this.fireEvent("paneladded", this, panel);
36937 * Hides the tab for the specified panel.
36938 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36940 hidePanel : function(panel){
36941 if(this.tabs && (panel = this.getPanel(panel))){
36942 this.tabs.hideTab(panel.getEl().id);
36947 * Unhides the tab for a previously hidden panel.
36948 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36950 unhidePanel : function(panel){
36951 if(this.tabs && (panel = this.getPanel(panel))){
36952 this.tabs.unhideTab(panel.getEl().id);
36956 clearPanels : function(){
36957 while(this.panels.getCount() > 0){
36958 this.remove(this.panels.first());
36963 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36964 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36965 * @param {Boolean} preservePanel Overrides the config preservePanel option
36966 * @return {Roo.ContentPanel} The panel that was removed
36968 remove : function(panel, preservePanel)
36970 panel = this.getPanel(panel);
36975 this.fireEvent("beforeremove", this, panel, e);
36976 if(e.cancel === true){
36979 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36980 var panelId = panel.getId();
36981 this.panels.removeKey(panelId);
36983 document.body.appendChild(panel.getEl().dom);
36986 this.tabs.removeTab(panel.getEl().id);
36987 }else if (!preservePanel){
36988 this.bodyEl.dom.removeChild(panel.getEl().dom);
36990 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36991 var p = this.panels.first();
36992 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36993 tempEl.appendChild(p.getEl().dom);
36994 this.bodyEl.update("");
36995 this.bodyEl.dom.appendChild(p.getEl().dom);
36997 this.updateTitle(p.getTitle());
36999 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
37000 this.setActivePanel(p);
37002 panel.setRegion(null);
37003 if(this.activePanel == panel){
37004 this.activePanel = null;
37006 if(this.config.autoDestroy !== false && preservePanel !== true){
37007 try{panel.destroy();}catch(e){}
37009 this.fireEvent("panelremoved", this, panel);
37014 * Returns the TabPanel component used by this region
37015 * @return {Roo.TabPanel}
37017 getTabs : function(){
37021 createTool : function(parentEl, className){
37022 var btn = Roo.DomHelper.append(parentEl, {
37024 cls: "x-layout-tools-button",
37027 cls: "roo-layout-tools-button-inner " + className,
37031 btn.addClassOnOver("roo-layout-tools-button-over");
37036 * Ext JS Library 1.1.1
37037 * Copyright(c) 2006-2007, Ext JS, LLC.
37039 * Originally Released Under LGPL - original licence link has changed is not relivant.
37042 * <script type="text/javascript">
37048 * @class Roo.SplitLayoutRegion
37049 * @extends Roo.LayoutRegion
37050 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
37052 Roo.bootstrap.layout.Split = function(config){
37053 this.cursor = config.cursor;
37054 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
37057 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
37059 splitTip : "Drag to resize.",
37060 collapsibleSplitTip : "Drag to resize. Double click to hide.",
37061 useSplitTips : false,
37063 applyConfig : function(config){
37064 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
37067 onRender : function(ctr,pos) {
37069 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
37070 if(!this.config.split){
37075 var splitEl = Roo.DomHelper.append(ctr.dom, {
37077 id: this.el.id + "-split",
37078 cls: "roo-layout-split roo-layout-split-"+this.position,
37081 /** The SplitBar for this region
37082 * @type Roo.SplitBar */
37083 // does not exist yet...
37084 Roo.log([this.position, this.orientation]);
37086 this.split = new Roo.bootstrap.SplitBar({
37087 dragElement : splitEl,
37088 resizingElement: this.el,
37089 orientation : this.orientation
37092 this.split.on("moved", this.onSplitMove, this);
37093 this.split.useShim = this.config.useShim === true;
37094 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
37095 if(this.useSplitTips){
37096 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
37098 //if(config.collapsible){
37099 // this.split.el.on("dblclick", this.collapse, this);
37102 if(typeof this.config.minSize != "undefined"){
37103 this.split.minSize = this.config.minSize;
37105 if(typeof this.config.maxSize != "undefined"){
37106 this.split.maxSize = this.config.maxSize;
37108 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
37109 this.hideSplitter();
37114 getHMaxSize : function(){
37115 var cmax = this.config.maxSize || 10000;
37116 var center = this.mgr.getRegion("center");
37117 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
37120 getVMaxSize : function(){
37121 var cmax = this.config.maxSize || 10000;
37122 var center = this.mgr.getRegion("center");
37123 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
37126 onSplitMove : function(split, newSize){
37127 this.fireEvent("resized", this, newSize);
37131 * Returns the {@link Roo.SplitBar} for this region.
37132 * @return {Roo.SplitBar}
37134 getSplitBar : function(){
37139 this.hideSplitter();
37140 Roo.bootstrap.layout.Split.superclass.hide.call(this);
37143 hideSplitter : function(){
37145 this.split.el.setLocation(-2000,-2000);
37146 this.split.el.hide();
37152 this.split.el.show();
37154 Roo.bootstrap.layout.Split.superclass.show.call(this);
37157 beforeSlide: function(){
37158 if(Roo.isGecko){// firefox overflow auto bug workaround
37159 this.bodyEl.clip();
37161 this.tabs.bodyEl.clip();
37163 if(this.activePanel){
37164 this.activePanel.getEl().clip();
37166 if(this.activePanel.beforeSlide){
37167 this.activePanel.beforeSlide();
37173 afterSlide : function(){
37174 if(Roo.isGecko){// firefox overflow auto bug workaround
37175 this.bodyEl.unclip();
37177 this.tabs.bodyEl.unclip();
37179 if(this.activePanel){
37180 this.activePanel.getEl().unclip();
37181 if(this.activePanel.afterSlide){
37182 this.activePanel.afterSlide();
37188 initAutoHide : function(){
37189 if(this.autoHide !== false){
37190 if(!this.autoHideHd){
37191 var st = new Roo.util.DelayedTask(this.slideIn, this);
37192 this.autoHideHd = {
37193 "mouseout": function(e){
37194 if(!e.within(this.el, true)){
37198 "mouseover" : function(e){
37204 this.el.on(this.autoHideHd);
37208 clearAutoHide : function(){
37209 if(this.autoHide !== false){
37210 this.el.un("mouseout", this.autoHideHd.mouseout);
37211 this.el.un("mouseover", this.autoHideHd.mouseover);
37215 clearMonitor : function(){
37216 Roo.get(document).un("click", this.slideInIf, this);
37219 // these names are backwards but not changed for compat
37220 slideOut : function(){
37221 if(this.isSlid || this.el.hasActiveFx()){
37224 this.isSlid = true;
37225 if(this.collapseBtn){
37226 this.collapseBtn.hide();
37228 this.closeBtnState = this.closeBtn.getStyle('display');
37229 this.closeBtn.hide();
37231 this.stickBtn.show();
37234 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37235 this.beforeSlide();
37236 this.el.setStyle("z-index", 10001);
37237 this.el.slideIn(this.getSlideAnchor(), {
37238 callback: function(){
37240 this.initAutoHide();
37241 Roo.get(document).on("click", this.slideInIf, this);
37242 this.fireEvent("slideshow", this);
37249 afterSlideIn : function(){
37250 this.clearAutoHide();
37251 this.isSlid = false;
37252 this.clearMonitor();
37253 this.el.setStyle("z-index", "");
37254 if(this.collapseBtn){
37255 this.collapseBtn.show();
37257 this.closeBtn.setStyle('display', this.closeBtnState);
37259 this.stickBtn.hide();
37261 this.fireEvent("slidehide", this);
37264 slideIn : function(cb){
37265 if(!this.isSlid || this.el.hasActiveFx()){
37269 this.isSlid = false;
37270 this.beforeSlide();
37271 this.el.slideOut(this.getSlideAnchor(), {
37272 callback: function(){
37273 this.el.setLeftTop(-10000, -10000);
37275 this.afterSlideIn();
37283 slideInIf : function(e){
37284 if(!e.within(this.el)){
37289 animateCollapse : function(){
37290 this.beforeSlide();
37291 this.el.setStyle("z-index", 20000);
37292 var anchor = this.getSlideAnchor();
37293 this.el.slideOut(anchor, {
37294 callback : function(){
37295 this.el.setStyle("z-index", "");
37296 this.collapsedEl.slideIn(anchor, {duration:.3});
37298 this.el.setLocation(-10000,-10000);
37300 this.fireEvent("collapsed", this);
37307 animateExpand : function(){
37308 this.beforeSlide();
37309 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37310 this.el.setStyle("z-index", 20000);
37311 this.collapsedEl.hide({
37314 this.el.slideIn(this.getSlideAnchor(), {
37315 callback : function(){
37316 this.el.setStyle("z-index", "");
37319 this.split.el.show();
37321 this.fireEvent("invalidated", this);
37322 this.fireEvent("expanded", this);
37350 getAnchor : function(){
37351 return this.anchors[this.position];
37354 getCollapseAnchor : function(){
37355 return this.canchors[this.position];
37358 getSlideAnchor : function(){
37359 return this.sanchors[this.position];
37362 getAlignAdj : function(){
37363 var cm = this.cmargins;
37364 switch(this.position){
37380 getExpandAdj : function(){
37381 var c = this.collapsedEl, cm = this.cmargins;
37382 switch(this.position){
37384 return [-(cm.right+c.getWidth()+cm.left), 0];
37387 return [cm.right+c.getWidth()+cm.left, 0];
37390 return [0, -(cm.top+cm.bottom+c.getHeight())];
37393 return [0, cm.top+cm.bottom+c.getHeight()];
37399 * Ext JS Library 1.1.1
37400 * Copyright(c) 2006-2007, Ext JS, LLC.
37402 * Originally Released Under LGPL - original licence link has changed is not relivant.
37405 * <script type="text/javascript">
37408 * These classes are private internal classes
37410 Roo.bootstrap.layout.Center = function(config){
37411 config.region = "center";
37412 Roo.bootstrap.layout.Region.call(this, config);
37413 this.visible = true;
37414 this.minWidth = config.minWidth || 20;
37415 this.minHeight = config.minHeight || 20;
37418 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37420 // center panel can't be hidden
37424 // center panel can't be hidden
37427 getMinWidth: function(){
37428 return this.minWidth;
37431 getMinHeight: function(){
37432 return this.minHeight;
37446 Roo.bootstrap.layout.North = function(config)
37448 config.region = 'north';
37449 config.cursor = 'n-resize';
37451 Roo.bootstrap.layout.Split.call(this, config);
37455 this.split.placement = Roo.bootstrap.SplitBar.TOP;
37456 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37457 this.split.el.addClass("roo-layout-split-v");
37459 var size = config.initialSize || config.height;
37460 if(typeof size != "undefined"){
37461 this.el.setHeight(size);
37464 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37466 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37470 getBox : function(){
37471 if(this.collapsed){
37472 return this.collapsedEl.getBox();
37474 var box = this.el.getBox();
37476 box.height += this.split.el.getHeight();
37481 updateBox : function(box){
37482 if(this.split && !this.collapsed){
37483 box.height -= this.split.el.getHeight();
37484 this.split.el.setLeft(box.x);
37485 this.split.el.setTop(box.y+box.height);
37486 this.split.el.setWidth(box.width);
37488 if(this.collapsed){
37489 this.updateBody(box.width, null);
37491 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37499 Roo.bootstrap.layout.South = function(config){
37500 config.region = 'south';
37501 config.cursor = 's-resize';
37502 Roo.bootstrap.layout.Split.call(this, config);
37504 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37505 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37506 this.split.el.addClass("roo-layout-split-v");
37508 var size = config.initialSize || config.height;
37509 if(typeof size != "undefined"){
37510 this.el.setHeight(size);
37514 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37515 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37516 getBox : function(){
37517 if(this.collapsed){
37518 return this.collapsedEl.getBox();
37520 var box = this.el.getBox();
37522 var sh = this.split.el.getHeight();
37529 updateBox : function(box){
37530 if(this.split && !this.collapsed){
37531 var sh = this.split.el.getHeight();
37534 this.split.el.setLeft(box.x);
37535 this.split.el.setTop(box.y-sh);
37536 this.split.el.setWidth(box.width);
37538 if(this.collapsed){
37539 this.updateBody(box.width, null);
37541 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37545 Roo.bootstrap.layout.East = function(config){
37546 config.region = "east";
37547 config.cursor = "e-resize";
37548 Roo.bootstrap.layout.Split.call(this, config);
37550 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37551 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37552 this.split.el.addClass("roo-layout-split-h");
37554 var size = config.initialSize || config.width;
37555 if(typeof size != "undefined"){
37556 this.el.setWidth(size);
37559 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37560 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37561 getBox : function(){
37562 if(this.collapsed){
37563 return this.collapsedEl.getBox();
37565 var box = this.el.getBox();
37567 var sw = this.split.el.getWidth();
37574 updateBox : function(box){
37575 if(this.split && !this.collapsed){
37576 var sw = this.split.el.getWidth();
37578 this.split.el.setLeft(box.x);
37579 this.split.el.setTop(box.y);
37580 this.split.el.setHeight(box.height);
37583 if(this.collapsed){
37584 this.updateBody(null, box.height);
37586 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37590 Roo.bootstrap.layout.West = function(config){
37591 config.region = "west";
37592 config.cursor = "w-resize";
37594 Roo.bootstrap.layout.Split.call(this, config);
37596 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37597 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37598 this.split.el.addClass("roo-layout-split-h");
37602 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37603 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37605 onRender: function(ctr, pos)
37607 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37608 var size = this.config.initialSize || this.config.width;
37609 if(typeof size != "undefined"){
37610 this.el.setWidth(size);
37614 getBox : function(){
37615 if(this.collapsed){
37616 return this.collapsedEl.getBox();
37618 var box = this.el.getBox();
37620 box.width += this.split.el.getWidth();
37625 updateBox : function(box){
37626 if(this.split && !this.collapsed){
37627 var sw = this.split.el.getWidth();
37629 this.split.el.setLeft(box.x+box.width);
37630 this.split.el.setTop(box.y);
37631 this.split.el.setHeight(box.height);
37633 if(this.collapsed){
37634 this.updateBody(null, box.height);
37636 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37638 });Roo.namespace("Roo.bootstrap.panel");/*
37640 * Ext JS Library 1.1.1
37641 * Copyright(c) 2006-2007, Ext JS, LLC.
37643 * Originally Released Under LGPL - original licence link has changed is not relivant.
37646 * <script type="text/javascript">
37649 * @class Roo.ContentPanel
37650 * @extends Roo.util.Observable
37651 * A basic ContentPanel element.
37652 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37653 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37654 * @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
37655 * @cfg {Boolean} closable True if the panel can be closed/removed
37656 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37657 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37658 * @cfg {Toolbar} toolbar A toolbar for this panel
37659 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37660 * @cfg {String} title The title for this panel
37661 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37662 * @cfg {String} url Calls {@link #setUrl} with this value
37663 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37664 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37665 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37666 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37667 * @cfg {Boolean} badges render the badges
37670 * Create a new ContentPanel.
37671 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37672 * @param {String/Object} config A string to set only the title or a config object
37673 * @param {String} content (optional) Set the HTML content for this panel
37674 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37676 Roo.bootstrap.panel.Content = function( config){
37678 this.tpl = config.tpl || false;
37680 var el = config.el;
37681 var content = config.content;
37683 if(config.autoCreate){ // xtype is available if this is called from factory
37686 this.el = Roo.get(el);
37687 if(!this.el && config && config.autoCreate){
37688 if(typeof config.autoCreate == "object"){
37689 if(!config.autoCreate.id){
37690 config.autoCreate.id = config.id||el;
37692 this.el = Roo.DomHelper.append(document.body,
37693 config.autoCreate, true);
37695 var elcfg = { tag: "div",
37696 cls: "roo-layout-inactive-content",
37700 elcfg.html = config.html;
37704 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37707 this.closable = false;
37708 this.loaded = false;
37709 this.active = false;
37712 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37714 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37716 this.wrapEl = this.el; //this.el.wrap();
37718 if (config.toolbar.items) {
37719 ti = config.toolbar.items ;
37720 delete config.toolbar.items ;
37724 this.toolbar.render(this.wrapEl, 'before');
37725 for(var i =0;i < ti.length;i++) {
37726 // Roo.log(['add child', items[i]]);
37727 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37729 this.toolbar.items = nitems;
37730 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37731 delete config.toolbar;
37735 // xtype created footer. - not sure if will work as we normally have to render first..
37736 if (this.footer && !this.footer.el && this.footer.xtype) {
37737 if (!this.wrapEl) {
37738 this.wrapEl = this.el.wrap();
37741 this.footer.container = this.wrapEl.createChild();
37743 this.footer = Roo.factory(this.footer, Roo);
37748 if(typeof config == "string"){
37749 this.title = config;
37751 Roo.apply(this, config);
37755 this.resizeEl = Roo.get(this.resizeEl, true);
37757 this.resizeEl = this.el;
37759 // handle view.xtype
37767 * Fires when this panel is activated.
37768 * @param {Roo.ContentPanel} this
37772 * @event deactivate
37773 * Fires when this panel is activated.
37774 * @param {Roo.ContentPanel} this
37776 "deactivate" : true,
37780 * Fires when this panel is resized if fitToFrame is true.
37781 * @param {Roo.ContentPanel} this
37782 * @param {Number} width The width after any component adjustments
37783 * @param {Number} height The height after any component adjustments
37789 * Fires when this tab is created
37790 * @param {Roo.ContentPanel} this
37801 if(this.autoScroll){
37802 this.resizeEl.setStyle("overflow", "auto");
37804 // fix randome scrolling
37805 //this.el.on('scroll', function() {
37806 // Roo.log('fix random scolling');
37807 // this.scrollTo('top',0);
37810 content = content || this.content;
37812 this.setContent(content);
37814 if(config && config.url){
37815 this.setUrl(this.url, this.params, this.loadOnce);
37820 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37822 if (this.view && typeof(this.view.xtype) != 'undefined') {
37823 this.view.el = this.el.appendChild(document.createElement("div"));
37824 this.view = Roo.factory(this.view);
37825 this.view.render && this.view.render(false, '');
37829 this.fireEvent('render', this);
37832 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37836 setRegion : function(region){
37837 this.region = region;
37838 this.setActiveClass(region && !this.background);
37842 setActiveClass: function(state)
37845 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37846 this.el.setStyle('position','relative');
37848 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37849 this.el.setStyle('position', 'absolute');
37854 * Returns the toolbar for this Panel if one was configured.
37855 * @return {Roo.Toolbar}
37857 getToolbar : function(){
37858 return this.toolbar;
37861 setActiveState : function(active)
37863 this.active = active;
37864 this.setActiveClass(active);
37866 if(this.fireEvent("deactivate", this) === false){
37871 this.fireEvent("activate", this);
37875 * Updates this panel's element
37876 * @param {String} content The new content
37877 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37879 setContent : function(content, loadScripts){
37880 this.el.update(content, loadScripts);
37883 ignoreResize : function(w, h){
37884 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37887 this.lastSize = {width: w, height: h};
37892 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37893 * @return {Roo.UpdateManager} The UpdateManager
37895 getUpdateManager : function(){
37896 return this.el.getUpdateManager();
37899 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37900 * @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:
37903 url: "your-url.php",
37904 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37905 callback: yourFunction,
37906 scope: yourObject, //(optional scope)
37909 text: "Loading...",
37914 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37915 * 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.
37916 * @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}
37917 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37918 * @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.
37919 * @return {Roo.ContentPanel} this
37922 var um = this.el.getUpdateManager();
37923 um.update.apply(um, arguments);
37929 * 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.
37930 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37931 * @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)
37932 * @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)
37933 * @return {Roo.UpdateManager} The UpdateManager
37935 setUrl : function(url, params, loadOnce){
37936 if(this.refreshDelegate){
37937 this.removeListener("activate", this.refreshDelegate);
37939 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37940 this.on("activate", this.refreshDelegate);
37941 return this.el.getUpdateManager();
37944 _handleRefresh : function(url, params, loadOnce){
37945 if(!loadOnce || !this.loaded){
37946 var updater = this.el.getUpdateManager();
37947 updater.update(url, params, this._setLoaded.createDelegate(this));
37951 _setLoaded : function(){
37952 this.loaded = true;
37956 * Returns this panel's id
37959 getId : function(){
37964 * Returns this panel's element - used by regiosn to add.
37965 * @return {Roo.Element}
37967 getEl : function(){
37968 return this.wrapEl || this.el;
37973 adjustForComponents : function(width, height)
37975 //Roo.log('adjustForComponents ');
37976 if(this.resizeEl != this.el){
37977 width -= this.el.getFrameWidth('lr');
37978 height -= this.el.getFrameWidth('tb');
37981 var te = this.toolbar.getEl();
37982 te.setWidth(width);
37983 height -= te.getHeight();
37986 var te = this.footer.getEl();
37987 te.setWidth(width);
37988 height -= te.getHeight();
37992 if(this.adjustments){
37993 width += this.adjustments[0];
37994 height += this.adjustments[1];
37996 return {"width": width, "height": height};
37999 setSize : function(width, height){
38000 if(this.fitToFrame && !this.ignoreResize(width, height)){
38001 if(this.fitContainer && this.resizeEl != this.el){
38002 this.el.setSize(width, height);
38004 var size = this.adjustForComponents(width, height);
38005 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
38006 this.fireEvent('resize', this, size.width, size.height);
38011 * Returns this panel's title
38014 getTitle : function(){
38016 if (typeof(this.title) != 'object') {
38021 for (var k in this.title) {
38022 if (!this.title.hasOwnProperty(k)) {
38026 if (k.indexOf('-') >= 0) {
38027 var s = k.split('-');
38028 for (var i = 0; i<s.length; i++) {
38029 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
38032 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
38039 * Set this panel's title
38040 * @param {String} title
38042 setTitle : function(title){
38043 this.title = title;
38045 this.region.updatePanelTitle(this, title);
38050 * Returns true is this panel was configured to be closable
38051 * @return {Boolean}
38053 isClosable : function(){
38054 return this.closable;
38057 beforeSlide : function(){
38059 this.resizeEl.clip();
38062 afterSlide : function(){
38064 this.resizeEl.unclip();
38068 * Force a content refresh from the URL specified in the {@link #setUrl} method.
38069 * Will fail silently if the {@link #setUrl} method has not been called.
38070 * This does not activate the panel, just updates its content.
38072 refresh : function(){
38073 if(this.refreshDelegate){
38074 this.loaded = false;
38075 this.refreshDelegate();
38080 * Destroys this panel
38082 destroy : function(){
38083 this.el.removeAllListeners();
38084 var tempEl = document.createElement("span");
38085 tempEl.appendChild(this.el.dom);
38086 tempEl.innerHTML = "";
38092 * form - if the content panel contains a form - this is a reference to it.
38093 * @type {Roo.form.Form}
38097 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
38098 * This contains a reference to it.
38104 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
38114 * @param {Object} cfg Xtype definition of item to add.
38118 getChildContainer: function () {
38119 return this.getEl();
38124 var ret = new Roo.factory(cfg);
38129 if (cfg.xtype.match(/^Form$/)) {
38132 //if (this.footer) {
38133 // el = this.footer.container.insertSibling(false, 'before');
38135 el = this.el.createChild();
38138 this.form = new Roo.form.Form(cfg);
38141 if ( this.form.allItems.length) {
38142 this.form.render(el.dom);
38146 // should only have one of theses..
38147 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
38148 // views.. should not be just added - used named prop 'view''
38150 cfg.el = this.el.appendChild(document.createElement("div"));
38153 var ret = new Roo.factory(cfg);
38155 ret.render && ret.render(false, ''); // render blank..
38165 * @class Roo.bootstrap.panel.Grid
38166 * @extends Roo.bootstrap.panel.Content
38168 * Create a new GridPanel.
38169 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
38170 * @param {Object} config A the config object
38176 Roo.bootstrap.panel.Grid = function(config)
38180 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
38181 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
38183 config.el = this.wrapper;
38184 //this.el = this.wrapper;
38186 if (config.container) {
38187 // ctor'ed from a Border/panel.grid
38190 this.wrapper.setStyle("overflow", "hidden");
38191 this.wrapper.addClass('roo-grid-container');
38196 if(config.toolbar){
38197 var tool_el = this.wrapper.createChild();
38198 this.toolbar = Roo.factory(config.toolbar);
38200 if (config.toolbar.items) {
38201 ti = config.toolbar.items ;
38202 delete config.toolbar.items ;
38206 this.toolbar.render(tool_el);
38207 for(var i =0;i < ti.length;i++) {
38208 // Roo.log(['add child', items[i]]);
38209 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38211 this.toolbar.items = nitems;
38213 delete config.toolbar;
38216 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
38217 config.grid.scrollBody = true;;
38218 config.grid.monitorWindowResize = false; // turn off autosizing
38219 config.grid.autoHeight = false;
38220 config.grid.autoWidth = false;
38222 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
38224 if (config.background) {
38225 // render grid on panel activation (if panel background)
38226 this.on('activate', function(gp) {
38227 if (!gp.grid.rendered) {
38228 gp.grid.render(this.wrapper);
38229 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
38234 this.grid.render(this.wrapper);
38235 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
38238 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
38239 // ??? needed ??? config.el = this.wrapper;
38244 // xtype created footer. - not sure if will work as we normally have to render first..
38245 if (this.footer && !this.footer.el && this.footer.xtype) {
38247 var ctr = this.grid.getView().getFooterPanel(true);
38248 this.footer.dataSource = this.grid.dataSource;
38249 this.footer = Roo.factory(this.footer, Roo);
38250 this.footer.render(ctr);
38260 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38261 getId : function(){
38262 return this.grid.id;
38266 * Returns the grid for this panel
38267 * @return {Roo.bootstrap.Table}
38269 getGrid : function(){
38273 setSize : function(width, height){
38274 if(!this.ignoreResize(width, height)){
38275 var grid = this.grid;
38276 var size = this.adjustForComponents(width, height);
38277 var gridel = grid.getGridEl();
38278 gridel.setSize(size.width, size.height);
38280 var thd = grid.getGridEl().select('thead',true).first();
38281 var tbd = grid.getGridEl().select('tbody', true).first();
38283 tbd.setSize(width, height - thd.getHeight());
38292 beforeSlide : function(){
38293 this.grid.getView().scroller.clip();
38296 afterSlide : function(){
38297 this.grid.getView().scroller.unclip();
38300 destroy : function(){
38301 this.grid.destroy();
38303 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
38308 * @class Roo.bootstrap.panel.Nest
38309 * @extends Roo.bootstrap.panel.Content
38311 * Create a new Panel, that can contain a layout.Border.
38314 * @param {Roo.BorderLayout} layout The layout for this panel
38315 * @param {String/Object} config A string to set only the title or a config object
38317 Roo.bootstrap.panel.Nest = function(config)
38319 // construct with only one argument..
38320 /* FIXME - implement nicer consturctors
38321 if (layout.layout) {
38323 layout = config.layout;
38324 delete config.layout;
38326 if (layout.xtype && !layout.getEl) {
38327 // then layout needs constructing..
38328 layout = Roo.factory(layout, Roo);
38332 config.el = config.layout.getEl();
38334 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38336 config.layout.monitorWindowResize = false; // turn off autosizing
38337 this.layout = config.layout;
38338 this.layout.getEl().addClass("roo-layout-nested-layout");
38339 this.layout.parent = this;
38346 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38348 setSize : function(width, height){
38349 if(!this.ignoreResize(width, height)){
38350 var size = this.adjustForComponents(width, height);
38351 var el = this.layout.getEl();
38352 if (size.height < 1) {
38353 el.setWidth(size.width);
38355 el.setSize(size.width, size.height);
38357 var touch = el.dom.offsetWidth;
38358 this.layout.layout();
38359 // ie requires a double layout on the first pass
38360 if(Roo.isIE && !this.initialized){
38361 this.initialized = true;
38362 this.layout.layout();
38367 // activate all subpanels if not currently active..
38369 setActiveState : function(active){
38370 this.active = active;
38371 this.setActiveClass(active);
38374 this.fireEvent("deactivate", this);
38378 this.fireEvent("activate", this);
38379 // not sure if this should happen before or after..
38380 if (!this.layout) {
38381 return; // should not happen..
38384 for (var r in this.layout.regions) {
38385 reg = this.layout.getRegion(r);
38386 if (reg.getActivePanel()) {
38387 //reg.showPanel(reg.getActivePanel()); // force it to activate..
38388 reg.setActivePanel(reg.getActivePanel());
38391 if (!reg.panels.length) {
38394 reg.showPanel(reg.getPanel(0));
38403 * Returns the nested BorderLayout for this panel
38404 * @return {Roo.BorderLayout}
38406 getLayout : function(){
38407 return this.layout;
38411 * Adds a xtype elements to the layout of the nested panel
38415 xtype : 'ContentPanel',
38422 xtype : 'NestedLayoutPanel',
38428 items : [ ... list of content panels or nested layout panels.. ]
38432 * @param {Object} cfg Xtype definition of item to add.
38434 addxtype : function(cfg) {
38435 return this.layout.addxtype(cfg);
38440 * Ext JS Library 1.1.1
38441 * Copyright(c) 2006-2007, Ext JS, LLC.
38443 * Originally Released Under LGPL - original licence link has changed is not relivant.
38446 * <script type="text/javascript">
38449 * @class Roo.TabPanel
38450 * @extends Roo.util.Observable
38451 * A lightweight tab container.
38455 // basic tabs 1, built from existing content
38456 var tabs = new Roo.TabPanel("tabs1");
38457 tabs.addTab("script", "View Script");
38458 tabs.addTab("markup", "View Markup");
38459 tabs.activate("script");
38461 // more advanced tabs, built from javascript
38462 var jtabs = new Roo.TabPanel("jtabs");
38463 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38465 // set up the UpdateManager
38466 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38467 var updater = tab2.getUpdateManager();
38468 updater.setDefaultUrl("ajax1.htm");
38469 tab2.on('activate', updater.refresh, updater, true);
38471 // Use setUrl for Ajax loading
38472 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38473 tab3.setUrl("ajax2.htm", null, true);
38476 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38479 jtabs.activate("jtabs-1");
38482 * Create a new TabPanel.
38483 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38484 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38486 Roo.bootstrap.panel.Tabs = function(config){
38488 * The container element for this TabPanel.
38489 * @type Roo.Element
38491 this.el = Roo.get(config.el);
38494 if(typeof config == "boolean"){
38495 this.tabPosition = config ? "bottom" : "top";
38497 Roo.apply(this, config);
38501 if(this.tabPosition == "bottom"){
38502 // if tabs are at the bottom = create the body first.
38503 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38504 this.el.addClass("roo-tabs-bottom");
38506 // next create the tabs holders
38508 if (this.tabPosition == "west"){
38510 var reg = this.region; // fake it..
38512 if (!reg.mgr.parent) {
38515 reg = reg.mgr.parent.region;
38517 Roo.log("got nest?");
38519 if (reg.mgr.getRegion('west')) {
38520 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38521 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38522 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38523 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38524 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38532 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38533 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38534 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38535 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38540 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38543 // finally - if tabs are at the top, then create the body last..
38544 if(this.tabPosition != "bottom"){
38545 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38546 * @type Roo.Element
38548 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38549 this.el.addClass("roo-tabs-top");
38553 this.bodyEl.setStyle("position", "relative");
38555 this.active = null;
38556 this.activateDelegate = this.activate.createDelegate(this);
38561 * Fires when the active tab changes
38562 * @param {Roo.TabPanel} this
38563 * @param {Roo.TabPanelItem} activePanel The new active tab
38567 * @event beforetabchange
38568 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38569 * @param {Roo.TabPanel} this
38570 * @param {Object} e Set cancel to true on this object to cancel the tab change
38571 * @param {Roo.TabPanelItem} tab The tab being changed to
38573 "beforetabchange" : true
38576 Roo.EventManager.onWindowResize(this.onResize, this);
38577 this.cpad = this.el.getPadding("lr");
38578 this.hiddenCount = 0;
38581 // toolbar on the tabbar support...
38582 if (this.toolbar) {
38583 alert("no toolbar support yet");
38584 this.toolbar = false;
38586 var tcfg = this.toolbar;
38587 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
38588 this.toolbar = new Roo.Toolbar(tcfg);
38589 if (Roo.isSafari) {
38590 var tbl = tcfg.container.child('table', true);
38591 tbl.setAttribute('width', '100%');
38599 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38602 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38604 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38606 tabPosition : "top",
38608 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38610 currentTabWidth : 0,
38612 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38616 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38620 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38622 preferredTabWidth : 175,
38624 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38626 resizeTabs : false,
38628 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38630 monitorResize : true,
38632 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
38634 toolbar : false, // set by caller..
38636 region : false, /// set by caller
38638 disableTooltips : true, // not used yet...
38641 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38642 * @param {String} id The id of the div to use <b>or create</b>
38643 * @param {String} text The text for the tab
38644 * @param {String} content (optional) Content to put in the TabPanelItem body
38645 * @param {Boolean} closable (optional) True to create a close icon on the tab
38646 * @return {Roo.TabPanelItem} The created TabPanelItem
38648 addTab : function(id, text, content, closable, tpl)
38650 var item = new Roo.bootstrap.panel.TabItem({
38654 closable : closable,
38657 this.addTabItem(item);
38659 item.setContent(content);
38665 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38666 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38667 * @return {Roo.TabPanelItem}
38669 getTab : function(id){
38670 return this.items[id];
38674 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38675 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38677 hideTab : function(id){
38678 var t = this.items[id];
38681 this.hiddenCount++;
38682 this.autoSizeTabs();
38687 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38688 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38690 unhideTab : function(id){
38691 var t = this.items[id];
38693 t.setHidden(false);
38694 this.hiddenCount--;
38695 this.autoSizeTabs();
38700 * Adds an existing {@link Roo.TabPanelItem}.
38701 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38703 addTabItem : function(item)
38705 this.items[item.id] = item;
38706 this.items.push(item);
38707 this.autoSizeTabs();
38708 // if(this.resizeTabs){
38709 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38710 // this.autoSizeTabs();
38712 // item.autoSize();
38717 * Removes a {@link Roo.TabPanelItem}.
38718 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38720 removeTab : function(id){
38721 var items = this.items;
38722 var tab = items[id];
38723 if(!tab) { return; }
38724 var index = items.indexOf(tab);
38725 if(this.active == tab && items.length > 1){
38726 var newTab = this.getNextAvailable(index);
38731 this.stripEl.dom.removeChild(tab.pnode.dom);
38732 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38733 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38735 items.splice(index, 1);
38736 delete this.items[tab.id];
38737 tab.fireEvent("close", tab);
38738 tab.purgeListeners();
38739 this.autoSizeTabs();
38742 getNextAvailable : function(start){
38743 var items = this.items;
38745 // look for a next tab that will slide over to
38746 // replace the one being removed
38747 while(index < items.length){
38748 var item = items[++index];
38749 if(item && !item.isHidden()){
38753 // if one isn't found select the previous tab (on the left)
38756 var item = items[--index];
38757 if(item && !item.isHidden()){
38765 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38766 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38768 disableTab : function(id){
38769 var tab = this.items[id];
38770 if(tab && this.active != tab){
38776 * Enables a {@link Roo.TabPanelItem} that is disabled.
38777 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38779 enableTab : function(id){
38780 var tab = this.items[id];
38785 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38786 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38787 * @return {Roo.TabPanelItem} The TabPanelItem.
38789 activate : function(id)
38791 //Roo.log('activite:' + id);
38793 var tab = this.items[id];
38797 if(tab == this.active || tab.disabled){
38801 this.fireEvent("beforetabchange", this, e, tab);
38802 if(e.cancel !== true && !tab.disabled){
38804 this.active.hide();
38806 this.active = this.items[id];
38807 this.active.show();
38808 this.fireEvent("tabchange", this, this.active);
38814 * Gets the active {@link Roo.TabPanelItem}.
38815 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38817 getActiveTab : function(){
38818 return this.active;
38822 * Updates the tab body element to fit the height of the container element
38823 * for overflow scrolling
38824 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38826 syncHeight : function(targetHeight){
38827 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38828 var bm = this.bodyEl.getMargins();
38829 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38830 this.bodyEl.setHeight(newHeight);
38834 onResize : function(){
38835 if(this.monitorResize){
38836 this.autoSizeTabs();
38841 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38843 beginUpdate : function(){
38844 this.updating = true;
38848 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38850 endUpdate : function(){
38851 this.updating = false;
38852 this.autoSizeTabs();
38856 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38858 autoSizeTabs : function()
38860 var count = this.items.length;
38861 var vcount = count - this.hiddenCount;
38864 this.stripEl.hide();
38866 this.stripEl.show();
38869 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38874 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38875 var availWidth = Math.floor(w / vcount);
38876 var b = this.stripBody;
38877 if(b.getWidth() > w){
38878 var tabs = this.items;
38879 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38880 if(availWidth < this.minTabWidth){
38881 /*if(!this.sleft){ // incomplete scrolling code
38882 this.createScrollButtons();
38885 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38888 if(this.currentTabWidth < this.preferredTabWidth){
38889 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38895 * Returns the number of tabs in this TabPanel.
38898 getCount : function(){
38899 return this.items.length;
38903 * Resizes all the tabs to the passed width
38904 * @param {Number} The new width
38906 setTabWidth : function(width){
38907 this.currentTabWidth = width;
38908 for(var i = 0, len = this.items.length; i < len; i++) {
38909 if(!this.items[i].isHidden()) {
38910 this.items[i].setWidth(width);
38916 * Destroys this TabPanel
38917 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38919 destroy : function(removeEl){
38920 Roo.EventManager.removeResizeListener(this.onResize, this);
38921 for(var i = 0, len = this.items.length; i < len; i++){
38922 this.items[i].purgeListeners();
38924 if(removeEl === true){
38925 this.el.update("");
38930 createStrip : function(container)
38932 var strip = document.createElement("nav");
38933 strip.className = Roo.bootstrap.version == 4 ?
38934 "navbar-light bg-light" :
38935 "navbar navbar-default"; //"x-tabs-wrap";
38936 container.appendChild(strip);
38940 createStripList : function(strip)
38942 // div wrapper for retard IE
38943 // returns the "tr" element.
38944 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38945 //'<div class="x-tabs-strip-wrap">'+
38946 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38947 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38948 return strip.firstChild; //.firstChild.firstChild.firstChild;
38950 createBody : function(container)
38952 var body = document.createElement("div");
38953 Roo.id(body, "tab-body");
38954 //Roo.fly(body).addClass("x-tabs-body");
38955 Roo.fly(body).addClass("tab-content");
38956 container.appendChild(body);
38959 createItemBody :function(bodyEl, id){
38960 var body = Roo.getDom(id);
38962 body = document.createElement("div");
38965 //Roo.fly(body).addClass("x-tabs-item-body");
38966 Roo.fly(body).addClass("tab-pane");
38967 bodyEl.insertBefore(body, bodyEl.firstChild);
38971 createStripElements : function(stripEl, text, closable, tpl)
38973 var td = document.createElement("li"); // was td..
38974 td.className = 'nav-item';
38976 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38979 stripEl.appendChild(td);
38981 td.className = "x-tabs-closable";
38982 if(!this.closeTpl){
38983 this.closeTpl = new Roo.Template(
38984 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38985 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38986 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38989 var el = this.closeTpl.overwrite(td, {"text": text});
38990 var close = el.getElementsByTagName("div")[0];
38991 var inner = el.getElementsByTagName("em")[0];
38992 return {"el": el, "close": close, "inner": inner};
38995 // not sure what this is..
38996 // if(!this.tabTpl){
38997 //this.tabTpl = new Roo.Template(
38998 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38999 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
39001 // this.tabTpl = new Roo.Template(
39002 // '<a href="#">' +
39003 // '<span unselectable="on"' +
39004 // (this.disableTooltips ? '' : ' title="{text}"') +
39005 // ' >{text}</span></a>'
39011 var template = tpl || this.tabTpl || false;
39014 template = new Roo.Template(
39015 Roo.bootstrap.version == 4 ?
39017 '<a class="nav-link" href="#" unselectable="on"' +
39018 (this.disableTooltips ? '' : ' title="{text}"') +
39021 '<a class="nav-link" href="#">' +
39022 '<span unselectable="on"' +
39023 (this.disableTooltips ? '' : ' title="{text}"') +
39024 ' >{text}</span></a>'
39029 switch (typeof(template)) {
39033 template = new Roo.Template(template);
39039 var el = template.overwrite(td, {"text": text});
39041 var inner = el.getElementsByTagName("span")[0];
39043 return {"el": el, "inner": inner};
39051 * @class Roo.TabPanelItem
39052 * @extends Roo.util.Observable
39053 * Represents an individual item (tab plus body) in a TabPanel.
39054 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
39055 * @param {String} id The id of this TabPanelItem
39056 * @param {String} text The text for the tab of this TabPanelItem
39057 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
39059 Roo.bootstrap.panel.TabItem = function(config){
39061 * The {@link Roo.TabPanel} this TabPanelItem belongs to
39062 * @type Roo.TabPanel
39064 this.tabPanel = config.panel;
39066 * The id for this TabPanelItem
39069 this.id = config.id;
39071 this.disabled = false;
39073 this.text = config.text;
39075 this.loaded = false;
39076 this.closable = config.closable;
39079 * The body element for this TabPanelItem.
39080 * @type Roo.Element
39082 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
39083 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
39084 this.bodyEl.setStyle("display", "block");
39085 this.bodyEl.setStyle("zoom", "1");
39086 //this.hideAction();
39088 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
39090 this.el = Roo.get(els.el);
39091 this.inner = Roo.get(els.inner, true);
39092 this.textEl = Roo.bootstrap.version == 4 ?
39093 this.el : Roo.get(this.el.dom.firstChild, true);
39095 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
39096 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
39099 // this.el.on("mousedown", this.onTabMouseDown, this);
39100 this.el.on("click", this.onTabClick, this);
39102 if(config.closable){
39103 var c = Roo.get(els.close, true);
39104 c.dom.title = this.closeText;
39105 c.addClassOnOver("close-over");
39106 c.on("click", this.closeClick, this);
39112 * Fires when this tab becomes the active tab.
39113 * @param {Roo.TabPanel} tabPanel The parent TabPanel
39114 * @param {Roo.TabPanelItem} this
39118 * @event beforeclose
39119 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
39120 * @param {Roo.TabPanelItem} this
39121 * @param {Object} e Set cancel to true on this object to cancel the close.
39123 "beforeclose": true,
39126 * Fires when this tab is closed.
39127 * @param {Roo.TabPanelItem} this
39131 * @event deactivate
39132 * Fires when this tab is no longer the active tab.
39133 * @param {Roo.TabPanel} tabPanel The parent TabPanel
39134 * @param {Roo.TabPanelItem} this
39136 "deactivate" : true
39138 this.hidden = false;
39140 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
39143 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
39145 purgeListeners : function(){
39146 Roo.util.Observable.prototype.purgeListeners.call(this);
39147 this.el.removeAllListeners();
39150 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
39153 this.status_node.addClass("active");
39156 this.tabPanel.stripWrap.repaint();
39158 this.fireEvent("activate", this.tabPanel, this);
39162 * Returns true if this tab is the active tab.
39163 * @return {Boolean}
39165 isActive : function(){
39166 return this.tabPanel.getActiveTab() == this;
39170 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
39173 this.status_node.removeClass("active");
39175 this.fireEvent("deactivate", this.tabPanel, this);
39178 hideAction : function(){
39179 this.bodyEl.hide();
39180 this.bodyEl.setStyle("position", "absolute");
39181 this.bodyEl.setLeft("-20000px");
39182 this.bodyEl.setTop("-20000px");
39185 showAction : function(){
39186 this.bodyEl.setStyle("position", "relative");
39187 this.bodyEl.setTop("");
39188 this.bodyEl.setLeft("");
39189 this.bodyEl.show();
39193 * Set the tooltip for the tab.
39194 * @param {String} tooltip The tab's tooltip
39196 setTooltip : function(text){
39197 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
39198 this.textEl.dom.qtip = text;
39199 this.textEl.dom.removeAttribute('title');
39201 this.textEl.dom.title = text;
39205 onTabClick : function(e){
39206 e.preventDefault();
39207 this.tabPanel.activate(this.id);
39210 onTabMouseDown : function(e){
39211 e.preventDefault();
39212 this.tabPanel.activate(this.id);
39215 getWidth : function(){
39216 return this.inner.getWidth();
39219 setWidth : function(width){
39220 var iwidth = width - this.linode.getPadding("lr");
39221 this.inner.setWidth(iwidth);
39222 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
39223 this.linode.setWidth(width);
39227 * Show or hide the tab
39228 * @param {Boolean} hidden True to hide or false to show.
39230 setHidden : function(hidden){
39231 this.hidden = hidden;
39232 this.linode.setStyle("display", hidden ? "none" : "");
39236 * Returns true if this tab is "hidden"
39237 * @return {Boolean}
39239 isHidden : function(){
39240 return this.hidden;
39244 * Returns the text for this tab
39247 getText : function(){
39251 autoSize : function(){
39252 //this.el.beginMeasure();
39253 this.textEl.setWidth(1);
39255 * #2804 [new] Tabs in Roojs
39256 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39258 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39259 //this.el.endMeasure();
39263 * Sets the text for the tab (Note: this also sets the tooltip text)
39264 * @param {String} text The tab's text and tooltip
39266 setText : function(text){
39268 this.textEl.update(text);
39269 this.setTooltip(text);
39270 //if(!this.tabPanel.resizeTabs){
39271 // this.autoSize();
39275 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39277 activate : function(){
39278 this.tabPanel.activate(this.id);
39282 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39284 disable : function(){
39285 if(this.tabPanel.active != this){
39286 this.disabled = true;
39287 this.status_node.addClass("disabled");
39292 * Enables this TabPanelItem if it was previously disabled.
39294 enable : function(){
39295 this.disabled = false;
39296 this.status_node.removeClass("disabled");
39300 * Sets the content for this TabPanelItem.
39301 * @param {String} content The content
39302 * @param {Boolean} loadScripts true to look for and load scripts
39304 setContent : function(content, loadScripts){
39305 this.bodyEl.update(content, loadScripts);
39309 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39310 * @return {Roo.UpdateManager} The UpdateManager
39312 getUpdateManager : function(){
39313 return this.bodyEl.getUpdateManager();
39317 * Set a URL to be used to load the content for this TabPanelItem.
39318 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39319 * @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)
39320 * @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)
39321 * @return {Roo.UpdateManager} The UpdateManager
39323 setUrl : function(url, params, loadOnce){
39324 if(this.refreshDelegate){
39325 this.un('activate', this.refreshDelegate);
39327 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39328 this.on("activate", this.refreshDelegate);
39329 return this.bodyEl.getUpdateManager();
39333 _handleRefresh : function(url, params, loadOnce){
39334 if(!loadOnce || !this.loaded){
39335 var updater = this.bodyEl.getUpdateManager();
39336 updater.update(url, params, this._setLoaded.createDelegate(this));
39341 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
39342 * Will fail silently if the setUrl method has not been called.
39343 * This does not activate the panel, just updates its content.
39345 refresh : function(){
39346 if(this.refreshDelegate){
39347 this.loaded = false;
39348 this.refreshDelegate();
39353 _setLoaded : function(){
39354 this.loaded = true;
39358 closeClick : function(e){
39361 this.fireEvent("beforeclose", this, o);
39362 if(o.cancel !== true){
39363 this.tabPanel.removeTab(this.id);
39367 * The text displayed in the tooltip for the close icon.
39370 closeText : "Close this tab"
39373 * This script refer to:
39374 * Title: International Telephone Input
39375 * Author: Jack O'Connor
39376 * Code version: v12.1.12
39377 * Availability: https://github.com/jackocnr/intl-tel-input.git
39380 Roo.bootstrap.PhoneInputData = function() {
39383 "Afghanistan (افغانستان)",
39388 "Albania (Shqipëri)",
39393 "Algeria (الجزائر)",
39418 "Antigua and Barbuda",
39428 "Armenia (Հայաստան)",
39444 "Austria (Österreich)",
39449 "Azerbaijan (Azərbaycan)",
39459 "Bahrain (البحرين)",
39464 "Bangladesh (বাংলাদেশ)",
39474 "Belarus (Беларусь)",
39479 "Belgium (België)",
39509 "Bosnia and Herzegovina (Босна и Херцеговина)",
39524 "British Indian Ocean Territory",
39529 "British Virgin Islands",
39539 "Bulgaria (България)",
39549 "Burundi (Uburundi)",
39554 "Cambodia (កម្ពុជា)",
39559 "Cameroon (Cameroun)",
39568 ["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"]
39571 "Cape Verde (Kabu Verdi)",
39576 "Caribbean Netherlands",
39587 "Central African Republic (République centrafricaine)",
39607 "Christmas Island",
39613 "Cocos (Keeling) Islands",
39624 "Comoros (جزر القمر)",
39629 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39634 "Congo (Republic) (Congo-Brazzaville)",
39654 "Croatia (Hrvatska)",
39675 "Czech Republic (Česká republika)",
39680 "Denmark (Danmark)",
39695 "Dominican Republic (República Dominicana)",
39699 ["809", "829", "849"]
39717 "Equatorial Guinea (Guinea Ecuatorial)",
39737 "Falkland Islands (Islas Malvinas)",
39742 "Faroe Islands (Føroyar)",
39763 "French Guiana (Guyane française)",
39768 "French Polynesia (Polynésie française)",
39783 "Georgia (საქართველო)",
39788 "Germany (Deutschland)",
39808 "Greenland (Kalaallit Nunaat)",
39845 "Guinea-Bissau (Guiné Bissau)",
39870 "Hungary (Magyarország)",
39875 "Iceland (Ísland)",
39895 "Iraq (العراق)",
39911 "Israel (ישראל)",
39938 "Jordan (الأردن)",
39943 "Kazakhstan (Казахстан)",
39964 "Kuwait (الكويت)",
39969 "Kyrgyzstan (Кыргызстан)",
39979 "Latvia (Latvija)",
39984 "Lebanon (لبنان)",
39999 "Libya (ليبيا)",
40009 "Lithuania (Lietuva)",
40024 "Macedonia (FYROM) (Македонија)",
40029 "Madagascar (Madagasikara)",
40059 "Marshall Islands",
40069 "Mauritania (موريتانيا)",
40074 "Mauritius (Moris)",
40095 "Moldova (Republica Moldova)",
40105 "Mongolia (Монгол)",
40110 "Montenegro (Crna Gora)",
40120 "Morocco (المغرب)",
40126 "Mozambique (Moçambique)",
40131 "Myanmar (Burma) (မြန်မာ)",
40136 "Namibia (Namibië)",
40151 "Netherlands (Nederland)",
40156 "New Caledonia (Nouvelle-Calédonie)",
40191 "North Korea (조선 민주주의 인민 공화국)",
40196 "Northern Mariana Islands",
40212 "Pakistan (پاکستان)",
40222 "Palestine (فلسطين)",
40232 "Papua New Guinea",
40274 "Réunion (La Réunion)",
40280 "Romania (România)",
40296 "Saint Barthélemy",
40307 "Saint Kitts and Nevis",
40317 "Saint Martin (Saint-Martin (partie française))",
40323 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40328 "Saint Vincent and the Grenadines",
40343 "São Tomé and Príncipe (São Tomé e Príncipe)",
40348 "Saudi Arabia (المملكة العربية السعودية)",
40353 "Senegal (Sénégal)",
40383 "Slovakia (Slovensko)",
40388 "Slovenia (Slovenija)",
40398 "Somalia (Soomaaliya)",
40408 "South Korea (대한민국)",
40413 "South Sudan (جنوب السودان)",
40423 "Sri Lanka (ශ්රී ලංකාව)",
40428 "Sudan (السودان)",
40438 "Svalbard and Jan Mayen",
40449 "Sweden (Sverige)",
40454 "Switzerland (Schweiz)",
40459 "Syria (سوريا)",
40504 "Trinidad and Tobago",
40509 "Tunisia (تونس)",
40514 "Turkey (Türkiye)",
40524 "Turks and Caicos Islands",
40534 "U.S. Virgin Islands",
40544 "Ukraine (Україна)",
40549 "United Arab Emirates (الإمارات العربية المتحدة)",
40571 "Uzbekistan (Oʻzbekiston)",
40581 "Vatican City (Città del Vaticano)",
40592 "Vietnam (Việt Nam)",
40597 "Wallis and Futuna (Wallis-et-Futuna)",
40602 "Western Sahara (الصحراء الغربية)",
40608 "Yemen (اليمن)",
40632 * This script refer to:
40633 * Title: International Telephone Input
40634 * Author: Jack O'Connor
40635 * Code version: v12.1.12
40636 * Availability: https://github.com/jackocnr/intl-tel-input.git
40640 * @class Roo.bootstrap.PhoneInput
40641 * @extends Roo.bootstrap.TriggerField
40642 * An input with International dial-code selection
40644 * @cfg {String} defaultDialCode default '+852'
40645 * @cfg {Array} preferedCountries default []
40648 * Create a new PhoneInput.
40649 * @param {Object} config Configuration options
40652 Roo.bootstrap.PhoneInput = function(config) {
40653 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40656 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40658 listWidth: undefined,
40660 selectedClass: 'active',
40662 invalidClass : "has-warning",
40664 validClass: 'has-success',
40666 allowed: '0123456789',
40671 * @cfg {String} defaultDialCode The default dial code when initializing the input
40673 defaultDialCode: '+852',
40676 * @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
40678 preferedCountries: false,
40680 getAutoCreate : function()
40682 var data = Roo.bootstrap.PhoneInputData();
40683 var align = this.labelAlign || this.parentLabelAlign();
40686 this.allCountries = [];
40687 this.dialCodeMapping = [];
40689 for (var i = 0; i < data.length; i++) {
40691 this.allCountries[i] = {
40695 priority: c[3] || 0,
40696 areaCodes: c[4] || null
40698 this.dialCodeMapping[c[2]] = {
40701 priority: c[3] || 0,
40702 areaCodes: c[4] || null
40714 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40715 maxlength: this.max_length,
40716 cls : 'form-control tel-input',
40717 autocomplete: 'new-password'
40720 var hiddenInput = {
40723 cls: 'hidden-tel-input'
40727 hiddenInput.name = this.name;
40730 if (this.disabled) {
40731 input.disabled = true;
40734 var flag_container = {
40751 cls: this.hasFeedback ? 'has-feedback' : '',
40757 cls: 'dial-code-holder',
40764 cls: 'roo-select2-container input-group',
40771 if (this.fieldLabel.length) {
40774 tooltip: 'This field is required'
40780 cls: 'control-label',
40786 html: this.fieldLabel
40789 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40795 if(this.indicatorpos == 'right') {
40796 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40803 if(align == 'left') {
40811 if(this.labelWidth > 12){
40812 label.style = "width: " + this.labelWidth + 'px';
40814 if(this.labelWidth < 13 && this.labelmd == 0){
40815 this.labelmd = this.labelWidth;
40817 if(this.labellg > 0){
40818 label.cls += ' col-lg-' + this.labellg;
40819 input.cls += ' col-lg-' + (12 - this.labellg);
40821 if(this.labelmd > 0){
40822 label.cls += ' col-md-' + this.labelmd;
40823 container.cls += ' col-md-' + (12 - this.labelmd);
40825 if(this.labelsm > 0){
40826 label.cls += ' col-sm-' + this.labelsm;
40827 container.cls += ' col-sm-' + (12 - this.labelsm);
40829 if(this.labelxs > 0){
40830 label.cls += ' col-xs-' + this.labelxs;
40831 container.cls += ' col-xs-' + (12 - this.labelxs);
40841 var settings = this;
40843 ['xs','sm','md','lg'].map(function(size){
40844 if (settings[size]) {
40845 cfg.cls += ' col-' + size + '-' + settings[size];
40849 this.store = new Roo.data.Store({
40850 proxy : new Roo.data.MemoryProxy({}),
40851 reader : new Roo.data.JsonReader({
40862 'name' : 'dialCode',
40866 'name' : 'priority',
40870 'name' : 'areaCodes',
40877 if(!this.preferedCountries) {
40878 this.preferedCountries = [
40885 var p = this.preferedCountries.reverse();
40888 for (var i = 0; i < p.length; i++) {
40889 for (var j = 0; j < this.allCountries.length; j++) {
40890 if(this.allCountries[j].iso2 == p[i]) {
40891 var t = this.allCountries[j];
40892 this.allCountries.splice(j,1);
40893 this.allCountries.unshift(t);
40899 this.store.proxy.data = {
40901 data: this.allCountries
40907 initEvents : function()
40910 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40912 this.indicator = this.indicatorEl();
40913 this.flag = this.flagEl();
40914 this.dialCodeHolder = this.dialCodeHolderEl();
40916 this.trigger = this.el.select('div.flag-box',true).first();
40917 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40922 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40923 _this.list.setWidth(lw);
40926 this.list.on('mouseover', this.onViewOver, this);
40927 this.list.on('mousemove', this.onViewMove, this);
40928 this.inputEl().on("keyup", this.onKeyUp, this);
40929 this.inputEl().on("keypress", this.onKeyPress, this);
40931 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40933 this.view = new Roo.View(this.list, this.tpl, {
40934 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40937 this.view.on('click', this.onViewClick, this);
40938 this.setValue(this.defaultDialCode);
40941 onTriggerClick : function(e)
40943 Roo.log('trigger click');
40948 if(this.isExpanded()){
40950 this.hasFocus = false;
40952 this.store.load({});
40953 this.hasFocus = true;
40958 isExpanded : function()
40960 return this.list.isVisible();
40963 collapse : function()
40965 if(!this.isExpanded()){
40969 Roo.get(document).un('mousedown', this.collapseIf, this);
40970 Roo.get(document).un('mousewheel', this.collapseIf, this);
40971 this.fireEvent('collapse', this);
40975 expand : function()
40979 if(this.isExpanded() || !this.hasFocus){
40983 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40984 this.list.setWidth(lw);
40987 this.restrictHeight();
40989 Roo.get(document).on('mousedown', this.collapseIf, this);
40990 Roo.get(document).on('mousewheel', this.collapseIf, this);
40992 this.fireEvent('expand', this);
40995 restrictHeight : function()
40997 this.list.alignTo(this.inputEl(), this.listAlign);
40998 this.list.alignTo(this.inputEl(), this.listAlign);
41001 onViewOver : function(e, t)
41003 if(this.inKeyMode){
41006 var item = this.view.findItemFromChild(t);
41009 var index = this.view.indexOf(item);
41010 this.select(index, false);
41015 onViewClick : function(view, doFocus, el, e)
41017 var index = this.view.getSelectedIndexes()[0];
41019 var r = this.store.getAt(index);
41022 this.onSelect(r, index);
41024 if(doFocus !== false && !this.blockFocus){
41025 this.inputEl().focus();
41029 onViewMove : function(e, t)
41031 this.inKeyMode = false;
41034 select : function(index, scrollIntoView)
41036 this.selectedIndex = index;
41037 this.view.select(index);
41038 if(scrollIntoView !== false){
41039 var el = this.view.getNode(index);
41041 this.list.scrollChildIntoView(el, false);
41046 createList : function()
41048 this.list = Roo.get(document.body).createChild({
41050 cls: 'typeahead typeahead-long dropdown-menu tel-list',
41051 style: 'display:none'
41054 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
41057 collapseIf : function(e)
41059 var in_combo = e.within(this.el);
41060 var in_list = e.within(this.list);
41061 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
41063 if (in_combo || in_list || is_list) {
41069 onSelect : function(record, index)
41071 if(this.fireEvent('beforeselect', this, record, index) !== false){
41073 this.setFlagClass(record.data.iso2);
41074 this.setDialCode(record.data.dialCode);
41075 this.hasFocus = false;
41077 this.fireEvent('select', this, record, index);
41081 flagEl : function()
41083 var flag = this.el.select('div.flag',true).first();
41090 dialCodeHolderEl : function()
41092 var d = this.el.select('input.dial-code-holder',true).first();
41099 setDialCode : function(v)
41101 this.dialCodeHolder.dom.value = '+'+v;
41104 setFlagClass : function(n)
41106 this.flag.dom.className = 'flag '+n;
41109 getValue : function()
41111 var v = this.inputEl().getValue();
41112 if(this.dialCodeHolder) {
41113 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
41118 setValue : function(v)
41120 var d = this.getDialCode(v);
41122 //invalid dial code
41123 if(v.length == 0 || !d || d.length == 0) {
41125 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41126 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41132 this.setFlagClass(this.dialCodeMapping[d].iso2);
41133 this.setDialCode(d);
41134 this.inputEl().dom.value = v.replace('+'+d,'');
41135 this.hiddenEl().dom.value = this.getValue();
41140 getDialCode : function(v)
41144 if (v.length == 0) {
41145 return this.dialCodeHolder.dom.value;
41149 if (v.charAt(0) != "+") {
41152 var numericChars = "";
41153 for (var i = 1; i < v.length; i++) {
41154 var c = v.charAt(i);
41157 if (this.dialCodeMapping[numericChars]) {
41158 dialCode = v.substr(1, i);
41160 if (numericChars.length == 4) {
41170 this.setValue(this.defaultDialCode);
41174 hiddenEl : function()
41176 return this.el.select('input.hidden-tel-input',true).first();
41179 // after setting val
41180 onKeyUp : function(e){
41181 this.setValue(this.getValue());
41184 onKeyPress : function(e){
41185 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
41192 * @class Roo.bootstrap.MoneyField
41193 * @extends Roo.bootstrap.ComboBox
41194 * Bootstrap MoneyField class
41197 * Create a new MoneyField.
41198 * @param {Object} config Configuration options
41201 Roo.bootstrap.MoneyField = function(config) {
41203 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
41207 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
41210 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41212 allowDecimals : true,
41214 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41216 decimalSeparator : ".",
41218 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41220 decimalPrecision : 0,
41222 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41224 allowNegative : true,
41226 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41230 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41232 minValue : Number.NEGATIVE_INFINITY,
41234 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41236 maxValue : Number.MAX_VALUE,
41238 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41240 minText : "The minimum value for this field is {0}",
41242 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41244 maxText : "The maximum value for this field is {0}",
41246 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
41247 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41249 nanText : "{0} is not a valid number",
41251 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41255 * @cfg {String} defaults currency of the MoneyField
41256 * value should be in lkey
41258 defaultCurrency : false,
41260 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41262 thousandsDelimiter : false,
41264 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41275 getAutoCreate : function()
41277 var align = this.labelAlign || this.parentLabelAlign();
41289 cls : 'form-control roo-money-amount-input',
41290 autocomplete: 'new-password'
41293 var hiddenInput = {
41297 cls: 'hidden-number-input'
41300 if(this.max_length) {
41301 input.maxlength = this.max_length;
41305 hiddenInput.name = this.name;
41308 if (this.disabled) {
41309 input.disabled = true;
41312 var clg = 12 - this.inputlg;
41313 var cmd = 12 - this.inputmd;
41314 var csm = 12 - this.inputsm;
41315 var cxs = 12 - this.inputxs;
41319 cls : 'row roo-money-field',
41323 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41327 cls: 'roo-select2-container input-group',
41331 cls : 'form-control roo-money-currency-input',
41332 autocomplete: 'new-password',
41334 name : this.currencyName
41338 cls : 'input-group-addon',
41352 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41356 cls: this.hasFeedback ? 'has-feedback' : '',
41367 if (this.fieldLabel.length) {
41370 tooltip: 'This field is required'
41376 cls: 'control-label',
41382 html: this.fieldLabel
41385 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41391 if(this.indicatorpos == 'right') {
41392 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41399 if(align == 'left') {
41407 if(this.labelWidth > 12){
41408 label.style = "width: " + this.labelWidth + 'px';
41410 if(this.labelWidth < 13 && this.labelmd == 0){
41411 this.labelmd = this.labelWidth;
41413 if(this.labellg > 0){
41414 label.cls += ' col-lg-' + this.labellg;
41415 input.cls += ' col-lg-' + (12 - this.labellg);
41417 if(this.labelmd > 0){
41418 label.cls += ' col-md-' + this.labelmd;
41419 container.cls += ' col-md-' + (12 - this.labelmd);
41421 if(this.labelsm > 0){
41422 label.cls += ' col-sm-' + this.labelsm;
41423 container.cls += ' col-sm-' + (12 - this.labelsm);
41425 if(this.labelxs > 0){
41426 label.cls += ' col-xs-' + this.labelxs;
41427 container.cls += ' col-xs-' + (12 - this.labelxs);
41438 var settings = this;
41440 ['xs','sm','md','lg'].map(function(size){
41441 if (settings[size]) {
41442 cfg.cls += ' col-' + size + '-' + settings[size];
41449 initEvents : function()
41451 this.indicator = this.indicatorEl();
41453 this.initCurrencyEvent();
41455 this.initNumberEvent();
41458 initCurrencyEvent : function()
41461 throw "can not find store for combo";
41464 this.store = Roo.factory(this.store, Roo.data);
41465 this.store.parent = this;
41469 this.triggerEl = this.el.select('.input-group-addon', true).first();
41471 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41476 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41477 _this.list.setWidth(lw);
41480 this.list.on('mouseover', this.onViewOver, this);
41481 this.list.on('mousemove', this.onViewMove, this);
41482 this.list.on('scroll', this.onViewScroll, this);
41485 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41488 this.view = new Roo.View(this.list, this.tpl, {
41489 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41492 this.view.on('click', this.onViewClick, this);
41494 this.store.on('beforeload', this.onBeforeLoad, this);
41495 this.store.on('load', this.onLoad, this);
41496 this.store.on('loadexception', this.onLoadException, this);
41498 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41499 "up" : function(e){
41500 this.inKeyMode = true;
41504 "down" : function(e){
41505 if(!this.isExpanded()){
41506 this.onTriggerClick();
41508 this.inKeyMode = true;
41513 "enter" : function(e){
41516 if(this.fireEvent("specialkey", this, e)){
41517 this.onViewClick(false);
41523 "esc" : function(e){
41527 "tab" : function(e){
41530 if(this.fireEvent("specialkey", this, e)){
41531 this.onViewClick(false);
41539 doRelay : function(foo, bar, hname){
41540 if(hname == 'down' || this.scope.isExpanded()){
41541 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41549 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41553 initNumberEvent : function(e)
41555 this.inputEl().on("keydown" , this.fireKey, this);
41556 this.inputEl().on("focus", this.onFocus, this);
41557 this.inputEl().on("blur", this.onBlur, this);
41559 this.inputEl().relayEvent('keyup', this);
41561 if(this.indicator){
41562 this.indicator.addClass('invisible');
41565 this.originalValue = this.getValue();
41567 if(this.validationEvent == 'keyup'){
41568 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41569 this.inputEl().on('keyup', this.filterValidation, this);
41571 else if(this.validationEvent !== false){
41572 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41575 if(this.selectOnFocus){
41576 this.on("focus", this.preFocus, this);
41579 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41580 this.inputEl().on("keypress", this.filterKeys, this);
41582 this.inputEl().relayEvent('keypress', this);
41585 var allowed = "0123456789";
41587 if(this.allowDecimals){
41588 allowed += this.decimalSeparator;
41591 if(this.allowNegative){
41595 if(this.thousandsDelimiter) {
41599 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41601 var keyPress = function(e){
41603 var k = e.getKey();
41605 var c = e.getCharCode();
41608 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41609 allowed.indexOf(String.fromCharCode(c)) === -1
41615 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41619 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41624 this.inputEl().on("keypress", keyPress, this);
41628 onTriggerClick : function(e)
41635 this.loadNext = false;
41637 if(this.isExpanded()){
41642 this.hasFocus = true;
41644 if(this.triggerAction == 'all') {
41645 this.doQuery(this.allQuery, true);
41649 this.doQuery(this.getRawValue());
41652 getCurrency : function()
41654 var v = this.currencyEl().getValue();
41659 restrictHeight : function()
41661 this.list.alignTo(this.currencyEl(), this.listAlign);
41662 this.list.alignTo(this.currencyEl(), this.listAlign);
41665 onViewClick : function(view, doFocus, el, e)
41667 var index = this.view.getSelectedIndexes()[0];
41669 var r = this.store.getAt(index);
41672 this.onSelect(r, index);
41676 onSelect : function(record, index){
41678 if(this.fireEvent('beforeselect', this, record, index) !== false){
41680 this.setFromCurrencyData(index > -1 ? record.data : false);
41684 this.fireEvent('select', this, record, index);
41688 setFromCurrencyData : function(o)
41692 this.lastCurrency = o;
41694 if (this.currencyField) {
41695 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41697 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41700 this.lastSelectionText = currency;
41702 //setting default currency
41703 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41704 this.setCurrency(this.defaultCurrency);
41708 this.setCurrency(currency);
41711 setFromData : function(o)
41715 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41717 this.setFromCurrencyData(c);
41722 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41724 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41727 this.setValue(value);
41731 setCurrency : function(v)
41733 this.currencyValue = v;
41736 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41741 setValue : function(v)
41743 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41749 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41751 this.inputEl().dom.value = (v == '') ? '' :
41752 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41754 if(!this.allowZero && v === '0') {
41755 this.hiddenEl().dom.value = '';
41756 this.inputEl().dom.value = '';
41763 getRawValue : function()
41765 var v = this.inputEl().getValue();
41770 getValue : function()
41772 return this.fixPrecision(this.parseValue(this.getRawValue()));
41775 parseValue : function(value)
41777 if(this.thousandsDelimiter) {
41779 r = new RegExp(",", "g");
41780 value = value.replace(r, "");
41783 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41784 return isNaN(value) ? '' : value;
41788 fixPrecision : function(value)
41790 if(this.thousandsDelimiter) {
41792 r = new RegExp(",", "g");
41793 value = value.replace(r, "");
41796 var nan = isNaN(value);
41798 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41799 return nan ? '' : value;
41801 return parseFloat(value).toFixed(this.decimalPrecision);
41804 decimalPrecisionFcn : function(v)
41806 return Math.floor(v);
41809 validateValue : function(value)
41811 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41815 var num = this.parseValue(value);
41818 this.markInvalid(String.format(this.nanText, value));
41822 if(num < this.minValue){
41823 this.markInvalid(String.format(this.minText, this.minValue));
41827 if(num > this.maxValue){
41828 this.markInvalid(String.format(this.maxText, this.maxValue));
41835 validate : function()
41837 if(this.disabled || this.allowBlank){
41842 var currency = this.getCurrency();
41844 if(this.validateValue(this.getRawValue()) && currency.length){
41849 this.markInvalid();
41853 getName: function()
41858 beforeBlur : function()
41864 var v = this.parseValue(this.getRawValue());
41871 onBlur : function()
41875 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41876 //this.el.removeClass(this.focusClass);
41879 this.hasFocus = false;
41881 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41885 var v = this.getValue();
41887 if(String(v) !== String(this.startValue)){
41888 this.fireEvent('change', this, v, this.startValue);
41891 this.fireEvent("blur", this);
41894 inputEl : function()
41896 return this.el.select('.roo-money-amount-input', true).first();
41899 currencyEl : function()
41901 return this.el.select('.roo-money-currency-input', true).first();
41904 hiddenEl : function()
41906 return this.el.select('input.hidden-number-input',true).first();
41910 * @class Roo.bootstrap.BezierSignature
41911 * @extends Roo.bootstrap.Component
41912 * Bootstrap BezierSignature class
41913 * This script refer to:
41914 * Title: Signature Pad
41916 * Availability: https://github.com/szimek/signature_pad
41919 * Create a new BezierSignature
41920 * @param {Object} config The config object
41923 Roo.bootstrap.BezierSignature = function(config){
41924 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41930 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41937 mouse_btn_down: true,
41940 * @cfg {int} canvas height
41942 canvas_height: '200px',
41945 * @cfg {float|function} Radius of a single dot.
41950 * @cfg {float} Minimum width of a line. Defaults to 0.5.
41955 * @cfg {float} Maximum width of a line. Defaults to 2.5.
41960 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41965 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41970 * @cfg {string} Color used to clear the background. Can be any color format accepted by context.fillStyle. Defaults to "rgba(0,0,0,0)" (transparent black). Use a non-transparent color e.g. "rgb(255,255,255)" (opaque white) if you'd like to save signatures as JPEG images.
41972 bg_color: 'rgba(0, 0, 0, 0)',
41975 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41977 dot_color: 'black',
41980 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41982 velocity_filter_weight: 0.7,
41985 * @cfg {function} Callback when stroke begin.
41990 * @cfg {function} Callback when stroke end.
41994 getAutoCreate : function()
41996 var cls = 'roo-signature column';
41999 cls += ' ' + this.cls;
42009 for(var i = 0; i < col_sizes.length; i++) {
42010 if(this[col_sizes[i]]) {
42011 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
42021 cls: 'roo-signature-body',
42025 cls: 'roo-signature-body-canvas',
42026 height: this.canvas_height,
42027 width: this.canvas_width
42034 style: 'display: none'
42042 initEvents: function()
42044 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
42046 var canvas = this.canvasEl();
42048 // mouse && touch event swapping...
42049 canvas.dom.style.touchAction = 'none';
42050 canvas.dom.style.msTouchAction = 'none';
42052 this.mouse_btn_down = false;
42053 canvas.on('mousedown', this._handleMouseDown, this);
42054 canvas.on('mousemove', this._handleMouseMove, this);
42055 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
42057 if (window.PointerEvent) {
42058 canvas.on('pointerdown', this._handleMouseDown, this);
42059 canvas.on('pointermove', this._handleMouseMove, this);
42060 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
42063 if ('ontouchstart' in window) {
42064 canvas.on('touchstart', this._handleTouchStart, this);
42065 canvas.on('touchmove', this._handleTouchMove, this);
42066 canvas.on('touchend', this._handleTouchEnd, this);
42069 Roo.EventManager.onWindowResize(this.resize, this, true);
42071 // file input event
42072 this.fileEl().on('change', this.uploadImage, this);
42079 resize: function(){
42081 var canvas = this.canvasEl().dom;
42082 var ctx = this.canvasElCtx();
42083 var img_data = false;
42085 if(canvas.width > 0) {
42086 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
42088 // setting canvas width will clean img data
42091 var style = window.getComputedStyle ?
42092 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
42094 var padding_left = parseInt(style.paddingLeft) || 0;
42095 var padding_right = parseInt(style.paddingRight) || 0;
42097 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
42100 ctx.putImageData(img_data, 0, 0);
42104 _handleMouseDown: function(e)
42106 if (e.browserEvent.which === 1) {
42107 this.mouse_btn_down = true;
42108 this.strokeBegin(e);
42112 _handleMouseMove: function (e)
42114 if (this.mouse_btn_down) {
42115 this.strokeMoveUpdate(e);
42119 _handleMouseUp: function (e)
42121 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
42122 this.mouse_btn_down = false;
42127 _handleTouchStart: function (e) {
42129 e.preventDefault();
42130 if (e.browserEvent.targetTouches.length === 1) {
42131 // var touch = e.browserEvent.changedTouches[0];
42132 // this.strokeBegin(touch);
42134 this.strokeBegin(e); // assume e catching the correct xy...
42138 _handleTouchMove: function (e) {
42139 e.preventDefault();
42140 // var touch = event.targetTouches[0];
42141 // _this._strokeMoveUpdate(touch);
42142 this.strokeMoveUpdate(e);
42145 _handleTouchEnd: function (e) {
42146 var wasCanvasTouched = e.target === this.canvasEl().dom;
42147 if (wasCanvasTouched) {
42148 e.preventDefault();
42149 // var touch = event.changedTouches[0];
42150 // _this._strokeEnd(touch);
42155 reset: function () {
42156 this._lastPoints = [];
42157 this._lastVelocity = 0;
42158 this._lastWidth = (this.min_width + this.max_width) / 2;
42159 this.canvasElCtx().fillStyle = this.dot_color;
42162 strokeMoveUpdate: function(e)
42164 this.strokeUpdate(e);
42166 if (this.throttle) {
42167 this.throttleStroke(this.strokeUpdate, this.throttle);
42170 this.strokeUpdate(e);
42174 strokeBegin: function(e)
42176 var newPointGroup = {
42177 color: this.dot_color,
42181 if (typeof this.onBegin === 'function') {
42185 this.curve_data.push(newPointGroup);
42187 this.strokeUpdate(e);
42190 strokeUpdate: function(e)
42192 var rect = this.canvasEl().dom.getBoundingClientRect();
42193 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
42194 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
42195 var lastPoints = lastPointGroup.points;
42196 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
42197 var isLastPointTooClose = lastPoint
42198 ? point.distanceTo(lastPoint) <= this.min_distance
42200 var color = lastPointGroup.color;
42201 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
42202 var curve = this.addPoint(point);
42204 this.drawDot({color: color, point: point});
42207 this.drawCurve({color: color, curve: curve});
42217 strokeEnd: function(e)
42219 this.strokeUpdate(e);
42220 if (typeof this.onEnd === 'function') {
42225 addPoint: function (point) {
42226 var _lastPoints = this._lastPoints;
42227 _lastPoints.push(point);
42228 if (_lastPoints.length > 2) {
42229 if (_lastPoints.length === 3) {
42230 _lastPoints.unshift(_lastPoints[0]);
42232 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
42233 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
42234 _lastPoints.shift();
42240 calculateCurveWidths: function (startPoint, endPoint) {
42241 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42242 (1 - this.velocity_filter_weight) * this._lastVelocity;
42244 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42247 start: this._lastWidth
42250 this._lastVelocity = velocity;
42251 this._lastWidth = newWidth;
42255 drawDot: function (_a) {
42256 var color = _a.color, point = _a.point;
42257 var ctx = this.canvasElCtx();
42258 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42260 this.drawCurveSegment(point.x, point.y, width);
42262 ctx.fillStyle = color;
42266 drawCurve: function (_a) {
42267 var color = _a.color, curve = _a.curve;
42268 var ctx = this.canvasElCtx();
42269 var widthDelta = curve.endWidth - curve.startWidth;
42270 var drawSteps = Math.floor(curve.length()) * 2;
42272 ctx.fillStyle = color;
42273 for (var i = 0; i < drawSteps; i += 1) {
42274 var t = i / drawSteps;
42280 var x = uuu * curve.startPoint.x;
42281 x += 3 * uu * t * curve.control1.x;
42282 x += 3 * u * tt * curve.control2.x;
42283 x += ttt * curve.endPoint.x;
42284 var y = uuu * curve.startPoint.y;
42285 y += 3 * uu * t * curve.control1.y;
42286 y += 3 * u * tt * curve.control2.y;
42287 y += ttt * curve.endPoint.y;
42288 var width = curve.startWidth + ttt * widthDelta;
42289 this.drawCurveSegment(x, y, width);
42295 drawCurveSegment: function (x, y, width) {
42296 var ctx = this.canvasElCtx();
42298 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42299 this.is_empty = false;
42304 var ctx = this.canvasElCtx();
42305 var canvas = this.canvasEl().dom;
42306 ctx.fillStyle = this.bg_color;
42307 ctx.clearRect(0, 0, canvas.width, canvas.height);
42308 ctx.fillRect(0, 0, canvas.width, canvas.height);
42309 this.curve_data = [];
42311 this.is_empty = true;
42316 return this.el.select('input',true).first();
42319 canvasEl: function()
42321 return this.el.select('canvas',true).first();
42324 canvasElCtx: function()
42326 return this.el.select('canvas',true).first().dom.getContext('2d');
42329 getImage: function(type)
42331 if(this.is_empty) {
42336 return this.canvasEl().dom.toDataURL('image/'+type, 1);
42339 drawFromImage: function(img_src)
42341 var img = new Image();
42343 img.onload = function(){
42344 this.canvasElCtx().drawImage(img, 0, 0);
42349 this.is_empty = false;
42352 selectImage: function()
42354 this.fileEl().dom.click();
42357 uploadImage: function(e)
42359 var reader = new FileReader();
42361 reader.onload = function(e){
42362 var img = new Image();
42363 img.onload = function(){
42365 this.canvasElCtx().drawImage(img, 0, 0);
42367 img.src = e.target.result;
42370 reader.readAsDataURL(e.target.files[0]);
42373 // Bezier Point Constructor
42374 Point: (function () {
42375 function Point(x, y, time) {
42378 this.time = time || Date.now();
42380 Point.prototype.distanceTo = function (start) {
42381 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42383 Point.prototype.equals = function (other) {
42384 return this.x === other.x && this.y === other.y && this.time === other.time;
42386 Point.prototype.velocityFrom = function (start) {
42387 return this.time !== start.time
42388 ? this.distanceTo(start) / (this.time - start.time)
42395 // Bezier Constructor
42396 Bezier: (function () {
42397 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42398 this.startPoint = startPoint;
42399 this.control2 = control2;
42400 this.control1 = control1;
42401 this.endPoint = endPoint;
42402 this.startWidth = startWidth;
42403 this.endWidth = endWidth;
42405 Bezier.fromPoints = function (points, widths, scope) {
42406 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42407 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42408 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42410 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42411 var dx1 = s1.x - s2.x;
42412 var dy1 = s1.y - s2.y;
42413 var dx2 = s2.x - s3.x;
42414 var dy2 = s2.y - s3.y;
42415 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42416 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42417 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42418 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42419 var dxm = m1.x - m2.x;
42420 var dym = m1.y - m2.y;
42421 var k = l2 / (l1 + l2);
42422 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42423 var tx = s2.x - cm.x;
42424 var ty = s2.y - cm.y;
42426 c1: new scope.Point(m1.x + tx, m1.y + ty),
42427 c2: new scope.Point(m2.x + tx, m2.y + ty)
42430 Bezier.prototype.length = function () {
42435 for (var i = 0; i <= steps; i += 1) {
42437 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42438 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42440 var xdiff = cx - px;
42441 var ydiff = cy - py;
42442 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42449 Bezier.prototype.point = function (t, start, c1, c2, end) {
42450 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42451 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42452 + (3.0 * c2 * (1.0 - t) * t * t)
42453 + (end * t * t * t);
42458 throttleStroke: function(fn, wait) {
42459 if (wait === void 0) { wait = 250; }
42461 var timeout = null;
42465 var later = function () {
42466 previous = Date.now();
42468 result = fn.apply(storedContext, storedArgs);
42470 storedContext = null;
42474 return function wrapper() {
42476 for (var _i = 0; _i < arguments.length; _i++) {
42477 args[_i] = arguments[_i];
42479 var now = Date.now();
42480 var remaining = wait - (now - previous);
42481 storedContext = this;
42483 if (remaining <= 0 || remaining > wait) {
42485 clearTimeout(timeout);
42489 result = fn.apply(storedContext, storedArgs);
42491 storedContext = null;
42495 else if (!timeout) {
42496 timeout = window.setTimeout(later, remaining);