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 var sizes = ['xs','sm','md','lg'];
1064 sizes.map(function(size ,ix){
1065 //Roo.log( size + ':' + settings[size]);
1067 if (settings[size+'off'] !== false) {
1068 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1071 if (settings[size] === false) {
1075 if (!settings[size]) { // 0 = hidden
1076 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1078 for (var i = ix; i > -1; i--) {
1079 cfg.cls += ' d-' + sizes[i] + '-none';
1085 cfg.cls += ' col-' + size + '-' + settings[size] + (
1086 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1092 cfg.cls += ' hidden';
1095 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1096 cfg.cls +=' alert alert-' + this.alert;
1100 if (this.html.length) {
1101 cfg.html = this.html;
1105 if (this.fasize > 1) {
1106 fasize = ' fa-' + this.fasize + 'x';
1108 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1113 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1132 * @class Roo.bootstrap.Container
1133 * @extends Roo.bootstrap.Component
1134 * Bootstrap Container class
1135 * @cfg {Boolean} jumbotron is it a jumbotron element
1136 * @cfg {String} html content of element
1137 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1138 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1139 * @cfg {String} header content of header (for panel)
1140 * @cfg {String} footer content of footer (for panel)
1141 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1142 * @cfg {String} tag (header|aside|section) type of HTML tag.
1143 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1144 * @cfg {String} fa font awesome icon
1145 * @cfg {String} icon (info-sign|check|...) glyphicon name
1146 * @cfg {Boolean} hidden (true|false) hide the element
1147 * @cfg {Boolean} expandable (true|false) default false
1148 * @cfg {Boolean} expanded (true|false) default true
1149 * @cfg {String} rheader contet on the right of header
1150 * @cfg {Boolean} clickable (true|false) default false
1154 * Create a new Container
1155 * @param {Object} config The config object
1158 Roo.bootstrap.Container = function(config){
1159 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1165 * After the panel has been expand
1167 * @param {Roo.bootstrap.Container} this
1172 * After the panel has been collapsed
1174 * @param {Roo.bootstrap.Container} this
1179 * When a element is chick
1180 * @param {Roo.bootstrap.Container} this
1181 * @param {Roo.EventObject} e
1187 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1205 getChildContainer : function() {
1211 if (this.panel.length) {
1212 return this.el.select('.panel-body',true).first();
1219 getAutoCreate : function(){
1222 tag : this.tag || 'div',
1226 if (this.jumbotron) {
1227 cfg.cls = 'jumbotron';
1232 // - this is applied by the parent..
1234 // cfg.cls = this.cls + '';
1237 if (this.sticky.length) {
1239 var bd = Roo.get(document.body);
1240 if (!bd.hasClass('bootstrap-sticky')) {
1241 bd.addClass('bootstrap-sticky');
1242 Roo.select('html',true).setStyle('height', '100%');
1245 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1249 if (this.well.length) {
1250 switch (this.well) {
1253 cfg.cls +=' well well-' +this.well;
1262 cfg.cls += ' hidden';
1266 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1267 cfg.cls +=' alert alert-' + this.alert;
1272 if (this.panel.length) {
1273 cfg.cls += ' panel panel-' + this.panel;
1275 if (this.header.length) {
1279 if(this.expandable){
1281 cfg.cls = cfg.cls + ' expandable';
1285 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1293 cls : 'panel-title',
1294 html : (this.expandable ? ' ' : '') + this.header
1298 cls: 'panel-header-right',
1304 cls : 'panel-heading',
1305 style : this.expandable ? 'cursor: pointer' : '',
1313 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1318 if (this.footer.length) {
1320 cls : 'panel-footer',
1329 body.html = this.html || cfg.html;
1330 // prefix with the icons..
1332 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1335 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1340 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1341 cfg.cls = 'container';
1347 initEvents: function()
1349 if(this.expandable){
1350 var headerEl = this.headerEl();
1353 headerEl.on('click', this.onToggleClick, this);
1358 this.el.on('click', this.onClick, this);
1363 onToggleClick : function()
1365 var headerEl = this.headerEl();
1381 if(this.fireEvent('expand', this)) {
1383 this.expanded = true;
1385 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1387 this.el.select('.panel-body',true).first().removeClass('hide');
1389 var toggleEl = this.toggleEl();
1395 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1400 collapse : function()
1402 if(this.fireEvent('collapse', this)) {
1404 this.expanded = false;
1406 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1407 this.el.select('.panel-body',true).first().addClass('hide');
1409 var toggleEl = this.toggleEl();
1415 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1419 toggleEl : function()
1421 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1425 return this.el.select('.panel-heading .fa',true).first();
1428 headerEl : function()
1430 if(!this.el || !this.panel.length || !this.header.length){
1434 return this.el.select('.panel-heading',true).first()
1439 if(!this.el || !this.panel.length){
1443 return this.el.select('.panel-body',true).first()
1446 titleEl : function()
1448 if(!this.el || !this.panel.length || !this.header.length){
1452 return this.el.select('.panel-title',true).first();
1455 setTitle : function(v)
1457 var titleEl = this.titleEl();
1463 titleEl.dom.innerHTML = v;
1466 getTitle : function()
1469 var titleEl = this.titleEl();
1475 return titleEl.dom.innerHTML;
1478 setRightTitle : function(v)
1480 var t = this.el.select('.panel-header-right',true).first();
1486 t.dom.innerHTML = v;
1489 onClick : function(e)
1493 this.fireEvent('click', this, e);
1500 * This is BS4's Card element.. - similar to our containers probably..
1504 * @class Roo.bootstrap.Card
1505 * @extends Roo.bootstrap.Component
1506 * Bootstrap Card class
1509 * possible... may not be implemented..
1510 * @cfg {String} header_image src url of image.
1511 * @cfg {String} header
1512 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1514 * @cfg {String} title
1515 * @cfg {String} subtitle
1516 * @cfg {String} html -- html contents - or just use children..
1517 * @cfg {String} footer
1519 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1521 * @cfg {String} margin (0|1|2|3|4|5|auto)
1522 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1523 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1524 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1525 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1526 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1527 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1529 * @cfg {String} padding (0|1|2|3|4|5)
1530 * @cfg {String} padding_top (0|1|2|3|4|5)
1531 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1532 * @cfg {String} padding_left (0|1|2|3|4|5)
1533 * @cfg {String} padding_right (0|1|2|3|4|5)
1534 * @cfg {String} padding_x (0|1|2|3|4|5)
1535 * @cfg {String} padding_y (0|1|2|3|4|5)
1537 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1538 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1539 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1540 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1541 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1543 * @config {Boolean} dragable if this card can be dragged.
1544 * @config {Boolean} drag_group group for drag
1548 * Create a new Container
1549 * @param {Object} config The config object
1552 Roo.bootstrap.Card = function(config){
1553 Roo.bootstrap.Card.superclass.constructor.call(this, config);
1561 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
1566 margin: '', /// may be better in component?
1599 childContainer : false,
1601 layoutCls : function()
1605 Roo.log(this.margin_bottom.length);
1606 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
1607 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
1609 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
1610 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
1612 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
1613 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
1617 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
1618 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
1619 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
1623 // more generic support?
1631 // Roo.log("Call onRender: " + this.xtype);
1632 /* We are looking at something like this.
1634 <img src="..." class="card-img-top" alt="...">
1635 <div class="card-body">
1636 <h5 class="card-title">Card title</h5>
1637 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
1639 >> this bit is really the body...
1640 <div> << we will ad dthis in hopefully it will not break shit.
1642 ** card text does not actually have any styling...
1644 <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>
1647 <a href="#" class="card-link">Card link</a>
1650 <div class="card-footer">
1651 <small class="text-muted">Last updated 3 mins ago</small>
1655 getAutoCreate : function(){
1663 if (this.weight.length && this.weight != 'light') {
1664 cfg.cls += ' text-white';
1666 cfg.cls += ' text-dark'; // need as it's nested..
1668 if (this.weight.length) {
1669 cfg.cls += ' bg-' + this.weight;
1672 cfg.cls += this.layoutCls();
1674 if (this.header.length) {
1676 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
1677 cls : 'card-header',
1678 html : this.header // escape?
1681 if (this.header_image.length) {
1684 cls : 'card-img-top',
1685 src: this.header_image // escape?
1696 if (this.title.length) {
1700 src: this.title // escape?
1704 if (this.subtitle.length) {
1708 src: this.subtitle // escape?
1714 cls : 'roo-card-body-ctr'
1717 if (this.html.length) {
1723 // fixme ? handle objects?
1724 if (this.footer.length) {
1727 cls : 'card-footer',
1728 html: this.footer // escape?
1737 getChildContainer : function()
1743 return this.el.select('.roo-card-body-ctr',true).first();
1746 initEvents: function()
1749 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
1750 containerScroll: true,
1751 ddGroup: this.drag_group || 'default_card_drag_group'
1753 this.dragZone.getDragData = this.getDragData.createDelegate(this);
1759 getDragData : function(e) {
1760 var target = this.getEl();
1762 //this.handleSelection(e);
1767 nodes: this.getEl(),
1772 dragData.ddel = target.dom ; // the div element
1773 Roo.log(target.getWidth( ));
1774 dragData.ddel.style.width = target.getWidth() + 'px';
1792 * @class Roo.bootstrap.Img
1793 * @extends Roo.bootstrap.Component
1794 * Bootstrap Img class
1795 * @cfg {Boolean} imgResponsive false | true
1796 * @cfg {String} border rounded | circle | thumbnail
1797 * @cfg {String} src image source
1798 * @cfg {String} alt image alternative text
1799 * @cfg {String} href a tag href
1800 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1801 * @cfg {String} xsUrl xs image source
1802 * @cfg {String} smUrl sm image source
1803 * @cfg {String} mdUrl md image source
1804 * @cfg {String} lgUrl lg image source
1807 * Create a new Input
1808 * @param {Object} config The config object
1811 Roo.bootstrap.Img = function(config){
1812 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1818 * The img click event for the img.
1819 * @param {Roo.EventObject} e
1825 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1827 imgResponsive: true,
1837 getAutoCreate : function()
1839 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1840 return this.createSingleImg();
1845 cls: 'roo-image-responsive-group',
1850 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1852 if(!_this[size + 'Url']){
1858 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1859 html: _this.html || cfg.html,
1860 src: _this[size + 'Url']
1863 img.cls += ' roo-image-responsive-' + size;
1865 var s = ['xs', 'sm', 'md', 'lg'];
1867 s.splice(s.indexOf(size), 1);
1869 Roo.each(s, function(ss){
1870 img.cls += ' hidden-' + ss;
1873 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1874 cfg.cls += ' img-' + _this.border;
1878 cfg.alt = _this.alt;
1891 a.target = _this.target;
1895 cfg.cn.push((_this.href) ? a : img);
1902 createSingleImg : function()
1906 cls: (this.imgResponsive) ? 'img-responsive' : '',
1908 src : 'about:blank' // just incase src get's set to undefined?!?
1911 cfg.html = this.html || cfg.html;
1913 cfg.src = this.src || cfg.src;
1915 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1916 cfg.cls += ' img-' + this.border;
1933 a.target = this.target;
1938 return (this.href) ? a : cfg;
1941 initEvents: function()
1944 this.el.on('click', this.onClick, this);
1949 onClick : function(e)
1951 Roo.log('img onclick');
1952 this.fireEvent('click', this, e);
1955 * Sets the url of the image - used to update it
1956 * @param {String} url the url of the image
1959 setSrc : function(url)
1963 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1964 this.el.dom.src = url;
1968 this.el.select('img', true).first().dom.src = url;
1984 * @class Roo.bootstrap.Link
1985 * @extends Roo.bootstrap.Component
1986 * Bootstrap Link Class
1987 * @cfg {String} alt image alternative text
1988 * @cfg {String} href a tag href
1989 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1990 * @cfg {String} html the content of the link.
1991 * @cfg {String} anchor name for the anchor link
1992 * @cfg {String} fa - favicon
1994 * @cfg {Boolean} preventDefault (true | false) default false
1998 * Create a new Input
1999 * @param {Object} config The config object
2002 Roo.bootstrap.Link = function(config){
2003 Roo.bootstrap.Link.superclass.constructor.call(this, config);
2009 * The img click event for the img.
2010 * @param {Roo.EventObject} e
2016 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
2020 preventDefault: false,
2026 getAutoCreate : function()
2028 var html = this.html || '';
2030 if (this.fa !== false) {
2031 html = '<i class="fa fa-' + this.fa + '"></i>';
2036 // anchor's do not require html/href...
2037 if (this.anchor === false) {
2039 cfg.href = this.href || '#';
2041 cfg.name = this.anchor;
2042 if (this.html !== false || this.fa !== false) {
2045 if (this.href !== false) {
2046 cfg.href = this.href;
2050 if(this.alt !== false){
2055 if(this.target !== false) {
2056 cfg.target = this.target;
2062 initEvents: function() {
2064 if(!this.href || this.preventDefault){
2065 this.el.on('click', this.onClick, this);
2069 onClick : function(e)
2071 if(this.preventDefault){
2074 //Roo.log('img onclick');
2075 this.fireEvent('click', this, e);
2088 * @class Roo.bootstrap.Header
2089 * @extends Roo.bootstrap.Component
2090 * Bootstrap Header class
2091 * @cfg {String} html content of header
2092 * @cfg {Number} level (1|2|3|4|5|6) default 1
2095 * Create a new Header
2096 * @param {Object} config The config object
2100 Roo.bootstrap.Header = function(config){
2101 Roo.bootstrap.Header.superclass.constructor.call(this, config);
2104 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
2112 getAutoCreate : function(){
2117 tag: 'h' + (1 *this.level),
2118 html: this.html || ''
2130 * Ext JS Library 1.1.1
2131 * Copyright(c) 2006-2007, Ext JS, LLC.
2133 * Originally Released Under LGPL - original licence link has changed is not relivant.
2136 * <script type="text/javascript">
2140 * @class Roo.bootstrap.MenuMgr
2141 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
2144 Roo.bootstrap.MenuMgr = function(){
2145 var menus, active, groups = {}, attached = false, lastShow = new Date();
2147 // private - called when first menu is created
2150 active = new Roo.util.MixedCollection();
2151 Roo.get(document).addKeyListener(27, function(){
2152 if(active.length > 0){
2160 if(active && active.length > 0){
2161 var c = active.clone();
2171 if(active.length < 1){
2172 Roo.get(document).un("mouseup", onMouseDown);
2180 var last = active.last();
2181 lastShow = new Date();
2184 Roo.get(document).on("mouseup", onMouseDown);
2189 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
2190 m.parentMenu.activeChild = m;
2191 }else if(last && last.isVisible()){
2192 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
2197 function onBeforeHide(m){
2199 m.activeChild.hide();
2201 if(m.autoHideTimer){
2202 clearTimeout(m.autoHideTimer);
2203 delete m.autoHideTimer;
2208 function onBeforeShow(m){
2209 var pm = m.parentMenu;
2210 if(!pm && !m.allowOtherMenus){
2212 }else if(pm && pm.activeChild && active != m){
2213 pm.activeChild.hide();
2217 // private this should really trigger on mouseup..
2218 function onMouseDown(e){
2219 Roo.log("on Mouse Up");
2221 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
2222 Roo.log("MenuManager hideAll");
2231 function onBeforeCheck(mi, state){
2233 var g = groups[mi.group];
2234 for(var i = 0, l = g.length; i < l; i++){
2236 g[i].setChecked(false);
2245 * Hides all menus that are currently visible
2247 hideAll : function(){
2252 register : function(menu){
2256 menus[menu.id] = menu;
2257 menu.on("beforehide", onBeforeHide);
2258 menu.on("hide", onHide);
2259 menu.on("beforeshow", onBeforeShow);
2260 menu.on("show", onShow);
2262 if(g && menu.events["checkchange"]){
2266 groups[g].push(menu);
2267 menu.on("checkchange", onCheck);
2272 * Returns a {@link Roo.menu.Menu} object
2273 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
2274 * be used to generate and return a new Menu instance.
2276 get : function(menu){
2277 if(typeof menu == "string"){ // menu id
2279 }else if(menu.events){ // menu instance
2282 /*else if(typeof menu.length == 'number'){ // array of menu items?
2283 return new Roo.bootstrap.Menu({items:menu});
2284 }else{ // otherwise, must be a config
2285 return new Roo.bootstrap.Menu(menu);
2292 unregister : function(menu){
2293 delete menus[menu.id];
2294 menu.un("beforehide", onBeforeHide);
2295 menu.un("hide", onHide);
2296 menu.un("beforeshow", onBeforeShow);
2297 menu.un("show", onShow);
2299 if(g && menu.events["checkchange"]){
2300 groups[g].remove(menu);
2301 menu.un("checkchange", onCheck);
2306 registerCheckable : function(menuItem){
2307 var g = menuItem.group;
2312 groups[g].push(menuItem);
2313 menuItem.on("beforecheckchange", onBeforeCheck);
2318 unregisterCheckable : function(menuItem){
2319 var g = menuItem.group;
2321 groups[g].remove(menuItem);
2322 menuItem.un("beforecheckchange", onBeforeCheck);
2334 * @class Roo.bootstrap.Menu
2335 * @extends Roo.bootstrap.Component
2336 * Bootstrap Menu class - container for MenuItems
2337 * @cfg {String} type (dropdown|treeview|submenu) type of menu
2338 * @cfg {bool} hidden if the menu should be hidden when rendered.
2339 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
2340 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
2344 * @param {Object} config The config object
2348 Roo.bootstrap.Menu = function(config){
2349 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2350 if (this.registerMenu && this.type != 'treeview') {
2351 Roo.bootstrap.MenuMgr.register(this);
2358 * Fires before this menu is displayed (return false to block)
2359 * @param {Roo.menu.Menu} this
2364 * Fires before this menu is hidden (return false to block)
2365 * @param {Roo.menu.Menu} this
2370 * Fires after this menu is displayed
2371 * @param {Roo.menu.Menu} this
2376 * Fires after this menu is hidden
2377 * @param {Roo.menu.Menu} this
2382 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2383 * @param {Roo.menu.Menu} this
2384 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2385 * @param {Roo.EventObject} e
2390 * Fires when the mouse is hovering over this menu
2391 * @param {Roo.menu.Menu} this
2392 * @param {Roo.EventObject} e
2393 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2398 * Fires when the mouse exits this menu
2399 * @param {Roo.menu.Menu} this
2400 * @param {Roo.EventObject} e
2401 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2406 * Fires when a menu item contained in this menu is clicked
2407 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2408 * @param {Roo.EventObject} e
2412 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2415 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2419 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2422 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2424 registerMenu : true,
2426 menuItems :false, // stores the menu items..
2436 getChildContainer : function() {
2440 getAutoCreate : function(){
2442 //if (['right'].indexOf(this.align)!==-1) {
2443 // cfg.cn[1].cls += ' pull-right'
2449 cls : 'dropdown-menu' ,
2450 style : 'z-index:1000'
2454 if (this.type === 'submenu') {
2455 cfg.cls = 'submenu active';
2457 if (this.type === 'treeview') {
2458 cfg.cls = 'treeview-menu';
2463 initEvents : function() {
2465 // Roo.log("ADD event");
2466 // Roo.log(this.triggerEl.dom);
2468 this.triggerEl.on('click', this.onTriggerClick, this);
2470 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2473 if (this.triggerEl.hasClass('nav-item')) {
2474 // dropdown toggle on the 'a' in BS4?
2475 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2477 this.triggerEl.addClass('dropdown-toggle');
2480 this.el.on('touchstart' , this.onTouch, this);
2482 this.el.on('click' , this.onClick, this);
2484 this.el.on("mouseover", this.onMouseOver, this);
2485 this.el.on("mouseout", this.onMouseOut, this);
2489 findTargetItem : function(e)
2491 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2495 //Roo.log(t); Roo.log(t.id);
2497 //Roo.log(this.menuitems);
2498 return this.menuitems.get(t.id);
2500 //return this.items.get(t.menuItemId);
2506 onTouch : function(e)
2508 Roo.log("menu.onTouch");
2509 //e.stopEvent(); this make the user popdown broken
2513 onClick : function(e)
2515 Roo.log("menu.onClick");
2517 var t = this.findTargetItem(e);
2518 if(!t || t.isContainer){
2523 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2524 if(t == this.activeItem && t.shouldDeactivate(e)){
2525 this.activeItem.deactivate();
2526 delete this.activeItem;
2530 this.setActiveItem(t, true);
2538 Roo.log('pass click event');
2542 this.fireEvent("click", this, t, e);
2546 if(!t.href.length || t.href == '#'){
2547 (function() { _this.hide(); }).defer(100);
2552 onMouseOver : function(e){
2553 var t = this.findTargetItem(e);
2556 // if(t.canActivate && !t.disabled){
2557 // this.setActiveItem(t, true);
2561 this.fireEvent("mouseover", this, e, t);
2563 isVisible : function(){
2564 return !this.hidden;
2566 onMouseOut : function(e){
2567 var t = this.findTargetItem(e);
2570 // if(t == this.activeItem && t.shouldDeactivate(e)){
2571 // this.activeItem.deactivate();
2572 // delete this.activeItem;
2575 this.fireEvent("mouseout", this, e, t);
2580 * Displays this menu relative to another element
2581 * @param {String/HTMLElement/Roo.Element} element The element to align to
2582 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2583 * the element (defaults to this.defaultAlign)
2584 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2586 show : function(el, pos, parentMenu)
2588 if (false === this.fireEvent("beforeshow", this)) {
2589 Roo.log("show canceled");
2592 this.parentMenu = parentMenu;
2597 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2600 * Displays this menu at a specific xy position
2601 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2602 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2604 showAt : function(xy, parentMenu, /* private: */_e){
2605 this.parentMenu = parentMenu;
2610 this.fireEvent("beforeshow", this);
2611 //xy = this.el.adjustForConstraints(xy);
2615 this.hideMenuItems();
2616 this.hidden = false;
2617 this.triggerEl.addClass('open');
2618 this.el.addClass('show');
2620 // reassign x when hitting right
2621 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2622 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2625 // reassign y when hitting bottom
2626 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2627 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2630 // but the list may align on trigger left or trigger top... should it be a properity?
2632 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2637 this.fireEvent("show", this);
2643 this.doFocus.defer(50, this);
2647 doFocus : function(){
2649 this.focusEl.focus();
2654 * Hides this menu and optionally all parent menus
2655 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2657 hide : function(deep)
2659 if (false === this.fireEvent("beforehide", this)) {
2660 Roo.log("hide canceled");
2663 this.hideMenuItems();
2664 if(this.el && this.isVisible()){
2666 if(this.activeItem){
2667 this.activeItem.deactivate();
2668 this.activeItem = null;
2670 this.triggerEl.removeClass('open');;
2671 this.el.removeClass('show');
2673 this.fireEvent("hide", this);
2675 if(deep === true && this.parentMenu){
2676 this.parentMenu.hide(true);
2680 onTriggerClick : function(e)
2682 Roo.log('trigger click');
2684 var target = e.getTarget();
2686 Roo.log(target.nodeName.toLowerCase());
2688 if(target.nodeName.toLowerCase() === 'i'){
2694 onTriggerPress : function(e)
2696 Roo.log('trigger press');
2697 //Roo.log(e.getTarget());
2698 // Roo.log(this.triggerEl.dom);
2700 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2701 var pel = Roo.get(e.getTarget());
2702 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2703 Roo.log('is treeview or dropdown?');
2707 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2711 if (this.isVisible()) {
2716 this.show(this.triggerEl, '?', false);
2719 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2726 hideMenuItems : function()
2728 Roo.log("hide Menu Items");
2733 this.el.select('.open',true).each(function(aa) {
2735 aa.removeClass('open');
2739 addxtypeChild : function (tree, cntr) {
2740 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2742 this.menuitems.add(comp);
2754 this.getEl().dom.innerHTML = '';
2755 this.menuitems.clear();
2769 * @class Roo.bootstrap.MenuItem
2770 * @extends Roo.bootstrap.Component
2771 * Bootstrap MenuItem class
2772 * @cfg {String} html the menu label
2773 * @cfg {String} href the link
2774 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2775 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2776 * @cfg {Boolean} active used on sidebars to highlight active itesm
2777 * @cfg {String} fa favicon to show on left of menu item.
2778 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2782 * Create a new MenuItem
2783 * @param {Object} config The config object
2787 Roo.bootstrap.MenuItem = function(config){
2788 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2793 * The raw click event for the entire grid.
2794 * @param {Roo.bootstrap.MenuItem} this
2795 * @param {Roo.EventObject} e
2801 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2805 preventDefault: false,
2806 isContainer : false,
2810 getAutoCreate : function(){
2812 if(this.isContainer){
2815 cls: 'dropdown-menu-item '
2825 cls : 'dropdown-item',
2830 if (this.fa !== false) {
2833 cls : 'fa fa-' + this.fa
2842 cls: 'dropdown-menu-item',
2845 if (this.parent().type == 'treeview') {
2846 cfg.cls = 'treeview-menu';
2849 cfg.cls += ' active';
2854 anc.href = this.href || cfg.cn[0].href ;
2855 ctag.html = this.html || cfg.cn[0].html ;
2859 initEvents: function()
2861 if (this.parent().type == 'treeview') {
2862 this.el.select('a').on('click', this.onClick, this);
2866 this.menu.parentType = this.xtype;
2867 this.menu.triggerEl = this.el;
2868 this.menu = this.addxtype(Roo.apply({}, this.menu));
2872 onClick : function(e)
2874 Roo.log('item on click ');
2876 if(this.preventDefault){
2879 //this.parent().hideMenuItems();
2881 this.fireEvent('click', this, e);
2900 * @class Roo.bootstrap.MenuSeparator
2901 * @extends Roo.bootstrap.Component
2902 * Bootstrap MenuSeparator class
2905 * Create a new MenuItem
2906 * @param {Object} config The config object
2910 Roo.bootstrap.MenuSeparator = function(config){
2911 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2914 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2916 getAutoCreate : function(){
2935 * @class Roo.bootstrap.Modal
2936 * @extends Roo.bootstrap.Component
2937 * Bootstrap Modal class
2938 * @cfg {String} title Title of dialog
2939 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2940 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2941 * @cfg {Boolean} specificTitle default false
2942 * @cfg {Array} buttons Array of buttons or standard button set..
2943 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2944 * @cfg {Boolean} animate default true
2945 * @cfg {Boolean} allow_close default true
2946 * @cfg {Boolean} fitwindow default false
2947 * @cfg {Number} width fixed width - usefull for chrome extension only really.
2948 * @cfg {Number} height fixed height - usefull for chrome extension only really.
2949 * @cfg {String} size (sm|lg) default empty
2950 * @cfg {Number} max_width set the max width of modal
2954 * Create a new Modal Dialog
2955 * @param {Object} config The config object
2958 Roo.bootstrap.Modal = function(config){
2959 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2964 * The raw btnclick event for the button
2965 * @param {Roo.EventObject} e
2970 * Fire when dialog resize
2971 * @param {Roo.bootstrap.Modal} this
2972 * @param {Roo.EventObject} e
2976 this.buttons = this.buttons || [];
2979 this.tmpl = Roo.factory(this.tmpl);
2984 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2986 title : 'test dialog',
2996 specificTitle: false,
2998 buttonPosition: 'right',
3021 onRender : function(ct, position)
3023 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3026 var cfg = Roo.apply({}, this.getAutoCreate());
3029 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3031 //if (!cfg.name.length) {
3035 cfg.cls += ' ' + this.cls;
3038 cfg.style = this.style;
3040 this.el = Roo.get(document.body).createChild(cfg, position);
3042 //var type = this.el.dom.type;
3045 if(this.tabIndex !== undefined){
3046 this.el.dom.setAttribute('tabIndex', this.tabIndex);
3049 this.dialogEl = this.el.select('.modal-dialog',true).first();
3050 this.bodyEl = this.el.select('.modal-body',true).first();
3051 this.closeEl = this.el.select('.modal-header .close', true).first();
3052 this.headerEl = this.el.select('.modal-header',true).first();
3053 this.titleEl = this.el.select('.modal-title',true).first();
3054 this.footerEl = this.el.select('.modal-footer',true).first();
3056 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3058 //this.el.addClass("x-dlg-modal");
3060 if (this.buttons.length) {
3061 Roo.each(this.buttons, function(bb) {
3062 var b = Roo.apply({}, bb);
3063 b.xns = b.xns || Roo.bootstrap;
3064 b.xtype = b.xtype || 'Button';
3065 if (typeof(b.listeners) == 'undefined') {
3066 b.listeners = { click : this.onButtonClick.createDelegate(this) };
3069 var btn = Roo.factory(b);
3071 btn.render(this.getButtonContainer());
3075 // render the children.
3078 if(typeof(this.items) != 'undefined'){
3079 var items = this.items;
3082 for(var i =0;i < items.length;i++) {
3083 nitems.push(this.addxtype(Roo.apply({}, items[i])));
3087 this.items = nitems;
3089 // where are these used - they used to be body/close/footer
3093 //this.el.addClass([this.fieldClass, this.cls]);
3097 getAutoCreate : function()
3099 // we will default to modal-body-overflow - might need to remove or make optional later.
3101 cls : 'modal-body enable-modal-body-overflow ',
3102 html : this.html || ''
3107 cls : 'modal-title',
3111 if(this.specificTitle){
3117 if (this.allow_close && Roo.bootstrap.version == 3) {
3127 if (this.allow_close && Roo.bootstrap.version == 4) {
3137 if(this.size.length){
3138 size = 'modal-' + this.size;
3141 var footer = Roo.bootstrap.version == 3 ?
3143 cls : 'modal-footer',
3147 cls: 'btn-' + this.buttonPosition
3152 { // BS4 uses mr-auto on left buttons....
3153 cls : 'modal-footer'
3164 cls: "modal-dialog " + size,
3167 cls : "modal-content",
3170 cls : 'modal-header',
3185 modal.cls += ' fade';
3191 getChildContainer : function() {
3196 getButtonContainer : function() {
3198 return Roo.bootstrap.version == 4 ?
3199 this.el.select('.modal-footer',true).first()
3200 : this.el.select('.modal-footer div',true).first();
3203 initEvents : function()
3205 if (this.allow_close) {
3206 this.closeEl.on('click', this.hide, this);
3208 Roo.EventManager.onWindowResize(this.resize, this, true);
3216 this.maskEl.setSize(
3217 Roo.lib.Dom.getViewWidth(true),
3218 Roo.lib.Dom.getViewHeight(true)
3221 if (this.fitwindow) {
3225 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
3226 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
3231 if(this.max_width !== 0) {
3233 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
3236 this.setSize(w, this.height);
3240 if(this.max_height) {
3241 this.setSize(w,Math.min(
3243 Roo.lib.Dom.getViewportHeight(true) - 60
3249 if(!this.fit_content) {
3250 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
3254 this.setSize(w, Math.min(
3256 this.headerEl.getHeight() +
3257 this.footerEl.getHeight() +
3258 this.getChildHeight(this.bodyEl.dom.childNodes),
3259 Roo.lib.Dom.getViewportHeight(true) - 60)
3265 setSize : function(w,h)
3276 if (!this.rendered) {
3280 //this.el.setStyle('display', 'block');
3281 this.el.removeClass('hideing');
3282 this.el.dom.style.display='block';
3284 Roo.get(document.body).addClass('modal-open');
3286 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
3289 this.el.addClass('show');
3290 this.el.addClass('in');
3293 this.el.addClass('show');
3294 this.el.addClass('in');
3297 // not sure how we can show data in here..
3299 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3302 Roo.get(document.body).addClass("x-body-masked");
3304 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3305 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3306 this.maskEl.dom.style.display = 'block';
3307 this.maskEl.addClass('show');
3312 this.fireEvent('show', this);
3314 // set zindex here - otherwise it appears to be ignored...
3315 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3318 this.items.forEach( function(e) {
3319 e.layout ? e.layout() : false;
3327 if(this.fireEvent("beforehide", this) !== false){
3329 this.maskEl.removeClass('show');
3331 this.maskEl.dom.style.display = '';
3332 Roo.get(document.body).removeClass("x-body-masked");
3333 this.el.removeClass('in');
3334 this.el.select('.modal-dialog', true).first().setStyle('transform','');
3336 if(this.animate){ // why
3337 this.el.addClass('hideing');
3338 this.el.removeClass('show');
3340 if (!this.el.hasClass('hideing')) {
3341 return; // it's been shown again...
3344 this.el.dom.style.display='';
3346 Roo.get(document.body).removeClass('modal-open');
3347 this.el.removeClass('hideing');
3351 this.el.removeClass('show');
3352 this.el.dom.style.display='';
3353 Roo.get(document.body).removeClass('modal-open');
3356 this.fireEvent('hide', this);
3359 isVisible : function()
3362 return this.el.hasClass('show') && !this.el.hasClass('hideing');
3366 addButton : function(str, cb)
3370 var b = Roo.apply({}, { html : str } );
3371 b.xns = b.xns || Roo.bootstrap;
3372 b.xtype = b.xtype || 'Button';
3373 if (typeof(b.listeners) == 'undefined') {
3374 b.listeners = { click : cb.createDelegate(this) };
3377 var btn = Roo.factory(b);
3379 btn.render(this.getButtonContainer());
3385 setDefaultButton : function(btn)
3387 //this.el.select('.modal-footer').()
3390 resizeTo: function(w,h)
3392 this.dialogEl.setWidth(w);
3394 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
3396 this.bodyEl.setHeight(h - diff);
3398 this.fireEvent('resize', this);
3401 setContentSize : function(w, h)
3405 onButtonClick: function(btn,e)
3408 this.fireEvent('btnclick', btn.name, e);
3411 * Set the title of the Dialog
3412 * @param {String} str new Title
3414 setTitle: function(str) {
3415 this.titleEl.dom.innerHTML = str;
3418 * Set the body of the Dialog
3419 * @param {String} str new Title
3421 setBody: function(str) {
3422 this.bodyEl.dom.innerHTML = str;
3425 * Set the body of the Dialog using the template
3426 * @param {Obj} data - apply this data to the template and replace the body contents.
3428 applyBody: function(obj)
3431 Roo.log("Error - using apply Body without a template");
3434 this.tmpl.overwrite(this.bodyEl, obj);
3437 getChildHeight : function(child_nodes)
3441 child_nodes.length == 0
3446 var child_height = 0;
3448 for(var i = 0; i < child_nodes.length; i++) {
3451 * for modal with tabs...
3452 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3454 var layout_childs = child_nodes[i].childNodes;
3456 for(var j = 0; j < layout_childs.length; j++) {
3458 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3460 var layout_body_childs = layout_childs[j].childNodes;
3462 for(var k = 0; k < layout_body_childs.length; k++) {
3464 if(layout_body_childs[k].classList.contains('navbar')) {
3465 child_height += layout_body_childs[k].offsetHeight;
3469 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3471 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3473 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3475 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3476 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3491 child_height += child_nodes[i].offsetHeight;
3492 // Roo.log(child_nodes[i].offsetHeight);
3495 return child_height;
3501 Roo.apply(Roo.bootstrap.Modal, {
3503 * Button config that displays a single OK button
3512 * Button config that displays Yes and No buttons
3528 * Button config that displays OK and Cancel buttons
3543 * Button config that displays Yes, No and Cancel buttons
3567 * messagebox - can be used as a replace
3571 * @class Roo.MessageBox
3572 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3576 Roo.Msg.alert('Status', 'Changes saved successfully.');
3578 // Prompt for user data:
3579 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3581 // process text value...
3585 // Show a dialog using config options:
3587 title:'Save Changes?',
3588 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3589 buttons: Roo.Msg.YESNOCANCEL,
3596 Roo.bootstrap.MessageBox = function(){
3597 var dlg, opt, mask, waitTimer;
3598 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3599 var buttons, activeTextEl, bwidth;
3603 var handleButton = function(button){
3605 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3609 var handleHide = function(){
3611 dlg.el.removeClass(opt.cls);
3614 // Roo.TaskMgr.stop(waitTimer);
3615 // waitTimer = null;
3620 var updateButtons = function(b){
3623 buttons["ok"].hide();
3624 buttons["cancel"].hide();
3625 buttons["yes"].hide();
3626 buttons["no"].hide();
3627 dlg.footerEl.hide();
3631 dlg.footerEl.show();
3632 for(var k in buttons){
3633 if(typeof buttons[k] != "function"){
3636 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3637 width += buttons[k].el.getWidth()+15;
3647 var handleEsc = function(d, k, e){
3648 if(opt && opt.closable !== false){
3658 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3659 * @return {Roo.BasicDialog} The BasicDialog element
3661 getDialog : function(){
3663 dlg = new Roo.bootstrap.Modal( {
3666 //constraintoviewport:false,
3668 //collapsible : false,
3673 //buttonAlign:"center",
3674 closeClick : function(){
3675 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3678 handleButton("cancel");
3683 dlg.on("hide", handleHide);
3685 //dlg.addKeyListener(27, handleEsc);
3687 this.buttons = buttons;
3688 var bt = this.buttonText;
3689 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3690 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3691 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3692 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3694 bodyEl = dlg.bodyEl.createChild({
3696 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3697 '<textarea class="roo-mb-textarea"></textarea>' +
3698 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3700 msgEl = bodyEl.dom.firstChild;
3701 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3702 textboxEl.enableDisplayMode();
3703 textboxEl.addKeyListener([10,13], function(){
3704 if(dlg.isVisible() && opt && opt.buttons){
3707 }else if(opt.buttons.yes){
3708 handleButton("yes");
3712 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3713 textareaEl.enableDisplayMode();
3714 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3715 progressEl.enableDisplayMode();
3717 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3718 var pf = progressEl.dom.firstChild;
3720 pp = Roo.get(pf.firstChild);
3721 pp.setHeight(pf.offsetHeight);
3729 * Updates the message box body text
3730 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3731 * the XHTML-compliant non-breaking space character '&#160;')
3732 * @return {Roo.MessageBox} This message box
3734 updateText : function(text)
3736 if(!dlg.isVisible() && !opt.width){
3737 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3738 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3740 msgEl.innerHTML = text || ' ';
3742 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3743 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3745 Math.min(opt.width || cw , this.maxWidth),
3746 Math.max(opt.minWidth || this.minWidth, bwidth)
3749 activeTextEl.setWidth(w);
3751 if(dlg.isVisible()){
3752 dlg.fixedcenter = false;
3754 // to big, make it scroll. = But as usual stupid IE does not support
3757 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3758 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3759 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3761 bodyEl.dom.style.height = '';
3762 bodyEl.dom.style.overflowY = '';
3765 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3767 bodyEl.dom.style.overflowX = '';
3770 dlg.setContentSize(w, bodyEl.getHeight());
3771 if(dlg.isVisible()){
3772 dlg.fixedcenter = true;
3778 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3779 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3780 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3781 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3782 * @return {Roo.MessageBox} This message box
3784 updateProgress : function(value, text){
3786 this.updateText(text);
3789 if (pp) { // weird bug on my firefox - for some reason this is not defined
3790 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3791 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3797 * Returns true if the message box is currently displayed
3798 * @return {Boolean} True if the message box is visible, else false
3800 isVisible : function(){
3801 return dlg && dlg.isVisible();
3805 * Hides the message box if it is displayed
3808 if(this.isVisible()){
3814 * Displays a new message box, or reinitializes an existing message box, based on the config options
3815 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3816 * The following config object properties are supported:
3818 Property Type Description
3819 ---------- --------------- ------------------------------------------------------------------------------------
3820 animEl String/Element An id or Element from which the message box should animate as it opens and
3821 closes (defaults to undefined)
3822 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3823 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3824 closable Boolean False to hide the top-right close button (defaults to true). Note that
3825 progress and wait dialogs will ignore this property and always hide the
3826 close button as they can only be closed programmatically.
3827 cls String A custom CSS class to apply to the message box element
3828 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3829 displayed (defaults to 75)
3830 fn Function A callback function to execute after closing the dialog. The arguments to the
3831 function will be btn (the name of the button that was clicked, if applicable,
3832 e.g. "ok"), and text (the value of the active text field, if applicable).
3833 Progress and wait dialogs will ignore this option since they do not respond to
3834 user actions and can only be closed programmatically, so any required function
3835 should be called by the same code after it closes the dialog.
3836 icon String A CSS class that provides a background image to be used as an icon for
3837 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3838 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3839 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3840 modal Boolean False to allow user interaction with the page while the message box is
3841 displayed (defaults to true)
3842 msg String A string that will replace the existing message box body text (defaults
3843 to the XHTML-compliant non-breaking space character ' ')
3844 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3845 progress Boolean True to display a progress bar (defaults to false)
3846 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3847 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3848 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3849 title String The title text
3850 value String The string value to set into the active textbox element if displayed
3851 wait Boolean True to display a progress bar (defaults to false)
3852 width Number The width of the dialog in pixels
3859 msg: 'Please enter your address:',
3861 buttons: Roo.MessageBox.OKCANCEL,
3864 animEl: 'addAddressBtn'
3867 * @param {Object} config Configuration options
3868 * @return {Roo.MessageBox} This message box
3870 show : function(options)
3873 // this causes nightmares if you show one dialog after another
3874 // especially on callbacks..
3876 if(this.isVisible()){
3879 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3880 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3881 Roo.log("New Dialog Message:" + options.msg )
3882 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3883 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3886 var d = this.getDialog();
3888 d.setTitle(opt.title || " ");
3889 d.closeEl.setDisplayed(opt.closable !== false);
3890 activeTextEl = textboxEl;
3891 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3896 textareaEl.setHeight(typeof opt.multiline == "number" ?
3897 opt.multiline : this.defaultTextHeight);
3898 activeTextEl = textareaEl;
3907 progressEl.setDisplayed(opt.progress === true);
3909 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3911 this.updateProgress(0);
3912 activeTextEl.dom.value = opt.value || "";
3914 dlg.setDefaultButton(activeTextEl);
3916 var bs = opt.buttons;
3920 }else if(bs && bs.yes){
3921 db = buttons["yes"];
3923 dlg.setDefaultButton(db);
3925 bwidth = updateButtons(opt.buttons);
3926 this.updateText(opt.msg);
3928 d.el.addClass(opt.cls);
3930 d.proxyDrag = opt.proxyDrag === true;
3931 d.modal = opt.modal !== false;
3932 d.mask = opt.modal !== false ? mask : false;
3934 // force it to the end of the z-index stack so it gets a cursor in FF
3935 document.body.appendChild(dlg.el.dom);
3936 d.animateTarget = null;
3937 d.show(options.animEl);
3943 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3944 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3945 * and closing the message box when the process is complete.
3946 * @param {String} title The title bar text
3947 * @param {String} msg The message box body text
3948 * @return {Roo.MessageBox} This message box
3950 progress : function(title, msg){
3957 minWidth: this.minProgressWidth,
3964 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3965 * If a callback function is passed it will be called after the user clicks the button, and the
3966 * id of the button that was clicked will be passed as the only parameter to the callback
3967 * (could also be the top-right close button).
3968 * @param {String} title The title bar text
3969 * @param {String} msg The message box body text
3970 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3971 * @param {Object} scope (optional) The scope of the callback function
3972 * @return {Roo.MessageBox} This message box
3974 alert : function(title, msg, fn, scope)
3989 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3990 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3991 * You are responsible for closing the message box when the process is complete.
3992 * @param {String} msg The message box body text
3993 * @param {String} title (optional) The title bar text
3994 * @return {Roo.MessageBox} This message box
3996 wait : function(msg, title){
4007 waitTimer = Roo.TaskMgr.start({
4009 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
4017 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
4018 * If a callback function is passed it will be called after the user clicks either button, and the id of the
4019 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
4020 * @param {String} title The title bar text
4021 * @param {String} msg The message box body text
4022 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4023 * @param {Object} scope (optional) The scope of the callback function
4024 * @return {Roo.MessageBox} This message box
4026 confirm : function(title, msg, fn, scope){
4030 buttons: this.YESNO,
4039 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
4040 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
4041 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
4042 * (could also be the top-right close button) and the text that was entered will be passed as the two
4043 * parameters to the callback.
4044 * @param {String} title The title bar text
4045 * @param {String} msg The message box body text
4046 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4047 * @param {Object} scope (optional) The scope of the callback function
4048 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
4049 * property, or the height in pixels to create the textbox (defaults to false / single-line)
4050 * @return {Roo.MessageBox} This message box
4052 prompt : function(title, msg, fn, scope, multiline){
4056 buttons: this.OKCANCEL,
4061 multiline: multiline,
4068 * Button config that displays a single OK button
4073 * Button config that displays Yes and No buttons
4076 YESNO : {yes:true, no:true},
4078 * Button config that displays OK and Cancel buttons
4081 OKCANCEL : {ok:true, cancel:true},
4083 * Button config that displays Yes, No and Cancel buttons
4086 YESNOCANCEL : {yes:true, no:true, cancel:true},
4089 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
4092 defaultTextHeight : 75,
4094 * The maximum width in pixels of the message box (defaults to 600)
4099 * The minimum width in pixels of the message box (defaults to 100)
4104 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
4105 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
4108 minProgressWidth : 250,
4110 * An object containing the default button text strings that can be overriden for localized language support.
4111 * Supported properties are: ok, cancel, yes and no.
4112 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
4125 * Shorthand for {@link Roo.MessageBox}
4127 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
4128 Roo.Msg = Roo.Msg || Roo.MessageBox;
4137 * @class Roo.bootstrap.Navbar
4138 * @extends Roo.bootstrap.Component
4139 * Bootstrap Navbar class
4142 * Create a new Navbar
4143 * @param {Object} config The config object
4147 Roo.bootstrap.Navbar = function(config){
4148 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
4152 * @event beforetoggle
4153 * Fire before toggle the menu
4154 * @param {Roo.EventObject} e
4156 "beforetoggle" : true
4160 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
4169 getAutoCreate : function(){
4172 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
4176 initEvents :function ()
4178 //Roo.log(this.el.select('.navbar-toggle',true));
4179 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
4186 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
4188 var size = this.el.getSize();
4189 this.maskEl.setSize(size.width, size.height);
4190 this.maskEl.enableDisplayMode("block");
4199 getChildContainer : function()
4201 if (this.el && this.el.select('.collapse').getCount()) {
4202 return this.el.select('.collapse',true).first();
4217 onToggle : function()
4220 if(this.fireEvent('beforetoggle', this) === false){
4223 var ce = this.el.select('.navbar-collapse',true).first();
4225 if (!ce.hasClass('show')) {
4235 * Expand the navbar pulldown
4237 expand : function ()
4240 var ce = this.el.select('.navbar-collapse',true).first();
4241 if (ce.hasClass('collapsing')) {
4244 ce.dom.style.height = '';
4246 ce.addClass('in'); // old...
4247 ce.removeClass('collapse');
4248 ce.addClass('show');
4249 var h = ce.getHeight();
4251 ce.removeClass('show');
4252 // at this point we should be able to see it..
4253 ce.addClass('collapsing');
4255 ce.setHeight(0); // resize it ...
4256 ce.on('transitionend', function() {
4257 //Roo.log('done transition');
4258 ce.removeClass('collapsing');
4259 ce.addClass('show');
4260 ce.removeClass('collapse');
4262 ce.dom.style.height = '';
4263 }, this, { single: true} );
4265 ce.dom.scrollTop = 0;
4268 * Collapse the navbar pulldown
4270 collapse : function()
4272 var ce = this.el.select('.navbar-collapse',true).first();
4274 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
4275 // it's collapsed or collapsing..
4278 ce.removeClass('in'); // old...
4279 ce.setHeight(ce.getHeight());
4280 ce.removeClass('show');
4281 ce.addClass('collapsing');
4283 ce.on('transitionend', function() {
4284 ce.dom.style.height = '';
4285 ce.removeClass('collapsing');
4286 ce.addClass('collapse');
4287 }, this, { single: true} );
4307 * @class Roo.bootstrap.NavSimplebar
4308 * @extends Roo.bootstrap.Navbar
4309 * Bootstrap Sidebar class
4311 * @cfg {Boolean} inverse is inverted color
4313 * @cfg {String} type (nav | pills | tabs)
4314 * @cfg {Boolean} arrangement stacked | justified
4315 * @cfg {String} align (left | right) alignment
4317 * @cfg {Boolean} main (true|false) main nav bar? default false
4318 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4320 * @cfg {String} tag (header|footer|nav|div) default is nav
4322 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4326 * Create a new Sidebar
4327 * @param {Object} config The config object
4331 Roo.bootstrap.NavSimplebar = function(config){
4332 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4335 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
4351 getAutoCreate : function(){
4355 tag : this.tag || 'div',
4356 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
4358 if (['light','white'].indexOf(this.weight) > -1) {
4359 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4361 cfg.cls += ' bg-' + this.weight;
4364 cfg.cls += ' navbar-inverse';
4368 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4370 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4379 cls: 'nav nav-' + this.xtype,
4385 this.type = this.type || 'nav';
4386 if (['tabs','pills'].indexOf(this.type) != -1) {
4387 cfg.cn[0].cls += ' nav-' + this.type
4391 if (this.type!=='nav') {
4392 Roo.log('nav type must be nav/tabs/pills')
4394 cfg.cn[0].cls += ' navbar-nav'
4400 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4401 cfg.cn[0].cls += ' nav-' + this.arrangement;
4405 if (this.align === 'right') {
4406 cfg.cn[0].cls += ' navbar-right';
4431 * navbar-expand-md fixed-top
4435 * @class Roo.bootstrap.NavHeaderbar
4436 * @extends Roo.bootstrap.NavSimplebar
4437 * Bootstrap Sidebar class
4439 * @cfg {String} brand what is brand
4440 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4441 * @cfg {String} brand_href href of the brand
4442 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4443 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4444 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4445 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4448 * Create a new Sidebar
4449 * @param {Object} config The config object
4453 Roo.bootstrap.NavHeaderbar = function(config){
4454 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4458 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4465 desktopCenter : false,
4468 getAutoCreate : function(){
4471 tag: this.nav || 'nav',
4472 cls: 'navbar navbar-expand-md',
4478 if (this.desktopCenter) {
4479 cn.push({cls : 'container', cn : []});
4487 cls: 'navbar-toggle navbar-toggler',
4488 'data-toggle': 'collapse',
4493 html: 'Toggle navigation'
4497 cls: 'icon-bar navbar-toggler-icon'
4510 cn.push( Roo.bootstrap.version == 4 ? btn : {
4512 cls: 'navbar-header',
4521 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
4525 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4527 if (['light','white'].indexOf(this.weight) > -1) {
4528 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4530 cfg.cls += ' bg-' + this.weight;
4533 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4534 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4536 // tag can override this..
4538 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4541 if (this.brand !== '') {
4542 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4543 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4545 href: this.brand_href ? this.brand_href : '#',
4546 cls: 'navbar-brand',
4554 cfg.cls += ' main-nav';
4562 getHeaderChildContainer : function()
4564 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4565 return this.el.select('.navbar-header',true).first();
4568 return this.getChildContainer();
4571 getChildContainer : function()
4574 return this.el.select('.roo-navbar-collapse',true).first();
4579 initEvents : function()
4581 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4583 if (this.autohide) {
4588 Roo.get(document).on('scroll',function(e) {
4589 var ns = Roo.get(document).getScroll().top;
4590 var os = prevScroll;
4594 ft.removeClass('slideDown');
4595 ft.addClass('slideUp');
4598 ft.removeClass('slideUp');
4599 ft.addClass('slideDown');
4620 * @class Roo.bootstrap.NavSidebar
4621 * @extends Roo.bootstrap.Navbar
4622 * Bootstrap Sidebar class
4625 * Create a new Sidebar
4626 * @param {Object} config The config object
4630 Roo.bootstrap.NavSidebar = function(config){
4631 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4634 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4636 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4638 getAutoCreate : function(){
4643 cls: 'sidebar sidebar-nav'
4665 * @class Roo.bootstrap.NavGroup
4666 * @extends Roo.bootstrap.Component
4667 * Bootstrap NavGroup class
4668 * @cfg {String} align (left|right)
4669 * @cfg {Boolean} inverse
4670 * @cfg {String} type (nav|pills|tab) default nav
4671 * @cfg {String} navId - reference Id for navbar.
4675 * Create a new nav group
4676 * @param {Object} config The config object
4679 Roo.bootstrap.NavGroup = function(config){
4680 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4683 Roo.bootstrap.NavGroup.register(this);
4687 * Fires when the active item changes
4688 * @param {Roo.bootstrap.NavGroup} this
4689 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4690 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4697 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4708 getAutoCreate : function()
4710 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4716 if (Roo.bootstrap.version == 4) {
4717 if (['tabs','pills'].indexOf(this.type) != -1) {
4718 cfg.cls += ' nav-' + this.type;
4720 // trying to remove so header bar can right align top?
4721 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
4722 // do not use on header bar...
4723 cfg.cls += ' navbar-nav';
4728 if (['tabs','pills'].indexOf(this.type) != -1) {
4729 cfg.cls += ' nav-' + this.type
4731 if (this.type !== 'nav') {
4732 Roo.log('nav type must be nav/tabs/pills')
4734 cfg.cls += ' navbar-nav'
4738 if (this.parent() && this.parent().sidebar) {
4741 cls: 'dashboard-menu sidebar-menu'
4747 if (this.form === true) {
4750 cls: 'navbar-form form-inline'
4752 //nav navbar-right ml-md-auto
4753 if (this.align === 'right') {
4754 cfg.cls += ' navbar-right ml-md-auto';
4756 cfg.cls += ' navbar-left';
4760 if (this.align === 'right') {
4761 cfg.cls += ' navbar-right ml-md-auto';
4763 cfg.cls += ' mr-auto';
4767 cfg.cls += ' navbar-inverse';
4775 * sets the active Navigation item
4776 * @param {Roo.bootstrap.NavItem} the new current navitem
4778 setActiveItem : function(item)
4781 Roo.each(this.navItems, function(v){
4786 v.setActive(false, true);
4793 item.setActive(true, true);
4794 this.fireEvent('changed', this, item, prev);
4799 * gets the active Navigation item
4800 * @return {Roo.bootstrap.NavItem} the current navitem
4802 getActive : function()
4806 Roo.each(this.navItems, function(v){
4817 indexOfNav : function()
4821 Roo.each(this.navItems, function(v,i){
4832 * adds a Navigation item
4833 * @param {Roo.bootstrap.NavItem} the navitem to add
4835 addItem : function(cfg)
4837 if (this.form && Roo.bootstrap.version == 4) {
4840 var cn = new Roo.bootstrap.NavItem(cfg);
4842 cn.parentId = this.id;
4843 cn.onRender(this.el, null);
4847 * register a Navigation item
4848 * @param {Roo.bootstrap.NavItem} the navitem to add
4850 register : function(item)
4852 this.navItems.push( item);
4853 item.navId = this.navId;
4858 * clear all the Navigation item
4861 clearAll : function()
4864 this.el.dom.innerHTML = '';
4867 getNavItem: function(tabId)
4870 Roo.each(this.navItems, function(e) {
4871 if (e.tabId == tabId) {
4881 setActiveNext : function()
4883 var i = this.indexOfNav(this.getActive());
4884 if (i > this.navItems.length) {
4887 this.setActiveItem(this.navItems[i+1]);
4889 setActivePrev : function()
4891 var i = this.indexOfNav(this.getActive());
4895 this.setActiveItem(this.navItems[i-1]);
4897 clearWasActive : function(except) {
4898 Roo.each(this.navItems, function(e) {
4899 if (e.tabId != except.tabId && e.was_active) {
4900 e.was_active = false;
4907 getWasActive : function ()
4910 Roo.each(this.navItems, function(e) {
4925 Roo.apply(Roo.bootstrap.NavGroup, {
4929 * register a Navigation Group
4930 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4932 register : function(navgrp)
4934 this.groups[navgrp.navId] = navgrp;
4938 * fetch a Navigation Group based on the navigation ID
4939 * @param {string} the navgroup to add
4940 * @returns {Roo.bootstrap.NavGroup} the navgroup
4942 get: function(navId) {
4943 if (typeof(this.groups[navId]) == 'undefined') {
4945 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4947 return this.groups[navId] ;
4962 * @class Roo.bootstrap.NavItem
4963 * @extends Roo.bootstrap.Component
4964 * Bootstrap Navbar.NavItem class
4965 * @cfg {String} href link to
4966 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4968 * @cfg {String} html content of button
4969 * @cfg {String} badge text inside badge
4970 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4971 * @cfg {String} glyphicon DEPRICATED - use fa
4972 * @cfg {String} icon DEPRICATED - use fa
4973 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4974 * @cfg {Boolean} active Is item active
4975 * @cfg {Boolean} disabled Is item disabled
4977 * @cfg {Boolean} preventDefault (true | false) default false
4978 * @cfg {String} tabId the tab that this item activates.
4979 * @cfg {String} tagtype (a|span) render as a href or span?
4980 * @cfg {Boolean} animateRef (true|false) link to element default false
4983 * Create a new Navbar Item
4984 * @param {Object} config The config object
4986 Roo.bootstrap.NavItem = function(config){
4987 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4992 * The raw click event for the entire grid.
4993 * @param {Roo.EventObject} e
4998 * Fires when the active item active state changes
4999 * @param {Roo.bootstrap.NavItem} this
5000 * @param {boolean} state the new state
5006 * Fires when scroll to element
5007 * @param {Roo.bootstrap.NavItem} this
5008 * @param {Object} options
5009 * @param {Roo.EventObject} e
5017 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
5026 preventDefault : false,
5034 button_outline : false,
5038 getAutoCreate : function(){
5046 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
5048 if (this.disabled) {
5049 cfg.cls += ' disabled';
5053 if (this.button_weight.length) {
5054 cfg.tag = this.href ? 'a' : 'button';
5055 cfg.html = this.html || '';
5056 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
5058 cfg.href = this.href;
5061 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
5064 // menu .. should add dropdown-menu class - so no need for carat..
5066 if (this.badge !== '') {
5068 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5073 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
5077 href : this.href || "#",
5078 html: this.html || ''
5081 if (this.tagtype == 'a') {
5082 cfg.cn[0].cls = 'nav-link';
5085 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
5088 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
5090 if(this.glyphicon) {
5091 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
5096 cfg.cn[0].html += " <span class='caret'></span>";
5100 if (this.badge !== '') {
5102 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5110 onRender : function(ct, position)
5112 // Roo.log("Call onRender: " + this.xtype);
5113 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
5117 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
5118 this.navLink = this.el.select('.nav-link',true).first();
5123 initEvents: function()
5125 if (typeof (this.menu) != 'undefined') {
5126 this.menu.parentType = this.xtype;
5127 this.menu.triggerEl = this.el;
5128 this.menu = this.addxtype(Roo.apply({}, this.menu));
5131 this.el.select('a',true).on('click', this.onClick, this);
5133 if(this.tagtype == 'span'){
5134 this.el.select('span',true).on('click', this.onClick, this);
5137 // at this point parent should be available..
5138 this.parent().register(this);
5141 onClick : function(e)
5143 if (e.getTarget('.dropdown-menu-item')) {
5144 // did you click on a menu itemm.... - then don't trigger onclick..
5149 this.preventDefault ||
5152 Roo.log("NavItem - prevent Default?");
5156 if (this.disabled) {
5160 var tg = Roo.bootstrap.TabGroup.get(this.navId);
5161 if (tg && tg.transition) {
5162 Roo.log("waiting for the transitionend");
5168 //Roo.log("fire event clicked");
5169 if(this.fireEvent('click', this, e) === false){
5173 if(this.tagtype == 'span'){
5177 //Roo.log(this.href);
5178 var ael = this.el.select('a',true).first();
5181 if(ael && this.animateRef && this.href.indexOf('#') > -1){
5182 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
5183 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
5184 return; // ignore... - it's a 'hash' to another page.
5186 Roo.log("NavItem - prevent Default?");
5188 this.scrollToElement(e);
5192 var p = this.parent();
5194 if (['tabs','pills'].indexOf(p.type)!==-1) {
5195 if (typeof(p.setActiveItem) !== 'undefined') {
5196 p.setActiveItem(this);
5200 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
5201 if (p.parentType == 'NavHeaderbar' && !this.menu) {
5202 // remove the collapsed menu expand...
5203 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
5207 isActive: function () {
5210 setActive : function(state, fire, is_was_active)
5212 if (this.active && !state && this.navId) {
5213 this.was_active = true;
5214 var nv = Roo.bootstrap.NavGroup.get(this.navId);
5216 nv.clearWasActive(this);
5220 this.active = state;
5223 this.el.removeClass('active');
5224 this.navLink ? this.navLink.removeClass('active') : false;
5225 } else if (!this.el.hasClass('active')) {
5227 this.el.addClass('active');
5228 if (Roo.bootstrap.version == 4 && this.navLink ) {
5229 this.navLink.addClass('active');
5234 this.fireEvent('changed', this, state);
5237 // show a panel if it's registered and related..
5239 if (!this.navId || !this.tabId || !state || is_was_active) {
5243 var tg = Roo.bootstrap.TabGroup.get(this.navId);
5247 var pan = tg.getPanelByName(this.tabId);
5251 // if we can not flip to new panel - go back to old nav highlight..
5252 if (false == tg.showPanel(pan)) {
5253 var nv = Roo.bootstrap.NavGroup.get(this.navId);
5255 var onav = nv.getWasActive();
5257 onav.setActive(true, false, true);
5266 // this should not be here...
5267 setDisabled : function(state)
5269 this.disabled = state;
5271 this.el.removeClass('disabled');
5272 } else if (!this.el.hasClass('disabled')) {
5273 this.el.addClass('disabled');
5279 * Fetch the element to display the tooltip on.
5280 * @return {Roo.Element} defaults to this.el
5282 tooltipEl : function()
5284 return this.el.select('' + this.tagtype + '', true).first();
5287 scrollToElement : function(e)
5289 var c = document.body;
5292 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
5294 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
5295 c = document.documentElement;
5298 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5304 var o = target.calcOffsetsTo(c);
5311 this.fireEvent('scrollto', this, options, e);
5313 Roo.get(c).scrollTo('top', options.value, true);
5326 * <span> icon </span>
5327 * <span> text </span>
5328 * <span>badge </span>
5332 * @class Roo.bootstrap.NavSidebarItem
5333 * @extends Roo.bootstrap.NavItem
5334 * Bootstrap Navbar.NavSidebarItem class
5335 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5336 * {Boolean} open is the menu open
5337 * {Boolean} buttonView use button as the tigger el rather that a (default false)
5338 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5339 * {String} buttonSize (sm|md|lg)the extra classes for the button
5340 * {Boolean} showArrow show arrow next to the text (default true)
5342 * Create a new Navbar Button
5343 * @param {Object} config The config object
5345 Roo.bootstrap.NavSidebarItem = function(config){
5346 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5351 * The raw click event for the entire grid.
5352 * @param {Roo.EventObject} e
5357 * Fires when the active item active state changes
5358 * @param {Roo.bootstrap.NavSidebarItem} this
5359 * @param {boolean} state the new state
5367 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
5369 badgeWeight : 'default',
5375 buttonWeight : 'default',
5381 getAutoCreate : function(){
5386 href : this.href || '#',
5392 if(this.buttonView){
5395 href : this.href || '#',
5396 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5409 cfg.cls += ' active';
5412 if (this.disabled) {
5413 cfg.cls += ' disabled';
5416 cfg.cls += ' open x-open';
5419 if (this.glyphicon || this.icon) {
5420 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
5421 a.cn.push({ tag : 'i', cls : c }) ;
5424 if(!this.buttonView){
5427 html : this.html || ''
5434 if (this.badge !== '') {
5435 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
5441 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5444 a.cls += ' dropdown-toggle treeview' ;
5450 initEvents : function()
5452 if (typeof (this.menu) != 'undefined') {
5453 this.menu.parentType = this.xtype;
5454 this.menu.triggerEl = this.el;
5455 this.menu = this.addxtype(Roo.apply({}, this.menu));
5458 this.el.on('click', this.onClick, this);
5460 if(this.badge !== ''){
5461 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5466 onClick : function(e)
5473 if(this.preventDefault){
5477 this.fireEvent('click', this, e);
5480 disable : function()
5482 this.setDisabled(true);
5487 this.setDisabled(false);
5490 setDisabled : function(state)
5492 if(this.disabled == state){
5496 this.disabled = state;
5499 this.el.addClass('disabled');
5503 this.el.removeClass('disabled');
5508 setActive : function(state)
5510 if(this.active == state){
5514 this.active = state;
5517 this.el.addClass('active');
5521 this.el.removeClass('active');
5526 isActive: function ()
5531 setBadge : function(str)
5537 this.badgeEl.dom.innerHTML = str;
5554 * @class Roo.bootstrap.Row
5555 * @extends Roo.bootstrap.Component
5556 * Bootstrap Row class (contains columns...)
5560 * @param {Object} config The config object
5563 Roo.bootstrap.Row = function(config){
5564 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5567 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5569 getAutoCreate : function(){
5588 * @class Roo.bootstrap.Element
5589 * @extends Roo.bootstrap.Component
5590 * Bootstrap Element class
5591 * @cfg {String} html contents of the element
5592 * @cfg {String} tag tag of the element
5593 * @cfg {String} cls class of the element
5594 * @cfg {Boolean} preventDefault (true|false) default false
5595 * @cfg {Boolean} clickable (true|false) default false
5598 * Create a new Element
5599 * @param {Object} config The config object
5602 Roo.bootstrap.Element = function(config){
5603 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5609 * When a element is chick
5610 * @param {Roo.bootstrap.Element} this
5611 * @param {Roo.EventObject} e
5617 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5622 preventDefault: false,
5625 getAutoCreate : function(){
5629 // cls: this.cls, double assign in parent class Component.js :: onRender
5636 initEvents: function()
5638 Roo.bootstrap.Element.superclass.initEvents.call(this);
5641 this.el.on('click', this.onClick, this);
5646 onClick : function(e)
5648 if(this.preventDefault){
5652 this.fireEvent('click', this, e);
5655 getValue : function()
5657 return this.el.dom.innerHTML;
5660 setValue : function(value)
5662 this.el.dom.innerHTML = value;
5677 * @class Roo.bootstrap.Pagination
5678 * @extends Roo.bootstrap.Component
5679 * Bootstrap Pagination class
5680 * @cfg {String} size xs | sm | md | lg
5681 * @cfg {Boolean} inverse false | true
5684 * Create a new Pagination
5685 * @param {Object} config The config object
5688 Roo.bootstrap.Pagination = function(config){
5689 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5692 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5698 getAutoCreate : function(){
5704 cfg.cls += ' inverse';
5710 cfg.cls += " " + this.cls;
5728 * @class Roo.bootstrap.PaginationItem
5729 * @extends Roo.bootstrap.Component
5730 * Bootstrap PaginationItem class
5731 * @cfg {String} html text
5732 * @cfg {String} href the link
5733 * @cfg {Boolean} preventDefault (true | false) default true
5734 * @cfg {Boolean} active (true | false) default false
5735 * @cfg {Boolean} disabled default false
5739 * Create a new PaginationItem
5740 * @param {Object} config The config object
5744 Roo.bootstrap.PaginationItem = function(config){
5745 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5750 * The raw click event for the entire grid.
5751 * @param {Roo.EventObject} e
5757 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5761 preventDefault: true,
5766 getAutoCreate : function(){
5772 href : this.href ? this.href : '#',
5773 html : this.html ? this.html : ''
5783 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5787 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5793 initEvents: function() {
5795 this.el.on('click', this.onClick, this);
5798 onClick : function(e)
5800 Roo.log('PaginationItem on click ');
5801 if(this.preventDefault){
5809 this.fireEvent('click', this, e);
5825 * @class Roo.bootstrap.Slider
5826 * @extends Roo.bootstrap.Component
5827 * Bootstrap Slider class
5830 * Create a new Slider
5831 * @param {Object} config The config object
5834 Roo.bootstrap.Slider = function(config){
5835 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5838 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5840 getAutoCreate : function(){
5844 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5848 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5860 * Ext JS Library 1.1.1
5861 * Copyright(c) 2006-2007, Ext JS, LLC.
5863 * Originally Released Under LGPL - original licence link has changed is not relivant.
5866 * <script type="text/javascript">
5871 * @class Roo.grid.ColumnModel
5872 * @extends Roo.util.Observable
5873 * This is the default implementation of a ColumnModel used by the Grid. It defines
5874 * the columns in the grid.
5877 var colModel = new Roo.grid.ColumnModel([
5878 {header: "Ticker", width: 60, sortable: true, locked: true},
5879 {header: "Company Name", width: 150, sortable: true},
5880 {header: "Market Cap.", width: 100, sortable: true},
5881 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5882 {header: "Employees", width: 100, sortable: true, resizable: false}
5887 * The config options listed for this class are options which may appear in each
5888 * individual column definition.
5889 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5891 * @param {Object} config An Array of column config objects. See this class's
5892 * config objects for details.
5894 Roo.grid.ColumnModel = function(config){
5896 * The config passed into the constructor
5898 this.config = config;
5901 // if no id, create one
5902 // if the column does not have a dataIndex mapping,
5903 // map it to the order it is in the config
5904 for(var i = 0, len = config.length; i < len; i++){
5906 if(typeof c.dataIndex == "undefined"){
5909 if(typeof c.renderer == "string"){
5910 c.renderer = Roo.util.Format[c.renderer];
5912 if(typeof c.id == "undefined"){
5915 if(c.editor && c.editor.xtype){
5916 c.editor = Roo.factory(c.editor, Roo.grid);
5918 if(c.editor && c.editor.isFormField){
5919 c.editor = new Roo.grid.GridEditor(c.editor);
5921 this.lookup[c.id] = c;
5925 * The width of columns which have no width specified (defaults to 100)
5928 this.defaultWidth = 100;
5931 * Default sortable of columns which have no sortable specified (defaults to false)
5934 this.defaultSortable = false;
5938 * @event widthchange
5939 * Fires when the width of a column changes.
5940 * @param {ColumnModel} this
5941 * @param {Number} columnIndex The column index
5942 * @param {Number} newWidth The new width
5944 "widthchange": true,
5946 * @event headerchange
5947 * Fires when the text of a header changes.
5948 * @param {ColumnModel} this
5949 * @param {Number} columnIndex The column index
5950 * @param {Number} newText The new header text
5952 "headerchange": true,
5954 * @event hiddenchange
5955 * Fires when a column is hidden or "unhidden".
5956 * @param {ColumnModel} this
5957 * @param {Number} columnIndex The column index
5958 * @param {Boolean} hidden true if hidden, false otherwise
5960 "hiddenchange": true,
5962 * @event columnmoved
5963 * Fires when a column is moved.
5964 * @param {ColumnModel} this
5965 * @param {Number} oldIndex
5966 * @param {Number} newIndex
5968 "columnmoved" : true,
5970 * @event columlockchange
5971 * Fires when a column's locked state is changed
5972 * @param {ColumnModel} this
5973 * @param {Number} colIndex
5974 * @param {Boolean} locked true if locked
5976 "columnlockchange" : true
5978 Roo.grid.ColumnModel.superclass.constructor.call(this);
5980 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5982 * @cfg {String} header The header text to display in the Grid view.
5985 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5986 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5987 * specified, the column's index is used as an index into the Record's data Array.
5990 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5991 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5994 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5995 * Defaults to the value of the {@link #defaultSortable} property.
5996 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5999 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
6002 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
6005 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6008 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6011 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6012 * given the cell's data value. See {@link #setRenderer}. If not specified, the
6013 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6014 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6017 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
6020 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
6023 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
6026 * @cfg {String} cursor (Optional)
6029 * @cfg {String} tooltip (Optional)
6032 * @cfg {Number} xs (Optional)
6035 * @cfg {Number} sm (Optional)
6038 * @cfg {Number} md (Optional)
6041 * @cfg {Number} lg (Optional)
6044 * Returns the id of the column at the specified index.
6045 * @param {Number} index The column index
6046 * @return {String} the id
6048 getColumnId : function(index){
6049 return this.config[index].id;
6053 * Returns the column for a specified id.
6054 * @param {String} id The column id
6055 * @return {Object} the column
6057 getColumnById : function(id){
6058 return this.lookup[id];
6063 * Returns the column for a specified dataIndex.
6064 * @param {String} dataIndex The column dataIndex
6065 * @return {Object|Boolean} the column or false if not found
6067 getColumnByDataIndex: function(dataIndex){
6068 var index = this.findColumnIndex(dataIndex);
6069 return index > -1 ? this.config[index] : false;
6073 * Returns the index for a specified column id.
6074 * @param {String} id The column id
6075 * @return {Number} the index, or -1 if not found
6077 getIndexById : function(id){
6078 for(var i = 0, len = this.config.length; i < len; i++){
6079 if(this.config[i].id == id){
6087 * Returns the index for a specified column dataIndex.
6088 * @param {String} dataIndex The column dataIndex
6089 * @return {Number} the index, or -1 if not found
6092 findColumnIndex : function(dataIndex){
6093 for(var i = 0, len = this.config.length; i < len; i++){
6094 if(this.config[i].dataIndex == dataIndex){
6102 moveColumn : function(oldIndex, newIndex){
6103 var c = this.config[oldIndex];
6104 this.config.splice(oldIndex, 1);
6105 this.config.splice(newIndex, 0, c);
6106 this.dataMap = null;
6107 this.fireEvent("columnmoved", this, oldIndex, newIndex);
6110 isLocked : function(colIndex){
6111 return this.config[colIndex].locked === true;
6114 setLocked : function(colIndex, value, suppressEvent){
6115 if(this.isLocked(colIndex) == value){
6118 this.config[colIndex].locked = value;
6120 this.fireEvent("columnlockchange", this, colIndex, value);
6124 getTotalLockedWidth : function(){
6126 for(var i = 0; i < this.config.length; i++){
6127 if(this.isLocked(i) && !this.isHidden(i)){
6128 this.totalWidth += this.getColumnWidth(i);
6134 getLockedCount : function(){
6135 for(var i = 0, len = this.config.length; i < len; i++){
6136 if(!this.isLocked(i)){
6141 return this.config.length;
6145 * Returns the number of columns.
6148 getColumnCount : function(visibleOnly){
6149 if(visibleOnly === true){
6151 for(var i = 0, len = this.config.length; i < len; i++){
6152 if(!this.isHidden(i)){
6158 return this.config.length;
6162 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
6163 * @param {Function} fn
6164 * @param {Object} scope (optional)
6165 * @return {Array} result
6167 getColumnsBy : function(fn, scope){
6169 for(var i = 0, len = this.config.length; i < len; i++){
6170 var c = this.config[i];
6171 if(fn.call(scope||this, c, i) === true){
6179 * Returns true if the specified column is sortable.
6180 * @param {Number} col The column index
6183 isSortable : function(col){
6184 if(typeof this.config[col].sortable == "undefined"){
6185 return this.defaultSortable;
6187 return this.config[col].sortable;
6191 * Returns the rendering (formatting) function defined for the column.
6192 * @param {Number} col The column index.
6193 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
6195 getRenderer : function(col){
6196 if(!this.config[col].renderer){
6197 return Roo.grid.ColumnModel.defaultRenderer;
6199 return this.config[col].renderer;
6203 * Sets the rendering (formatting) function for a column.
6204 * @param {Number} col The column index
6205 * @param {Function} fn The function to use to process the cell's raw data
6206 * to return HTML markup for the grid view. The render function is called with
6207 * the following parameters:<ul>
6208 * <li>Data value.</li>
6209 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
6210 * <li>css A CSS style string to apply to the table cell.</li>
6211 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
6212 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
6213 * <li>Row index</li>
6214 * <li>Column index</li>
6215 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
6217 setRenderer : function(col, fn){
6218 this.config[col].renderer = fn;
6222 * Returns the width for the specified column.
6223 * @param {Number} col The column index
6226 getColumnWidth : function(col){
6227 return this.config[col].width * 1 || this.defaultWidth;
6231 * Sets the width for a column.
6232 * @param {Number} col The column index
6233 * @param {Number} width The new width
6235 setColumnWidth : function(col, width, suppressEvent){
6236 this.config[col].width = width;
6237 this.totalWidth = null;
6239 this.fireEvent("widthchange", this, col, width);
6244 * Returns the total width of all columns.
6245 * @param {Boolean} includeHidden True to include hidden column widths
6248 getTotalWidth : function(includeHidden){
6249 if(!this.totalWidth){
6250 this.totalWidth = 0;
6251 for(var i = 0, len = this.config.length; i < len; i++){
6252 if(includeHidden || !this.isHidden(i)){
6253 this.totalWidth += this.getColumnWidth(i);
6257 return this.totalWidth;
6261 * Returns the header for the specified column.
6262 * @param {Number} col The column index
6265 getColumnHeader : function(col){
6266 return this.config[col].header;
6270 * Sets the header for a column.
6271 * @param {Number} col The column index
6272 * @param {String} header The new header
6274 setColumnHeader : function(col, header){
6275 this.config[col].header = header;
6276 this.fireEvent("headerchange", this, col, header);
6280 * Returns the tooltip for the specified column.
6281 * @param {Number} col The column index
6284 getColumnTooltip : function(col){
6285 return this.config[col].tooltip;
6288 * Sets the tooltip for a column.
6289 * @param {Number} col The column index
6290 * @param {String} tooltip The new tooltip
6292 setColumnTooltip : function(col, tooltip){
6293 this.config[col].tooltip = tooltip;
6297 * Returns the dataIndex for the specified column.
6298 * @param {Number} col The column index
6301 getDataIndex : function(col){
6302 return this.config[col].dataIndex;
6306 * Sets the dataIndex for a column.
6307 * @param {Number} col The column index
6308 * @param {Number} dataIndex The new dataIndex
6310 setDataIndex : function(col, dataIndex){
6311 this.config[col].dataIndex = dataIndex;
6317 * Returns true if the cell is editable.
6318 * @param {Number} colIndex The column index
6319 * @param {Number} rowIndex The row index - this is nto actually used..?
6322 isCellEditable : function(colIndex, rowIndex){
6323 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6327 * Returns the editor defined for the cell/column.
6328 * return false or null to disable editing.
6329 * @param {Number} colIndex The column index
6330 * @param {Number} rowIndex The row index
6333 getCellEditor : function(colIndex, rowIndex){
6334 return this.config[colIndex].editor;
6338 * Sets if a column is editable.
6339 * @param {Number} col The column index
6340 * @param {Boolean} editable True if the column is editable
6342 setEditable : function(col, editable){
6343 this.config[col].editable = editable;
6348 * Returns true if the column is hidden.
6349 * @param {Number} colIndex The column index
6352 isHidden : function(colIndex){
6353 return this.config[colIndex].hidden;
6358 * Returns true if the column width cannot be changed
6360 isFixed : function(colIndex){
6361 return this.config[colIndex].fixed;
6365 * Returns true if the column can be resized
6368 isResizable : function(colIndex){
6369 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6372 * Sets if a column is hidden.
6373 * @param {Number} colIndex The column index
6374 * @param {Boolean} hidden True if the column is hidden
6376 setHidden : function(colIndex, hidden){
6377 this.config[colIndex].hidden = hidden;
6378 this.totalWidth = null;
6379 this.fireEvent("hiddenchange", this, colIndex, hidden);
6383 * Sets the editor for a column.
6384 * @param {Number} col The column index
6385 * @param {Object} editor The editor object
6387 setEditor : function(col, editor){
6388 this.config[col].editor = editor;
6392 Roo.grid.ColumnModel.defaultRenderer = function(value)
6394 if(typeof value == "object") {
6397 if(typeof value == "string" && value.length < 1){
6401 return String.format("{0}", value);
6404 // Alias for backwards compatibility
6405 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6408 * Ext JS Library 1.1.1
6409 * Copyright(c) 2006-2007, Ext JS, LLC.
6411 * Originally Released Under LGPL - original licence link has changed is not relivant.
6414 * <script type="text/javascript">
6418 * @class Roo.LoadMask
6419 * A simple utility class for generically masking elements while loading data. If the element being masked has
6420 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6421 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
6422 * element's UpdateManager load indicator and will be destroyed after the initial load.
6424 * Create a new LoadMask
6425 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6426 * @param {Object} config The config object
6428 Roo.LoadMask = function(el, config){
6429 this.el = Roo.get(el);
6430 Roo.apply(this, config);
6432 this.store.on('beforeload', this.onBeforeLoad, this);
6433 this.store.on('load', this.onLoad, this);
6434 this.store.on('loadexception', this.onLoadException, this);
6435 this.removeMask = false;
6437 var um = this.el.getUpdateManager();
6438 um.showLoadIndicator = false; // disable the default indicator
6439 um.on('beforeupdate', this.onBeforeLoad, this);
6440 um.on('update', this.onLoad, this);
6441 um.on('failure', this.onLoad, this);
6442 this.removeMask = true;
6446 Roo.LoadMask.prototype = {
6448 * @cfg {Boolean} removeMask
6449 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6450 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
6454 * The text to display in a centered loading message box (defaults to 'Loading...')
6458 * @cfg {String} msgCls
6459 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6461 msgCls : 'x-mask-loading',
6464 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6470 * Disables the mask to prevent it from being displayed
6472 disable : function(){
6473 this.disabled = true;
6477 * Enables the mask so that it can be displayed
6479 enable : function(){
6480 this.disabled = false;
6483 onLoadException : function()
6487 if (typeof(arguments[3]) != 'undefined') {
6488 Roo.MessageBox.alert("Error loading",arguments[3]);
6492 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6493 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6500 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6505 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6509 onBeforeLoad : function(){
6511 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6516 destroy : function(){
6518 this.store.un('beforeload', this.onBeforeLoad, this);
6519 this.store.un('load', this.onLoad, this);
6520 this.store.un('loadexception', this.onLoadException, this);
6522 var um = this.el.getUpdateManager();
6523 um.un('beforeupdate', this.onBeforeLoad, this);
6524 um.un('update', this.onLoad, this);
6525 um.un('failure', this.onLoad, this);
6536 * @class Roo.bootstrap.Table
6537 * @extends Roo.bootstrap.Component
6538 * Bootstrap Table class
6539 * @cfg {String} cls table class
6540 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6541 * @cfg {String} bgcolor Specifies the background color for a table
6542 * @cfg {Number} border Specifies whether the table cells should have borders or not
6543 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6544 * @cfg {Number} cellspacing Specifies the space between cells
6545 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6546 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6547 * @cfg {String} sortable Specifies that the table should be sortable
6548 * @cfg {String} summary Specifies a summary of the content of a table
6549 * @cfg {Number} width Specifies the width of a table
6550 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6552 * @cfg {boolean} striped Should the rows be alternative striped
6553 * @cfg {boolean} bordered Add borders to the table
6554 * @cfg {boolean} hover Add hover highlighting
6555 * @cfg {boolean} condensed Format condensed
6556 * @cfg {boolean} responsive Format condensed
6557 * @cfg {Boolean} loadMask (true|false) default false
6558 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6559 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6560 * @cfg {Boolean} rowSelection (true|false) default false
6561 * @cfg {Boolean} cellSelection (true|false) default false
6562 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6563 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6564 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6565 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6569 * Create a new Table
6570 * @param {Object} config The config object
6573 Roo.bootstrap.Table = function(config){
6574 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6579 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6580 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6581 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6582 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6584 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6586 this.sm.grid = this;
6587 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6588 this.sm = this.selModel;
6589 this.sm.xmodule = this.xmodule || false;
6592 if (this.cm && typeof(this.cm.config) == 'undefined') {
6593 this.colModel = new Roo.grid.ColumnModel(this.cm);
6594 this.cm = this.colModel;
6595 this.cm.xmodule = this.xmodule || false;
6598 this.store= Roo.factory(this.store, Roo.data);
6599 this.ds = this.store;
6600 this.ds.xmodule = this.xmodule || false;
6603 if (this.footer && this.store) {
6604 this.footer.dataSource = this.ds;
6605 this.footer = Roo.factory(this.footer);
6612 * Fires when a cell is clicked
6613 * @param {Roo.bootstrap.Table} this
6614 * @param {Roo.Element} el
6615 * @param {Number} rowIndex
6616 * @param {Number} columnIndex
6617 * @param {Roo.EventObject} e
6621 * @event celldblclick
6622 * Fires when a cell is double clicked
6623 * @param {Roo.bootstrap.Table} this
6624 * @param {Roo.Element} el
6625 * @param {Number} rowIndex
6626 * @param {Number} columnIndex
6627 * @param {Roo.EventObject} e
6629 "celldblclick" : true,
6632 * Fires when a row is clicked
6633 * @param {Roo.bootstrap.Table} this
6634 * @param {Roo.Element} el
6635 * @param {Number} rowIndex
6636 * @param {Roo.EventObject} e
6640 * @event rowdblclick
6641 * Fires when a row is double clicked
6642 * @param {Roo.bootstrap.Table} this
6643 * @param {Roo.Element} el
6644 * @param {Number} rowIndex
6645 * @param {Roo.EventObject} e
6647 "rowdblclick" : true,
6650 * Fires when a mouseover occur
6651 * @param {Roo.bootstrap.Table} this
6652 * @param {Roo.Element} el
6653 * @param {Number} rowIndex
6654 * @param {Number} columnIndex
6655 * @param {Roo.EventObject} e
6660 * Fires when a mouseout occur
6661 * @param {Roo.bootstrap.Table} this
6662 * @param {Roo.Element} el
6663 * @param {Number} rowIndex
6664 * @param {Number} columnIndex
6665 * @param {Roo.EventObject} e
6670 * Fires when a row is rendered, so you can change add a style to it.
6671 * @param {Roo.bootstrap.Table} this
6672 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6676 * @event rowsrendered
6677 * Fires when all the rows have been rendered
6678 * @param {Roo.bootstrap.Table} this
6680 'rowsrendered' : true,
6682 * @event contextmenu
6683 * The raw contextmenu event for the entire grid.
6684 * @param {Roo.EventObject} e
6686 "contextmenu" : true,
6688 * @event rowcontextmenu
6689 * Fires when a row is right clicked
6690 * @param {Roo.bootstrap.Table} this
6691 * @param {Number} rowIndex
6692 * @param {Roo.EventObject} e
6694 "rowcontextmenu" : true,
6696 * @event cellcontextmenu
6697 * Fires when a cell is right clicked
6698 * @param {Roo.bootstrap.Table} this
6699 * @param {Number} rowIndex
6700 * @param {Number} cellIndex
6701 * @param {Roo.EventObject} e
6703 "cellcontextmenu" : true,
6705 * @event headercontextmenu
6706 * Fires when a header is right clicked
6707 * @param {Roo.bootstrap.Table} this
6708 * @param {Number} columnIndex
6709 * @param {Roo.EventObject} e
6711 "headercontextmenu" : true
6715 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6741 rowSelection : false,
6742 cellSelection : false,
6745 // Roo.Element - the tbody
6747 // Roo.Element - thead element
6750 container: false, // used by gridpanel...
6756 auto_hide_footer : false,
6758 getAutoCreate : function()
6760 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6767 if (this.scrollBody) {
6768 cfg.cls += ' table-body-fixed';
6771 cfg.cls += ' table-striped';
6775 cfg.cls += ' table-hover';
6777 if (this.bordered) {
6778 cfg.cls += ' table-bordered';
6780 if (this.condensed) {
6781 cfg.cls += ' table-condensed';
6783 if (this.responsive) {
6784 cfg.cls += ' table-responsive';
6788 cfg.cls+= ' ' +this.cls;
6791 // this lot should be simplifed...
6804 ].forEach(function(k) {
6812 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6815 if(this.store || this.cm){
6816 if(this.headerShow){
6817 cfg.cn.push(this.renderHeader());
6820 cfg.cn.push(this.renderBody());
6822 if(this.footerShow){
6823 cfg.cn.push(this.renderFooter());
6825 // where does this come from?
6826 //cfg.cls+= ' TableGrid';
6829 return { cn : [ cfg ] };
6832 initEvents : function()
6834 if(!this.store || !this.cm){
6837 if (this.selModel) {
6838 this.selModel.initEvents();
6842 //Roo.log('initEvents with ds!!!!');
6844 this.mainBody = this.el.select('tbody', true).first();
6845 this.mainHead = this.el.select('thead', true).first();
6846 this.mainFoot = this.el.select('tfoot', true).first();
6852 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6853 e.on('click', _this.sort, _this);
6856 this.mainBody.on("click", this.onClick, this);
6857 this.mainBody.on("dblclick", this.onDblClick, this);
6859 // why is this done????? = it breaks dialogs??
6860 //this.parent().el.setStyle('position', 'relative');
6864 this.footer.parentId = this.id;
6865 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6868 this.el.select('tfoot tr td').first().addClass('hide');
6873 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6876 this.store.on('load', this.onLoad, this);
6877 this.store.on('beforeload', this.onBeforeLoad, this);
6878 this.store.on('update', this.onUpdate, this);
6879 this.store.on('add', this.onAdd, this);
6880 this.store.on("clear", this.clear, this);
6882 this.el.on("contextmenu", this.onContextMenu, this);
6884 this.mainBody.on('scroll', this.onBodyScroll, this);
6886 this.cm.on("headerchange", this.onHeaderChange, this);
6888 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6892 onContextMenu : function(e, t)
6894 this.processEvent("contextmenu", e);
6897 processEvent : function(name, e)
6899 if (name != 'touchstart' ) {
6900 this.fireEvent(name, e);
6903 var t = e.getTarget();
6905 var cell = Roo.get(t);
6911 if(cell.findParent('tfoot', false, true)){
6915 if(cell.findParent('thead', false, true)){
6917 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6918 cell = Roo.get(t).findParent('th', false, true);
6920 Roo.log("failed to find th in thead?");
6921 Roo.log(e.getTarget());
6926 var cellIndex = cell.dom.cellIndex;
6928 var ename = name == 'touchstart' ? 'click' : name;
6929 this.fireEvent("header" + ename, this, cellIndex, e);
6934 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6935 cell = Roo.get(t).findParent('td', false, true);
6937 Roo.log("failed to find th in tbody?");
6938 Roo.log(e.getTarget());
6943 var row = cell.findParent('tr', false, true);
6944 var cellIndex = cell.dom.cellIndex;
6945 var rowIndex = row.dom.rowIndex - 1;
6949 this.fireEvent("row" + name, this, rowIndex, e);
6953 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6959 onMouseover : function(e, el)
6961 var cell = Roo.get(el);
6967 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6968 cell = cell.findParent('td', false, true);
6971 var row = cell.findParent('tr', false, true);
6972 var cellIndex = cell.dom.cellIndex;
6973 var rowIndex = row.dom.rowIndex - 1; // start from 0
6975 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6979 onMouseout : function(e, el)
6981 var cell = Roo.get(el);
6987 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6988 cell = cell.findParent('td', false, true);
6991 var row = cell.findParent('tr', false, true);
6992 var cellIndex = cell.dom.cellIndex;
6993 var rowIndex = row.dom.rowIndex - 1; // start from 0
6995 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6999 onClick : function(e, el)
7001 var cell = Roo.get(el);
7003 if(!cell || (!this.cellSelection && !this.rowSelection)){
7007 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7008 cell = cell.findParent('td', false, true);
7011 if(!cell || typeof(cell) == 'undefined'){
7015 var row = cell.findParent('tr', false, true);
7017 if(!row || typeof(row) == 'undefined'){
7021 var cellIndex = cell.dom.cellIndex;
7022 var rowIndex = this.getRowIndex(row);
7024 // why??? - should these not be based on SelectionModel?
7025 if(this.cellSelection){
7026 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7029 if(this.rowSelection){
7030 this.fireEvent('rowclick', this, row, rowIndex, e);
7036 onDblClick : function(e,el)
7038 var cell = Roo.get(el);
7040 if(!cell || (!this.cellSelection && !this.rowSelection)){
7044 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7045 cell = cell.findParent('td', false, true);
7048 if(!cell || typeof(cell) == 'undefined'){
7052 var row = cell.findParent('tr', false, true);
7054 if(!row || typeof(row) == 'undefined'){
7058 var cellIndex = cell.dom.cellIndex;
7059 var rowIndex = this.getRowIndex(row);
7061 if(this.cellSelection){
7062 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7065 if(this.rowSelection){
7066 this.fireEvent('rowdblclick', this, row, rowIndex, e);
7070 sort : function(e,el)
7072 var col = Roo.get(el);
7074 if(!col.hasClass('sortable')){
7078 var sort = col.attr('sort');
7081 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7085 this.store.sortInfo = {field : sort, direction : dir};
7088 Roo.log("calling footer first");
7089 this.footer.onClick('first');
7092 this.store.load({ params : { start : 0 } });
7096 renderHeader : function()
7104 this.totalWidth = 0;
7106 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7108 var config = cm.config[i];
7112 cls : 'x-hcol-' + i,
7114 html: cm.getColumnHeader(i)
7119 if(typeof(config.sortable) != 'undefined' && config.sortable){
7121 c.html = '<i class="glyphicon"></i>' + c.html;
7124 // could use BS4 hidden-..-down
7126 if(typeof(config.lgHeader) != 'undefined'){
7127 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
7130 if(typeof(config.mdHeader) != 'undefined'){
7131 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
7134 if(typeof(config.smHeader) != 'undefined'){
7135 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
7138 if(typeof(config.xsHeader) != 'undefined'){
7139 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
7146 if(typeof(config.tooltip) != 'undefined'){
7147 c.tooltip = config.tooltip;
7150 if(typeof(config.colspan) != 'undefined'){
7151 c.colspan = config.colspan;
7154 if(typeof(config.hidden) != 'undefined' && config.hidden){
7155 c.style += ' display:none;';
7158 if(typeof(config.dataIndex) != 'undefined'){
7159 c.sort = config.dataIndex;
7164 if(typeof(config.align) != 'undefined' && config.align.length){
7165 c.style += ' text-align:' + config.align + ';';
7168 if(typeof(config.width) != 'undefined'){
7169 c.style += ' width:' + config.width + 'px;';
7170 this.totalWidth += config.width;
7172 this.totalWidth += 100; // assume minimum of 100 per column?
7175 if(typeof(config.cls) != 'undefined'){
7176 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
7179 ['xs','sm','md','lg'].map(function(size){
7181 if(typeof(config[size]) == 'undefined'){
7185 if (!config[size]) { // 0 = hidden
7186 // BS 4 '0' is treated as hide that column and below.
7187 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
7191 c.cls += ' col-' + size + '-' + config[size] + (
7192 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7204 renderBody : function()
7214 colspan : this.cm.getColumnCount()
7224 renderFooter : function()
7234 colspan : this.cm.getColumnCount()
7248 // Roo.log('ds onload');
7253 var ds = this.store;
7255 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7256 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
7257 if (_this.store.sortInfo) {
7259 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
7260 e.select('i', true).addClass(['glyphicon-arrow-up']);
7263 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
7264 e.select('i', true).addClass(['glyphicon-arrow-down']);
7269 var tbody = this.mainBody;
7271 if(ds.getCount() > 0){
7272 ds.data.each(function(d,rowIndex){
7273 var row = this.renderRow(cm, ds, rowIndex);
7275 tbody.createChild(row);
7279 if(row.cellObjects.length){
7280 Roo.each(row.cellObjects, function(r){
7281 _this.renderCellObject(r);
7288 var tfoot = this.el.select('tfoot', true).first();
7290 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7292 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7294 var total = this.ds.getTotalCount();
7296 if(this.footer.pageSize < total){
7297 this.mainFoot.show();
7301 Roo.each(this.el.select('tbody td', true).elements, function(e){
7302 e.on('mouseover', _this.onMouseover, _this);
7305 Roo.each(this.el.select('tbody td', true).elements, function(e){
7306 e.on('mouseout', _this.onMouseout, _this);
7308 this.fireEvent('rowsrendered', this);
7314 onUpdate : function(ds,record)
7316 this.refreshRow(record);
7320 onRemove : function(ds, record, index, isUpdate){
7321 if(isUpdate !== true){
7322 this.fireEvent("beforerowremoved", this, index, record);
7324 var bt = this.mainBody.dom;
7326 var rows = this.el.select('tbody > tr', true).elements;
7328 if(typeof(rows[index]) != 'undefined'){
7329 bt.removeChild(rows[index].dom);
7332 // if(bt.rows[index]){
7333 // bt.removeChild(bt.rows[index]);
7336 if(isUpdate !== true){
7337 //this.stripeRows(index);
7338 //this.syncRowHeights(index, index);
7340 this.fireEvent("rowremoved", this, index, record);
7344 onAdd : function(ds, records, rowIndex)
7346 //Roo.log('on Add called');
7347 // - note this does not handle multiple adding very well..
7348 var bt = this.mainBody.dom;
7349 for (var i =0 ; i < records.length;i++) {
7350 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7351 //Roo.log(records[i]);
7352 //Roo.log(this.store.getAt(rowIndex+i));
7353 this.insertRow(this.store, rowIndex + i, false);
7360 refreshRow : function(record){
7361 var ds = this.store, index;
7362 if(typeof record == 'number'){
7364 record = ds.getAt(index);
7366 index = ds.indexOf(record);
7368 this.insertRow(ds, index, true);
7370 this.onRemove(ds, record, index+1, true);
7372 //this.syncRowHeights(index, index);
7374 this.fireEvent("rowupdated", this, index, record);
7377 insertRow : function(dm, rowIndex, isUpdate){
7380 this.fireEvent("beforerowsinserted", this, rowIndex);
7382 //var s = this.getScrollState();
7383 var row = this.renderRow(this.cm, this.store, rowIndex);
7384 // insert before rowIndex..
7385 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7389 if(row.cellObjects.length){
7390 Roo.each(row.cellObjects, function(r){
7391 _this.renderCellObject(r);
7396 this.fireEvent("rowsinserted", this, rowIndex);
7397 //this.syncRowHeights(firstRow, lastRow);
7398 //this.stripeRows(firstRow);
7405 getRowDom : function(rowIndex)
7407 var rows = this.el.select('tbody > tr', true).elements;
7409 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7412 // returns the object tree for a tr..
7415 renderRow : function(cm, ds, rowIndex)
7417 var d = ds.getAt(rowIndex);
7421 cls : 'x-row-' + rowIndex,
7425 var cellObjects = [];
7427 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7428 var config = cm.config[i];
7430 var renderer = cm.getRenderer(i);
7434 if(typeof(renderer) !== 'undefined'){
7435 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7437 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7438 // and are rendered into the cells after the row is rendered - using the id for the element.
7440 if(typeof(value) === 'object'){
7450 rowIndex : rowIndex,
7455 this.fireEvent('rowclass', this, rowcfg);
7459 cls : rowcfg.rowClass + ' x-col-' + i,
7461 html: (typeof(value) === 'object') ? '' : value
7468 if(typeof(config.colspan) != 'undefined'){
7469 td.colspan = config.colspan;
7472 if(typeof(config.hidden) != 'undefined' && config.hidden){
7473 td.style += ' display:none;';
7476 if(typeof(config.align) != 'undefined' && config.align.length){
7477 td.style += ' text-align:' + config.align + ';';
7479 if(typeof(config.valign) != 'undefined' && config.valign.length){
7480 td.style += ' vertical-align:' + config.valign + ';';
7483 if(typeof(config.width) != 'undefined'){
7484 td.style += ' width:' + config.width + 'px;';
7487 if(typeof(config.cursor) != 'undefined'){
7488 td.style += ' cursor:' + config.cursor + ';';
7491 if(typeof(config.cls) != 'undefined'){
7492 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7495 ['xs','sm','md','lg'].map(function(size){
7497 if(typeof(config[size]) == 'undefined'){
7503 if (!config[size]) { // 0 = hidden
7504 // BS 4 '0' is treated as hide that column and below.
7505 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7509 td.cls += ' col-' + size + '-' + config[size] + (
7510 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7520 row.cellObjects = cellObjects;
7528 onBeforeLoad : function()
7537 this.el.select('tbody', true).first().dom.innerHTML = '';
7540 * Show or hide a row.
7541 * @param {Number} rowIndex to show or hide
7542 * @param {Boolean} state hide
7544 setRowVisibility : function(rowIndex, state)
7546 var bt = this.mainBody.dom;
7548 var rows = this.el.select('tbody > tr', true).elements;
7550 if(typeof(rows[rowIndex]) == 'undefined'){
7553 rows[rowIndex].dom.style.display = state ? '' : 'none';
7557 getSelectionModel : function(){
7559 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7561 return this.selModel;
7564 * Render the Roo.bootstrap object from renderder
7566 renderCellObject : function(r)
7570 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7572 var t = r.cfg.render(r.container);
7575 Roo.each(r.cfg.cn, function(c){
7577 container: t.getChildContainer(),
7580 _this.renderCellObject(child);
7585 getRowIndex : function(row)
7589 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7600 * Returns the grid's underlying element = used by panel.Grid
7601 * @return {Element} The element
7603 getGridEl : function(){
7607 * Forces a resize - used by panel.Grid
7608 * @return {Element} The element
7610 autoSize : function()
7612 //var ctr = Roo.get(this.container.dom.parentElement);
7613 var ctr = Roo.get(this.el.dom);
7615 var thd = this.getGridEl().select('thead',true).first();
7616 var tbd = this.getGridEl().select('tbody', true).first();
7617 var tfd = this.getGridEl().select('tfoot', true).first();
7619 var cw = ctr.getWidth();
7623 tbd.setWidth(ctr.getWidth());
7624 // if the body has a max height - and then scrolls - we should perhaps set up the height here
7625 // this needs fixing for various usage - currently only hydra job advers I think..
7627 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7629 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7632 cw = Math.max(cw, this.totalWidth);
7633 this.getGridEl().select('tr',true).setWidth(cw);
7634 // resize 'expandable coloumn?
7636 return; // we doe not have a view in this design..
7639 onBodyScroll: function()
7641 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7643 this.mainHead.setStyle({
7644 'position' : 'relative',
7645 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7651 var scrollHeight = this.mainBody.dom.scrollHeight;
7653 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7655 var height = this.mainBody.getHeight();
7657 if(scrollHeight - height == scrollTop) {
7659 var total = this.ds.getTotalCount();
7661 if(this.footer.cursor + this.footer.pageSize < total){
7663 this.footer.ds.load({
7665 start : this.footer.cursor + this.footer.pageSize,
7666 limit : this.footer.pageSize
7676 onHeaderChange : function()
7678 var header = this.renderHeader();
7679 var table = this.el.select('table', true).first();
7681 this.mainHead.remove();
7682 this.mainHead = table.createChild(header, this.mainBody, false);
7685 onHiddenChange : function(colModel, colIndex, hidden)
7687 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7688 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7690 this.CSS.updateRule(thSelector, "display", "");
7691 this.CSS.updateRule(tdSelector, "display", "");
7694 this.CSS.updateRule(thSelector, "display", "none");
7695 this.CSS.updateRule(tdSelector, "display", "none");
7698 this.onHeaderChange();
7702 setColumnWidth: function(col_index, width)
7704 // width = "md-2 xs-2..."
7705 if(!this.colModel.config[col_index]) {
7709 var w = width.split(" ");
7711 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7713 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7716 for(var j = 0; j < w.length; j++) {
7722 var size_cls = w[j].split("-");
7724 if(!Number.isInteger(size_cls[1] * 1)) {
7728 if(!this.colModel.config[col_index][size_cls[0]]) {
7732 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7736 h_row[0].classList.replace(
7737 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7738 "col-"+size_cls[0]+"-"+size_cls[1]
7741 for(var i = 0; i < rows.length; i++) {
7743 var size_cls = w[j].split("-");
7745 if(!Number.isInteger(size_cls[1] * 1)) {
7749 if(!this.colModel.config[col_index][size_cls[0]]) {
7753 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7757 rows[i].classList.replace(
7758 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7759 "col-"+size_cls[0]+"-"+size_cls[1]
7763 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7778 * @class Roo.bootstrap.TableCell
7779 * @extends Roo.bootstrap.Component
7780 * Bootstrap TableCell class
7781 * @cfg {String} html cell contain text
7782 * @cfg {String} cls cell class
7783 * @cfg {String} tag cell tag (td|th) default td
7784 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7785 * @cfg {String} align Aligns the content in a cell
7786 * @cfg {String} axis Categorizes cells
7787 * @cfg {String} bgcolor Specifies the background color of a cell
7788 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7789 * @cfg {Number} colspan Specifies the number of columns a cell should span
7790 * @cfg {String} headers Specifies one or more header cells a cell is related to
7791 * @cfg {Number} height Sets the height of a cell
7792 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7793 * @cfg {Number} rowspan Sets the number of rows a cell should span
7794 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7795 * @cfg {String} valign Vertical aligns the content in a cell
7796 * @cfg {Number} width Specifies the width of a cell
7799 * Create a new TableCell
7800 * @param {Object} config The config object
7803 Roo.bootstrap.TableCell = function(config){
7804 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7807 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7827 getAutoCreate : function(){
7828 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7848 cfg.align=this.align
7854 cfg.bgcolor=this.bgcolor
7857 cfg.charoff=this.charoff
7860 cfg.colspan=this.colspan
7863 cfg.headers=this.headers
7866 cfg.height=this.height
7869 cfg.nowrap=this.nowrap
7872 cfg.rowspan=this.rowspan
7875 cfg.scope=this.scope
7878 cfg.valign=this.valign
7881 cfg.width=this.width
7900 * @class Roo.bootstrap.TableRow
7901 * @extends Roo.bootstrap.Component
7902 * Bootstrap TableRow class
7903 * @cfg {String} cls row class
7904 * @cfg {String} align Aligns the content in a table row
7905 * @cfg {String} bgcolor Specifies a background color for a table row
7906 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7907 * @cfg {String} valign Vertical aligns the content in a table row
7910 * Create a new TableRow
7911 * @param {Object} config The config object
7914 Roo.bootstrap.TableRow = function(config){
7915 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7918 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7926 getAutoCreate : function(){
7927 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7937 cfg.align = this.align;
7940 cfg.bgcolor = this.bgcolor;
7943 cfg.charoff = this.charoff;
7946 cfg.valign = this.valign;
7964 * @class Roo.bootstrap.TableBody
7965 * @extends Roo.bootstrap.Component
7966 * Bootstrap TableBody class
7967 * @cfg {String} cls element class
7968 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7969 * @cfg {String} align Aligns the content inside the element
7970 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7971 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7974 * Create a new TableBody
7975 * @param {Object} config The config object
7978 Roo.bootstrap.TableBody = function(config){
7979 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7982 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7990 getAutoCreate : function(){
7991 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8005 cfg.align = this.align;
8008 cfg.charoff = this.charoff;
8011 cfg.valign = this.valign;
8018 // initEvents : function()
8025 // this.store = Roo.factory(this.store, Roo.data);
8026 // this.store.on('load', this.onLoad, this);
8028 // this.store.load();
8032 // onLoad: function ()
8034 // this.fireEvent('load', this);
8044 * Ext JS Library 1.1.1
8045 * Copyright(c) 2006-2007, Ext JS, LLC.
8047 * Originally Released Under LGPL - original licence link has changed is not relivant.
8050 * <script type="text/javascript">
8053 // as we use this in bootstrap.
8054 Roo.namespace('Roo.form');
8056 * @class Roo.form.Action
8057 * Internal Class used to handle form actions
8059 * @param {Roo.form.BasicForm} el The form element or its id
8060 * @param {Object} config Configuration options
8065 // define the action interface
8066 Roo.form.Action = function(form, options){
8068 this.options = options || {};
8071 * Client Validation Failed
8074 Roo.form.Action.CLIENT_INVALID = 'client';
8076 * Server Validation Failed
8079 Roo.form.Action.SERVER_INVALID = 'server';
8081 * Connect to Server Failed
8084 Roo.form.Action.CONNECT_FAILURE = 'connect';
8086 * Reading Data from Server Failed
8089 Roo.form.Action.LOAD_FAILURE = 'load';
8091 Roo.form.Action.prototype = {
8093 failureType : undefined,
8094 response : undefined,
8098 run : function(options){
8103 success : function(response){
8108 handleResponse : function(response){
8112 // default connection failure
8113 failure : function(response){
8115 this.response = response;
8116 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8117 this.form.afterAction(this, false);
8120 processResponse : function(response){
8121 this.response = response;
8122 if(!response.responseText){
8125 this.result = this.handleResponse(response);
8129 // utility functions used internally
8130 getUrl : function(appendParams){
8131 var url = this.options.url || this.form.url || this.form.el.dom.action;
8133 var p = this.getParams();
8135 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
8141 getMethod : function(){
8142 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
8145 getParams : function(){
8146 var bp = this.form.baseParams;
8147 var p = this.options.params;
8149 if(typeof p == "object"){
8150 p = Roo.urlEncode(Roo.applyIf(p, bp));
8151 }else if(typeof p == 'string' && bp){
8152 p += '&' + Roo.urlEncode(bp);
8155 p = Roo.urlEncode(bp);
8160 createCallback : function(){
8162 success: this.success,
8163 failure: this.failure,
8165 timeout: (this.form.timeout*1000),
8166 upload: this.form.fileUpload ? this.success : undefined
8171 Roo.form.Action.Submit = function(form, options){
8172 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
8175 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
8178 haveProgress : false,
8179 uploadComplete : false,
8181 // uploadProgress indicator.
8182 uploadProgress : function()
8184 if (!this.form.progressUrl) {
8188 if (!this.haveProgress) {
8189 Roo.MessageBox.progress("Uploading", "Uploading");
8191 if (this.uploadComplete) {
8192 Roo.MessageBox.hide();
8196 this.haveProgress = true;
8198 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
8200 var c = new Roo.data.Connection();
8202 url : this.form.progressUrl,
8207 success : function(req){
8208 //console.log(data);
8212 rdata = Roo.decode(req.responseText)
8214 Roo.log("Invalid data from server..");
8218 if (!rdata || !rdata.success) {
8220 Roo.MessageBox.alert(Roo.encode(rdata));
8223 var data = rdata.data;
8225 if (this.uploadComplete) {
8226 Roo.MessageBox.hide();
8231 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
8232 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
8235 this.uploadProgress.defer(2000,this);
8238 failure: function(data) {
8239 Roo.log('progress url failed ');
8250 // run get Values on the form, so it syncs any secondary forms.
8251 this.form.getValues();
8253 var o = this.options;
8254 var method = this.getMethod();
8255 var isPost = method == 'POST';
8256 if(o.clientValidation === false || this.form.isValid()){
8258 if (this.form.progressUrl) {
8259 this.form.findField('UPLOAD_IDENTIFIER').setValue(
8260 (new Date() * 1) + '' + Math.random());
8265 Roo.Ajax.request(Roo.apply(this.createCallback(), {
8266 form:this.form.el.dom,
8267 url:this.getUrl(!isPost),
8269 params:isPost ? this.getParams() : null,
8270 isUpload: this.form.fileUpload,
8271 formData : this.form.formData
8274 this.uploadProgress();
8276 }else if (o.clientValidation !== false){ // client validation failed
8277 this.failureType = Roo.form.Action.CLIENT_INVALID;
8278 this.form.afterAction(this, false);
8282 success : function(response)
8284 this.uploadComplete= true;
8285 if (this.haveProgress) {
8286 Roo.MessageBox.hide();
8290 var result = this.processResponse(response);
8291 if(result === true || result.success){
8292 this.form.afterAction(this, true);
8296 this.form.markInvalid(result.errors);
8297 this.failureType = Roo.form.Action.SERVER_INVALID;
8299 this.form.afterAction(this, false);
8301 failure : function(response)
8303 this.uploadComplete= true;
8304 if (this.haveProgress) {
8305 Roo.MessageBox.hide();
8308 this.response = response;
8309 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8310 this.form.afterAction(this, false);
8313 handleResponse : function(response){
8314 if(this.form.errorReader){
8315 var rs = this.form.errorReader.read(response);
8318 for(var i = 0, len = rs.records.length; i < len; i++) {
8319 var r = rs.records[i];
8323 if(errors.length < 1){
8327 success : rs.success,
8333 ret = Roo.decode(response.responseText);
8337 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8347 Roo.form.Action.Load = function(form, options){
8348 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8349 this.reader = this.form.reader;
8352 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8357 Roo.Ajax.request(Roo.apply(
8358 this.createCallback(), {
8359 method:this.getMethod(),
8360 url:this.getUrl(false),
8361 params:this.getParams()
8365 success : function(response){
8367 var result = this.processResponse(response);
8368 if(result === true || !result.success || !result.data){
8369 this.failureType = Roo.form.Action.LOAD_FAILURE;
8370 this.form.afterAction(this, false);
8373 this.form.clearInvalid();
8374 this.form.setValues(result.data);
8375 this.form.afterAction(this, true);
8378 handleResponse : function(response){
8379 if(this.form.reader){
8380 var rs = this.form.reader.read(response);
8381 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8383 success : rs.success,
8387 return Roo.decode(response.responseText);
8391 Roo.form.Action.ACTION_TYPES = {
8392 'load' : Roo.form.Action.Load,
8393 'submit' : Roo.form.Action.Submit
8402 * @class Roo.bootstrap.Form
8403 * @extends Roo.bootstrap.Component
8404 * Bootstrap Form class
8405 * @cfg {String} method GET | POST (default POST)
8406 * @cfg {String} labelAlign top | left (default top)
8407 * @cfg {String} align left | right - for navbars
8408 * @cfg {Boolean} loadMask load mask when submit (default true)
8413 * @param {Object} config The config object
8417 Roo.bootstrap.Form = function(config){
8419 Roo.bootstrap.Form.superclass.constructor.call(this, config);
8421 Roo.bootstrap.Form.popover.apply();
8425 * @event clientvalidation
8426 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8427 * @param {Form} this
8428 * @param {Boolean} valid true if the form has passed client-side validation
8430 clientvalidation: true,
8432 * @event beforeaction
8433 * Fires before any action is performed. Return false to cancel the action.
8434 * @param {Form} this
8435 * @param {Action} action The action to be performed
8439 * @event actionfailed
8440 * Fires when an action fails.
8441 * @param {Form} this
8442 * @param {Action} action The action that failed
8444 actionfailed : true,
8446 * @event actioncomplete
8447 * Fires when an action is completed.
8448 * @param {Form} this
8449 * @param {Action} action The action that completed
8451 actioncomplete : true
8455 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
8458 * @cfg {String} method
8459 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8464 * The URL to use for form actions if one isn't supplied in the action options.
8467 * @cfg {Boolean} fileUpload
8468 * Set to true if this form is a file upload.
8472 * @cfg {Object} baseParams
8473 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8477 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8481 * @cfg {Sting} align (left|right) for navbar forms
8486 activeAction : null,
8489 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8490 * element by passing it or its id or mask the form itself by passing in true.
8493 waitMsgTarget : false,
8498 * @cfg {Boolean} errorMask (true|false) default false
8503 * @cfg {Number} maskOffset Default 100
8508 * @cfg {Boolean} maskBody
8512 getAutoCreate : function(){
8516 method : this.method || 'POST',
8517 id : this.id || Roo.id(),
8520 if (this.parent().xtype.match(/^Nav/)) {
8521 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8525 if (this.labelAlign == 'left' ) {
8526 cfg.cls += ' form-horizontal';
8532 initEvents : function()
8534 this.el.on('submit', this.onSubmit, this);
8535 // this was added as random key presses on the form where triggering form submit.
8536 this.el.on('keypress', function(e) {
8537 if (e.getCharCode() != 13) {
8540 // we might need to allow it for textareas.. and some other items.
8541 // check e.getTarget().
8543 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8547 Roo.log("keypress blocked");
8555 onSubmit : function(e){
8560 * Returns true if client-side validation on the form is successful.
8563 isValid : function(){
8564 var items = this.getItems();
8568 items.each(function(f){
8574 Roo.log('invalid field: ' + f.name);
8578 if(!target && f.el.isVisible(true)){
8584 if(this.errorMask && !valid){
8585 Roo.bootstrap.Form.popover.mask(this, target);
8592 * Returns true if any fields in this form have changed since their original load.
8595 isDirty : function(){
8597 var items = this.getItems();
8598 items.each(function(f){
8608 * Performs a predefined action (submit or load) or custom actions you define on this form.
8609 * @param {String} actionName The name of the action type
8610 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8611 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8612 * accept other config options):
8614 Property Type Description
8615 ---------------- --------------- ----------------------------------------------------------------------------------
8616 url String The url for the action (defaults to the form's url)
8617 method String The form method to use (defaults to the form's method, or POST if not defined)
8618 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8619 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8620 validate the form on the client (defaults to false)
8622 * @return {BasicForm} this
8624 doAction : function(action, options){
8625 if(typeof action == 'string'){
8626 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8628 if(this.fireEvent('beforeaction', this, action) !== false){
8629 this.beforeAction(action);
8630 action.run.defer(100, action);
8636 beforeAction : function(action){
8637 var o = action.options;
8642 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8644 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8647 // not really supported yet.. ??
8649 //if(this.waitMsgTarget === true){
8650 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8651 //}else if(this.waitMsgTarget){
8652 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8653 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8655 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8661 afterAction : function(action, success){
8662 this.activeAction = null;
8663 var o = action.options;
8668 Roo.get(document.body).unmask();
8674 //if(this.waitMsgTarget === true){
8675 // this.el.unmask();
8676 //}else if(this.waitMsgTarget){
8677 // this.waitMsgTarget.unmask();
8679 // Roo.MessageBox.updateProgress(1);
8680 // Roo.MessageBox.hide();
8687 Roo.callback(o.success, o.scope, [this, action]);
8688 this.fireEvent('actioncomplete', this, action);
8692 // failure condition..
8693 // we have a scenario where updates need confirming.
8694 // eg. if a locking scenario exists..
8695 // we look for { errors : { needs_confirm : true }} in the response.
8697 (typeof(action.result) != 'undefined') &&
8698 (typeof(action.result.errors) != 'undefined') &&
8699 (typeof(action.result.errors.needs_confirm) != 'undefined')
8702 Roo.log("not supported yet");
8705 Roo.MessageBox.confirm(
8706 "Change requires confirmation",
8707 action.result.errorMsg,
8712 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8722 Roo.callback(o.failure, o.scope, [this, action]);
8723 // show an error message if no failed handler is set..
8724 if (!this.hasListener('actionfailed')) {
8725 Roo.log("need to add dialog support");
8727 Roo.MessageBox.alert("Error",
8728 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8729 action.result.errorMsg :
8730 "Saving Failed, please check your entries or try again"
8735 this.fireEvent('actionfailed', this, action);
8740 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8741 * @param {String} id The value to search for
8744 findField : function(id){
8745 var items = this.getItems();
8746 var field = items.get(id);
8748 items.each(function(f){
8749 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8756 return field || null;
8759 * Mark fields in this form invalid in bulk.
8760 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8761 * @return {BasicForm} this
8763 markInvalid : function(errors){
8764 if(errors instanceof Array){
8765 for(var i = 0, len = errors.length; i < len; i++){
8766 var fieldError = errors[i];
8767 var f = this.findField(fieldError.id);
8769 f.markInvalid(fieldError.msg);
8775 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8776 field.markInvalid(errors[id]);
8780 //Roo.each(this.childForms || [], function (f) {
8781 // f.markInvalid(errors);
8788 * Set values for fields in this form in bulk.
8789 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8790 * @return {BasicForm} this
8792 setValues : function(values){
8793 if(values instanceof Array){ // array of objects
8794 for(var i = 0, len = values.length; i < len; i++){
8796 var f = this.findField(v.id);
8798 f.setValue(v.value);
8799 if(this.trackResetOnLoad){
8800 f.originalValue = f.getValue();
8804 }else{ // object hash
8807 if(typeof values[id] != 'function' && (field = this.findField(id))){
8809 if (field.setFromData &&
8811 field.displayField &&
8812 // combos' with local stores can
8813 // be queried via setValue()
8814 // to set their value..
8815 (field.store && !field.store.isLocal)
8819 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8820 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8821 field.setFromData(sd);
8823 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8825 field.setFromData(values);
8828 field.setValue(values[id]);
8832 if(this.trackResetOnLoad){
8833 field.originalValue = field.getValue();
8839 //Roo.each(this.childForms || [], function (f) {
8840 // f.setValues(values);
8847 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8848 * they are returned as an array.
8849 * @param {Boolean} asString
8852 getValues : function(asString){
8853 //if (this.childForms) {
8854 // copy values from the child forms
8855 // Roo.each(this.childForms, function (f) {
8856 // this.setValues(f.getValues());
8862 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8863 if(asString === true){
8866 return Roo.urlDecode(fs);
8870 * Returns the fields in this form as an object with key/value pairs.
8871 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8874 getFieldValues : function(with_hidden)
8876 var items = this.getItems();
8878 items.each(function(f){
8884 var v = f.getValue();
8886 if (f.inputType =='radio') {
8887 if (typeof(ret[f.getName()]) == 'undefined') {
8888 ret[f.getName()] = ''; // empty..
8891 if (!f.el.dom.checked) {
8899 if(f.xtype == 'MoneyField'){
8900 ret[f.currencyName] = f.getCurrency();
8903 // not sure if this supported any more..
8904 if ((typeof(v) == 'object') && f.getRawValue) {
8905 v = f.getRawValue() ; // dates..
8907 // combo boxes where name != hiddenName...
8908 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8909 ret[f.name] = f.getRawValue();
8911 ret[f.getName()] = v;
8918 * Clears all invalid messages in this form.
8919 * @return {BasicForm} this
8921 clearInvalid : function(){
8922 var items = this.getItems();
8924 items.each(function(f){
8933 * @return {BasicForm} this
8936 var items = this.getItems();
8937 items.each(function(f){
8941 Roo.each(this.childForms || [], function (f) {
8949 getItems : function()
8951 var r=new Roo.util.MixedCollection(false, function(o){
8952 return o.id || (o.id = Roo.id());
8954 var iter = function(el) {
8961 Roo.each(el.items,function(e) {
8970 hideFields : function(items)
8972 Roo.each(items, function(i){
8974 var f = this.findField(i);
8985 showFields : function(items)
8987 Roo.each(items, function(i){
8989 var f = this.findField(i);
9002 Roo.apply(Roo.bootstrap.Form, {
9029 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9030 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9031 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9032 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9035 this.maskEl.top.enableDisplayMode("block");
9036 this.maskEl.left.enableDisplayMode("block");
9037 this.maskEl.bottom.enableDisplayMode("block");
9038 this.maskEl.right.enableDisplayMode("block");
9040 this.toolTip = new Roo.bootstrap.Tooltip({
9041 cls : 'roo-form-error-popover',
9043 'left' : ['r-l', [-2,0], 'right'],
9044 'right' : ['l-r', [2,0], 'left'],
9045 'bottom' : ['tl-bl', [0,2], 'top'],
9046 'top' : [ 'bl-tl', [0,-2], 'bottom']
9050 this.toolTip.render(Roo.get(document.body));
9052 this.toolTip.el.enableDisplayMode("block");
9054 Roo.get(document.body).on('click', function(){
9058 Roo.get(document.body).on('touchstart', function(){
9062 this.isApplied = true
9065 mask : function(form, target)
9069 this.target = target;
9071 if(!this.form.errorMask || !target.el){
9075 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9077 Roo.log(scrollable);
9079 var ot = this.target.el.calcOffsetsTo(scrollable);
9081 var scrollTo = ot[1] - this.form.maskOffset;
9083 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9085 scrollable.scrollTo('top', scrollTo);
9087 var box = this.target.el.getBox();
9089 var zIndex = Roo.bootstrap.Modal.zIndex++;
9092 this.maskEl.top.setStyle('position', 'absolute');
9093 this.maskEl.top.setStyle('z-index', zIndex);
9094 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9095 this.maskEl.top.setLeft(0);
9096 this.maskEl.top.setTop(0);
9097 this.maskEl.top.show();
9099 this.maskEl.left.setStyle('position', 'absolute');
9100 this.maskEl.left.setStyle('z-index', zIndex);
9101 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9102 this.maskEl.left.setLeft(0);
9103 this.maskEl.left.setTop(box.y - this.padding);
9104 this.maskEl.left.show();
9106 this.maskEl.bottom.setStyle('position', 'absolute');
9107 this.maskEl.bottom.setStyle('z-index', zIndex);
9108 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9109 this.maskEl.bottom.setLeft(0);
9110 this.maskEl.bottom.setTop(box.bottom + this.padding);
9111 this.maskEl.bottom.show();
9113 this.maskEl.right.setStyle('position', 'absolute');
9114 this.maskEl.right.setStyle('z-index', zIndex);
9115 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9116 this.maskEl.right.setLeft(box.right + this.padding);
9117 this.maskEl.right.setTop(box.y - this.padding);
9118 this.maskEl.right.show();
9120 this.toolTip.bindEl = this.target.el;
9122 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9124 var tip = this.target.blankText;
9126 if(this.target.getValue() !== '' ) {
9128 if (this.target.invalidText.length) {
9129 tip = this.target.invalidText;
9130 } else if (this.target.regexText.length){
9131 tip = this.target.regexText;
9135 this.toolTip.show(tip);
9137 this.intervalID = window.setInterval(function() {
9138 Roo.bootstrap.Form.popover.unmask();
9141 window.onwheel = function(){ return false;};
9143 (function(){ this.isMasked = true; }).defer(500, this);
9149 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
9153 this.maskEl.top.setStyle('position', 'absolute');
9154 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
9155 this.maskEl.top.hide();
9157 this.maskEl.left.setStyle('position', 'absolute');
9158 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
9159 this.maskEl.left.hide();
9161 this.maskEl.bottom.setStyle('position', 'absolute');
9162 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
9163 this.maskEl.bottom.hide();
9165 this.maskEl.right.setStyle('position', 'absolute');
9166 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
9167 this.maskEl.right.hide();
9169 this.toolTip.hide();
9171 this.toolTip.el.hide();
9173 window.onwheel = function(){ return true;};
9175 if(this.intervalID){
9176 window.clearInterval(this.intervalID);
9177 this.intervalID = false;
9180 this.isMasked = false;
9190 * Ext JS Library 1.1.1
9191 * Copyright(c) 2006-2007, Ext JS, LLC.
9193 * Originally Released Under LGPL - original licence link has changed is not relivant.
9196 * <script type="text/javascript">
9199 * @class Roo.form.VTypes
9200 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
9203 Roo.form.VTypes = function(){
9204 // closure these in so they are only created once.
9205 var alpha = /^[a-zA-Z_]+$/;
9206 var alphanum = /^[a-zA-Z0-9_]+$/;
9207 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
9208 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
9210 // All these messages and functions are configurable
9213 * The function used to validate email addresses
9214 * @param {String} value The email address
9216 'email' : function(v){
9217 return email.test(v);
9220 * The error text to display when the email validation function returns false
9223 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
9225 * The keystroke filter mask to be applied on email input
9228 'emailMask' : /[a-z0-9_\.\-@]/i,
9231 * The function used to validate URLs
9232 * @param {String} value The URL
9234 'url' : function(v){
9238 * The error text to display when the url validation function returns false
9241 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
9244 * The function used to validate alpha values
9245 * @param {String} value The value
9247 'alpha' : function(v){
9248 return alpha.test(v);
9251 * The error text to display when the alpha validation function returns false
9254 'alphaText' : 'This field should only contain letters and _',
9256 * The keystroke filter mask to be applied on alpha input
9259 'alphaMask' : /[a-z_]/i,
9262 * The function used to validate alphanumeric values
9263 * @param {String} value The value
9265 'alphanum' : function(v){
9266 return alphanum.test(v);
9269 * The error text to display when the alphanumeric validation function returns false
9272 'alphanumText' : 'This field should only contain letters, numbers and _',
9274 * The keystroke filter mask to be applied on alphanumeric input
9277 'alphanumMask' : /[a-z0-9_]/i
9287 * @class Roo.bootstrap.Input
9288 * @extends Roo.bootstrap.Component
9289 * Bootstrap Input class
9290 * @cfg {Boolean} disabled is it disabled
9291 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9292 * @cfg {String} name name of the input
9293 * @cfg {string} fieldLabel - the label associated
9294 * @cfg {string} placeholder - placeholder to put in text.
9295 * @cfg {string} before - input group add on before
9296 * @cfg {string} after - input group add on after
9297 * @cfg {string} size - (lg|sm) or leave empty..
9298 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9299 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9300 * @cfg {Number} md colspan out of 12 for computer-sized screens
9301 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9302 * @cfg {string} value default value of the input
9303 * @cfg {Number} labelWidth set the width of label
9304 * @cfg {Number} labellg set the width of label (1-12)
9305 * @cfg {Number} labelmd set the width of label (1-12)
9306 * @cfg {Number} labelsm set the width of label (1-12)
9307 * @cfg {Number} labelxs set the width of label (1-12)
9308 * @cfg {String} labelAlign (top|left)
9309 * @cfg {Boolean} readOnly Specifies that the field should be read-only
9310 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9311 * @cfg {String} indicatorpos (left|right) default left
9312 * @cfg {String} capture (user|camera) use for file input only. (default empty)
9313 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9315 * @cfg {String} align (left|center|right) Default left
9316 * @cfg {Boolean} forceFeedback (true|false) Default false
9319 * Create a new Input
9320 * @param {Object} config The config object
9323 Roo.bootstrap.Input = function(config){
9325 Roo.bootstrap.Input.superclass.constructor.call(this, config);
9330 * Fires when this field receives input focus.
9331 * @param {Roo.form.Field} this
9336 * Fires when this field loses input focus.
9337 * @param {Roo.form.Field} this
9342 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
9343 * {@link Roo.EventObject#getKey} to determine which key was pressed.
9344 * @param {Roo.form.Field} this
9345 * @param {Roo.EventObject} e The event object
9350 * Fires just before the field blurs if the field value has changed.
9351 * @param {Roo.form.Field} this
9352 * @param {Mixed} newValue The new value
9353 * @param {Mixed} oldValue The original value
9358 * Fires after the field has been marked as invalid.
9359 * @param {Roo.form.Field} this
9360 * @param {String} msg The validation message
9365 * Fires after the field has been validated with no errors.
9366 * @param {Roo.form.Field} this
9371 * Fires after the key up
9372 * @param {Roo.form.Field} this
9373 * @param {Roo.EventObject} e The event Object
9379 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
9381 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9382 automatic validation (defaults to "keyup").
9384 validationEvent : "keyup",
9386 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9388 validateOnBlur : true,
9390 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9392 validationDelay : 250,
9394 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9396 focusClass : "x-form-focus", // not needed???
9400 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9402 invalidClass : "has-warning",
9405 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9407 validClass : "has-success",
9410 * @cfg {Boolean} hasFeedback (true|false) default true
9415 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9417 invalidFeedbackClass : "glyphicon-warning-sign",
9420 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9422 validFeedbackClass : "glyphicon-ok",
9425 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9427 selectOnFocus : false,
9430 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9434 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9439 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9441 disableKeyFilter : false,
9444 * @cfg {Boolean} disabled True to disable the field (defaults to false).
9448 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9452 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9454 blankText : "Please complete this mandatory field",
9457 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9461 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9463 maxLength : Number.MAX_VALUE,
9465 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9467 minLengthText : "The minimum length for this field is {0}",
9469 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9471 maxLengthText : "The maximum length for this field is {0}",
9475 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9476 * If available, this function will be called only after the basic validators all return true, and will be passed the
9477 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9481 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9482 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9483 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9487 * @cfg {String} regexText -- Depricated - use Invalid Text
9492 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9498 autocomplete: false,
9517 formatedValue : false,
9518 forceFeedback : false,
9520 indicatorpos : 'left',
9530 parentLabelAlign : function()
9533 while (parent.parent()) {
9534 parent = parent.parent();
9535 if (typeof(parent.labelAlign) !='undefined') {
9536 return parent.labelAlign;
9543 getAutoCreate : function()
9545 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9551 if(this.inputType != 'hidden'){
9552 cfg.cls = 'form-group' //input-group
9558 type : this.inputType,
9560 cls : 'form-control',
9561 placeholder : this.placeholder || '',
9562 autocomplete : this.autocomplete || 'new-password'
9565 if(this.capture.length){
9566 input.capture = this.capture;
9569 if(this.accept.length){
9570 input.accept = this.accept + "/*";
9574 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9577 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9578 input.maxLength = this.maxLength;
9581 if (this.disabled) {
9582 input.disabled=true;
9585 if (this.readOnly) {
9586 input.readonly=true;
9590 input.name = this.name;
9594 input.cls += ' input-' + this.size;
9598 ['xs','sm','md','lg'].map(function(size){
9599 if (settings[size]) {
9600 cfg.cls += ' col-' + size + '-' + settings[size];
9604 var inputblock = input;
9608 cls: 'glyphicon form-control-feedback'
9611 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9614 cls : 'has-feedback',
9622 if (this.before || this.after) {
9625 cls : 'input-group',
9629 if (this.before && typeof(this.before) == 'string') {
9631 inputblock.cn.push({
9633 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9637 if (this.before && typeof(this.before) == 'object') {
9638 this.before = Roo.factory(this.before);
9640 inputblock.cn.push({
9642 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9643 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9647 inputblock.cn.push(input);
9649 if (this.after && typeof(this.after) == 'string') {
9650 inputblock.cn.push({
9652 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9656 if (this.after && typeof(this.after) == 'object') {
9657 this.after = Roo.factory(this.after);
9659 inputblock.cn.push({
9661 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9662 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9666 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9667 inputblock.cls += ' has-feedback';
9668 inputblock.cn.push(feedback);
9673 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9674 tooltip : 'This field is required'
9676 if (Roo.bootstrap.version == 4) {
9679 style : 'display-none'
9682 if (align ==='left' && this.fieldLabel.length) {
9684 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
9691 cls : 'control-label col-form-label',
9692 html : this.fieldLabel
9703 var labelCfg = cfg.cn[1];
9704 var contentCfg = cfg.cn[2];
9706 if(this.indicatorpos == 'right'){
9711 cls : 'control-label col-form-label',
9715 html : this.fieldLabel
9729 labelCfg = cfg.cn[0];
9730 contentCfg = cfg.cn[1];
9734 if(this.labelWidth > 12){
9735 labelCfg.style = "width: " + this.labelWidth + 'px';
9738 if(this.labelWidth < 13 && this.labelmd == 0){
9739 this.labelmd = this.labelWidth;
9742 if(this.labellg > 0){
9743 labelCfg.cls += ' col-lg-' + this.labellg;
9744 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9747 if(this.labelmd > 0){
9748 labelCfg.cls += ' col-md-' + this.labelmd;
9749 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9752 if(this.labelsm > 0){
9753 labelCfg.cls += ' col-sm-' + this.labelsm;
9754 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9757 if(this.labelxs > 0){
9758 labelCfg.cls += ' col-xs-' + this.labelxs;
9759 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9763 } else if ( this.fieldLabel.length) {
9768 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9769 tooltip : 'This field is required'
9773 //cls : 'input-group-addon',
9774 html : this.fieldLabel
9782 if(this.indicatorpos == 'right'){
9787 //cls : 'input-group-addon',
9788 html : this.fieldLabel
9793 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9794 tooltip : 'This field is required'
9814 if (this.parentType === 'Navbar' && this.parent().bar) {
9815 cfg.cls += ' navbar-form';
9818 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9819 // on BS4 we do this only if not form
9820 cfg.cls += ' navbar-form';
9828 * return the real input element.
9830 inputEl: function ()
9832 return this.el.select('input.form-control',true).first();
9835 tooltipEl : function()
9837 return this.inputEl();
9840 indicatorEl : function()
9842 if (Roo.bootstrap.version == 4) {
9843 return false; // not enabled in v4 yet.
9846 var indicator = this.el.select('i.roo-required-indicator',true).first();
9856 setDisabled : function(v)
9858 var i = this.inputEl().dom;
9860 i.removeAttribute('disabled');
9864 i.setAttribute('disabled','true');
9866 initEvents : function()
9869 this.inputEl().on("keydown" , this.fireKey, this);
9870 this.inputEl().on("focus", this.onFocus, this);
9871 this.inputEl().on("blur", this.onBlur, this);
9873 this.inputEl().relayEvent('keyup', this);
9875 this.indicator = this.indicatorEl();
9878 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9881 // reference to original value for reset
9882 this.originalValue = this.getValue();
9883 //Roo.form.TextField.superclass.initEvents.call(this);
9884 if(this.validationEvent == 'keyup'){
9885 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9886 this.inputEl().on('keyup', this.filterValidation, this);
9888 else if(this.validationEvent !== false){
9889 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9892 if(this.selectOnFocus){
9893 this.on("focus", this.preFocus, this);
9896 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9897 this.inputEl().on("keypress", this.filterKeys, this);
9899 this.inputEl().relayEvent('keypress', this);
9902 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9903 this.el.on("click", this.autoSize, this);
9906 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9907 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9910 if (typeof(this.before) == 'object') {
9911 this.before.render(this.el.select('.roo-input-before',true).first());
9913 if (typeof(this.after) == 'object') {
9914 this.after.render(this.el.select('.roo-input-after',true).first());
9917 this.inputEl().on('change', this.onChange, this);
9920 filterValidation : function(e){
9921 if(!e.isNavKeyPress()){
9922 this.validationTask.delay(this.validationDelay);
9926 * Validates the field value
9927 * @return {Boolean} True if the value is valid, else false
9929 validate : function(){
9930 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9931 if(this.disabled || this.validateValue(this.getRawValue())){
9942 * Validates a value according to the field's validation rules and marks the field as invalid
9943 * if the validation fails
9944 * @param {Mixed} value The value to validate
9945 * @return {Boolean} True if the value is valid, else false
9947 validateValue : function(value)
9949 if(this.getVisibilityEl().hasClass('hidden')){
9953 if(value.length < 1) { // if it's blank
9954 if(this.allowBlank){
9960 if(value.length < this.minLength){
9963 if(value.length > this.maxLength){
9967 var vt = Roo.form.VTypes;
9968 if(!vt[this.vtype](value, this)){
9972 if(typeof this.validator == "function"){
9973 var msg = this.validator(value);
9977 if (typeof(msg) == 'string') {
9978 this.invalidText = msg;
9982 if(this.regex && !this.regex.test(value)){
9990 fireKey : function(e){
9991 //Roo.log('field ' + e.getKey());
9992 if(e.isNavKeyPress()){
9993 this.fireEvent("specialkey", this, e);
9996 focus : function (selectText){
9998 this.inputEl().focus();
9999 if(selectText === true){
10000 this.inputEl().dom.select();
10006 onFocus : function(){
10007 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10008 // this.el.addClass(this.focusClass);
10010 if(!this.hasFocus){
10011 this.hasFocus = true;
10012 this.startValue = this.getValue();
10013 this.fireEvent("focus", this);
10017 beforeBlur : Roo.emptyFn,
10021 onBlur : function(){
10023 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10024 //this.el.removeClass(this.focusClass);
10026 this.hasFocus = false;
10027 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10030 var v = this.getValue();
10031 if(String(v) !== String(this.startValue)){
10032 this.fireEvent('change', this, v, this.startValue);
10034 this.fireEvent("blur", this);
10037 onChange : function(e)
10039 var v = this.getValue();
10040 if(String(v) !== String(this.startValue)){
10041 this.fireEvent('change', this, v, this.startValue);
10047 * Resets the current field value to the originally loaded value and clears any validation messages
10049 reset : function(){
10050 this.setValue(this.originalValue);
10054 * Returns the name of the field
10055 * @return {Mixed} name The name field
10057 getName: function(){
10061 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
10062 * @return {Mixed} value The field value
10064 getValue : function(){
10066 var v = this.inputEl().getValue();
10071 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
10072 * @return {Mixed} value The field value
10074 getRawValue : function(){
10075 var v = this.inputEl().getValue();
10081 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
10082 * @param {Mixed} value The value to set
10084 setRawValue : function(v){
10085 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10088 selectText : function(start, end){
10089 var v = this.getRawValue();
10091 start = start === undefined ? 0 : start;
10092 end = end === undefined ? v.length : end;
10093 var d = this.inputEl().dom;
10094 if(d.setSelectionRange){
10095 d.setSelectionRange(start, end);
10096 }else if(d.createTextRange){
10097 var range = d.createTextRange();
10098 range.moveStart("character", start);
10099 range.moveEnd("character", v.length-end);
10106 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
10107 * @param {Mixed} value The value to set
10109 setValue : function(v){
10112 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10118 processValue : function(value){
10119 if(this.stripCharsRe){
10120 var newValue = value.replace(this.stripCharsRe, '');
10121 if(newValue !== value){
10122 this.setRawValue(newValue);
10129 preFocus : function(){
10131 if(this.selectOnFocus){
10132 this.inputEl().dom.select();
10135 filterKeys : function(e){
10136 var k = e.getKey();
10137 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
10140 var c = e.getCharCode(), cc = String.fromCharCode(c);
10141 if(Roo.isIE && (e.isSpecialKey() || !cc)){
10144 if(!this.maskRe.test(cc)){
10149 * Clear any invalid styles/messages for this field
10151 clearInvalid : function(){
10153 if(!this.el || this.preventMark){ // not rendered
10158 this.el.removeClass([this.invalidClass, 'is-invalid']);
10160 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10162 var feedback = this.el.select('.form-control-feedback', true).first();
10165 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10170 if(this.indicator){
10171 this.indicator.removeClass('visible');
10172 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10175 this.fireEvent('valid', this);
10179 * Mark this field as valid
10181 markValid : function()
10183 if(!this.el || this.preventMark){ // not rendered...
10187 this.el.removeClass([this.invalidClass, this.validClass]);
10188 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10190 var feedback = this.el.select('.form-control-feedback', true).first();
10193 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10196 if(this.indicator){
10197 this.indicator.removeClass('visible');
10198 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10205 if(this.allowBlank && !this.getRawValue().length){
10208 if (Roo.bootstrap.version == 3) {
10209 this.el.addClass(this.validClass);
10211 this.inputEl().addClass('is-valid');
10214 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10216 var feedback = this.el.select('.form-control-feedback', true).first();
10219 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10220 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10225 this.fireEvent('valid', this);
10229 * Mark this field as invalid
10230 * @param {String} msg The validation message
10232 markInvalid : function(msg)
10234 if(!this.el || this.preventMark){ // not rendered
10238 this.el.removeClass([this.invalidClass, this.validClass]);
10239 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10241 var feedback = this.el.select('.form-control-feedback', true).first();
10244 this.el.select('.form-control-feedback', true).first().removeClass(
10245 [this.invalidFeedbackClass, this.validFeedbackClass]);
10252 if(this.allowBlank && !this.getRawValue().length){
10256 if(this.indicator){
10257 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10258 this.indicator.addClass('visible');
10260 if (Roo.bootstrap.version == 3) {
10261 this.el.addClass(this.invalidClass);
10263 this.inputEl().addClass('is-invalid');
10268 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10270 var feedback = this.el.select('.form-control-feedback', true).first();
10273 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10275 if(this.getValue().length || this.forceFeedback){
10276 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10283 this.fireEvent('invalid', this, msg);
10286 SafariOnKeyDown : function(event)
10288 // this is a workaround for a password hang bug on chrome/ webkit.
10289 if (this.inputEl().dom.type != 'password') {
10293 var isSelectAll = false;
10295 if(this.inputEl().dom.selectionEnd > 0){
10296 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10298 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10299 event.preventDefault();
10304 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10306 event.preventDefault();
10307 // this is very hacky as keydown always get's upper case.
10309 var cc = String.fromCharCode(event.getCharCode());
10310 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
10314 adjustWidth : function(tag, w){
10315 tag = tag.toLowerCase();
10316 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10317 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10318 if(tag == 'input'){
10321 if(tag == 'textarea'){
10324 }else if(Roo.isOpera){
10325 if(tag == 'input'){
10328 if(tag == 'textarea'){
10336 setFieldLabel : function(v)
10338 if(!this.rendered){
10342 if(this.indicatorEl()){
10343 var ar = this.el.select('label > span',true);
10345 if (ar.elements.length) {
10346 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10347 this.fieldLabel = v;
10351 var br = this.el.select('label',true);
10353 if(br.elements.length) {
10354 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10355 this.fieldLabel = v;
10359 Roo.log('Cannot Found any of label > span || label in input');
10363 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10364 this.fieldLabel = v;
10379 * @class Roo.bootstrap.TextArea
10380 * @extends Roo.bootstrap.Input
10381 * Bootstrap TextArea class
10382 * @cfg {Number} cols Specifies the visible width of a text area
10383 * @cfg {Number} rows Specifies the visible number of lines in a text area
10384 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10385 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10386 * @cfg {string} html text
10389 * Create a new TextArea
10390 * @param {Object} config The config object
10393 Roo.bootstrap.TextArea = function(config){
10394 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10398 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
10408 getAutoCreate : function(){
10410 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10416 if(this.inputType != 'hidden'){
10417 cfg.cls = 'form-group' //input-group
10425 value : this.value || '',
10426 html: this.html || '',
10427 cls : 'form-control',
10428 placeholder : this.placeholder || ''
10432 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10433 input.maxLength = this.maxLength;
10437 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10441 input.cols = this.cols;
10444 if (this.readOnly) {
10445 input.readonly = true;
10449 input.name = this.name;
10453 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10457 ['xs','sm','md','lg'].map(function(size){
10458 if (settings[size]) {
10459 cfg.cls += ' col-' + size + '-' + settings[size];
10463 var inputblock = input;
10465 if(this.hasFeedback && !this.allowBlank){
10469 cls: 'glyphicon form-control-feedback'
10473 cls : 'has-feedback',
10482 if (this.before || this.after) {
10485 cls : 'input-group',
10489 inputblock.cn.push({
10491 cls : 'input-group-addon',
10496 inputblock.cn.push(input);
10498 if(this.hasFeedback && !this.allowBlank){
10499 inputblock.cls += ' has-feedback';
10500 inputblock.cn.push(feedback);
10504 inputblock.cn.push({
10506 cls : 'input-group-addon',
10513 if (align ==='left' && this.fieldLabel.length) {
10518 cls : 'control-label',
10519 html : this.fieldLabel
10530 if(this.labelWidth > 12){
10531 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10534 if(this.labelWidth < 13 && this.labelmd == 0){
10535 this.labelmd = this.labelWidth;
10538 if(this.labellg > 0){
10539 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10540 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10543 if(this.labelmd > 0){
10544 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10545 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10548 if(this.labelsm > 0){
10549 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10550 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10553 if(this.labelxs > 0){
10554 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10555 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10558 } else if ( this.fieldLabel.length) {
10563 //cls : 'input-group-addon',
10564 html : this.fieldLabel
10582 if (this.disabled) {
10583 input.disabled=true;
10590 * return the real textarea element.
10592 inputEl: function ()
10594 return this.el.select('textarea.form-control',true).first();
10598 * Clear any invalid styles/messages for this field
10600 clearInvalid : function()
10603 if(!this.el || this.preventMark){ // not rendered
10607 var label = this.el.select('label', true).first();
10608 var icon = this.el.select('i.fa-star', true).first();
10613 this.el.removeClass( this.validClass);
10614 this.inputEl().removeClass('is-invalid');
10616 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10618 var feedback = this.el.select('.form-control-feedback', true).first();
10621 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10626 this.fireEvent('valid', this);
10630 * Mark this field as valid
10632 markValid : function()
10634 if(!this.el || this.preventMark){ // not rendered
10638 this.el.removeClass([this.invalidClass, this.validClass]);
10639 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10641 var feedback = this.el.select('.form-control-feedback', true).first();
10644 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10647 if(this.disabled || this.allowBlank){
10651 var label = this.el.select('label', true).first();
10652 var icon = this.el.select('i.fa-star', true).first();
10657 if (Roo.bootstrap.version == 3) {
10658 this.el.addClass(this.validClass);
10660 this.inputEl().addClass('is-valid');
10664 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10666 var feedback = this.el.select('.form-control-feedback', true).first();
10669 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10670 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10675 this.fireEvent('valid', this);
10679 * Mark this field as invalid
10680 * @param {String} msg The validation message
10682 markInvalid : function(msg)
10684 if(!this.el || this.preventMark){ // not rendered
10688 this.el.removeClass([this.invalidClass, this.validClass]);
10689 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10691 var feedback = this.el.select('.form-control-feedback', true).first();
10694 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10697 if(this.disabled || this.allowBlank){
10701 var label = this.el.select('label', true).first();
10702 var icon = this.el.select('i.fa-star', true).first();
10704 if(!this.getValue().length && label && !icon){
10705 this.el.createChild({
10707 cls : 'text-danger fa fa-lg fa-star',
10708 tooltip : 'This field is required',
10709 style : 'margin-right:5px;'
10713 if (Roo.bootstrap.version == 3) {
10714 this.el.addClass(this.invalidClass);
10716 this.inputEl().addClass('is-invalid');
10719 // fixme ... this may be depricated need to test..
10720 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10722 var feedback = this.el.select('.form-control-feedback', true).first();
10725 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10727 if(this.getValue().length || this.forceFeedback){
10728 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10735 this.fireEvent('invalid', this, msg);
10743 * trigger field - base class for combo..
10748 * @class Roo.bootstrap.TriggerField
10749 * @extends Roo.bootstrap.Input
10750 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10751 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10752 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10753 * for which you can provide a custom implementation. For example:
10755 var trigger = new Roo.bootstrap.TriggerField();
10756 trigger.onTriggerClick = myTriggerFn;
10757 trigger.applyTo('my-field');
10760 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10761 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10762 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10763 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10764 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10767 * Create a new TriggerField.
10768 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10769 * to the base TextField)
10771 Roo.bootstrap.TriggerField = function(config){
10772 this.mimicing = false;
10773 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10776 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10778 * @cfg {String} triggerClass A CSS class to apply to the trigger
10781 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10786 * @cfg {Boolean} removable (true|false) special filter default false
10790 /** @cfg {Boolean} grow @hide */
10791 /** @cfg {Number} growMin @hide */
10792 /** @cfg {Number} growMax @hide */
10798 autoSize: Roo.emptyFn,
10802 deferHeight : true,
10805 actionMode : 'wrap',
10810 getAutoCreate : function(){
10812 var align = this.labelAlign || this.parentLabelAlign();
10817 cls: 'form-group' //input-group
10824 type : this.inputType,
10825 cls : 'form-control',
10826 autocomplete: 'new-password',
10827 placeholder : this.placeholder || ''
10831 input.name = this.name;
10834 input.cls += ' input-' + this.size;
10837 if (this.disabled) {
10838 input.disabled=true;
10841 var inputblock = input;
10843 if(this.hasFeedback && !this.allowBlank){
10847 cls: 'glyphicon form-control-feedback'
10850 if(this.removable && !this.editable && !this.tickable){
10852 cls : 'has-feedback',
10858 cls : 'roo-combo-removable-btn close'
10865 cls : 'has-feedback',
10874 if(this.removable && !this.editable && !this.tickable){
10876 cls : 'roo-removable',
10882 cls : 'roo-combo-removable-btn close'
10889 if (this.before || this.after) {
10892 cls : 'input-group',
10896 inputblock.cn.push({
10898 cls : 'input-group-addon input-group-prepend input-group-text',
10903 inputblock.cn.push(input);
10905 if(this.hasFeedback && !this.allowBlank){
10906 inputblock.cls += ' has-feedback';
10907 inputblock.cn.push(feedback);
10911 inputblock.cn.push({
10913 cls : 'input-group-addon input-group-append input-group-text',
10922 var ibwrap = inputblock;
10927 cls: 'roo-select2-choices',
10931 cls: 'roo-select2-search-field',
10943 cls: 'roo-select2-container input-group',
10948 cls: 'form-hidden-field'
10954 if(!this.multiple && this.showToggleBtn){
10960 if (this.caret != false) {
10963 cls: 'fa fa-' + this.caret
10970 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10972 Roo.bootstrap.version == 3 ? caret : '',
10975 cls: 'combobox-clear',
10989 combobox.cls += ' roo-select2-container-multi';
10993 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10994 tooltip : 'This field is required'
10996 if (Roo.bootstrap.version == 4) {
10999 style : 'display:none'
11004 if (align ==='left' && this.fieldLabel.length) {
11006 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11013 cls : 'control-label',
11014 html : this.fieldLabel
11026 var labelCfg = cfg.cn[1];
11027 var contentCfg = cfg.cn[2];
11029 if(this.indicatorpos == 'right'){
11034 cls : 'control-label',
11038 html : this.fieldLabel
11052 labelCfg = cfg.cn[0];
11053 contentCfg = cfg.cn[1];
11056 if(this.labelWidth > 12){
11057 labelCfg.style = "width: " + this.labelWidth + 'px';
11060 if(this.labelWidth < 13 && this.labelmd == 0){
11061 this.labelmd = this.labelWidth;
11064 if(this.labellg > 0){
11065 labelCfg.cls += ' col-lg-' + this.labellg;
11066 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11069 if(this.labelmd > 0){
11070 labelCfg.cls += ' col-md-' + this.labelmd;
11071 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11074 if(this.labelsm > 0){
11075 labelCfg.cls += ' col-sm-' + this.labelsm;
11076 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11079 if(this.labelxs > 0){
11080 labelCfg.cls += ' col-xs-' + this.labelxs;
11081 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11084 } else if ( this.fieldLabel.length) {
11085 // Roo.log(" label");
11090 //cls : 'input-group-addon',
11091 html : this.fieldLabel
11099 if(this.indicatorpos == 'right'){
11107 html : this.fieldLabel
11121 // Roo.log(" no label && no align");
11128 ['xs','sm','md','lg'].map(function(size){
11129 if (settings[size]) {
11130 cfg.cls += ' col-' + size + '-' + settings[size];
11141 onResize : function(w, h){
11142 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
11143 // if(typeof w == 'number'){
11144 // var x = w - this.trigger.getWidth();
11145 // this.inputEl().setWidth(this.adjustWidth('input', x));
11146 // this.trigger.setStyle('left', x+'px');
11151 adjustSize : Roo.BoxComponent.prototype.adjustSize,
11154 getResizeEl : function(){
11155 return this.inputEl();
11159 getPositionEl : function(){
11160 return this.inputEl();
11164 alignErrorIcon : function(){
11165 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
11169 initEvents : function(){
11173 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
11174 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
11175 if(!this.multiple && this.showToggleBtn){
11176 this.trigger = this.el.select('span.dropdown-toggle',true).first();
11177 if(this.hideTrigger){
11178 this.trigger.setDisplayed(false);
11180 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
11184 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
11187 if(this.removable && !this.editable && !this.tickable){
11188 var close = this.closeTriggerEl();
11191 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
11192 close.on('click', this.removeBtnClick, this, close);
11196 //this.trigger.addClassOnOver('x-form-trigger-over');
11197 //this.trigger.addClassOnClick('x-form-trigger-click');
11200 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
11204 closeTriggerEl : function()
11206 var close = this.el.select('.roo-combo-removable-btn', true).first();
11207 return close ? close : false;
11210 removeBtnClick : function(e, h, el)
11212 e.preventDefault();
11214 if(this.fireEvent("remove", this) !== false){
11216 this.fireEvent("afterremove", this)
11220 createList : function()
11222 this.list = Roo.get(document.body).createChild({
11223 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
11224 cls: 'typeahead typeahead-long dropdown-menu',
11225 style: 'display:none'
11228 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
11233 initTrigger : function(){
11238 onDestroy : function(){
11240 this.trigger.removeAllListeners();
11241 // this.trigger.remove();
11244 // this.wrap.remove();
11246 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
11250 onFocus : function(){
11251 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
11253 if(!this.mimicing){
11254 this.wrap.addClass('x-trigger-wrap-focus');
11255 this.mimicing = true;
11256 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
11257 if(this.monitorTab){
11258 this.el.on("keydown", this.checkTab, this);
11265 checkTab : function(e){
11266 if(e.getKey() == e.TAB){
11267 this.triggerBlur();
11272 onBlur : function(){
11277 mimicBlur : function(e, t){
11279 if(!this.wrap.contains(t) && this.validateBlur()){
11280 this.triggerBlur();
11286 triggerBlur : function(){
11287 this.mimicing = false;
11288 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
11289 if(this.monitorTab){
11290 this.el.un("keydown", this.checkTab, this);
11292 //this.wrap.removeClass('x-trigger-wrap-focus');
11293 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11297 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11298 validateBlur : function(e, t){
11303 onDisable : function(){
11304 this.inputEl().dom.disabled = true;
11305 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11307 // this.wrap.addClass('x-item-disabled');
11312 onEnable : function(){
11313 this.inputEl().dom.disabled = false;
11314 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11316 // this.el.removeClass('x-item-disabled');
11321 onShow : function(){
11322 var ae = this.getActionEl();
11325 ae.dom.style.display = '';
11326 ae.dom.style.visibility = 'visible';
11332 onHide : function(){
11333 var ae = this.getActionEl();
11334 ae.dom.style.display = 'none';
11338 * The function that should handle the trigger's click event. This method does nothing by default until overridden
11339 * by an implementing function.
11341 * @param {EventObject} e
11343 onTriggerClick : Roo.emptyFn
11347 * Ext JS Library 1.1.1
11348 * Copyright(c) 2006-2007, Ext JS, LLC.
11350 * Originally Released Under LGPL - original licence link has changed is not relivant.
11353 * <script type="text/javascript">
11358 * @class Roo.data.SortTypes
11360 * Defines the default sorting (casting?) comparison functions used when sorting data.
11362 Roo.data.SortTypes = {
11364 * Default sort that does nothing
11365 * @param {Mixed} s The value being converted
11366 * @return {Mixed} The comparison value
11368 none : function(s){
11373 * The regular expression used to strip tags
11377 stripTagsRE : /<\/?[^>]+>/gi,
11380 * Strips all HTML tags to sort on text only
11381 * @param {Mixed} s The value being converted
11382 * @return {String} The comparison value
11384 asText : function(s){
11385 return String(s).replace(this.stripTagsRE, "");
11389 * Strips all HTML tags to sort on text only - Case insensitive
11390 * @param {Mixed} s The value being converted
11391 * @return {String} The comparison value
11393 asUCText : function(s){
11394 return String(s).toUpperCase().replace(this.stripTagsRE, "");
11398 * Case insensitive string
11399 * @param {Mixed} s The value being converted
11400 * @return {String} The comparison value
11402 asUCString : function(s) {
11403 return String(s).toUpperCase();
11408 * @param {Mixed} s The value being converted
11409 * @return {Number} The comparison value
11411 asDate : function(s) {
11415 if(s instanceof Date){
11416 return s.getTime();
11418 return Date.parse(String(s));
11423 * @param {Mixed} s The value being converted
11424 * @return {Float} The comparison value
11426 asFloat : function(s) {
11427 var val = parseFloat(String(s).replace(/,/g, ""));
11436 * @param {Mixed} s The value being converted
11437 * @return {Number} The comparison value
11439 asInt : function(s) {
11440 var val = parseInt(String(s).replace(/,/g, ""));
11448 * Ext JS Library 1.1.1
11449 * Copyright(c) 2006-2007, Ext JS, LLC.
11451 * Originally Released Under LGPL - original licence link has changed is not relivant.
11454 * <script type="text/javascript">
11458 * @class Roo.data.Record
11459 * Instances of this class encapsulate both record <em>definition</em> information, and record
11460 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11461 * to access Records cached in an {@link Roo.data.Store} object.<br>
11463 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11464 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11467 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11469 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11470 * {@link #create}. The parameters are the same.
11471 * @param {Array} data An associative Array of data values keyed by the field name.
11472 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11473 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11474 * not specified an integer id is generated.
11476 Roo.data.Record = function(data, id){
11477 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11482 * Generate a constructor for a specific record layout.
11483 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11484 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11485 * Each field definition object may contain the following properties: <ul>
11486 * <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,
11487 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11488 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11489 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11490 * is being used, then this is a string containing the javascript expression to reference the data relative to
11491 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11492 * to the data item relative to the record element. If the mapping expression is the same as the field name,
11493 * this may be omitted.</p></li>
11494 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11495 * <ul><li>auto (Default, implies no conversion)</li>
11500 * <li>date</li></ul></p></li>
11501 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11502 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11503 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11504 * by the Reader into an object that will be stored in the Record. It is passed the
11505 * following parameters:<ul>
11506 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11508 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11510 * <br>usage:<br><pre><code>
11511 var TopicRecord = Roo.data.Record.create(
11512 {name: 'title', mapping: 'topic_title'},
11513 {name: 'author', mapping: 'username'},
11514 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11515 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11516 {name: 'lastPoster', mapping: 'user2'},
11517 {name: 'excerpt', mapping: 'post_text'}
11520 var myNewRecord = new TopicRecord({
11521 title: 'Do my job please',
11524 lastPost: new Date(),
11525 lastPoster: 'Animal',
11526 excerpt: 'No way dude!'
11528 myStore.add(myNewRecord);
11533 Roo.data.Record.create = function(o){
11534 var f = function(){
11535 f.superclass.constructor.apply(this, arguments);
11537 Roo.extend(f, Roo.data.Record);
11538 var p = f.prototype;
11539 p.fields = new Roo.util.MixedCollection(false, function(field){
11542 for(var i = 0, len = o.length; i < len; i++){
11543 p.fields.add(new Roo.data.Field(o[i]));
11545 f.getField = function(name){
11546 return p.fields.get(name);
11551 Roo.data.Record.AUTO_ID = 1000;
11552 Roo.data.Record.EDIT = 'edit';
11553 Roo.data.Record.REJECT = 'reject';
11554 Roo.data.Record.COMMIT = 'commit';
11556 Roo.data.Record.prototype = {
11558 * Readonly flag - true if this record has been modified.
11567 join : function(store){
11568 this.store = store;
11572 * Set the named field to the specified value.
11573 * @param {String} name The name of the field to set.
11574 * @param {Object} value The value to set the field to.
11576 set : function(name, value){
11577 if(this.data[name] == value){
11581 if(!this.modified){
11582 this.modified = {};
11584 if(typeof this.modified[name] == 'undefined'){
11585 this.modified[name] = this.data[name];
11587 this.data[name] = value;
11588 if(!this.editing && this.store){
11589 this.store.afterEdit(this);
11594 * Get the value of the named field.
11595 * @param {String} name The name of the field to get the value of.
11596 * @return {Object} The value of the field.
11598 get : function(name){
11599 return this.data[name];
11603 beginEdit : function(){
11604 this.editing = true;
11605 this.modified = {};
11609 cancelEdit : function(){
11610 this.editing = false;
11611 delete this.modified;
11615 endEdit : function(){
11616 this.editing = false;
11617 if(this.dirty && this.store){
11618 this.store.afterEdit(this);
11623 * Usually called by the {@link Roo.data.Store} which owns the Record.
11624 * Rejects all changes made to the Record since either creation, or the last commit operation.
11625 * Modified fields are reverted to their original values.
11627 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11628 * of reject operations.
11630 reject : function(){
11631 var m = this.modified;
11633 if(typeof m[n] != "function"){
11634 this.data[n] = m[n];
11637 this.dirty = false;
11638 delete this.modified;
11639 this.editing = false;
11641 this.store.afterReject(this);
11646 * Usually called by the {@link Roo.data.Store} which owns the Record.
11647 * Commits all changes made to the Record since either creation, or the last commit operation.
11649 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11650 * of commit operations.
11652 commit : function(){
11653 this.dirty = false;
11654 delete this.modified;
11655 this.editing = false;
11657 this.store.afterCommit(this);
11662 hasError : function(){
11663 return this.error != null;
11667 clearError : function(){
11672 * Creates a copy of this record.
11673 * @param {String} id (optional) A new record id if you don't want to use this record's id
11676 copy : function(newId) {
11677 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11681 * Ext JS Library 1.1.1
11682 * Copyright(c) 2006-2007, Ext JS, LLC.
11684 * Originally Released Under LGPL - original licence link has changed is not relivant.
11687 * <script type="text/javascript">
11693 * @class Roo.data.Store
11694 * @extends Roo.util.Observable
11695 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11696 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11698 * 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
11699 * has no knowledge of the format of the data returned by the Proxy.<br>
11701 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11702 * instances from the data object. These records are cached and made available through accessor functions.
11704 * Creates a new Store.
11705 * @param {Object} config A config object containing the objects needed for the Store to access data,
11706 * and read the data into Records.
11708 Roo.data.Store = function(config){
11709 this.data = new Roo.util.MixedCollection(false);
11710 this.data.getKey = function(o){
11713 this.baseParams = {};
11715 this.paramNames = {
11720 "multisort" : "_multisort"
11723 if(config && config.data){
11724 this.inlineData = config.data;
11725 delete config.data;
11728 Roo.apply(this, config);
11730 if(this.reader){ // reader passed
11731 this.reader = Roo.factory(this.reader, Roo.data);
11732 this.reader.xmodule = this.xmodule || false;
11733 if(!this.recordType){
11734 this.recordType = this.reader.recordType;
11736 if(this.reader.onMetaChange){
11737 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11741 if(this.recordType){
11742 this.fields = this.recordType.prototype.fields;
11744 this.modified = [];
11748 * @event datachanged
11749 * Fires when the data cache has changed, and a widget which is using this Store
11750 * as a Record cache should refresh its view.
11751 * @param {Store} this
11753 datachanged : true,
11755 * @event metachange
11756 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11757 * @param {Store} this
11758 * @param {Object} meta The JSON metadata
11763 * Fires when Records have been added to the Store
11764 * @param {Store} this
11765 * @param {Roo.data.Record[]} records The array of Records added
11766 * @param {Number} index The index at which the record(s) were added
11771 * Fires when a Record has been removed from the Store
11772 * @param {Store} this
11773 * @param {Roo.data.Record} record The Record that was removed
11774 * @param {Number} index The index at which the record was removed
11779 * Fires when a Record has been updated
11780 * @param {Store} this
11781 * @param {Roo.data.Record} record The Record that was updated
11782 * @param {String} operation The update operation being performed. Value may be one of:
11784 Roo.data.Record.EDIT
11785 Roo.data.Record.REJECT
11786 Roo.data.Record.COMMIT
11792 * Fires when the data cache has been cleared.
11793 * @param {Store} this
11797 * @event beforeload
11798 * Fires before a request is made for a new data object. If the beforeload handler returns false
11799 * the load action will be canceled.
11800 * @param {Store} this
11801 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11805 * @event beforeloadadd
11806 * Fires after a new set of Records has been loaded.
11807 * @param {Store} this
11808 * @param {Roo.data.Record[]} records The Records that were loaded
11809 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11811 beforeloadadd : true,
11814 * Fires after a new set of Records has been loaded, before they are added to the store.
11815 * @param {Store} this
11816 * @param {Roo.data.Record[]} records The Records that were loaded
11817 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11818 * @params {Object} return from reader
11822 * @event loadexception
11823 * Fires if an exception occurs in the Proxy during loading.
11824 * Called with the signature of the Proxy's "loadexception" event.
11825 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11828 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11829 * @param {Object} load options
11830 * @param {Object} jsonData from your request (normally this contains the Exception)
11832 loadexception : true
11836 this.proxy = Roo.factory(this.proxy, Roo.data);
11837 this.proxy.xmodule = this.xmodule || false;
11838 this.relayEvents(this.proxy, ["loadexception"]);
11840 this.sortToggle = {};
11841 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11843 Roo.data.Store.superclass.constructor.call(this);
11845 if(this.inlineData){
11846 this.loadData(this.inlineData);
11847 delete this.inlineData;
11851 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11853 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11854 * without a remote query - used by combo/forms at present.
11858 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11861 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11864 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11865 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11868 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11869 * on any HTTP request
11872 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11875 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11879 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11880 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11882 remoteSort : false,
11885 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11886 * loaded or when a record is removed. (defaults to false).
11888 pruneModifiedRecords : false,
11891 lastOptions : null,
11894 * Add Records to the Store and fires the add event.
11895 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11897 add : function(records){
11898 records = [].concat(records);
11899 for(var i = 0, len = records.length; i < len; i++){
11900 records[i].join(this);
11902 var index = this.data.length;
11903 this.data.addAll(records);
11904 this.fireEvent("add", this, records, index);
11908 * Remove a Record from the Store and fires the remove event.
11909 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11911 remove : function(record){
11912 var index = this.data.indexOf(record);
11913 this.data.removeAt(index);
11915 if(this.pruneModifiedRecords){
11916 this.modified.remove(record);
11918 this.fireEvent("remove", this, record, index);
11922 * Remove all Records from the Store and fires the clear event.
11924 removeAll : function(){
11926 if(this.pruneModifiedRecords){
11927 this.modified = [];
11929 this.fireEvent("clear", this);
11933 * Inserts Records to the Store at the given index and fires the add event.
11934 * @param {Number} index The start index at which to insert the passed Records.
11935 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11937 insert : function(index, records){
11938 records = [].concat(records);
11939 for(var i = 0, len = records.length; i < len; i++){
11940 this.data.insert(index, records[i]);
11941 records[i].join(this);
11943 this.fireEvent("add", this, records, index);
11947 * Get the index within the cache of the passed Record.
11948 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11949 * @return {Number} The index of the passed Record. Returns -1 if not found.
11951 indexOf : function(record){
11952 return this.data.indexOf(record);
11956 * Get the index within the cache of the Record with the passed id.
11957 * @param {String} id The id of the Record to find.
11958 * @return {Number} The index of the Record. Returns -1 if not found.
11960 indexOfId : function(id){
11961 return this.data.indexOfKey(id);
11965 * Get the Record with the specified id.
11966 * @param {String} id The id of the Record to find.
11967 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11969 getById : function(id){
11970 return this.data.key(id);
11974 * Get the Record at the specified index.
11975 * @param {Number} index The index of the Record to find.
11976 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11978 getAt : function(index){
11979 return this.data.itemAt(index);
11983 * Returns a range of Records between specified indices.
11984 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11985 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11986 * @return {Roo.data.Record[]} An array of Records
11988 getRange : function(start, end){
11989 return this.data.getRange(start, end);
11993 storeOptions : function(o){
11994 o = Roo.apply({}, o);
11997 this.lastOptions = o;
12001 * Loads the Record cache from the configured Proxy using the configured Reader.
12003 * If using remote paging, then the first load call must specify the <em>start</em>
12004 * and <em>limit</em> properties in the options.params property to establish the initial
12005 * position within the dataset, and the number of Records to cache on each read from the Proxy.
12007 * <strong>It is important to note that for remote data sources, loading is asynchronous,
12008 * and this call will return before the new data has been loaded. Perform any post-processing
12009 * in a callback function, or in a "load" event handler.</strong>
12011 * @param {Object} options An object containing properties which control loading options:<ul>
12012 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
12013 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
12014 * passed the following arguments:<ul>
12015 * <li>r : Roo.data.Record[]</li>
12016 * <li>options: Options object from the load call</li>
12017 * <li>success: Boolean success indicator</li></ul></li>
12018 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
12019 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
12022 load : function(options){
12023 options = options || {};
12024 if(this.fireEvent("beforeload", this, options) !== false){
12025 this.storeOptions(options);
12026 var p = Roo.apply(options.params || {}, this.baseParams);
12027 // if meta was not loaded from remote source.. try requesting it.
12028 if (!this.reader.metaFromRemote) {
12029 p._requestMeta = 1;
12031 if(this.sortInfo && this.remoteSort){
12032 var pn = this.paramNames;
12033 p[pn["sort"]] = this.sortInfo.field;
12034 p[pn["dir"]] = this.sortInfo.direction;
12036 if (this.multiSort) {
12037 var pn = this.paramNames;
12038 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
12041 this.proxy.load(p, this.reader, this.loadRecords, this, options);
12046 * Reloads the Record cache from the configured Proxy using the configured Reader and
12047 * the options from the last load operation performed.
12048 * @param {Object} options (optional) An object containing properties which may override the options
12049 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
12050 * the most recently used options are reused).
12052 reload : function(options){
12053 this.load(Roo.applyIf(options||{}, this.lastOptions));
12057 // Called as a callback by the Reader during a load operation.
12058 loadRecords : function(o, options, success){
12059 if(!o || success === false){
12060 if(success !== false){
12061 this.fireEvent("load", this, [], options, o);
12063 if(options.callback){
12064 options.callback.call(options.scope || this, [], options, false);
12068 // if data returned failure - throw an exception.
12069 if (o.success === false) {
12070 // show a message if no listener is registered.
12071 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
12072 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
12074 // loadmask wil be hooked into this..
12075 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
12078 var r = o.records, t = o.totalRecords || r.length;
12080 this.fireEvent("beforeloadadd", this, r, options, o);
12082 if(!options || options.add !== true){
12083 if(this.pruneModifiedRecords){
12084 this.modified = [];
12086 for(var i = 0, len = r.length; i < len; i++){
12090 this.data = this.snapshot;
12091 delete this.snapshot;
12094 this.data.addAll(r);
12095 this.totalLength = t;
12097 this.fireEvent("datachanged", this);
12099 this.totalLength = Math.max(t, this.data.length+r.length);
12103 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
12105 var e = new Roo.data.Record({});
12107 e.set(this.parent.displayField, this.parent.emptyTitle);
12108 e.set(this.parent.valueField, '');
12113 this.fireEvent("load", this, r, options, o);
12114 if(options.callback){
12115 options.callback.call(options.scope || this, r, options, true);
12121 * Loads data from a passed data block. A Reader which understands the format of the data
12122 * must have been configured in the constructor.
12123 * @param {Object} data The data block from which to read the Records. The format of the data expected
12124 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
12125 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
12127 loadData : function(o, append){
12128 var r = this.reader.readRecords(o);
12129 this.loadRecords(r, {add: append}, true);
12133 * using 'cn' the nested child reader read the child array into it's child stores.
12134 * @param {Object} rec The record with a 'children array
12136 loadDataFromChildren : function(rec)
12138 this.loadData(this.reader.toLoadData(rec));
12143 * Gets the number of cached records.
12145 * <em>If using paging, this may not be the total size of the dataset. If the data object
12146 * used by the Reader contains the dataset size, then the getTotalCount() function returns
12147 * the data set size</em>
12149 getCount : function(){
12150 return this.data.length || 0;
12154 * Gets the total number of records in the dataset as returned by the server.
12156 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
12157 * the dataset size</em>
12159 getTotalCount : function(){
12160 return this.totalLength || 0;
12164 * Returns the sort state of the Store as an object with two properties:
12166 field {String} The name of the field by which the Records are sorted
12167 direction {String} The sort order, "ASC" or "DESC"
12170 getSortState : function(){
12171 return this.sortInfo;
12175 applySort : function(){
12176 if(this.sortInfo && !this.remoteSort){
12177 var s = this.sortInfo, f = s.field;
12178 var st = this.fields.get(f).sortType;
12179 var fn = function(r1, r2){
12180 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
12181 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12183 this.data.sort(s.direction, fn);
12184 if(this.snapshot && this.snapshot != this.data){
12185 this.snapshot.sort(s.direction, fn);
12191 * Sets the default sort column and order to be used by the next load operation.
12192 * @param {String} fieldName The name of the field to sort by.
12193 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12195 setDefaultSort : function(field, dir){
12196 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
12200 * Sort the Records.
12201 * If remote sorting is used, the sort is performed on the server, and the cache is
12202 * reloaded. If local sorting is used, the cache is sorted internally.
12203 * @param {String} fieldName The name of the field to sort by.
12204 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12206 sort : function(fieldName, dir){
12207 var f = this.fields.get(fieldName);
12209 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
12211 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
12212 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
12217 this.sortToggle[f.name] = dir;
12218 this.sortInfo = {field: f.name, direction: dir};
12219 if(!this.remoteSort){
12221 this.fireEvent("datachanged", this);
12223 this.load(this.lastOptions);
12228 * Calls the specified function for each of the Records in the cache.
12229 * @param {Function} fn The function to call. The Record is passed as the first parameter.
12230 * Returning <em>false</em> aborts and exits the iteration.
12231 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
12233 each : function(fn, scope){
12234 this.data.each(fn, scope);
12238 * Gets all records modified since the last commit. Modified records are persisted across load operations
12239 * (e.g., during paging).
12240 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
12242 getModifiedRecords : function(){
12243 return this.modified;
12247 createFilterFn : function(property, value, anyMatch){
12248 if(!value.exec){ // not a regex
12249 value = String(value);
12250 if(value.length == 0){
12253 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
12255 return function(r){
12256 return value.test(r.data[property]);
12261 * Sums the value of <i>property</i> for each record between start and end and returns the result.
12262 * @param {String} property A field on your records
12263 * @param {Number} start The record index to start at (defaults to 0)
12264 * @param {Number} end The last record index to include (defaults to length - 1)
12265 * @return {Number} The sum
12267 sum : function(property, start, end){
12268 var rs = this.data.items, v = 0;
12269 start = start || 0;
12270 end = (end || end === 0) ? end : rs.length-1;
12272 for(var i = start; i <= end; i++){
12273 v += (rs[i].data[property] || 0);
12279 * Filter the records by a specified property.
12280 * @param {String} field A field on your records
12281 * @param {String/RegExp} value Either a string that the field
12282 * should start with or a RegExp to test against the field
12283 * @param {Boolean} anyMatch True to match any part not just the beginning
12285 filter : function(property, value, anyMatch){
12286 var fn = this.createFilterFn(property, value, anyMatch);
12287 return fn ? this.filterBy(fn) : this.clearFilter();
12291 * Filter by a function. The specified function will be called with each
12292 * record in this data source. If the function returns true the record is included,
12293 * otherwise it is filtered.
12294 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12295 * @param {Object} scope (optional) The scope of the function (defaults to this)
12297 filterBy : function(fn, scope){
12298 this.snapshot = this.snapshot || this.data;
12299 this.data = this.queryBy(fn, scope||this);
12300 this.fireEvent("datachanged", this);
12304 * Query the records by a specified property.
12305 * @param {String} field A field on your records
12306 * @param {String/RegExp} value Either a string that the field
12307 * should start with or a RegExp to test against the field
12308 * @param {Boolean} anyMatch True to match any part not just the beginning
12309 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12311 query : function(property, value, anyMatch){
12312 var fn = this.createFilterFn(property, value, anyMatch);
12313 return fn ? this.queryBy(fn) : this.data.clone();
12317 * Query by a function. The specified function will be called with each
12318 * record in this data source. If the function returns true the record is included
12320 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12321 * @param {Object} scope (optional) The scope of the function (defaults to this)
12322 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12324 queryBy : function(fn, scope){
12325 var data = this.snapshot || this.data;
12326 return data.filterBy(fn, scope||this);
12330 * Collects unique values for a particular dataIndex from this store.
12331 * @param {String} dataIndex The property to collect
12332 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12333 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12334 * @return {Array} An array of the unique values
12336 collect : function(dataIndex, allowNull, bypassFilter){
12337 var d = (bypassFilter === true && this.snapshot) ?
12338 this.snapshot.items : this.data.items;
12339 var v, sv, r = [], l = {};
12340 for(var i = 0, len = d.length; i < len; i++){
12341 v = d[i].data[dataIndex];
12343 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12352 * Revert to a view of the Record cache with no filtering applied.
12353 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12355 clearFilter : function(suppressEvent){
12356 if(this.snapshot && this.snapshot != this.data){
12357 this.data = this.snapshot;
12358 delete this.snapshot;
12359 if(suppressEvent !== true){
12360 this.fireEvent("datachanged", this);
12366 afterEdit : function(record){
12367 if(this.modified.indexOf(record) == -1){
12368 this.modified.push(record);
12370 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12374 afterReject : function(record){
12375 this.modified.remove(record);
12376 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12380 afterCommit : function(record){
12381 this.modified.remove(record);
12382 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12386 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12387 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12389 commitChanges : function(){
12390 var m = this.modified.slice(0);
12391 this.modified = [];
12392 for(var i = 0, len = m.length; i < len; i++){
12398 * Cancel outstanding changes on all changed records.
12400 rejectChanges : function(){
12401 var m = this.modified.slice(0);
12402 this.modified = [];
12403 for(var i = 0, len = m.length; i < len; i++){
12408 onMetaChange : function(meta, rtype, o){
12409 this.recordType = rtype;
12410 this.fields = rtype.prototype.fields;
12411 delete this.snapshot;
12412 this.sortInfo = meta.sortInfo || this.sortInfo;
12413 this.modified = [];
12414 this.fireEvent('metachange', this, this.reader.meta);
12417 moveIndex : function(data, type)
12419 var index = this.indexOf(data);
12421 var newIndex = index + type;
12425 this.insert(newIndex, data);
12430 * Ext JS Library 1.1.1
12431 * Copyright(c) 2006-2007, Ext JS, LLC.
12433 * Originally Released Under LGPL - original licence link has changed is not relivant.
12436 * <script type="text/javascript">
12440 * @class Roo.data.SimpleStore
12441 * @extends Roo.data.Store
12442 * Small helper class to make creating Stores from Array data easier.
12443 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12444 * @cfg {Array} fields An array of field definition objects, or field name strings.
12445 * @cfg {Object} an existing reader (eg. copied from another store)
12446 * @cfg {Array} data The multi-dimensional array of data
12448 * @param {Object} config
12450 Roo.data.SimpleStore = function(config)
12452 Roo.data.SimpleStore.superclass.constructor.call(this, {
12454 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
12457 Roo.data.Record.create(config.fields)
12459 proxy : new Roo.data.MemoryProxy(config.data)
12463 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12465 * Ext JS Library 1.1.1
12466 * Copyright(c) 2006-2007, Ext JS, LLC.
12468 * Originally Released Under LGPL - original licence link has changed is not relivant.
12471 * <script type="text/javascript">
12476 * @extends Roo.data.Store
12477 * @class Roo.data.JsonStore
12478 * Small helper class to make creating Stores for JSON data easier. <br/>
12480 var store = new Roo.data.JsonStore({
12481 url: 'get-images.php',
12483 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12486 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12487 * JsonReader and HttpProxy (unless inline data is provided).</b>
12488 * @cfg {Array} fields An array of field definition objects, or field name strings.
12490 * @param {Object} config
12492 Roo.data.JsonStore = function(c){
12493 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12494 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12495 reader: new Roo.data.JsonReader(c, c.fields)
12498 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12500 * Ext JS Library 1.1.1
12501 * Copyright(c) 2006-2007, Ext JS, LLC.
12503 * Originally Released Under LGPL - original licence link has changed is not relivant.
12506 * <script type="text/javascript">
12510 Roo.data.Field = function(config){
12511 if(typeof config == "string"){
12512 config = {name: config};
12514 Roo.apply(this, config);
12517 this.type = "auto";
12520 var st = Roo.data.SortTypes;
12521 // named sortTypes are supported, here we look them up
12522 if(typeof this.sortType == "string"){
12523 this.sortType = st[this.sortType];
12526 // set default sortType for strings and dates
12527 if(!this.sortType){
12530 this.sortType = st.asUCString;
12533 this.sortType = st.asDate;
12536 this.sortType = st.none;
12541 var stripRe = /[\$,%]/g;
12543 // prebuilt conversion function for this field, instead of
12544 // switching every time we're reading a value
12546 var cv, dateFormat = this.dateFormat;
12551 cv = function(v){ return v; };
12554 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12558 return v !== undefined && v !== null && v !== '' ?
12559 parseInt(String(v).replace(stripRe, ""), 10) : '';
12564 return v !== undefined && v !== null && v !== '' ?
12565 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12570 cv = function(v){ return v === true || v === "true" || v == 1; };
12577 if(v instanceof Date){
12581 if(dateFormat == "timestamp"){
12582 return new Date(v*1000);
12584 return Date.parseDate(v, dateFormat);
12586 var parsed = Date.parse(v);
12587 return parsed ? new Date(parsed) : null;
12596 Roo.data.Field.prototype = {
12604 * Ext JS Library 1.1.1
12605 * Copyright(c) 2006-2007, Ext JS, LLC.
12607 * Originally Released Under LGPL - original licence link has changed is not relivant.
12610 * <script type="text/javascript">
12613 // Base class for reading structured data from a data source. This class is intended to be
12614 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12617 * @class Roo.data.DataReader
12618 * Base class for reading structured data from a data source. This class is intended to be
12619 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12622 Roo.data.DataReader = function(meta, recordType){
12626 this.recordType = recordType instanceof Array ?
12627 Roo.data.Record.create(recordType) : recordType;
12630 Roo.data.DataReader.prototype = {
12633 readerType : 'Data',
12635 * Create an empty record
12636 * @param {Object} data (optional) - overlay some values
12637 * @return {Roo.data.Record} record created.
12639 newRow : function(d) {
12641 this.recordType.prototype.fields.each(function(c) {
12643 case 'int' : da[c.name] = 0; break;
12644 case 'date' : da[c.name] = new Date(); break;
12645 case 'float' : da[c.name] = 0.0; break;
12646 case 'boolean' : da[c.name] = false; break;
12647 default : da[c.name] = ""; break;
12651 return new this.recordType(Roo.apply(da, d));
12657 * Ext JS Library 1.1.1
12658 * Copyright(c) 2006-2007, Ext JS, LLC.
12660 * Originally Released Under LGPL - original licence link has changed is not relivant.
12663 * <script type="text/javascript">
12667 * @class Roo.data.DataProxy
12668 * @extends Roo.data.Observable
12669 * This class is an abstract base class for implementations which provide retrieval of
12670 * unformatted data objects.<br>
12672 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12673 * (of the appropriate type which knows how to parse the data object) to provide a block of
12674 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12676 * Custom implementations must implement the load method as described in
12677 * {@link Roo.data.HttpProxy#load}.
12679 Roo.data.DataProxy = function(){
12682 * @event beforeload
12683 * Fires before a network request is made to retrieve a data object.
12684 * @param {Object} This DataProxy object.
12685 * @param {Object} params The params parameter to the load function.
12690 * Fires before the load method's callback is called.
12691 * @param {Object} This DataProxy object.
12692 * @param {Object} o The data object.
12693 * @param {Object} arg The callback argument object passed to the load function.
12697 * @event loadexception
12698 * Fires if an Exception occurs during data retrieval.
12699 * @param {Object} This DataProxy object.
12700 * @param {Object} o The data object.
12701 * @param {Object} arg The callback argument object passed to the load function.
12702 * @param {Object} e The Exception.
12704 loadexception : true
12706 Roo.data.DataProxy.superclass.constructor.call(this);
12709 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12712 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12716 * Ext JS Library 1.1.1
12717 * Copyright(c) 2006-2007, Ext JS, LLC.
12719 * Originally Released Under LGPL - original licence link has changed is not relivant.
12722 * <script type="text/javascript">
12725 * @class Roo.data.MemoryProxy
12726 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12727 * to the Reader when its load method is called.
12729 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12731 Roo.data.MemoryProxy = function(data){
12735 Roo.data.MemoryProxy.superclass.constructor.call(this);
12739 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12742 * Load data from the requested source (in this case an in-memory
12743 * data object passed to the constructor), read the data object into
12744 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12745 * process that block using the passed callback.
12746 * @param {Object} params This parameter is not used by the MemoryProxy class.
12747 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12748 * object into a block of Roo.data.Records.
12749 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12750 * The function must be passed <ul>
12751 * <li>The Record block object</li>
12752 * <li>The "arg" argument from the load function</li>
12753 * <li>A boolean success indicator</li>
12755 * @param {Object} scope The scope in which to call the callback
12756 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12758 load : function(params, reader, callback, scope, arg){
12759 params = params || {};
12762 result = reader.readRecords(params.data ? params.data :this.data);
12764 this.fireEvent("loadexception", this, arg, null, e);
12765 callback.call(scope, null, arg, false);
12768 callback.call(scope, result, arg, true);
12772 update : function(params, records){
12777 * Ext JS Library 1.1.1
12778 * Copyright(c) 2006-2007, Ext JS, LLC.
12780 * Originally Released Under LGPL - original licence link has changed is not relivant.
12783 * <script type="text/javascript">
12786 * @class Roo.data.HttpProxy
12787 * @extends Roo.data.DataProxy
12788 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12789 * configured to reference a certain URL.<br><br>
12791 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12792 * from which the running page was served.<br><br>
12794 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12796 * Be aware that to enable the browser to parse an XML document, the server must set
12797 * the Content-Type header in the HTTP response to "text/xml".
12799 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12800 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12801 * will be used to make the request.
12803 Roo.data.HttpProxy = function(conn){
12804 Roo.data.HttpProxy.superclass.constructor.call(this);
12805 // is conn a conn config or a real conn?
12807 this.useAjax = !conn || !conn.events;
12811 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12812 // thse are take from connection...
12815 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12818 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12819 * extra parameters to each request made by this object. (defaults to undefined)
12822 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12823 * to each request made by this object. (defaults to undefined)
12826 * @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)
12829 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12832 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12838 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12842 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12843 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12844 * a finer-grained basis than the DataProxy events.
12846 getConnection : function(){
12847 return this.useAjax ? Roo.Ajax : this.conn;
12851 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12852 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12853 * process that block using the passed callback.
12854 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12855 * for the request to the remote server.
12856 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12857 * object into a block of Roo.data.Records.
12858 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12859 * The function must be passed <ul>
12860 * <li>The Record block object</li>
12861 * <li>The "arg" argument from the load function</li>
12862 * <li>A boolean success indicator</li>
12864 * @param {Object} scope The scope in which to call the callback
12865 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12867 load : function(params, reader, callback, scope, arg){
12868 if(this.fireEvent("beforeload", this, params) !== false){
12870 params : params || {},
12872 callback : callback,
12877 callback : this.loadResponse,
12881 Roo.applyIf(o, this.conn);
12882 if(this.activeRequest){
12883 Roo.Ajax.abort(this.activeRequest);
12885 this.activeRequest = Roo.Ajax.request(o);
12887 this.conn.request(o);
12890 callback.call(scope||this, null, arg, false);
12895 loadResponse : function(o, success, response){
12896 delete this.activeRequest;
12898 this.fireEvent("loadexception", this, o, response);
12899 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12904 result = o.reader.read(response);
12906 this.fireEvent("loadexception", this, o, response, e);
12907 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12911 this.fireEvent("load", this, o, o.request.arg);
12912 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12916 update : function(dataSet){
12921 updateResponse : function(dataSet){
12926 * Ext JS Library 1.1.1
12927 * Copyright(c) 2006-2007, Ext JS, LLC.
12929 * Originally Released Under LGPL - original licence link has changed is not relivant.
12932 * <script type="text/javascript">
12936 * @class Roo.data.ScriptTagProxy
12937 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12938 * other than the originating domain of the running page.<br><br>
12940 * <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
12941 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12943 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12944 * source code that is used as the source inside a <script> tag.<br><br>
12946 * In order for the browser to process the returned data, the server must wrap the data object
12947 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12948 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12949 * depending on whether the callback name was passed:
12952 boolean scriptTag = false;
12953 String cb = request.getParameter("callback");
12956 response.setContentType("text/javascript");
12958 response.setContentType("application/x-json");
12960 Writer out = response.getWriter();
12962 out.write(cb + "(");
12964 out.print(dataBlock.toJsonString());
12971 * @param {Object} config A configuration object.
12973 Roo.data.ScriptTagProxy = function(config){
12974 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12975 Roo.apply(this, config);
12976 this.head = document.getElementsByTagName("head")[0];
12979 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12981 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12983 * @cfg {String} url The URL from which to request the data object.
12986 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12990 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12991 * the server the name of the callback function set up by the load call to process the returned data object.
12992 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12993 * javascript output which calls this named function passing the data object as its only parameter.
12995 callbackParam : "callback",
12997 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12998 * name to the request.
13003 * Load data from the configured URL, read the data object into
13004 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13005 * process that block using the passed callback.
13006 * @param {Object} params An object containing properties which are to be used as HTTP parameters
13007 * for the request to the remote server.
13008 * @param {Roo.data.DataReader} reader The Reader object which converts the data
13009 * object into a block of Roo.data.Records.
13010 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
13011 * The function must be passed <ul>
13012 * <li>The Record block object</li>
13013 * <li>The "arg" argument from the load function</li>
13014 * <li>A boolean success indicator</li>
13016 * @param {Object} scope The scope in which to call the callback
13017 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13019 load : function(params, reader, callback, scope, arg){
13020 if(this.fireEvent("beforeload", this, params) !== false){
13022 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
13024 var url = this.url;
13025 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
13027 url += "&_dc=" + (new Date().getTime());
13029 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
13032 cb : "stcCallback"+transId,
13033 scriptId : "stcScript"+transId,
13037 callback : callback,
13043 window[trans.cb] = function(o){
13044 conn.handleResponse(o, trans);
13047 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
13049 if(this.autoAbort !== false){
13053 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
13055 var script = document.createElement("script");
13056 script.setAttribute("src", url);
13057 script.setAttribute("type", "text/javascript");
13058 script.setAttribute("id", trans.scriptId);
13059 this.head.appendChild(script);
13061 this.trans = trans;
13063 callback.call(scope||this, null, arg, false);
13068 isLoading : function(){
13069 return this.trans ? true : false;
13073 * Abort the current server request.
13075 abort : function(){
13076 if(this.isLoading()){
13077 this.destroyTrans(this.trans);
13082 destroyTrans : function(trans, isLoaded){
13083 this.head.removeChild(document.getElementById(trans.scriptId));
13084 clearTimeout(trans.timeoutId);
13086 window[trans.cb] = undefined;
13088 delete window[trans.cb];
13091 // if hasn't been loaded, wait for load to remove it to prevent script error
13092 window[trans.cb] = function(){
13093 window[trans.cb] = undefined;
13095 delete window[trans.cb];
13102 handleResponse : function(o, trans){
13103 this.trans = false;
13104 this.destroyTrans(trans, true);
13107 result = trans.reader.readRecords(o);
13109 this.fireEvent("loadexception", this, o, trans.arg, e);
13110 trans.callback.call(trans.scope||window, null, trans.arg, false);
13113 this.fireEvent("load", this, o, trans.arg);
13114 trans.callback.call(trans.scope||window, result, trans.arg, true);
13118 handleFailure : function(trans){
13119 this.trans = false;
13120 this.destroyTrans(trans, false);
13121 this.fireEvent("loadexception", this, null, trans.arg);
13122 trans.callback.call(trans.scope||window, null, trans.arg, false);
13126 * Ext JS Library 1.1.1
13127 * Copyright(c) 2006-2007, Ext JS, LLC.
13129 * Originally Released Under LGPL - original licence link has changed is not relivant.
13132 * <script type="text/javascript">
13136 * @class Roo.data.JsonReader
13137 * @extends Roo.data.DataReader
13138 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
13139 * based on mappings in a provided Roo.data.Record constructor.
13141 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
13142 * in the reply previously.
13147 var RecordDef = Roo.data.Record.create([
13148 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
13149 {name: 'occupation'} // This field will use "occupation" as the mapping.
13151 var myReader = new Roo.data.JsonReader({
13152 totalProperty: "results", // The property which contains the total dataset size (optional)
13153 root: "rows", // The property which contains an Array of row objects
13154 id: "id" // The property within each row object that provides an ID for the record (optional)
13158 * This would consume a JSON file like this:
13160 { 'results': 2, 'rows': [
13161 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
13162 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
13165 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
13166 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
13167 * paged from the remote server.
13168 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
13169 * @cfg {String} root name of the property which contains the Array of row objects.
13170 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13171 * @cfg {Array} fields Array of field definition objects
13173 * Create a new JsonReader
13174 * @param {Object} meta Metadata configuration options
13175 * @param {Object} recordType Either an Array of field definition objects,
13176 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
13178 Roo.data.JsonReader = function(meta, recordType){
13181 // set some defaults:
13182 Roo.applyIf(meta, {
13183 totalProperty: 'total',
13184 successProperty : 'success',
13189 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13191 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
13193 readerType : 'Json',
13196 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
13197 * Used by Store query builder to append _requestMeta to params.
13200 metaFromRemote : false,
13202 * This method is only used by a DataProxy which has retrieved data from a remote server.
13203 * @param {Object} response The XHR object which contains the JSON data in its responseText.
13204 * @return {Object} data A data block which is used by an Roo.data.Store object as
13205 * a cache of Roo.data.Records.
13207 read : function(response){
13208 var json = response.responseText;
13210 var o = /* eval:var:o */ eval("("+json+")");
13212 throw {message: "JsonReader.read: Json object not found"};
13218 this.metaFromRemote = true;
13219 this.meta = o.metaData;
13220 this.recordType = Roo.data.Record.create(o.metaData.fields);
13221 this.onMetaChange(this.meta, this.recordType, o);
13223 return this.readRecords(o);
13226 // private function a store will implement
13227 onMetaChange : function(meta, recordType, o){
13234 simpleAccess: function(obj, subsc) {
13241 getJsonAccessor: function(){
13243 return function(expr) {
13245 return(re.test(expr))
13246 ? new Function("obj", "return obj." + expr)
13251 return Roo.emptyFn;
13256 * Create a data block containing Roo.data.Records from an XML document.
13257 * @param {Object} o An object which contains an Array of row objects in the property specified
13258 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
13259 * which contains the total size of the dataset.
13260 * @return {Object} data A data block which is used by an Roo.data.Store object as
13261 * a cache of Roo.data.Records.
13263 readRecords : function(o){
13265 * After any data loads, the raw JSON data is available for further custom processing.
13269 var s = this.meta, Record = this.recordType,
13270 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
13272 // Generate extraction functions for the totalProperty, the root, the id, and for each field
13274 if(s.totalProperty) {
13275 this.getTotal = this.getJsonAccessor(s.totalProperty);
13277 if(s.successProperty) {
13278 this.getSuccess = this.getJsonAccessor(s.successProperty);
13280 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
13282 var g = this.getJsonAccessor(s.id);
13283 this.getId = function(rec) {
13285 return (r === undefined || r === "") ? null : r;
13288 this.getId = function(){return null;};
13291 for(var jj = 0; jj < fl; jj++){
13293 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
13294 this.ef[jj] = this.getJsonAccessor(map);
13298 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
13299 if(s.totalProperty){
13300 var vt = parseInt(this.getTotal(o), 10);
13305 if(s.successProperty){
13306 var vs = this.getSuccess(o);
13307 if(vs === false || vs === 'false'){
13312 for(var i = 0; i < c; i++){
13315 var id = this.getId(n);
13316 for(var j = 0; j < fl; j++){
13318 var v = this.ef[j](n);
13320 Roo.log('missing convert for ' + f.name);
13324 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13326 var record = new Record(values, id);
13328 records[i] = record;
13334 totalRecords : totalRecords
13337 // used when loading children.. @see loadDataFromChildren
13338 toLoadData: function(rec)
13340 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13341 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13342 return { data : data, total : data.length };
13347 * Ext JS Library 1.1.1
13348 * Copyright(c) 2006-2007, Ext JS, LLC.
13350 * Originally Released Under LGPL - original licence link has changed is not relivant.
13353 * <script type="text/javascript">
13357 * @class Roo.data.ArrayReader
13358 * @extends Roo.data.DataReader
13359 * Data reader class to create an Array of Roo.data.Record objects from an Array.
13360 * Each element of that Array represents a row of data fields. The
13361 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13362 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13366 var RecordDef = Roo.data.Record.create([
13367 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
13368 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
13370 var myReader = new Roo.data.ArrayReader({
13371 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
13375 * This would consume an Array like this:
13377 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13381 * Create a new JsonReader
13382 * @param {Object} meta Metadata configuration options.
13383 * @param {Object|Array} recordType Either an Array of field definition objects
13385 * @cfg {Array} fields Array of field definition objects
13386 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13387 * as specified to {@link Roo.data.Record#create},
13388 * or an {@link Roo.data.Record} object
13391 * created using {@link Roo.data.Record#create}.
13393 Roo.data.ArrayReader = function(meta, recordType)
13395 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13398 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13401 * Create a data block containing Roo.data.Records from an XML document.
13402 * @param {Object} o An Array of row objects which represents the dataset.
13403 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13404 * a cache of Roo.data.Records.
13406 readRecords : function(o)
13408 var sid = this.meta ? this.meta.id : null;
13409 var recordType = this.recordType, fields = recordType.prototype.fields;
13412 for(var i = 0; i < root.length; i++){
13415 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13416 for(var j = 0, jlen = fields.length; j < jlen; j++){
13417 var f = fields.items[j];
13418 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13419 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13421 values[f.name] = v;
13423 var record = new recordType(values, id);
13425 records[records.length] = record;
13429 totalRecords : records.length
13432 // used when loading children.. @see loadDataFromChildren
13433 toLoadData: function(rec)
13435 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13436 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13447 * @class Roo.bootstrap.ComboBox
13448 * @extends Roo.bootstrap.TriggerField
13449 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13450 * @cfg {Boolean} append (true|false) default false
13451 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13452 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13453 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13454 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13455 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13456 * @cfg {Boolean} animate default true
13457 * @cfg {Boolean} emptyResultText only for touch device
13458 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13459 * @cfg {String} emptyTitle default ''
13461 * Create a new ComboBox.
13462 * @param {Object} config Configuration options
13464 Roo.bootstrap.ComboBox = function(config){
13465 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13469 * Fires when the dropdown list is expanded
13470 * @param {Roo.bootstrap.ComboBox} combo This combo box
13475 * Fires when the dropdown list is collapsed
13476 * @param {Roo.bootstrap.ComboBox} combo This combo box
13480 * @event beforeselect
13481 * Fires before a list item is selected. Return false to cancel the selection.
13482 * @param {Roo.bootstrap.ComboBox} combo This combo box
13483 * @param {Roo.data.Record} record The data record returned from the underlying store
13484 * @param {Number} index The index of the selected item in the dropdown list
13486 'beforeselect' : true,
13489 * Fires when a list item is selected
13490 * @param {Roo.bootstrap.ComboBox} combo This combo box
13491 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13492 * @param {Number} index The index of the selected item in the dropdown list
13496 * @event beforequery
13497 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13498 * The event object passed has these properties:
13499 * @param {Roo.bootstrap.ComboBox} combo This combo box
13500 * @param {String} query The query
13501 * @param {Boolean} forceAll true to force "all" query
13502 * @param {Boolean} cancel true to cancel the query
13503 * @param {Object} e The query event object
13505 'beforequery': true,
13508 * Fires when the 'add' icon is pressed (add a listener to enable add button)
13509 * @param {Roo.bootstrap.ComboBox} combo This combo box
13514 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13515 * @param {Roo.bootstrap.ComboBox} combo This combo box
13516 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13521 * Fires when the remove value from the combobox array
13522 * @param {Roo.bootstrap.ComboBox} combo This combo box
13526 * @event afterremove
13527 * Fires when the remove value from the combobox array
13528 * @param {Roo.bootstrap.ComboBox} combo This combo box
13530 'afterremove' : true,
13532 * @event specialfilter
13533 * Fires when specialfilter
13534 * @param {Roo.bootstrap.ComboBox} combo This combo box
13536 'specialfilter' : true,
13539 * Fires when tick the element
13540 * @param {Roo.bootstrap.ComboBox} combo This combo box
13544 * @event touchviewdisplay
13545 * Fires when touch view require special display (default is using displayField)
13546 * @param {Roo.bootstrap.ComboBox} combo This combo box
13547 * @param {Object} cfg set html .
13549 'touchviewdisplay' : true
13554 this.tickItems = [];
13556 this.selectedIndex = -1;
13557 if(this.mode == 'local'){
13558 if(config.queryDelay === undefined){
13559 this.queryDelay = 10;
13561 if(config.minChars === undefined){
13567 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13570 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13571 * rendering into an Roo.Editor, defaults to false)
13574 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13575 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13578 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13581 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13582 * the dropdown list (defaults to undefined, with no header element)
13586 * @cfg {String/Roo.Template} tpl The template to use to render the output
13590 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13592 listWidth: undefined,
13594 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13595 * mode = 'remote' or 'text' if mode = 'local')
13597 displayField: undefined,
13600 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13601 * mode = 'remote' or 'value' if mode = 'local').
13602 * Note: use of a valueField requires the user make a selection
13603 * in order for a value to be mapped.
13605 valueField: undefined,
13607 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13612 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13613 * field's data value (defaults to the underlying DOM element's name)
13615 hiddenName: undefined,
13617 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13621 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13623 selectedClass: 'active',
13626 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13630 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13631 * anchor positions (defaults to 'tl-bl')
13633 listAlign: 'tl-bl?',
13635 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13639 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13640 * query specified by the allQuery config option (defaults to 'query')
13642 triggerAction: 'query',
13644 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13645 * (defaults to 4, does not apply if editable = false)
13649 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13650 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13654 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13655 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13659 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13660 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13664 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13665 * when editable = true (defaults to false)
13667 selectOnFocus:false,
13669 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13671 queryParam: 'query',
13673 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13674 * when mode = 'remote' (defaults to 'Loading...')
13676 loadingText: 'Loading...',
13678 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13682 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13686 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13687 * traditional select (defaults to true)
13691 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13695 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13699 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13700 * listWidth has a higher value)
13704 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13705 * allow the user to set arbitrary text into the field (defaults to false)
13707 forceSelection:false,
13709 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13710 * if typeAhead = true (defaults to 250)
13712 typeAheadDelay : 250,
13714 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13715 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13717 valueNotFoundText : undefined,
13719 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13721 blockFocus : false,
13724 * @cfg {Boolean} disableClear Disable showing of clear button.
13726 disableClear : false,
13728 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13730 alwaysQuery : false,
13733 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13738 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13740 invalidClass : "has-warning",
13743 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13745 validClass : "has-success",
13748 * @cfg {Boolean} specialFilter (true|false) special filter default false
13750 specialFilter : false,
13753 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13755 mobileTouchView : true,
13758 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13760 useNativeIOS : false,
13763 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13765 mobile_restrict_height : false,
13767 ios_options : false,
13779 btnPosition : 'right',
13780 triggerList : true,
13781 showToggleBtn : true,
13783 emptyResultText: 'Empty',
13784 triggerText : 'Select',
13787 // element that contains real text value.. (when hidden is used..)
13789 getAutoCreate : function()
13794 * Render classic select for iso
13797 if(Roo.isIOS && this.useNativeIOS){
13798 cfg = this.getAutoCreateNativeIOS();
13806 if(Roo.isTouch && this.mobileTouchView){
13807 cfg = this.getAutoCreateTouchView();
13814 if(!this.tickable){
13815 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13820 * ComboBox with tickable selections
13823 var align = this.labelAlign || this.parentLabelAlign();
13826 cls : 'form-group roo-combobox-tickable' //input-group
13829 var btn_text_select = '';
13830 var btn_text_done = '';
13831 var btn_text_cancel = '';
13833 if (this.btn_text_show) {
13834 btn_text_select = 'Select';
13835 btn_text_done = 'Done';
13836 btn_text_cancel = 'Cancel';
13841 cls : 'tickable-buttons',
13846 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13847 //html : this.triggerText
13848 html: btn_text_select
13854 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13856 html: btn_text_done
13862 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13864 html: btn_text_cancel
13870 buttons.cn.unshift({
13872 cls: 'roo-select2-search-field-input'
13878 Roo.each(buttons.cn, function(c){
13880 c.cls += ' btn-' + _this.size;
13883 if (_this.disabled) {
13890 style : 'display: contents',
13895 cls: 'form-hidden-field'
13899 cls: 'roo-select2-choices',
13903 cls: 'roo-select2-search-field',
13914 cls: 'roo-select2-container input-group roo-select2-container-multi',
13920 // cls: 'typeahead typeahead-long dropdown-menu',
13921 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13926 if(this.hasFeedback && !this.allowBlank){
13930 cls: 'glyphicon form-control-feedback'
13933 combobox.cn.push(feedback);
13938 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13939 tooltip : 'This field is required'
13941 if (Roo.bootstrap.version == 4) {
13944 style : 'display:none'
13947 if (align ==='left' && this.fieldLabel.length) {
13949 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13956 cls : 'control-label col-form-label',
13957 html : this.fieldLabel
13969 var labelCfg = cfg.cn[1];
13970 var contentCfg = cfg.cn[2];
13973 if(this.indicatorpos == 'right'){
13979 cls : 'control-label col-form-label',
13983 html : this.fieldLabel
13999 labelCfg = cfg.cn[0];
14000 contentCfg = cfg.cn[1];
14004 if(this.labelWidth > 12){
14005 labelCfg.style = "width: " + this.labelWidth + 'px';
14008 if(this.labelWidth < 13 && this.labelmd == 0){
14009 this.labelmd = this.labelWidth;
14012 if(this.labellg > 0){
14013 labelCfg.cls += ' col-lg-' + this.labellg;
14014 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14017 if(this.labelmd > 0){
14018 labelCfg.cls += ' col-md-' + this.labelmd;
14019 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14022 if(this.labelsm > 0){
14023 labelCfg.cls += ' col-sm-' + this.labelsm;
14024 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14027 if(this.labelxs > 0){
14028 labelCfg.cls += ' col-xs-' + this.labelxs;
14029 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14033 } else if ( this.fieldLabel.length) {
14034 // Roo.log(" label");
14039 //cls : 'input-group-addon',
14040 html : this.fieldLabel
14045 if(this.indicatorpos == 'right'){
14049 //cls : 'input-group-addon',
14050 html : this.fieldLabel
14060 // Roo.log(" no label && no align");
14067 ['xs','sm','md','lg'].map(function(size){
14068 if (settings[size]) {
14069 cfg.cls += ' col-' + size + '-' + settings[size];
14077 _initEventsCalled : false,
14080 initEvents: function()
14082 if (this._initEventsCalled) { // as we call render... prevent looping...
14085 this._initEventsCalled = true;
14088 throw "can not find store for combo";
14091 this.indicator = this.indicatorEl();
14093 this.store = Roo.factory(this.store, Roo.data);
14094 this.store.parent = this;
14096 // if we are building from html. then this element is so complex, that we can not really
14097 // use the rendered HTML.
14098 // so we have to trash and replace the previous code.
14099 if (Roo.XComponent.build_from_html) {
14100 // remove this element....
14101 var e = this.el.dom, k=0;
14102 while (e ) { e = e.previousSibling; ++k;}
14107 this.rendered = false;
14109 this.render(this.parent().getChildContainer(true), k);
14112 if(Roo.isIOS && this.useNativeIOS){
14113 this.initIOSView();
14121 if(Roo.isTouch && this.mobileTouchView){
14122 this.initTouchView();
14127 this.initTickableEvents();
14131 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
14133 if(this.hiddenName){
14135 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14137 this.hiddenField.dom.value =
14138 this.hiddenValue !== undefined ? this.hiddenValue :
14139 this.value !== undefined ? this.value : '';
14141 // prevent input submission
14142 this.el.dom.removeAttribute('name');
14143 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14148 // this.el.dom.setAttribute('autocomplete', 'off');
14151 var cls = 'x-combo-list';
14153 //this.list = new Roo.Layer({
14154 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
14160 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14161 _this.list.setWidth(lw);
14164 this.list.on('mouseover', this.onViewOver, this);
14165 this.list.on('mousemove', this.onViewMove, this);
14166 this.list.on('scroll', this.onViewScroll, this);
14169 this.list.swallowEvent('mousewheel');
14170 this.assetHeight = 0;
14173 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
14174 this.assetHeight += this.header.getHeight();
14177 this.innerList = this.list.createChild({cls:cls+'-inner'});
14178 this.innerList.on('mouseover', this.onViewOver, this);
14179 this.innerList.on('mousemove', this.onViewMove, this);
14180 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14182 if(this.allowBlank && !this.pageSize && !this.disableClear){
14183 this.footer = this.list.createChild({cls:cls+'-ft'});
14184 this.pageTb = new Roo.Toolbar(this.footer);
14188 this.footer = this.list.createChild({cls:cls+'-ft'});
14189 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
14190 {pageSize: this.pageSize});
14194 if (this.pageTb && this.allowBlank && !this.disableClear) {
14196 this.pageTb.add(new Roo.Toolbar.Fill(), {
14197 cls: 'x-btn-icon x-btn-clear',
14199 handler: function()
14202 _this.clearValue();
14203 _this.onSelect(false, -1);
14208 this.assetHeight += this.footer.getHeight();
14213 this.tpl = Roo.bootstrap.version == 4 ?
14214 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
14215 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
14218 this.view = new Roo.View(this.list, this.tpl, {
14219 singleSelect:true, store: this.store, selectedClass: this.selectedClass
14221 //this.view.wrapEl.setDisplayed(false);
14222 this.view.on('click', this.onViewClick, this);
14225 this.store.on('beforeload', this.onBeforeLoad, this);
14226 this.store.on('load', this.onLoad, this);
14227 this.store.on('loadexception', this.onLoadException, this);
14229 if(this.resizable){
14230 this.resizer = new Roo.Resizable(this.list, {
14231 pinned:true, handles:'se'
14233 this.resizer.on('resize', function(r, w, h){
14234 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
14235 this.listWidth = w;
14236 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
14237 this.restrictHeight();
14239 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
14242 if(!this.editable){
14243 this.editable = true;
14244 this.setEditable(false);
14249 if (typeof(this.events.add.listeners) != 'undefined') {
14251 this.addicon = this.wrap.createChild(
14252 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
14254 this.addicon.on('click', function(e) {
14255 this.fireEvent('add', this);
14258 if (typeof(this.events.edit.listeners) != 'undefined') {
14260 this.editicon = this.wrap.createChild(
14261 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
14262 if (this.addicon) {
14263 this.editicon.setStyle('margin-left', '40px');
14265 this.editicon.on('click', function(e) {
14267 // we fire even if inothing is selected..
14268 this.fireEvent('edit', this, this.lastData );
14274 this.keyNav = new Roo.KeyNav(this.inputEl(), {
14275 "up" : function(e){
14276 this.inKeyMode = true;
14280 "down" : function(e){
14281 if(!this.isExpanded()){
14282 this.onTriggerClick();
14284 this.inKeyMode = true;
14289 "enter" : function(e){
14290 // this.onViewClick();
14294 if(this.fireEvent("specialkey", this, e)){
14295 this.onViewClick(false);
14301 "esc" : function(e){
14305 "tab" : function(e){
14308 if(this.fireEvent("specialkey", this, e)){
14309 this.onViewClick(false);
14317 doRelay : function(foo, bar, hname){
14318 if(hname == 'down' || this.scope.isExpanded()){
14319 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14328 this.queryDelay = Math.max(this.queryDelay || 10,
14329 this.mode == 'local' ? 10 : 250);
14332 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14334 if(this.typeAhead){
14335 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14337 if(this.editable !== false){
14338 this.inputEl().on("keyup", this.onKeyUp, this);
14340 if(this.forceSelection){
14341 this.inputEl().on('blur', this.doForce, this);
14345 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14346 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14350 initTickableEvents: function()
14354 if(this.hiddenName){
14356 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14358 this.hiddenField.dom.value =
14359 this.hiddenValue !== undefined ? this.hiddenValue :
14360 this.value !== undefined ? this.value : '';
14362 // prevent input submission
14363 this.el.dom.removeAttribute('name');
14364 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14369 // this.list = this.el.select('ul.dropdown-menu',true).first();
14371 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14372 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14373 if(this.triggerList){
14374 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14377 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14378 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14380 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14381 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14383 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14384 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14386 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14387 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14388 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14391 this.cancelBtn.hide();
14396 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14397 _this.list.setWidth(lw);
14400 this.list.on('mouseover', this.onViewOver, this);
14401 this.list.on('mousemove', this.onViewMove, this);
14403 this.list.on('scroll', this.onViewScroll, this);
14406 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
14407 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14410 this.view = new Roo.View(this.list, this.tpl, {
14415 selectedClass: this.selectedClass
14418 //this.view.wrapEl.setDisplayed(false);
14419 this.view.on('click', this.onViewClick, this);
14423 this.store.on('beforeload', this.onBeforeLoad, this);
14424 this.store.on('load', this.onLoad, this);
14425 this.store.on('loadexception', this.onLoadException, this);
14428 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14429 "up" : function(e){
14430 this.inKeyMode = true;
14434 "down" : function(e){
14435 this.inKeyMode = true;
14439 "enter" : function(e){
14440 if(this.fireEvent("specialkey", this, e)){
14441 this.onViewClick(false);
14447 "esc" : function(e){
14448 this.onTickableFooterButtonClick(e, false, false);
14451 "tab" : function(e){
14452 this.fireEvent("specialkey", this, e);
14454 this.onTickableFooterButtonClick(e, false, false);
14461 doRelay : function(e, fn, key){
14462 if(this.scope.isExpanded()){
14463 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14472 this.queryDelay = Math.max(this.queryDelay || 10,
14473 this.mode == 'local' ? 10 : 250);
14476 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14478 if(this.typeAhead){
14479 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14482 if(this.editable !== false){
14483 this.tickableInputEl().on("keyup", this.onKeyUp, this);
14486 this.indicator = this.indicatorEl();
14488 if(this.indicator){
14489 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14490 this.indicator.hide();
14495 onDestroy : function(){
14497 this.view.setStore(null);
14498 this.view.el.removeAllListeners();
14499 this.view.el.remove();
14500 this.view.purgeListeners();
14503 this.list.dom.innerHTML = '';
14507 this.store.un('beforeload', this.onBeforeLoad, this);
14508 this.store.un('load', this.onLoad, this);
14509 this.store.un('loadexception', this.onLoadException, this);
14511 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14515 fireKey : function(e){
14516 if(e.isNavKeyPress() && !this.list.isVisible()){
14517 this.fireEvent("specialkey", this, e);
14522 onResize: function(w, h){
14523 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14525 // if(typeof w != 'number'){
14526 // // we do not handle it!?!?
14529 // var tw = this.trigger.getWidth();
14530 // // tw += this.addicon ? this.addicon.getWidth() : 0;
14531 // // tw += this.editicon ? this.editicon.getWidth() : 0;
14533 // this.inputEl().setWidth( this.adjustWidth('input', x));
14535 // //this.trigger.setStyle('left', x+'px');
14537 // if(this.list && this.listWidth === undefined){
14538 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14539 // this.list.setWidth(lw);
14540 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14548 * Allow or prevent the user from directly editing the field text. If false is passed,
14549 * the user will only be able to select from the items defined in the dropdown list. This method
14550 * is the runtime equivalent of setting the 'editable' config option at config time.
14551 * @param {Boolean} value True to allow the user to directly edit the field text
14553 setEditable : function(value){
14554 if(value == this.editable){
14557 this.editable = value;
14559 this.inputEl().dom.setAttribute('readOnly', true);
14560 this.inputEl().on('mousedown', this.onTriggerClick, this);
14561 this.inputEl().addClass('x-combo-noedit');
14563 this.inputEl().dom.setAttribute('readOnly', false);
14564 this.inputEl().un('mousedown', this.onTriggerClick, this);
14565 this.inputEl().removeClass('x-combo-noedit');
14571 onBeforeLoad : function(combo,opts){
14572 if(!this.hasFocus){
14576 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14578 this.restrictHeight();
14579 this.selectedIndex = -1;
14583 onLoad : function(){
14585 this.hasQuery = false;
14587 if(!this.hasFocus){
14591 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14592 this.loading.hide();
14595 if(this.store.getCount() > 0){
14598 this.restrictHeight();
14599 if(this.lastQuery == this.allQuery){
14600 if(this.editable && !this.tickable){
14601 this.inputEl().dom.select();
14605 !this.selectByValue(this.value, true) &&
14608 !this.store.lastOptions ||
14609 typeof(this.store.lastOptions.add) == 'undefined' ||
14610 this.store.lastOptions.add != true
14613 this.select(0, true);
14616 if(this.autoFocus){
14619 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14620 this.taTask.delay(this.typeAheadDelay);
14624 this.onEmptyResults();
14630 onLoadException : function()
14632 this.hasQuery = false;
14634 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14635 this.loading.hide();
14638 if(this.tickable && this.editable){
14643 // only causes errors at present
14644 //Roo.log(this.store.reader.jsonData);
14645 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14647 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14653 onTypeAhead : function(){
14654 if(this.store.getCount() > 0){
14655 var r = this.store.getAt(0);
14656 var newValue = r.data[this.displayField];
14657 var len = newValue.length;
14658 var selStart = this.getRawValue().length;
14660 if(selStart != len){
14661 this.setRawValue(newValue);
14662 this.selectText(selStart, newValue.length);
14668 onSelect : function(record, index){
14670 if(this.fireEvent('beforeselect', this, record, index) !== false){
14672 this.setFromData(index > -1 ? record.data : false);
14675 this.fireEvent('select', this, record, index);
14680 * Returns the currently selected field value or empty string if no value is set.
14681 * @return {String} value The selected value
14683 getValue : function()
14685 if(Roo.isIOS && this.useNativeIOS){
14686 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14690 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14693 if(this.valueField){
14694 return typeof this.value != 'undefined' ? this.value : '';
14696 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14700 getRawValue : function()
14702 if(Roo.isIOS && this.useNativeIOS){
14703 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14706 var v = this.inputEl().getValue();
14712 * Clears any text/value currently set in the field
14714 clearValue : function(){
14716 if(this.hiddenField){
14717 this.hiddenField.dom.value = '';
14720 this.setRawValue('');
14721 this.lastSelectionText = '';
14722 this.lastData = false;
14724 var close = this.closeTriggerEl();
14735 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14736 * will be displayed in the field. If the value does not match the data value of an existing item,
14737 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14738 * Otherwise the field will be blank (although the value will still be set).
14739 * @param {String} value The value to match
14741 setValue : function(v)
14743 if(Roo.isIOS && this.useNativeIOS){
14744 this.setIOSValue(v);
14754 if(this.valueField){
14755 var r = this.findRecord(this.valueField, v);
14757 text = r.data[this.displayField];
14758 }else if(this.valueNotFoundText !== undefined){
14759 text = this.valueNotFoundText;
14762 this.lastSelectionText = text;
14763 if(this.hiddenField){
14764 this.hiddenField.dom.value = v;
14766 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14769 var close = this.closeTriggerEl();
14772 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14778 * @property {Object} the last set data for the element
14783 * Sets the value of the field based on a object which is related to the record format for the store.
14784 * @param {Object} value the value to set as. or false on reset?
14786 setFromData : function(o){
14793 var dv = ''; // display value
14794 var vv = ''; // value value..
14796 if (this.displayField) {
14797 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14799 // this is an error condition!!!
14800 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14803 if(this.valueField){
14804 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14807 var close = this.closeTriggerEl();
14810 if(dv.length || vv * 1 > 0){
14812 this.blockFocus=true;
14818 if(this.hiddenField){
14819 this.hiddenField.dom.value = vv;
14821 this.lastSelectionText = dv;
14822 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14826 // no hidden field.. - we store the value in 'value', but still display
14827 // display field!!!!
14828 this.lastSelectionText = dv;
14829 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14836 reset : function(){
14837 // overridden so that last data is reset..
14844 this.setValue(this.originalValue);
14845 //this.clearInvalid();
14846 this.lastData = false;
14848 this.view.clearSelections();
14854 findRecord : function(prop, value){
14856 if(this.store.getCount() > 0){
14857 this.store.each(function(r){
14858 if(r.data[prop] == value){
14868 getName: function()
14870 // returns hidden if it's set..
14871 if (!this.rendered) {return ''};
14872 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14876 onViewMove : function(e, t){
14877 this.inKeyMode = false;
14881 onViewOver : function(e, t){
14882 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14885 var item = this.view.findItemFromChild(t);
14888 var index = this.view.indexOf(item);
14889 this.select(index, false);
14894 onViewClick : function(view, doFocus, el, e)
14896 var index = this.view.getSelectedIndexes()[0];
14898 var r = this.store.getAt(index);
14902 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14909 Roo.each(this.tickItems, function(v,k){
14911 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14913 _this.tickItems.splice(k, 1);
14915 if(typeof(e) == 'undefined' && view == false){
14916 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14928 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14929 this.tickItems.push(r.data);
14932 if(typeof(e) == 'undefined' && view == false){
14933 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14940 this.onSelect(r, index);
14942 if(doFocus !== false && !this.blockFocus){
14943 this.inputEl().focus();
14948 restrictHeight : function(){
14949 //this.innerList.dom.style.height = '';
14950 //var inner = this.innerList.dom;
14951 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14952 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14953 //this.list.beginUpdate();
14954 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14955 this.list.alignTo(this.inputEl(), this.listAlign);
14956 this.list.alignTo(this.inputEl(), this.listAlign);
14957 //this.list.endUpdate();
14961 onEmptyResults : function(){
14963 if(this.tickable && this.editable){
14964 this.hasFocus = false;
14965 this.restrictHeight();
14973 * Returns true if the dropdown list is expanded, else false.
14975 isExpanded : function(){
14976 return this.list.isVisible();
14980 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14981 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14982 * @param {String} value The data value of the item to select
14983 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14984 * selected item if it is not currently in view (defaults to true)
14985 * @return {Boolean} True if the value matched an item in the list, else false
14987 selectByValue : function(v, scrollIntoView){
14988 if(v !== undefined && v !== null){
14989 var r = this.findRecord(this.valueField || this.displayField, v);
14991 this.select(this.store.indexOf(r), scrollIntoView);
14999 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
15000 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15001 * @param {Number} index The zero-based index of the list item to select
15002 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15003 * selected item if it is not currently in view (defaults to true)
15005 select : function(index, scrollIntoView){
15006 this.selectedIndex = index;
15007 this.view.select(index);
15008 if(scrollIntoView !== false){
15009 var el = this.view.getNode(index);
15011 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
15014 this.list.scrollChildIntoView(el, false);
15020 selectNext : function(){
15021 var ct = this.store.getCount();
15023 if(this.selectedIndex == -1){
15025 }else if(this.selectedIndex < ct-1){
15026 this.select(this.selectedIndex+1);
15032 selectPrev : function(){
15033 var ct = this.store.getCount();
15035 if(this.selectedIndex == -1){
15037 }else if(this.selectedIndex != 0){
15038 this.select(this.selectedIndex-1);
15044 onKeyUp : function(e){
15045 if(this.editable !== false && !e.isSpecialKey()){
15046 this.lastKey = e.getKey();
15047 this.dqTask.delay(this.queryDelay);
15052 validateBlur : function(){
15053 return !this.list || !this.list.isVisible();
15057 initQuery : function(){
15059 var v = this.getRawValue();
15061 if(this.tickable && this.editable){
15062 v = this.tickableInputEl().getValue();
15069 doForce : function(){
15070 if(this.inputEl().dom.value.length > 0){
15071 this.inputEl().dom.value =
15072 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
15078 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
15079 * query allowing the query action to be canceled if needed.
15080 * @param {String} query The SQL query to execute
15081 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
15082 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
15083 * saved in the current store (defaults to false)
15085 doQuery : function(q, forceAll){
15087 if(q === undefined || q === null){
15092 forceAll: forceAll,
15096 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
15101 forceAll = qe.forceAll;
15102 if(forceAll === true || (q.length >= this.minChars)){
15104 this.hasQuery = true;
15106 if(this.lastQuery != q || this.alwaysQuery){
15107 this.lastQuery = q;
15108 if(this.mode == 'local'){
15109 this.selectedIndex = -1;
15111 this.store.clearFilter();
15114 if(this.specialFilter){
15115 this.fireEvent('specialfilter', this);
15120 this.store.filter(this.displayField, q);
15123 this.store.fireEvent("datachanged", this.store);
15130 this.store.baseParams[this.queryParam] = q;
15132 var options = {params : this.getParams(q)};
15135 options.add = true;
15136 options.params.start = this.page * this.pageSize;
15139 this.store.load(options);
15142 * this code will make the page width larger, at the beginning, the list not align correctly,
15143 * we should expand the list on onLoad
15144 * so command out it
15149 this.selectedIndex = -1;
15154 this.loadNext = false;
15158 getParams : function(q){
15160 //p[this.queryParam] = q;
15164 p.limit = this.pageSize;
15170 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
15172 collapse : function(){
15173 if(!this.isExpanded()){
15179 this.hasFocus = false;
15183 this.cancelBtn.hide();
15184 this.trigger.show();
15187 this.tickableInputEl().dom.value = '';
15188 this.tickableInputEl().blur();
15193 Roo.get(document).un('mousedown', this.collapseIf, this);
15194 Roo.get(document).un('mousewheel', this.collapseIf, this);
15195 if (!this.editable) {
15196 Roo.get(document).un('keydown', this.listKeyPress, this);
15198 this.fireEvent('collapse', this);
15204 collapseIf : function(e){
15205 var in_combo = e.within(this.el);
15206 var in_list = e.within(this.list);
15207 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
15209 if (in_combo || in_list || is_list) {
15210 //e.stopPropagation();
15215 this.onTickableFooterButtonClick(e, false, false);
15223 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
15225 expand : function(){
15227 if(this.isExpanded() || !this.hasFocus){
15231 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
15232 this.list.setWidth(lw);
15238 this.restrictHeight();
15242 this.tickItems = Roo.apply([], this.item);
15245 this.cancelBtn.show();
15246 this.trigger.hide();
15249 this.tickableInputEl().focus();
15254 Roo.get(document).on('mousedown', this.collapseIf, this);
15255 Roo.get(document).on('mousewheel', this.collapseIf, this);
15256 if (!this.editable) {
15257 Roo.get(document).on('keydown', this.listKeyPress, this);
15260 this.fireEvent('expand', this);
15264 // Implements the default empty TriggerField.onTriggerClick function
15265 onTriggerClick : function(e)
15267 Roo.log('trigger click');
15269 if(this.disabled || !this.triggerList){
15274 this.loadNext = false;
15276 if(this.isExpanded()){
15278 if (!this.blockFocus) {
15279 this.inputEl().focus();
15283 this.hasFocus = true;
15284 if(this.triggerAction == 'all') {
15285 this.doQuery(this.allQuery, true);
15287 this.doQuery(this.getRawValue());
15289 if (!this.blockFocus) {
15290 this.inputEl().focus();
15295 onTickableTriggerClick : function(e)
15302 this.loadNext = false;
15303 this.hasFocus = true;
15305 if(this.triggerAction == 'all') {
15306 this.doQuery(this.allQuery, true);
15308 this.doQuery(this.getRawValue());
15312 onSearchFieldClick : function(e)
15314 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15315 this.onTickableFooterButtonClick(e, false, false);
15319 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15324 this.loadNext = false;
15325 this.hasFocus = true;
15327 if(this.triggerAction == 'all') {
15328 this.doQuery(this.allQuery, true);
15330 this.doQuery(this.getRawValue());
15334 listKeyPress : function(e)
15336 //Roo.log('listkeypress');
15337 // scroll to first matching element based on key pres..
15338 if (e.isSpecialKey()) {
15341 var k = String.fromCharCode(e.getKey()).toUpperCase();
15344 var csel = this.view.getSelectedNodes();
15345 var cselitem = false;
15347 var ix = this.view.indexOf(csel[0]);
15348 cselitem = this.store.getAt(ix);
15349 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15355 this.store.each(function(v) {
15357 // start at existing selection.
15358 if (cselitem.id == v.id) {
15364 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15365 match = this.store.indexOf(v);
15371 if (match === false) {
15372 return true; // no more action?
15375 this.view.select(match);
15376 var sn = Roo.get(this.view.getSelectedNodes()[0]);
15377 sn.scrollIntoView(sn.dom.parentNode, false);
15380 onViewScroll : function(e, t){
15382 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){
15386 this.hasQuery = true;
15388 this.loading = this.list.select('.loading', true).first();
15390 if(this.loading === null){
15391 this.list.createChild({
15393 cls: 'loading roo-select2-more-results roo-select2-active',
15394 html: 'Loading more results...'
15397 this.loading = this.list.select('.loading', true).first();
15399 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15401 this.loading.hide();
15404 this.loading.show();
15409 this.loadNext = true;
15411 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15416 addItem : function(o)
15418 var dv = ''; // display value
15420 if (this.displayField) {
15421 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15423 // this is an error condition!!!
15424 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
15431 var choice = this.choices.createChild({
15433 cls: 'roo-select2-search-choice',
15442 cls: 'roo-select2-search-choice-close fa fa-times',
15447 }, this.searchField);
15449 var close = choice.select('a.roo-select2-search-choice-close', true).first();
15451 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15459 this.inputEl().dom.value = '';
15464 onRemoveItem : function(e, _self, o)
15466 e.preventDefault();
15468 this.lastItem = Roo.apply([], this.item);
15470 var index = this.item.indexOf(o.data) * 1;
15473 Roo.log('not this item?!');
15477 this.item.splice(index, 1);
15482 this.fireEvent('remove', this, e);
15488 syncValue : function()
15490 if(!this.item.length){
15497 Roo.each(this.item, function(i){
15498 if(_this.valueField){
15499 value.push(i[_this.valueField]);
15506 this.value = value.join(',');
15508 if(this.hiddenField){
15509 this.hiddenField.dom.value = this.value;
15512 this.store.fireEvent("datachanged", this.store);
15517 clearItem : function()
15519 if(!this.multiple){
15525 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15533 if(this.tickable && !Roo.isTouch){
15534 this.view.refresh();
15538 inputEl: function ()
15540 if(Roo.isIOS && this.useNativeIOS){
15541 return this.el.select('select.roo-ios-select', true).first();
15544 if(Roo.isTouch && this.mobileTouchView){
15545 return this.el.select('input.form-control',true).first();
15549 return this.searchField;
15552 return this.el.select('input.form-control',true).first();
15555 onTickableFooterButtonClick : function(e, btn, el)
15557 e.preventDefault();
15559 this.lastItem = Roo.apply([], this.item);
15561 if(btn && btn.name == 'cancel'){
15562 this.tickItems = Roo.apply([], this.item);
15571 Roo.each(this.tickItems, function(o){
15579 validate : function()
15581 if(this.getVisibilityEl().hasClass('hidden')){
15585 var v = this.getRawValue();
15588 v = this.getValue();
15591 if(this.disabled || this.allowBlank || v.length){
15596 this.markInvalid();
15600 tickableInputEl : function()
15602 if(!this.tickable || !this.editable){
15603 return this.inputEl();
15606 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15610 getAutoCreateTouchView : function()
15615 cls: 'form-group' //input-group
15621 type : this.inputType,
15622 cls : 'form-control x-combo-noedit',
15623 autocomplete: 'new-password',
15624 placeholder : this.placeholder || '',
15629 input.name = this.name;
15633 input.cls += ' input-' + this.size;
15636 if (this.disabled) {
15637 input.disabled = true;
15648 inputblock.cls += ' input-group';
15650 inputblock.cn.unshift({
15652 cls : 'input-group-addon input-group-prepend input-group-text',
15657 if(this.removable && !this.multiple){
15658 inputblock.cls += ' roo-removable';
15660 inputblock.cn.push({
15663 cls : 'roo-combo-removable-btn close'
15667 if(this.hasFeedback && !this.allowBlank){
15669 inputblock.cls += ' has-feedback';
15671 inputblock.cn.push({
15673 cls: 'glyphicon form-control-feedback'
15680 inputblock.cls += (this.before) ? '' : ' input-group';
15682 inputblock.cn.push({
15684 cls : 'input-group-addon input-group-append input-group-text',
15690 var ibwrap = inputblock;
15695 cls: 'roo-select2-choices',
15699 cls: 'roo-select2-search-field',
15712 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15717 cls: 'form-hidden-field'
15723 if(!this.multiple && this.showToggleBtn){
15729 if (this.caret != false) {
15732 cls: 'fa fa-' + this.caret
15739 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15741 Roo.bootstrap.version == 3 ? caret : '',
15744 cls: 'combobox-clear',
15758 combobox.cls += ' roo-select2-container-multi';
15761 var align = this.labelAlign || this.parentLabelAlign();
15763 if (align ==='left' && this.fieldLabel.length) {
15768 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15769 tooltip : 'This field is required'
15773 cls : 'control-label col-form-label',
15774 html : this.fieldLabel
15785 var labelCfg = cfg.cn[1];
15786 var contentCfg = cfg.cn[2];
15789 if(this.indicatorpos == 'right'){
15794 cls : 'control-label col-form-label',
15798 html : this.fieldLabel
15802 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15803 tooltip : 'This field is required'
15816 labelCfg = cfg.cn[0];
15817 contentCfg = cfg.cn[1];
15822 if(this.labelWidth > 12){
15823 labelCfg.style = "width: " + this.labelWidth + 'px';
15826 if(this.labelWidth < 13 && this.labelmd == 0){
15827 this.labelmd = this.labelWidth;
15830 if(this.labellg > 0){
15831 labelCfg.cls += ' col-lg-' + this.labellg;
15832 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15835 if(this.labelmd > 0){
15836 labelCfg.cls += ' col-md-' + this.labelmd;
15837 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15840 if(this.labelsm > 0){
15841 labelCfg.cls += ' col-sm-' + this.labelsm;
15842 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15845 if(this.labelxs > 0){
15846 labelCfg.cls += ' col-xs-' + this.labelxs;
15847 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15851 } else if ( this.fieldLabel.length) {
15855 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15856 tooltip : 'This field is required'
15860 cls : 'control-label',
15861 html : this.fieldLabel
15872 if(this.indicatorpos == 'right'){
15876 cls : 'control-label',
15877 html : this.fieldLabel,
15881 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15882 tooltip : 'This field is required'
15899 var settings = this;
15901 ['xs','sm','md','lg'].map(function(size){
15902 if (settings[size]) {
15903 cfg.cls += ' col-' + size + '-' + settings[size];
15910 initTouchView : function()
15912 this.renderTouchView();
15914 this.touchViewEl.on('scroll', function(){
15915 this.el.dom.scrollTop = 0;
15918 this.originalValue = this.getValue();
15920 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15922 this.inputEl().on("click", this.showTouchView, this);
15923 if (this.triggerEl) {
15924 this.triggerEl.on("click", this.showTouchView, this);
15928 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15929 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15931 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15933 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15934 this.store.on('load', this.onTouchViewLoad, this);
15935 this.store.on('loadexception', this.onTouchViewLoadException, this);
15937 if(this.hiddenName){
15939 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15941 this.hiddenField.dom.value =
15942 this.hiddenValue !== undefined ? this.hiddenValue :
15943 this.value !== undefined ? this.value : '';
15945 this.el.dom.removeAttribute('name');
15946 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15950 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15951 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15954 if(this.removable && !this.multiple){
15955 var close = this.closeTriggerEl();
15957 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15958 close.on('click', this.removeBtnClick, this, close);
15962 * fix the bug in Safari iOS8
15964 this.inputEl().on("focus", function(e){
15965 document.activeElement.blur();
15968 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15975 renderTouchView : function()
15977 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15978 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15980 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15981 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15983 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15984 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15985 this.touchViewBodyEl.setStyle('overflow', 'auto');
15987 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15988 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15990 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15991 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15995 showTouchView : function()
16001 this.touchViewHeaderEl.hide();
16003 if(this.modalTitle.length){
16004 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
16005 this.touchViewHeaderEl.show();
16008 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
16009 this.touchViewEl.show();
16011 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
16013 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
16014 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
16016 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16018 if(this.modalTitle.length){
16019 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16022 this.touchViewBodyEl.setHeight(bodyHeight);
16026 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
16028 this.touchViewEl.addClass('in');
16031 if(this._touchViewMask){
16032 Roo.get(document.body).addClass("x-body-masked");
16033 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
16034 this._touchViewMask.setStyle('z-index', 10000);
16035 this._touchViewMask.addClass('show');
16038 this.doTouchViewQuery();
16042 hideTouchView : function()
16044 this.touchViewEl.removeClass('in');
16048 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
16050 this.touchViewEl.setStyle('display', 'none');
16053 if(this._touchViewMask){
16054 this._touchViewMask.removeClass('show');
16055 Roo.get(document.body).removeClass("x-body-masked");
16059 setTouchViewValue : function()
16066 Roo.each(this.tickItems, function(o){
16071 this.hideTouchView();
16074 doTouchViewQuery : function()
16083 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
16087 if(!this.alwaysQuery || this.mode == 'local'){
16088 this.onTouchViewLoad();
16095 onTouchViewBeforeLoad : function(combo,opts)
16101 onTouchViewLoad : function()
16103 if(this.store.getCount() < 1){
16104 this.onTouchViewEmptyResults();
16108 this.clearTouchView();
16110 var rawValue = this.getRawValue();
16112 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
16114 this.tickItems = [];
16116 this.store.data.each(function(d, rowIndex){
16117 var row = this.touchViewListGroup.createChild(template);
16119 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
16120 row.addClass(d.data.cls);
16123 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16126 html : d.data[this.displayField]
16129 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
16130 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
16133 row.removeClass('selected');
16134 if(!this.multiple && this.valueField &&
16135 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
16138 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16139 row.addClass('selected');
16142 if(this.multiple && this.valueField &&
16143 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
16147 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16148 this.tickItems.push(d.data);
16151 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
16155 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
16157 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16159 if(this.modalTitle.length){
16160 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16163 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
16165 if(this.mobile_restrict_height && listHeight < bodyHeight){
16166 this.touchViewBodyEl.setHeight(listHeight);
16171 if(firstChecked && listHeight > bodyHeight){
16172 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
16177 onTouchViewLoadException : function()
16179 this.hideTouchView();
16182 onTouchViewEmptyResults : function()
16184 this.clearTouchView();
16186 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
16188 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
16192 clearTouchView : function()
16194 this.touchViewListGroup.dom.innerHTML = '';
16197 onTouchViewClick : function(e, el, o)
16199 e.preventDefault();
16202 var rowIndex = o.rowIndex;
16204 var r = this.store.getAt(rowIndex);
16206 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
16208 if(!this.multiple){
16209 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
16210 c.dom.removeAttribute('checked');
16213 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16215 this.setFromData(r.data);
16217 var close = this.closeTriggerEl();
16223 this.hideTouchView();
16225 this.fireEvent('select', this, r, rowIndex);
16230 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
16231 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
16232 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
16236 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16237 this.addItem(r.data);
16238 this.tickItems.push(r.data);
16242 getAutoCreateNativeIOS : function()
16245 cls: 'form-group' //input-group,
16250 cls : 'roo-ios-select'
16254 combobox.name = this.name;
16257 if (this.disabled) {
16258 combobox.disabled = true;
16261 var settings = this;
16263 ['xs','sm','md','lg'].map(function(size){
16264 if (settings[size]) {
16265 cfg.cls += ' col-' + size + '-' + settings[size];
16275 initIOSView : function()
16277 this.store.on('load', this.onIOSViewLoad, this);
16282 onIOSViewLoad : function()
16284 if(this.store.getCount() < 1){
16288 this.clearIOSView();
16290 if(this.allowBlank) {
16292 var default_text = '-- SELECT --';
16294 if(this.placeholder.length){
16295 default_text = this.placeholder;
16298 if(this.emptyTitle.length){
16299 default_text += ' - ' + this.emptyTitle + ' -';
16302 var opt = this.inputEl().createChild({
16305 html : default_text
16309 o[this.valueField] = 0;
16310 o[this.displayField] = default_text;
16312 this.ios_options.push({
16319 this.store.data.each(function(d, rowIndex){
16323 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16324 html = d.data[this.displayField];
16329 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16330 value = d.data[this.valueField];
16339 if(this.value == d.data[this.valueField]){
16340 option['selected'] = true;
16343 var opt = this.inputEl().createChild(option);
16345 this.ios_options.push({
16352 this.inputEl().on('change', function(){
16353 this.fireEvent('select', this);
16358 clearIOSView: function()
16360 this.inputEl().dom.innerHTML = '';
16362 this.ios_options = [];
16365 setIOSValue: function(v)
16369 if(!this.ios_options){
16373 Roo.each(this.ios_options, function(opts){
16375 opts.el.dom.removeAttribute('selected');
16377 if(opts.data[this.valueField] != v){
16381 opts.el.dom.setAttribute('selected', true);
16387 * @cfg {Boolean} grow
16391 * @cfg {Number} growMin
16395 * @cfg {Number} growMax
16404 Roo.apply(Roo.bootstrap.ComboBox, {
16408 cls: 'modal-header',
16430 cls: 'list-group-item',
16434 cls: 'roo-combobox-list-group-item-value'
16438 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16452 listItemCheckbox : {
16454 cls: 'list-group-item',
16458 cls: 'roo-combobox-list-group-item-value'
16462 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16478 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16483 cls: 'modal-footer',
16491 cls: 'col-xs-6 text-left',
16494 cls: 'btn btn-danger roo-touch-view-cancel',
16500 cls: 'col-xs-6 text-right',
16503 cls: 'btn btn-success roo-touch-view-ok',
16514 Roo.apply(Roo.bootstrap.ComboBox, {
16516 touchViewTemplate : {
16518 cls: 'modal fade roo-combobox-touch-view',
16522 cls: 'modal-dialog',
16523 style : 'position:fixed', // we have to fix position....
16527 cls: 'modal-content',
16529 Roo.bootstrap.ComboBox.header,
16530 Roo.bootstrap.ComboBox.body,
16531 Roo.bootstrap.ComboBox.footer
16540 * Ext JS Library 1.1.1
16541 * Copyright(c) 2006-2007, Ext JS, LLC.
16543 * Originally Released Under LGPL - original licence link has changed is not relivant.
16546 * <script type="text/javascript">
16551 * @extends Roo.util.Observable
16552 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16553 * This class also supports single and multi selection modes. <br>
16554 * Create a data model bound view:
16556 var store = new Roo.data.Store(...);
16558 var view = new Roo.View({
16560 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16562 singleSelect: true,
16563 selectedClass: "ydataview-selected",
16567 // listen for node click?
16568 view.on("click", function(vw, index, node, e){
16569 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16573 dataModel.load("foobar.xml");
16575 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16577 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16578 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16580 * Note: old style constructor is still suported (container, template, config)
16583 * Create a new View
16584 * @param {Object} config The config object
16587 Roo.View = function(config, depreciated_tpl, depreciated_config){
16589 this.parent = false;
16591 if (typeof(depreciated_tpl) == 'undefined') {
16592 // new way.. - universal constructor.
16593 Roo.apply(this, config);
16594 this.el = Roo.get(this.el);
16597 this.el = Roo.get(config);
16598 this.tpl = depreciated_tpl;
16599 Roo.apply(this, depreciated_config);
16601 this.wrapEl = this.el.wrap().wrap();
16602 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16605 if(typeof(this.tpl) == "string"){
16606 this.tpl = new Roo.Template(this.tpl);
16608 // support xtype ctors..
16609 this.tpl = new Roo.factory(this.tpl, Roo);
16613 this.tpl.compile();
16618 * @event beforeclick
16619 * Fires before a click is processed. Returns false to cancel the default action.
16620 * @param {Roo.View} this
16621 * @param {Number} index The index of the target node
16622 * @param {HTMLElement} node The target node
16623 * @param {Roo.EventObject} e The raw event object
16625 "beforeclick" : true,
16628 * Fires when a template node is clicked.
16629 * @param {Roo.View} this
16630 * @param {Number} index The index of the target node
16631 * @param {HTMLElement} node The target node
16632 * @param {Roo.EventObject} e The raw event object
16637 * Fires when a template node is double clicked.
16638 * @param {Roo.View} this
16639 * @param {Number} index The index of the target node
16640 * @param {HTMLElement} node The target node
16641 * @param {Roo.EventObject} e The raw event object
16645 * @event contextmenu
16646 * Fires when a template node is right clicked.
16647 * @param {Roo.View} this
16648 * @param {Number} index The index of the target node
16649 * @param {HTMLElement} node The target node
16650 * @param {Roo.EventObject} e The raw event object
16652 "contextmenu" : true,
16654 * @event selectionchange
16655 * Fires when the selected nodes change.
16656 * @param {Roo.View} this
16657 * @param {Array} selections Array of the selected nodes
16659 "selectionchange" : true,
16662 * @event beforeselect
16663 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16664 * @param {Roo.View} this
16665 * @param {HTMLElement} node The node to be selected
16666 * @param {Array} selections Array of currently selected nodes
16668 "beforeselect" : true,
16670 * @event preparedata
16671 * Fires on every row to render, to allow you to change the data.
16672 * @param {Roo.View} this
16673 * @param {Object} data to be rendered (change this)
16675 "preparedata" : true
16683 "click": this.onClick,
16684 "dblclick": this.onDblClick,
16685 "contextmenu": this.onContextMenu,
16689 this.selections = [];
16691 this.cmp = new Roo.CompositeElementLite([]);
16693 this.store = Roo.factory(this.store, Roo.data);
16694 this.setStore(this.store, true);
16697 if ( this.footer && this.footer.xtype) {
16699 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16701 this.footer.dataSource = this.store;
16702 this.footer.container = fctr;
16703 this.footer = Roo.factory(this.footer, Roo);
16704 fctr.insertFirst(this.el);
16706 // this is a bit insane - as the paging toolbar seems to detach the el..
16707 // dom.parentNode.parentNode.parentNode
16708 // they get detached?
16712 Roo.View.superclass.constructor.call(this);
16717 Roo.extend(Roo.View, Roo.util.Observable, {
16720 * @cfg {Roo.data.Store} store Data store to load data from.
16725 * @cfg {String|Roo.Element} el The container element.
16730 * @cfg {String|Roo.Template} tpl The template used by this View
16734 * @cfg {String} dataName the named area of the template to use as the data area
16735 * Works with domtemplates roo-name="name"
16739 * @cfg {String} selectedClass The css class to add to selected nodes
16741 selectedClass : "x-view-selected",
16743 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16748 * @cfg {String} text to display on mask (default Loading)
16752 * @cfg {Boolean} multiSelect Allow multiple selection
16754 multiSelect : false,
16756 * @cfg {Boolean} singleSelect Allow single selection
16758 singleSelect: false,
16761 * @cfg {Boolean} toggleSelect - selecting
16763 toggleSelect : false,
16766 * @cfg {Boolean} tickable - selecting
16771 * Returns the element this view is bound to.
16772 * @return {Roo.Element}
16774 getEl : function(){
16775 return this.wrapEl;
16781 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16783 refresh : function(){
16784 //Roo.log('refresh');
16787 // if we are using something like 'domtemplate', then
16788 // the what gets used is:
16789 // t.applySubtemplate(NAME, data, wrapping data..)
16790 // the outer template then get' applied with
16791 // the store 'extra data'
16792 // and the body get's added to the
16793 // roo-name="data" node?
16794 // <span class='roo-tpl-{name}'></span> ?????
16798 this.clearSelections();
16799 this.el.update("");
16801 var records = this.store.getRange();
16802 if(records.length < 1) {
16804 // is this valid?? = should it render a template??
16806 this.el.update(this.emptyText);
16810 if (this.dataName) {
16811 this.el.update(t.apply(this.store.meta)); //????
16812 el = this.el.child('.roo-tpl-' + this.dataName);
16815 for(var i = 0, len = records.length; i < len; i++){
16816 var data = this.prepareData(records[i].data, i, records[i]);
16817 this.fireEvent("preparedata", this, data, i, records[i]);
16819 var d = Roo.apply({}, data);
16822 Roo.apply(d, {'roo-id' : Roo.id()});
16826 Roo.each(this.parent.item, function(item){
16827 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16830 Roo.apply(d, {'roo-data-checked' : 'checked'});
16834 html[html.length] = Roo.util.Format.trim(
16836 t.applySubtemplate(this.dataName, d, this.store.meta) :
16843 el.update(html.join(""));
16844 this.nodes = el.dom.childNodes;
16845 this.updateIndexes(0);
16850 * Function to override to reformat the data that is sent to
16851 * the template for each node.
16852 * DEPRICATED - use the preparedata event handler.
16853 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16854 * a JSON object for an UpdateManager bound view).
16856 prepareData : function(data, index, record)
16858 this.fireEvent("preparedata", this, data, index, record);
16862 onUpdate : function(ds, record){
16863 // Roo.log('on update');
16864 this.clearSelections();
16865 var index = this.store.indexOf(record);
16866 var n = this.nodes[index];
16867 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16868 n.parentNode.removeChild(n);
16869 this.updateIndexes(index, index);
16875 onAdd : function(ds, records, index)
16877 //Roo.log(['on Add', ds, records, index] );
16878 this.clearSelections();
16879 if(this.nodes.length == 0){
16883 var n = this.nodes[index];
16884 for(var i = 0, len = records.length; i < len; i++){
16885 var d = this.prepareData(records[i].data, i, records[i]);
16887 this.tpl.insertBefore(n, d);
16890 this.tpl.append(this.el, d);
16893 this.updateIndexes(index);
16896 onRemove : function(ds, record, index){
16897 // Roo.log('onRemove');
16898 this.clearSelections();
16899 var el = this.dataName ?
16900 this.el.child('.roo-tpl-' + this.dataName) :
16903 el.dom.removeChild(this.nodes[index]);
16904 this.updateIndexes(index);
16908 * Refresh an individual node.
16909 * @param {Number} index
16911 refreshNode : function(index){
16912 this.onUpdate(this.store, this.store.getAt(index));
16915 updateIndexes : function(startIndex, endIndex){
16916 var ns = this.nodes;
16917 startIndex = startIndex || 0;
16918 endIndex = endIndex || ns.length - 1;
16919 for(var i = startIndex; i <= endIndex; i++){
16920 ns[i].nodeIndex = i;
16925 * Changes the data store this view uses and refresh the view.
16926 * @param {Store} store
16928 setStore : function(store, initial){
16929 if(!initial && this.store){
16930 this.store.un("datachanged", this.refresh);
16931 this.store.un("add", this.onAdd);
16932 this.store.un("remove", this.onRemove);
16933 this.store.un("update", this.onUpdate);
16934 this.store.un("clear", this.refresh);
16935 this.store.un("beforeload", this.onBeforeLoad);
16936 this.store.un("load", this.onLoad);
16937 this.store.un("loadexception", this.onLoad);
16941 store.on("datachanged", this.refresh, this);
16942 store.on("add", this.onAdd, this);
16943 store.on("remove", this.onRemove, this);
16944 store.on("update", this.onUpdate, this);
16945 store.on("clear", this.refresh, this);
16946 store.on("beforeload", this.onBeforeLoad, this);
16947 store.on("load", this.onLoad, this);
16948 store.on("loadexception", this.onLoad, this);
16956 * onbeforeLoad - masks the loading area.
16959 onBeforeLoad : function(store,opts)
16961 //Roo.log('onBeforeLoad');
16963 this.el.update("");
16965 this.el.mask(this.mask ? this.mask : "Loading" );
16967 onLoad : function ()
16974 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16975 * @param {HTMLElement} node
16976 * @return {HTMLElement} The template node
16978 findItemFromChild : function(node){
16979 var el = this.dataName ?
16980 this.el.child('.roo-tpl-' + this.dataName,true) :
16983 if(!node || node.parentNode == el){
16986 var p = node.parentNode;
16987 while(p && p != el){
16988 if(p.parentNode == el){
16997 onClick : function(e){
16998 var item = this.findItemFromChild(e.getTarget());
17000 var index = this.indexOf(item);
17001 if(this.onItemClick(item, index, e) !== false){
17002 this.fireEvent("click", this, index, item, e);
17005 this.clearSelections();
17010 onContextMenu : function(e){
17011 var item = this.findItemFromChild(e.getTarget());
17013 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
17018 onDblClick : function(e){
17019 var item = this.findItemFromChild(e.getTarget());
17021 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
17025 onItemClick : function(item, index, e)
17027 if(this.fireEvent("beforeclick", this, index, item, e) === false){
17030 if (this.toggleSelect) {
17031 var m = this.isSelected(item) ? 'unselect' : 'select';
17034 _t[m](item, true, false);
17037 if(this.multiSelect || this.singleSelect){
17038 if(this.multiSelect && e.shiftKey && this.lastSelection){
17039 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
17041 this.select(item, this.multiSelect && e.ctrlKey);
17042 this.lastSelection = item;
17045 if(!this.tickable){
17046 e.preventDefault();
17054 * Get the number of selected nodes.
17057 getSelectionCount : function(){
17058 return this.selections.length;
17062 * Get the currently selected nodes.
17063 * @return {Array} An array of HTMLElements
17065 getSelectedNodes : function(){
17066 return this.selections;
17070 * Get the indexes of the selected nodes.
17073 getSelectedIndexes : function(){
17074 var indexes = [], s = this.selections;
17075 for(var i = 0, len = s.length; i < len; i++){
17076 indexes.push(s[i].nodeIndex);
17082 * Clear all selections
17083 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
17085 clearSelections : function(suppressEvent){
17086 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
17087 this.cmp.elements = this.selections;
17088 this.cmp.removeClass(this.selectedClass);
17089 this.selections = [];
17090 if(!suppressEvent){
17091 this.fireEvent("selectionchange", this, this.selections);
17097 * Returns true if the passed node is selected
17098 * @param {HTMLElement/Number} node The node or node index
17099 * @return {Boolean}
17101 isSelected : function(node){
17102 var s = this.selections;
17106 node = this.getNode(node);
17107 return s.indexOf(node) !== -1;
17112 * @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
17113 * @param {Boolean} keepExisting (optional) true to keep existing selections
17114 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17116 select : function(nodeInfo, keepExisting, suppressEvent){
17117 if(nodeInfo instanceof Array){
17119 this.clearSelections(true);
17121 for(var i = 0, len = nodeInfo.length; i < len; i++){
17122 this.select(nodeInfo[i], true, true);
17126 var node = this.getNode(nodeInfo);
17127 if(!node || this.isSelected(node)){
17128 return; // already selected.
17131 this.clearSelections(true);
17134 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
17135 Roo.fly(node).addClass(this.selectedClass);
17136 this.selections.push(node);
17137 if(!suppressEvent){
17138 this.fireEvent("selectionchange", this, this.selections);
17146 * @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
17147 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
17148 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17150 unselect : function(nodeInfo, keepExisting, suppressEvent)
17152 if(nodeInfo instanceof Array){
17153 Roo.each(this.selections, function(s) {
17154 this.unselect(s, nodeInfo);
17158 var node = this.getNode(nodeInfo);
17159 if(!node || !this.isSelected(node)){
17160 //Roo.log("not selected");
17161 return; // not selected.
17165 Roo.each(this.selections, function(s) {
17167 Roo.fly(node).removeClass(this.selectedClass);
17174 this.selections= ns;
17175 this.fireEvent("selectionchange", this, this.selections);
17179 * Gets a template node.
17180 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17181 * @return {HTMLElement} The node or null if it wasn't found
17183 getNode : function(nodeInfo){
17184 if(typeof nodeInfo == "string"){
17185 return document.getElementById(nodeInfo);
17186 }else if(typeof nodeInfo == "number"){
17187 return this.nodes[nodeInfo];
17193 * Gets a range template nodes.
17194 * @param {Number} startIndex
17195 * @param {Number} endIndex
17196 * @return {Array} An array of nodes
17198 getNodes : function(start, end){
17199 var ns = this.nodes;
17200 start = start || 0;
17201 end = typeof end == "undefined" ? ns.length - 1 : end;
17204 for(var i = start; i <= end; i++){
17208 for(var i = start; i >= end; i--){
17216 * Finds the index of the passed node
17217 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17218 * @return {Number} The index of the node or -1
17220 indexOf : function(node){
17221 node = this.getNode(node);
17222 if(typeof node.nodeIndex == "number"){
17223 return node.nodeIndex;
17225 var ns = this.nodes;
17226 for(var i = 0, len = ns.length; i < len; i++){
17237 * based on jquery fullcalendar
17241 Roo.bootstrap = Roo.bootstrap || {};
17243 * @class Roo.bootstrap.Calendar
17244 * @extends Roo.bootstrap.Component
17245 * Bootstrap Calendar class
17246 * @cfg {Boolean} loadMask (true|false) default false
17247 * @cfg {Object} header generate the user specific header of the calendar, default false
17250 * Create a new Container
17251 * @param {Object} config The config object
17256 Roo.bootstrap.Calendar = function(config){
17257 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
17261 * Fires when a date is selected
17262 * @param {DatePicker} this
17263 * @param {Date} date The selected date
17267 * @event monthchange
17268 * Fires when the displayed month changes
17269 * @param {DatePicker} this
17270 * @param {Date} date The selected month
17272 'monthchange': true,
17274 * @event evententer
17275 * Fires when mouse over an event
17276 * @param {Calendar} this
17277 * @param {event} Event
17279 'evententer': true,
17281 * @event eventleave
17282 * Fires when the mouse leaves an
17283 * @param {Calendar} this
17286 'eventleave': true,
17288 * @event eventclick
17289 * Fires when the mouse click an
17290 * @param {Calendar} this
17299 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
17302 * @cfg {Number} startDay
17303 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17311 getAutoCreate : function(){
17314 var fc_button = function(name, corner, style, content ) {
17315 return Roo.apply({},{
17317 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
17319 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17322 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17333 style : 'width:100%',
17340 cls : 'fc-header-left',
17342 fc_button('prev', 'left', 'arrow', '‹' ),
17343 fc_button('next', 'right', 'arrow', '›' ),
17344 { tag: 'span', cls: 'fc-header-space' },
17345 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
17353 cls : 'fc-header-center',
17357 cls: 'fc-header-title',
17360 html : 'month / year'
17368 cls : 'fc-header-right',
17370 /* fc_button('month', 'left', '', 'month' ),
17371 fc_button('week', '', '', 'week' ),
17372 fc_button('day', 'right', '', 'day' )
17384 header = this.header;
17387 var cal_heads = function() {
17389 // fixme - handle this.
17391 for (var i =0; i < Date.dayNames.length; i++) {
17392 var d = Date.dayNames[i];
17395 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17396 html : d.substring(0,3)
17400 ret[0].cls += ' fc-first';
17401 ret[6].cls += ' fc-last';
17404 var cal_cell = function(n) {
17407 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17412 cls: 'fc-day-number',
17416 cls: 'fc-day-content',
17420 style: 'position: relative;' // height: 17px;
17432 var cal_rows = function() {
17435 for (var r = 0; r < 6; r++) {
17442 for (var i =0; i < Date.dayNames.length; i++) {
17443 var d = Date.dayNames[i];
17444 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17447 row.cn[0].cls+=' fc-first';
17448 row.cn[0].cn[0].style = 'min-height:90px';
17449 row.cn[6].cls+=' fc-last';
17453 ret[0].cls += ' fc-first';
17454 ret[4].cls += ' fc-prev-last';
17455 ret[5].cls += ' fc-last';
17462 cls: 'fc-border-separate',
17463 style : 'width:100%',
17471 cls : 'fc-first fc-last',
17489 cls : 'fc-content',
17490 style : "position: relative;",
17493 cls : 'fc-view fc-view-month fc-grid',
17494 style : 'position: relative',
17495 unselectable : 'on',
17498 cls : 'fc-event-container',
17499 style : 'position:absolute;z-index:8;top:0;left:0;'
17517 initEvents : function()
17520 throw "can not find store for calendar";
17526 style: "text-align:center",
17530 style: "background-color:white;width:50%;margin:250 auto",
17534 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17545 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17547 var size = this.el.select('.fc-content', true).first().getSize();
17548 this.maskEl.setSize(size.width, size.height);
17549 this.maskEl.enableDisplayMode("block");
17550 if(!this.loadMask){
17551 this.maskEl.hide();
17554 this.store = Roo.factory(this.store, Roo.data);
17555 this.store.on('load', this.onLoad, this);
17556 this.store.on('beforeload', this.onBeforeLoad, this);
17560 this.cells = this.el.select('.fc-day',true);
17561 //Roo.log(this.cells);
17562 this.textNodes = this.el.query('.fc-day-number');
17563 this.cells.addClassOnOver('fc-state-hover');
17565 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17566 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17567 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17568 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17570 this.on('monthchange', this.onMonthChange, this);
17572 this.update(new Date().clearTime());
17575 resize : function() {
17576 var sz = this.el.getSize();
17578 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17579 this.el.select('.fc-day-content div',true).setHeight(34);
17584 showPrevMonth : function(e){
17585 this.update(this.activeDate.add("mo", -1));
17587 showToday : function(e){
17588 this.update(new Date().clearTime());
17591 showNextMonth : function(e){
17592 this.update(this.activeDate.add("mo", 1));
17596 showPrevYear : function(){
17597 this.update(this.activeDate.add("y", -1));
17601 showNextYear : function(){
17602 this.update(this.activeDate.add("y", 1));
17607 update : function(date)
17609 var vd = this.activeDate;
17610 this.activeDate = date;
17611 // if(vd && this.el){
17612 // var t = date.getTime();
17613 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17614 // Roo.log('using add remove');
17616 // this.fireEvent('monthchange', this, date);
17618 // this.cells.removeClass("fc-state-highlight");
17619 // this.cells.each(function(c){
17620 // if(c.dateValue == t){
17621 // c.addClass("fc-state-highlight");
17622 // setTimeout(function(){
17623 // try{c.dom.firstChild.focus();}catch(e){}
17633 var days = date.getDaysInMonth();
17635 var firstOfMonth = date.getFirstDateOfMonth();
17636 var startingPos = firstOfMonth.getDay()-this.startDay;
17638 if(startingPos < this.startDay){
17642 var pm = date.add(Date.MONTH, -1);
17643 var prevStart = pm.getDaysInMonth()-startingPos;
17645 this.cells = this.el.select('.fc-day',true);
17646 this.textNodes = this.el.query('.fc-day-number');
17647 this.cells.addClassOnOver('fc-state-hover');
17649 var cells = this.cells.elements;
17650 var textEls = this.textNodes;
17652 Roo.each(cells, function(cell){
17653 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17656 days += startingPos;
17658 // convert everything to numbers so it's fast
17659 var day = 86400000;
17660 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17663 //Roo.log(prevStart);
17665 var today = new Date().clearTime().getTime();
17666 var sel = date.clearTime().getTime();
17667 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17668 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17669 var ddMatch = this.disabledDatesRE;
17670 var ddText = this.disabledDatesText;
17671 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17672 var ddaysText = this.disabledDaysText;
17673 var format = this.format;
17675 var setCellClass = function(cal, cell){
17679 //Roo.log('set Cell Class');
17681 var t = d.getTime();
17685 cell.dateValue = t;
17687 cell.className += " fc-today";
17688 cell.className += " fc-state-highlight";
17689 cell.title = cal.todayText;
17692 // disable highlight in other month..
17693 //cell.className += " fc-state-highlight";
17698 cell.className = " fc-state-disabled";
17699 cell.title = cal.minText;
17703 cell.className = " fc-state-disabled";
17704 cell.title = cal.maxText;
17708 if(ddays.indexOf(d.getDay()) != -1){
17709 cell.title = ddaysText;
17710 cell.className = " fc-state-disabled";
17713 if(ddMatch && format){
17714 var fvalue = d.dateFormat(format);
17715 if(ddMatch.test(fvalue)){
17716 cell.title = ddText.replace("%0", fvalue);
17717 cell.className = " fc-state-disabled";
17721 if (!cell.initialClassName) {
17722 cell.initialClassName = cell.dom.className;
17725 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17730 for(; i < startingPos; i++) {
17731 textEls[i].innerHTML = (++prevStart);
17732 d.setDate(d.getDate()+1);
17734 cells[i].className = "fc-past fc-other-month";
17735 setCellClass(this, cells[i]);
17740 for(; i < days; i++){
17741 intDay = i - startingPos + 1;
17742 textEls[i].innerHTML = (intDay);
17743 d.setDate(d.getDate()+1);
17745 cells[i].className = ''; // "x-date-active";
17746 setCellClass(this, cells[i]);
17750 for(; i < 42; i++) {
17751 textEls[i].innerHTML = (++extraDays);
17752 d.setDate(d.getDate()+1);
17754 cells[i].className = "fc-future fc-other-month";
17755 setCellClass(this, cells[i]);
17758 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17760 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17762 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17763 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17765 if(totalRows != 6){
17766 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17767 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17770 this.fireEvent('monthchange', this, date);
17774 if(!this.internalRender){
17775 var main = this.el.dom.firstChild;
17776 var w = main.offsetWidth;
17777 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17778 Roo.fly(main).setWidth(w);
17779 this.internalRender = true;
17780 // opera does not respect the auto grow header center column
17781 // then, after it gets a width opera refuses to recalculate
17782 // without a second pass
17783 if(Roo.isOpera && !this.secondPass){
17784 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17785 this.secondPass = true;
17786 this.update.defer(10, this, [date]);
17793 findCell : function(dt) {
17794 dt = dt.clearTime().getTime();
17796 this.cells.each(function(c){
17797 //Roo.log("check " +c.dateValue + '?=' + dt);
17798 if(c.dateValue == dt){
17808 findCells : function(ev) {
17809 var s = ev.start.clone().clearTime().getTime();
17811 var e= ev.end.clone().clearTime().getTime();
17814 this.cells.each(function(c){
17815 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17817 if(c.dateValue > e){
17820 if(c.dateValue < s){
17829 // findBestRow: function(cells)
17833 // for (var i =0 ; i < cells.length;i++) {
17834 // ret = Math.max(cells[i].rows || 0,ret);
17841 addItem : function(ev)
17843 // look for vertical location slot in
17844 var cells = this.findCells(ev);
17846 // ev.row = this.findBestRow(cells);
17848 // work out the location.
17852 for(var i =0; i < cells.length; i++) {
17854 cells[i].row = cells[0].row;
17857 cells[i].row = cells[i].row + 1;
17867 if (crow.start.getY() == cells[i].getY()) {
17869 crow.end = cells[i];
17886 cells[0].events.push(ev);
17888 this.calevents.push(ev);
17891 clearEvents: function() {
17893 if(!this.calevents){
17897 Roo.each(this.cells.elements, function(c){
17903 Roo.each(this.calevents, function(e) {
17904 Roo.each(e.els, function(el) {
17905 el.un('mouseenter' ,this.onEventEnter, this);
17906 el.un('mouseleave' ,this.onEventLeave, this);
17911 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17917 renderEvents: function()
17921 this.cells.each(function(c) {
17930 if(c.row != c.events.length){
17931 r = 4 - (4 - (c.row - c.events.length));
17934 c.events = ev.slice(0, r);
17935 c.more = ev.slice(r);
17937 if(c.more.length && c.more.length == 1){
17938 c.events.push(c.more.pop());
17941 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17945 this.cells.each(function(c) {
17947 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17950 for (var e = 0; e < c.events.length; e++){
17951 var ev = c.events[e];
17952 var rows = ev.rows;
17954 for(var i = 0; i < rows.length; i++) {
17956 // how many rows should it span..
17959 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17960 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17962 unselectable : "on",
17965 cls: 'fc-event-inner',
17969 // cls: 'fc-event-time',
17970 // html : cells.length > 1 ? '' : ev.time
17974 cls: 'fc-event-title',
17975 html : String.format('{0}', ev.title)
17982 cls: 'ui-resizable-handle ui-resizable-e',
17983 html : '  '
17990 cfg.cls += ' fc-event-start';
17992 if ((i+1) == rows.length) {
17993 cfg.cls += ' fc-event-end';
17996 var ctr = _this.el.select('.fc-event-container',true).first();
17997 var cg = ctr.createChild(cfg);
17999 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
18000 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
18002 var r = (c.more.length) ? 1 : 0;
18003 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
18004 cg.setWidth(ebox.right - sbox.x -2);
18006 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
18007 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
18008 cg.on('click', _this.onEventClick, _this, ev);
18019 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
18020 style : 'position: absolute',
18021 unselectable : "on",
18024 cls: 'fc-event-inner',
18028 cls: 'fc-event-title',
18036 cls: 'ui-resizable-handle ui-resizable-e',
18037 html : '  '
18043 var ctr = _this.el.select('.fc-event-container',true).first();
18044 var cg = ctr.createChild(cfg);
18046 var sbox = c.select('.fc-day-content',true).first().getBox();
18047 var ebox = c.select('.fc-day-content',true).first().getBox();
18049 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
18050 cg.setWidth(ebox.right - sbox.x -2);
18052 cg.on('click', _this.onMoreEventClick, _this, c.more);
18062 onEventEnter: function (e, el,event,d) {
18063 this.fireEvent('evententer', this, el, event);
18066 onEventLeave: function (e, el,event,d) {
18067 this.fireEvent('eventleave', this, el, event);
18070 onEventClick: function (e, el,event,d) {
18071 this.fireEvent('eventclick', this, el, event);
18074 onMonthChange: function () {
18078 onMoreEventClick: function(e, el, more)
18082 this.calpopover.placement = 'right';
18083 this.calpopover.setTitle('More');
18085 this.calpopover.setContent('');
18087 var ctr = this.calpopover.el.select('.popover-content', true).first();
18089 Roo.each(more, function(m){
18091 cls : 'fc-event-hori fc-event-draggable',
18094 var cg = ctr.createChild(cfg);
18096 cg.on('click', _this.onEventClick, _this, m);
18099 this.calpopover.show(el);
18104 onLoad: function ()
18106 this.calevents = [];
18109 if(this.store.getCount() > 0){
18110 this.store.data.each(function(d){
18113 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
18114 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
18115 time : d.data.start_time,
18116 title : d.data.title,
18117 description : d.data.description,
18118 venue : d.data.venue
18123 this.renderEvents();
18125 if(this.calevents.length && this.loadMask){
18126 this.maskEl.hide();
18130 onBeforeLoad: function()
18132 this.clearEvents();
18134 this.maskEl.show();
18148 * @class Roo.bootstrap.Popover
18149 * @extends Roo.bootstrap.Component
18150 * Bootstrap Popover class
18151 * @cfg {String} html contents of the popover (or false to use children..)
18152 * @cfg {String} title of popover (or false to hide)
18153 * @cfg {String} placement how it is placed
18154 * @cfg {String} trigger click || hover (or false to trigger manually)
18155 * @cfg {String} over what (parent or false to trigger manually.)
18156 * @cfg {Number} delay - delay before showing
18159 * Create a new Popover
18160 * @param {Object} config The config object
18163 Roo.bootstrap.Popover = function(config){
18164 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
18170 * After the popover show
18172 * @param {Roo.bootstrap.Popover} this
18177 * After the popover hide
18179 * @param {Roo.bootstrap.Popover} this
18185 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
18187 title: 'Fill in a title',
18190 placement : 'right',
18191 trigger : 'hover', // hover
18197 can_build_overlaid : false,
18199 getChildContainer : function()
18201 return this.el.select('.popover-content',true).first();
18204 getAutoCreate : function(){
18207 cls : 'popover roo-dynamic',
18208 style: 'display:block',
18214 cls : 'popover-inner',
18218 cls: 'popover-title popover-header',
18222 cls : 'popover-content popover-body',
18233 setTitle: function(str)
18236 this.el.select('.popover-title',true).first().dom.innerHTML = str;
18238 setContent: function(str)
18241 this.el.select('.popover-content',true).first().dom.innerHTML = str;
18243 // as it get's added to the bottom of the page.
18244 onRender : function(ct, position)
18246 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18248 var cfg = Roo.apply({}, this.getAutoCreate());
18252 cfg.cls += ' ' + this.cls;
18255 cfg.style = this.style;
18257 //Roo.log("adding to ");
18258 this.el = Roo.get(document.body).createChild(cfg, position);
18259 // Roo.log(this.el);
18264 initEvents : function()
18266 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
18267 this.el.enableDisplayMode('block');
18269 if (this.over === false) {
18272 if (this.triggers === false) {
18275 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18276 var triggers = this.trigger ? this.trigger.split(' ') : [];
18277 Roo.each(triggers, function(trigger) {
18279 if (trigger == 'click') {
18280 on_el.on('click', this.toggle, this);
18281 } else if (trigger != 'manual') {
18282 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
18283 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
18285 on_el.on(eventIn ,this.enter, this);
18286 on_el.on(eventOut, this.leave, this);
18297 toggle : function () {
18298 this.hoverState == 'in' ? this.leave() : this.enter();
18301 enter : function () {
18303 clearTimeout(this.timeout);
18305 this.hoverState = 'in';
18307 if (!this.delay || !this.delay.show) {
18312 this.timeout = setTimeout(function () {
18313 if (_t.hoverState == 'in') {
18316 }, this.delay.show)
18319 leave : function() {
18320 clearTimeout(this.timeout);
18322 this.hoverState = 'out';
18324 if (!this.delay || !this.delay.hide) {
18329 this.timeout = setTimeout(function () {
18330 if (_t.hoverState == 'out') {
18333 }, this.delay.hide)
18336 show : function (on_el)
18339 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18343 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18344 if (this.html !== false) {
18345 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18347 this.el.removeClass([
18348 'fade','top','bottom', 'left', 'right','in',
18349 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18351 if (!this.title.length) {
18352 this.el.select('.popover-title',true).hide();
18355 var placement = typeof this.placement == 'function' ?
18356 this.placement.call(this, this.el, on_el) :
18359 var autoToken = /\s?auto?\s?/i;
18360 var autoPlace = autoToken.test(placement);
18362 placement = placement.replace(autoToken, '') || 'top';
18366 //this.el.setXY([0,0]);
18368 this.el.dom.style.display='block';
18369 this.el.addClass(placement);
18371 //this.el.appendTo(on_el);
18373 var p = this.getPosition();
18374 var box = this.el.getBox();
18379 var align = Roo.bootstrap.Popover.alignment[placement];
18382 this.el.alignTo(on_el, align[0],align[1]);
18383 //var arrow = this.el.select('.arrow',true).first();
18384 //arrow.set(align[2],
18386 this.el.addClass('in');
18389 if (this.el.hasClass('fade')) {
18393 this.hoverState = 'in';
18395 this.fireEvent('show', this);
18400 this.el.setXY([0,0]);
18401 this.el.removeClass('in');
18403 this.hoverState = null;
18405 this.fireEvent('hide', this);
18410 Roo.bootstrap.Popover.alignment = {
18411 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18412 'right' : ['l-r', [10,0], 'left bs-popover-left'],
18413 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18414 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18425 * @class Roo.bootstrap.Progress
18426 * @extends Roo.bootstrap.Component
18427 * Bootstrap Progress class
18428 * @cfg {Boolean} striped striped of the progress bar
18429 * @cfg {Boolean} active animated of the progress bar
18433 * Create a new Progress
18434 * @param {Object} config The config object
18437 Roo.bootstrap.Progress = function(config){
18438 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18441 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
18446 getAutoCreate : function(){
18454 cfg.cls += ' progress-striped';
18458 cfg.cls += ' active';
18477 * @class Roo.bootstrap.ProgressBar
18478 * @extends Roo.bootstrap.Component
18479 * Bootstrap ProgressBar class
18480 * @cfg {Number} aria_valuenow aria-value now
18481 * @cfg {Number} aria_valuemin aria-value min
18482 * @cfg {Number} aria_valuemax aria-value max
18483 * @cfg {String} label label for the progress bar
18484 * @cfg {String} panel (success | info | warning | danger )
18485 * @cfg {String} role role of the progress bar
18486 * @cfg {String} sr_only text
18490 * Create a new ProgressBar
18491 * @param {Object} config The config object
18494 Roo.bootstrap.ProgressBar = function(config){
18495 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18498 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
18502 aria_valuemax : 100,
18508 getAutoCreate : function()
18513 cls: 'progress-bar',
18514 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18526 cfg.role = this.role;
18529 if(this.aria_valuenow){
18530 cfg['aria-valuenow'] = this.aria_valuenow;
18533 if(this.aria_valuemin){
18534 cfg['aria-valuemin'] = this.aria_valuemin;
18537 if(this.aria_valuemax){
18538 cfg['aria-valuemax'] = this.aria_valuemax;
18541 if(this.label && !this.sr_only){
18542 cfg.html = this.label;
18546 cfg.cls += ' progress-bar-' + this.panel;
18552 update : function(aria_valuenow)
18554 this.aria_valuenow = aria_valuenow;
18556 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18571 * @class Roo.bootstrap.TabGroup
18572 * @extends Roo.bootstrap.Column
18573 * Bootstrap Column class
18574 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18575 * @cfg {Boolean} carousel true to make the group behave like a carousel
18576 * @cfg {Boolean} bullets show bullets for the panels
18577 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18578 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18579 * @cfg {Boolean} showarrow (true|false) show arrow default true
18582 * Create a new TabGroup
18583 * @param {Object} config The config object
18586 Roo.bootstrap.TabGroup = function(config){
18587 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18589 this.navId = Roo.id();
18592 Roo.bootstrap.TabGroup.register(this);
18596 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18599 transition : false,
18604 slideOnTouch : false,
18607 getAutoCreate : function()
18609 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18611 cfg.cls += ' tab-content';
18613 if (this.carousel) {
18614 cfg.cls += ' carousel slide';
18617 cls : 'carousel-inner',
18621 if(this.bullets && !Roo.isTouch){
18624 cls : 'carousel-bullets',
18628 if(this.bullets_cls){
18629 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18636 cfg.cn[0].cn.push(bullets);
18639 if(this.showarrow){
18640 cfg.cn[0].cn.push({
18642 class : 'carousel-arrow',
18646 class : 'carousel-prev',
18650 class : 'fa fa-chevron-left'
18656 class : 'carousel-next',
18660 class : 'fa fa-chevron-right'
18673 initEvents: function()
18675 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18676 // this.el.on("touchstart", this.onTouchStart, this);
18679 if(this.autoslide){
18682 this.slideFn = window.setInterval(function() {
18683 _this.showPanelNext();
18687 if(this.showarrow){
18688 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18689 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18695 // onTouchStart : function(e, el, o)
18697 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18701 // this.showPanelNext();
18705 getChildContainer : function()
18707 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18711 * register a Navigation item
18712 * @param {Roo.bootstrap.NavItem} the navitem to add
18714 register : function(item)
18716 this.tabs.push( item);
18717 item.navId = this.navId; // not really needed..
18722 getActivePanel : function()
18725 Roo.each(this.tabs, function(t) {
18735 getPanelByName : function(n)
18738 Roo.each(this.tabs, function(t) {
18739 if (t.tabId == n) {
18747 indexOfPanel : function(p)
18750 Roo.each(this.tabs, function(t,i) {
18751 if (t.tabId == p.tabId) {
18760 * show a specific panel
18761 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18762 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18764 showPanel : function (pan)
18766 if(this.transition || typeof(pan) == 'undefined'){
18767 Roo.log("waiting for the transitionend");
18771 if (typeof(pan) == 'number') {
18772 pan = this.tabs[pan];
18775 if (typeof(pan) == 'string') {
18776 pan = this.getPanelByName(pan);
18779 var cur = this.getActivePanel();
18782 Roo.log('pan or acitve pan is undefined');
18786 if (pan.tabId == this.getActivePanel().tabId) {
18790 if (false === cur.fireEvent('beforedeactivate')) {
18794 if(this.bullets > 0 && !Roo.isTouch){
18795 this.setActiveBullet(this.indexOfPanel(pan));
18798 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18800 //class="carousel-item carousel-item-next carousel-item-left"
18802 this.transition = true;
18803 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18804 var lr = dir == 'next' ? 'left' : 'right';
18805 pan.el.addClass(dir); // or prev
18806 pan.el.addClass('carousel-item-' + dir); // or prev
18807 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18808 cur.el.addClass(lr); // or right
18809 pan.el.addClass(lr);
18810 cur.el.addClass('carousel-item-' +lr); // or right
18811 pan.el.addClass('carousel-item-' +lr);
18815 cur.el.on('transitionend', function() {
18816 Roo.log("trans end?");
18818 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18819 pan.setActive(true);
18821 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18822 cur.setActive(false);
18824 _this.transition = false;
18826 }, this, { single: true } );
18831 cur.setActive(false);
18832 pan.setActive(true);
18837 showPanelNext : function()
18839 var i = this.indexOfPanel(this.getActivePanel());
18841 if (i >= this.tabs.length - 1 && !this.autoslide) {
18845 if (i >= this.tabs.length - 1 && this.autoslide) {
18849 this.showPanel(this.tabs[i+1]);
18852 showPanelPrev : function()
18854 var i = this.indexOfPanel(this.getActivePanel());
18856 if (i < 1 && !this.autoslide) {
18860 if (i < 1 && this.autoslide) {
18861 i = this.tabs.length;
18864 this.showPanel(this.tabs[i-1]);
18868 addBullet: function()
18870 if(!this.bullets || Roo.isTouch){
18873 var ctr = this.el.select('.carousel-bullets',true).first();
18874 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18875 var bullet = ctr.createChild({
18876 cls : 'bullet bullet-' + i
18877 },ctr.dom.lastChild);
18882 bullet.on('click', (function(e, el, o, ii, t){
18884 e.preventDefault();
18886 this.showPanel(ii);
18888 if(this.autoslide && this.slideFn){
18889 clearInterval(this.slideFn);
18890 this.slideFn = window.setInterval(function() {
18891 _this.showPanelNext();
18895 }).createDelegate(this, [i, bullet], true));
18900 setActiveBullet : function(i)
18906 Roo.each(this.el.select('.bullet', true).elements, function(el){
18907 el.removeClass('selected');
18910 var bullet = this.el.select('.bullet-' + i, true).first();
18916 bullet.addClass('selected');
18927 Roo.apply(Roo.bootstrap.TabGroup, {
18931 * register a Navigation Group
18932 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18934 register : function(navgrp)
18936 this.groups[navgrp.navId] = navgrp;
18940 * fetch a Navigation Group based on the navigation ID
18941 * if one does not exist , it will get created.
18942 * @param {string} the navgroup to add
18943 * @returns {Roo.bootstrap.NavGroup} the navgroup
18945 get: function(navId) {
18946 if (typeof(this.groups[navId]) == 'undefined') {
18947 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18949 return this.groups[navId] ;
18964 * @class Roo.bootstrap.TabPanel
18965 * @extends Roo.bootstrap.Component
18966 * Bootstrap TabPanel class
18967 * @cfg {Boolean} active panel active
18968 * @cfg {String} html panel content
18969 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18970 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18971 * @cfg {String} href click to link..
18975 * Create a new TabPanel
18976 * @param {Object} config The config object
18979 Roo.bootstrap.TabPanel = function(config){
18980 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18984 * Fires when the active status changes
18985 * @param {Roo.bootstrap.TabPanel} this
18986 * @param {Boolean} state the new state
18991 * @event beforedeactivate
18992 * Fires before a tab is de-activated - can be used to do validation on a form.
18993 * @param {Roo.bootstrap.TabPanel} this
18994 * @return {Boolean} false if there is an error
18997 'beforedeactivate': true
19000 this.tabId = this.tabId || Roo.id();
19004 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
19012 getAutoCreate : function(){
19017 // item is needed for carousel - not sure if it has any effect otherwise
19018 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
19019 html: this.html || ''
19023 cfg.cls += ' active';
19027 cfg.tabId = this.tabId;
19035 initEvents: function()
19037 var p = this.parent();
19039 this.navId = this.navId || p.navId;
19041 if (typeof(this.navId) != 'undefined') {
19042 // not really needed.. but just in case.. parent should be a NavGroup.
19043 var tg = Roo.bootstrap.TabGroup.get(this.navId);
19047 var i = tg.tabs.length - 1;
19049 if(this.active && tg.bullets > 0 && i < tg.bullets){
19050 tg.setActiveBullet(i);
19054 this.el.on('click', this.onClick, this);
19057 this.el.on("touchstart", this.onTouchStart, this);
19058 this.el.on("touchmove", this.onTouchMove, this);
19059 this.el.on("touchend", this.onTouchEnd, this);
19064 onRender : function(ct, position)
19066 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
19069 setActive : function(state)
19071 Roo.log("panel - set active " + this.tabId + "=" + state);
19073 this.active = state;
19075 this.el.removeClass('active');
19077 } else if (!this.el.hasClass('active')) {
19078 this.el.addClass('active');
19081 this.fireEvent('changed', this, state);
19084 onClick : function(e)
19086 e.preventDefault();
19088 if(!this.href.length){
19092 window.location.href = this.href;
19101 onTouchStart : function(e)
19103 this.swiping = false;
19105 this.startX = e.browserEvent.touches[0].clientX;
19106 this.startY = e.browserEvent.touches[0].clientY;
19109 onTouchMove : function(e)
19111 this.swiping = true;
19113 this.endX = e.browserEvent.touches[0].clientX;
19114 this.endY = e.browserEvent.touches[0].clientY;
19117 onTouchEnd : function(e)
19124 var tabGroup = this.parent();
19126 if(this.endX > this.startX){ // swiping right
19127 tabGroup.showPanelPrev();
19131 if(this.startX > this.endX){ // swiping left
19132 tabGroup.showPanelNext();
19151 * @class Roo.bootstrap.DateField
19152 * @extends Roo.bootstrap.Input
19153 * Bootstrap DateField class
19154 * @cfg {Number} weekStart default 0
19155 * @cfg {String} viewMode default empty, (months|years)
19156 * @cfg {String} minViewMode default empty, (months|years)
19157 * @cfg {Number} startDate default -Infinity
19158 * @cfg {Number} endDate default Infinity
19159 * @cfg {Boolean} todayHighlight default false
19160 * @cfg {Boolean} todayBtn default false
19161 * @cfg {Boolean} calendarWeeks default false
19162 * @cfg {Object} daysOfWeekDisabled default empty
19163 * @cfg {Boolean} singleMode default false (true | false)
19165 * @cfg {Boolean} keyboardNavigation default true
19166 * @cfg {String} language default en
19169 * Create a new DateField
19170 * @param {Object} config The config object
19173 Roo.bootstrap.DateField = function(config){
19174 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
19178 * Fires when this field show.
19179 * @param {Roo.bootstrap.DateField} this
19180 * @param {Mixed} date The date value
19185 * Fires when this field hide.
19186 * @param {Roo.bootstrap.DateField} this
19187 * @param {Mixed} date The date value
19192 * Fires when select a date.
19193 * @param {Roo.bootstrap.DateField} this
19194 * @param {Mixed} date The date value
19198 * @event beforeselect
19199 * Fires when before select a date.
19200 * @param {Roo.bootstrap.DateField} this
19201 * @param {Mixed} date The date value
19203 beforeselect : true
19207 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
19210 * @cfg {String} format
19211 * The default date format string which can be overriden for localization support. The format must be
19212 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
19216 * @cfg {String} altFormats
19217 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
19218 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
19220 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
19228 todayHighlight : false,
19234 keyboardNavigation: true,
19236 calendarWeeks: false,
19238 startDate: -Infinity,
19242 daysOfWeekDisabled: [],
19246 singleMode : false,
19248 UTCDate: function()
19250 return new Date(Date.UTC.apply(Date, arguments));
19253 UTCToday: function()
19255 var today = new Date();
19256 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
19259 getDate: function() {
19260 var d = this.getUTCDate();
19261 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
19264 getUTCDate: function() {
19268 setDate: function(d) {
19269 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
19272 setUTCDate: function(d) {
19274 this.setValue(this.formatDate(this.date));
19277 onRender: function(ct, position)
19280 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
19282 this.language = this.language || 'en';
19283 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
19284 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
19286 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
19287 this.format = this.format || 'm/d/y';
19288 this.isInline = false;
19289 this.isInput = true;
19290 this.component = this.el.select('.add-on', true).first() || false;
19291 this.component = (this.component && this.component.length === 0) ? false : this.component;
19292 this.hasInput = this.component && this.inputEl().length;
19294 if (typeof(this.minViewMode === 'string')) {
19295 switch (this.minViewMode) {
19297 this.minViewMode = 1;
19300 this.minViewMode = 2;
19303 this.minViewMode = 0;
19308 if (typeof(this.viewMode === 'string')) {
19309 switch (this.viewMode) {
19322 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19324 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19326 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19328 this.picker().on('mousedown', this.onMousedown, this);
19329 this.picker().on('click', this.onClick, this);
19331 this.picker().addClass('datepicker-dropdown');
19333 this.startViewMode = this.viewMode;
19335 if(this.singleMode){
19336 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19337 v.setVisibilityMode(Roo.Element.DISPLAY);
19341 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19342 v.setStyle('width', '189px');
19346 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19347 if(!this.calendarWeeks){
19352 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19353 v.attr('colspan', function(i, val){
19354 return parseInt(val) + 1;
19359 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19361 this.setStartDate(this.startDate);
19362 this.setEndDate(this.endDate);
19364 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19371 if(this.isInline) {
19376 picker : function()
19378 return this.pickerEl;
19379 // return this.el.select('.datepicker', true).first();
19382 fillDow: function()
19384 var dowCnt = this.weekStart;
19393 if(this.calendarWeeks){
19401 while (dowCnt < this.weekStart + 7) {
19405 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19409 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19412 fillMonths: function()
19415 var months = this.picker().select('>.datepicker-months td', true).first();
19417 months.dom.innerHTML = '';
19423 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19426 months.createChild(month);
19433 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;
19435 if (this.date < this.startDate) {
19436 this.viewDate = new Date(this.startDate);
19437 } else if (this.date > this.endDate) {
19438 this.viewDate = new Date(this.endDate);
19440 this.viewDate = new Date(this.date);
19448 var d = new Date(this.viewDate),
19449 year = d.getUTCFullYear(),
19450 month = d.getUTCMonth(),
19451 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19452 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19453 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19454 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19455 currentDate = this.date && this.date.valueOf(),
19456 today = this.UTCToday();
19458 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19460 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19462 // this.picker.select('>tfoot th.today').
19463 // .text(dates[this.language].today)
19464 // .toggle(this.todayBtn !== false);
19466 this.updateNavArrows();
19469 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19471 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19473 prevMonth.setUTCDate(day);
19475 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19477 var nextMonth = new Date(prevMonth);
19479 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19481 nextMonth = nextMonth.valueOf();
19483 var fillMonths = false;
19485 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19487 while(prevMonth.valueOf() <= nextMonth) {
19490 if (prevMonth.getUTCDay() === this.weekStart) {
19492 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19500 if(this.calendarWeeks){
19501 // ISO 8601: First week contains first thursday.
19502 // ISO also states week starts on Monday, but we can be more abstract here.
19504 // Start of current week: based on weekstart/current date
19505 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19506 // Thursday of this week
19507 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19508 // First Thursday of year, year from thursday
19509 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19510 // Calendar week: ms between thursdays, div ms per day, div 7 days
19511 calWeek = (th - yth) / 864e5 / 7 + 1;
19513 fillMonths.cn.push({
19521 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19523 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19526 if (this.todayHighlight &&
19527 prevMonth.getUTCFullYear() == today.getFullYear() &&
19528 prevMonth.getUTCMonth() == today.getMonth() &&
19529 prevMonth.getUTCDate() == today.getDate()) {
19530 clsName += ' today';
19533 if (currentDate && prevMonth.valueOf() === currentDate) {
19534 clsName += ' active';
19537 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19538 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19539 clsName += ' disabled';
19542 fillMonths.cn.push({
19544 cls: 'day ' + clsName,
19545 html: prevMonth.getDate()
19548 prevMonth.setDate(prevMonth.getDate()+1);
19551 var currentYear = this.date && this.date.getUTCFullYear();
19552 var currentMonth = this.date && this.date.getUTCMonth();
19554 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19556 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19557 v.removeClass('active');
19559 if(currentYear === year && k === currentMonth){
19560 v.addClass('active');
19563 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19564 v.addClass('disabled');
19570 year = parseInt(year/10, 10) * 10;
19572 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19574 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19577 for (var i = -1; i < 11; i++) {
19578 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19580 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19588 showMode: function(dir)
19591 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19594 Roo.each(this.picker().select('>div',true).elements, function(v){
19595 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19598 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19603 if(this.isInline) {
19607 this.picker().removeClass(['bottom', 'top']);
19609 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19611 * place to the top of element!
19615 this.picker().addClass('top');
19616 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19621 this.picker().addClass('bottom');
19623 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19626 parseDate : function(value)
19628 if(!value || value instanceof Date){
19631 var v = Date.parseDate(value, this.format);
19632 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19633 v = Date.parseDate(value, 'Y-m-d');
19635 if(!v && this.altFormats){
19636 if(!this.altFormatsArray){
19637 this.altFormatsArray = this.altFormats.split("|");
19639 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19640 v = Date.parseDate(value, this.altFormatsArray[i]);
19646 formatDate : function(date, fmt)
19648 return (!date || !(date instanceof Date)) ?
19649 date : date.dateFormat(fmt || this.format);
19652 onFocus : function()
19654 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19658 onBlur : function()
19660 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19662 var d = this.inputEl().getValue();
19669 showPopup : function()
19671 this.picker().show();
19675 this.fireEvent('showpopup', this, this.date);
19678 hidePopup : function()
19680 if(this.isInline) {
19683 this.picker().hide();
19684 this.viewMode = this.startViewMode;
19687 this.fireEvent('hidepopup', this, this.date);
19691 onMousedown: function(e)
19693 e.stopPropagation();
19694 e.preventDefault();
19699 Roo.bootstrap.DateField.superclass.keyup.call(this);
19703 setValue: function(v)
19705 if(this.fireEvent('beforeselect', this, v) !== false){
19706 var d = new Date(this.parseDate(v) ).clearTime();
19708 if(isNaN(d.getTime())){
19709 this.date = this.viewDate = '';
19710 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19714 v = this.formatDate(d);
19716 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19718 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19722 this.fireEvent('select', this, this.date);
19726 getValue: function()
19728 return this.formatDate(this.date);
19731 fireKey: function(e)
19733 if (!this.picker().isVisible()){
19734 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19740 var dateChanged = false,
19742 newDate, newViewDate;
19747 e.preventDefault();
19751 if (!this.keyboardNavigation) {
19754 dir = e.keyCode == 37 ? -1 : 1;
19757 newDate = this.moveYear(this.date, dir);
19758 newViewDate = this.moveYear(this.viewDate, dir);
19759 } else if (e.shiftKey){
19760 newDate = this.moveMonth(this.date, dir);
19761 newViewDate = this.moveMonth(this.viewDate, dir);
19763 newDate = new Date(this.date);
19764 newDate.setUTCDate(this.date.getUTCDate() + dir);
19765 newViewDate = new Date(this.viewDate);
19766 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19768 if (this.dateWithinRange(newDate)){
19769 this.date = newDate;
19770 this.viewDate = newViewDate;
19771 this.setValue(this.formatDate(this.date));
19773 e.preventDefault();
19774 dateChanged = true;
19779 if (!this.keyboardNavigation) {
19782 dir = e.keyCode == 38 ? -1 : 1;
19784 newDate = this.moveYear(this.date, dir);
19785 newViewDate = this.moveYear(this.viewDate, dir);
19786 } else if (e.shiftKey){
19787 newDate = this.moveMonth(this.date, dir);
19788 newViewDate = this.moveMonth(this.viewDate, dir);
19790 newDate = new Date(this.date);
19791 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19792 newViewDate = new Date(this.viewDate);
19793 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19795 if (this.dateWithinRange(newDate)){
19796 this.date = newDate;
19797 this.viewDate = newViewDate;
19798 this.setValue(this.formatDate(this.date));
19800 e.preventDefault();
19801 dateChanged = true;
19805 this.setValue(this.formatDate(this.date));
19807 e.preventDefault();
19810 this.setValue(this.formatDate(this.date));
19824 onClick: function(e)
19826 e.stopPropagation();
19827 e.preventDefault();
19829 var target = e.getTarget();
19831 if(target.nodeName.toLowerCase() === 'i'){
19832 target = Roo.get(target).dom.parentNode;
19835 var nodeName = target.nodeName;
19836 var className = target.className;
19837 var html = target.innerHTML;
19838 //Roo.log(nodeName);
19840 switch(nodeName.toLowerCase()) {
19842 switch(className) {
19848 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19849 switch(this.viewMode){
19851 this.viewDate = this.moveMonth(this.viewDate, dir);
19855 this.viewDate = this.moveYear(this.viewDate, dir);
19861 var date = new Date();
19862 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19864 this.setValue(this.formatDate(this.date));
19871 if (className.indexOf('disabled') < 0) {
19872 this.viewDate.setUTCDate(1);
19873 if (className.indexOf('month') > -1) {
19874 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19876 var year = parseInt(html, 10) || 0;
19877 this.viewDate.setUTCFullYear(year);
19881 if(this.singleMode){
19882 this.setValue(this.formatDate(this.viewDate));
19893 //Roo.log(className);
19894 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19895 var day = parseInt(html, 10) || 1;
19896 var year = this.viewDate.getUTCFullYear(),
19897 month = this.viewDate.getUTCMonth();
19899 if (className.indexOf('old') > -1) {
19906 } else if (className.indexOf('new') > -1) {
19914 //Roo.log([year,month,day]);
19915 this.date = this.UTCDate(year, month, day,0,0,0,0);
19916 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19918 //Roo.log(this.formatDate(this.date));
19919 this.setValue(this.formatDate(this.date));
19926 setStartDate: function(startDate)
19928 this.startDate = startDate || -Infinity;
19929 if (this.startDate !== -Infinity) {
19930 this.startDate = this.parseDate(this.startDate);
19933 this.updateNavArrows();
19936 setEndDate: function(endDate)
19938 this.endDate = endDate || Infinity;
19939 if (this.endDate !== Infinity) {
19940 this.endDate = this.parseDate(this.endDate);
19943 this.updateNavArrows();
19946 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19948 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19949 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19950 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19952 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19953 return parseInt(d, 10);
19956 this.updateNavArrows();
19959 updateNavArrows: function()
19961 if(this.singleMode){
19965 var d = new Date(this.viewDate),
19966 year = d.getUTCFullYear(),
19967 month = d.getUTCMonth();
19969 Roo.each(this.picker().select('.prev', true).elements, function(v){
19971 switch (this.viewMode) {
19974 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19980 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19987 Roo.each(this.picker().select('.next', true).elements, function(v){
19989 switch (this.viewMode) {
19992 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19998 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
20006 moveMonth: function(date, dir)
20011 var new_date = new Date(date.valueOf()),
20012 day = new_date.getUTCDate(),
20013 month = new_date.getUTCMonth(),
20014 mag = Math.abs(dir),
20016 dir = dir > 0 ? 1 : -1;
20019 // If going back one month, make sure month is not current month
20020 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
20022 return new_date.getUTCMonth() == month;
20024 // If going forward one month, make sure month is as expected
20025 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
20027 return new_date.getUTCMonth() != new_month;
20029 new_month = month + dir;
20030 new_date.setUTCMonth(new_month);
20031 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
20032 if (new_month < 0 || new_month > 11) {
20033 new_month = (new_month + 12) % 12;
20036 // For magnitudes >1, move one month at a time...
20037 for (var i=0; i<mag; i++) {
20038 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
20039 new_date = this.moveMonth(new_date, dir);
20041 // ...then reset the day, keeping it in the new month
20042 new_month = new_date.getUTCMonth();
20043 new_date.setUTCDate(day);
20045 return new_month != new_date.getUTCMonth();
20048 // Common date-resetting loop -- if date is beyond end of month, make it
20051 new_date.setUTCDate(--day);
20052 new_date.setUTCMonth(new_month);
20057 moveYear: function(date, dir)
20059 return this.moveMonth(date, dir*12);
20062 dateWithinRange: function(date)
20064 return date >= this.startDate && date <= this.endDate;
20070 this.picker().remove();
20073 validateValue : function(value)
20075 if(this.getVisibilityEl().hasClass('hidden')){
20079 if(value.length < 1) {
20080 if(this.allowBlank){
20086 if(value.length < this.minLength){
20089 if(value.length > this.maxLength){
20093 var vt = Roo.form.VTypes;
20094 if(!vt[this.vtype](value, this)){
20098 if(typeof this.validator == "function"){
20099 var msg = this.validator(value);
20105 if(this.regex && !this.regex.test(value)){
20109 if(typeof(this.parseDate(value)) == 'undefined'){
20113 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
20117 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
20127 this.date = this.viewDate = '';
20129 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20134 Roo.apply(Roo.bootstrap.DateField, {
20145 html: '<i class="fa fa-arrow-left"/>'
20155 html: '<i class="fa fa-arrow-right"/>'
20197 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
20198 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
20199 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
20200 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20201 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
20214 navFnc: 'FullYear',
20219 navFnc: 'FullYear',
20224 Roo.apply(Roo.bootstrap.DateField, {
20228 cls: 'datepicker dropdown-menu roo-dynamic',
20232 cls: 'datepicker-days',
20236 cls: 'table-condensed',
20238 Roo.bootstrap.DateField.head,
20242 Roo.bootstrap.DateField.footer
20249 cls: 'datepicker-months',
20253 cls: 'table-condensed',
20255 Roo.bootstrap.DateField.head,
20256 Roo.bootstrap.DateField.content,
20257 Roo.bootstrap.DateField.footer
20264 cls: 'datepicker-years',
20268 cls: 'table-condensed',
20270 Roo.bootstrap.DateField.head,
20271 Roo.bootstrap.DateField.content,
20272 Roo.bootstrap.DateField.footer
20291 * @class Roo.bootstrap.TimeField
20292 * @extends Roo.bootstrap.Input
20293 * Bootstrap DateField class
20297 * Create a new TimeField
20298 * @param {Object} config The config object
20301 Roo.bootstrap.TimeField = function(config){
20302 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20306 * Fires when this field show.
20307 * @param {Roo.bootstrap.DateField} thisthis
20308 * @param {Mixed} date The date value
20313 * Fires when this field hide.
20314 * @param {Roo.bootstrap.DateField} this
20315 * @param {Mixed} date The date value
20320 * Fires when select a date.
20321 * @param {Roo.bootstrap.DateField} this
20322 * @param {Mixed} date The date value
20328 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
20331 * @cfg {String} format
20332 * The default time format string which can be overriden for localization support. The format must be
20333 * valid according to {@link Date#parseDate} (defaults to 'H:i').
20337 onRender: function(ct, position)
20340 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20342 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20344 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20346 this.pop = this.picker().select('>.datepicker-time',true).first();
20347 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20349 this.picker().on('mousedown', this.onMousedown, this);
20350 this.picker().on('click', this.onClick, this);
20352 this.picker().addClass('datepicker-dropdown');
20357 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20358 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20359 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20360 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20361 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20362 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20366 fireKey: function(e){
20367 if (!this.picker().isVisible()){
20368 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20374 e.preventDefault();
20382 this.onTogglePeriod();
20385 this.onIncrementMinutes();
20388 this.onDecrementMinutes();
20397 onClick: function(e) {
20398 e.stopPropagation();
20399 e.preventDefault();
20402 picker : function()
20404 return this.el.select('.datepicker', true).first();
20407 fillTime: function()
20409 var time = this.pop.select('tbody', true).first();
20411 time.dom.innerHTML = '';
20426 cls: 'hours-up glyphicon glyphicon-chevron-up'
20446 cls: 'minutes-up glyphicon glyphicon-chevron-up'
20467 cls: 'timepicker-hour',
20482 cls: 'timepicker-minute',
20497 cls: 'btn btn-primary period',
20519 cls: 'hours-down glyphicon glyphicon-chevron-down'
20539 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20557 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20564 var hours = this.time.getHours();
20565 var minutes = this.time.getMinutes();
20578 hours = hours - 12;
20582 hours = '0' + hours;
20586 minutes = '0' + minutes;
20589 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20590 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20591 this.pop.select('button', true).first().dom.innerHTML = period;
20597 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20599 var cls = ['bottom'];
20601 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20608 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20613 this.picker().addClass(cls.join('-'));
20617 Roo.each(cls, function(c){
20619 _this.picker().setTop(_this.inputEl().getHeight());
20623 _this.picker().setTop(0 - _this.picker().getHeight());
20628 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20632 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20639 onFocus : function()
20641 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20645 onBlur : function()
20647 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20653 this.picker().show();
20658 this.fireEvent('show', this, this.date);
20663 this.picker().hide();
20666 this.fireEvent('hide', this, this.date);
20669 setTime : function()
20672 this.setValue(this.time.format(this.format));
20674 this.fireEvent('select', this, this.date);
20679 onMousedown: function(e){
20680 e.stopPropagation();
20681 e.preventDefault();
20684 onIncrementHours: function()
20686 Roo.log('onIncrementHours');
20687 this.time = this.time.add(Date.HOUR, 1);
20692 onDecrementHours: function()
20694 Roo.log('onDecrementHours');
20695 this.time = this.time.add(Date.HOUR, -1);
20699 onIncrementMinutes: function()
20701 Roo.log('onIncrementMinutes');
20702 this.time = this.time.add(Date.MINUTE, 1);
20706 onDecrementMinutes: function()
20708 Roo.log('onDecrementMinutes');
20709 this.time = this.time.add(Date.MINUTE, -1);
20713 onTogglePeriod: function()
20715 Roo.log('onTogglePeriod');
20716 this.time = this.time.add(Date.HOUR, 12);
20723 Roo.apply(Roo.bootstrap.TimeField, {
20753 cls: 'btn btn-info ok',
20765 Roo.apply(Roo.bootstrap.TimeField, {
20769 cls: 'datepicker dropdown-menu',
20773 cls: 'datepicker-time',
20777 cls: 'table-condensed',
20779 Roo.bootstrap.TimeField.content,
20780 Roo.bootstrap.TimeField.footer
20799 * @class Roo.bootstrap.MonthField
20800 * @extends Roo.bootstrap.Input
20801 * Bootstrap MonthField class
20803 * @cfg {String} language default en
20806 * Create a new MonthField
20807 * @param {Object} config The config object
20810 Roo.bootstrap.MonthField = function(config){
20811 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20816 * Fires when this field show.
20817 * @param {Roo.bootstrap.MonthField} this
20818 * @param {Mixed} date The date value
20823 * Fires when this field hide.
20824 * @param {Roo.bootstrap.MonthField} this
20825 * @param {Mixed} date The date value
20830 * Fires when select a date.
20831 * @param {Roo.bootstrap.MonthField} this
20832 * @param {String} oldvalue The old value
20833 * @param {String} newvalue The new value
20839 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20841 onRender: function(ct, position)
20844 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20846 this.language = this.language || 'en';
20847 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20848 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20850 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20851 this.isInline = false;
20852 this.isInput = true;
20853 this.component = this.el.select('.add-on', true).first() || false;
20854 this.component = (this.component && this.component.length === 0) ? false : this.component;
20855 this.hasInput = this.component && this.inputEL().length;
20857 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20859 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20861 this.picker().on('mousedown', this.onMousedown, this);
20862 this.picker().on('click', this.onClick, this);
20864 this.picker().addClass('datepicker-dropdown');
20866 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20867 v.setStyle('width', '189px');
20874 if(this.isInline) {
20880 setValue: function(v, suppressEvent)
20882 var o = this.getValue();
20884 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20888 if(suppressEvent !== true){
20889 this.fireEvent('select', this, o, v);
20894 getValue: function()
20899 onClick: function(e)
20901 e.stopPropagation();
20902 e.preventDefault();
20904 var target = e.getTarget();
20906 if(target.nodeName.toLowerCase() === 'i'){
20907 target = Roo.get(target).dom.parentNode;
20910 var nodeName = target.nodeName;
20911 var className = target.className;
20912 var html = target.innerHTML;
20914 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20918 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20920 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20926 picker : function()
20928 return this.pickerEl;
20931 fillMonths: function()
20934 var months = this.picker().select('>.datepicker-months td', true).first();
20936 months.dom.innerHTML = '';
20942 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20945 months.createChild(month);
20954 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20955 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20958 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20959 e.removeClass('active');
20961 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20962 e.addClass('active');
20969 if(this.isInline) {
20973 this.picker().removeClass(['bottom', 'top']);
20975 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20977 * place to the top of element!
20981 this.picker().addClass('top');
20982 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20987 this.picker().addClass('bottom');
20989 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20992 onFocus : function()
20994 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20998 onBlur : function()
21000 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
21002 var d = this.inputEl().getValue();
21011 this.picker().show();
21012 this.picker().select('>.datepicker-months', true).first().show();
21016 this.fireEvent('show', this, this.date);
21021 if(this.isInline) {
21024 this.picker().hide();
21025 this.fireEvent('hide', this, this.date);
21029 onMousedown: function(e)
21031 e.stopPropagation();
21032 e.preventDefault();
21037 Roo.bootstrap.MonthField.superclass.keyup.call(this);
21041 fireKey: function(e)
21043 if (!this.picker().isVisible()){
21044 if (e.keyCode == 27) {// allow escape to hide and re-show picker
21055 e.preventDefault();
21059 dir = e.keyCode == 37 ? -1 : 1;
21061 this.vIndex = this.vIndex + dir;
21063 if(this.vIndex < 0){
21067 if(this.vIndex > 11){
21071 if(isNaN(this.vIndex)){
21075 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21081 dir = e.keyCode == 38 ? -1 : 1;
21083 this.vIndex = this.vIndex + dir * 4;
21085 if(this.vIndex < 0){
21089 if(this.vIndex > 11){
21093 if(isNaN(this.vIndex)){
21097 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21102 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21103 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21107 e.preventDefault();
21110 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21111 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21127 this.picker().remove();
21132 Roo.apply(Roo.bootstrap.MonthField, {
21151 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21152 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
21157 Roo.apply(Roo.bootstrap.MonthField, {
21161 cls: 'datepicker dropdown-menu roo-dynamic',
21165 cls: 'datepicker-months',
21169 cls: 'table-condensed',
21171 Roo.bootstrap.DateField.content
21191 * @class Roo.bootstrap.CheckBox
21192 * @extends Roo.bootstrap.Input
21193 * Bootstrap CheckBox class
21195 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
21196 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
21197 * @cfg {String} boxLabel The text that appears beside the checkbox
21198 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
21199 * @cfg {Boolean} checked initnal the element
21200 * @cfg {Boolean} inline inline the element (default false)
21201 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
21202 * @cfg {String} tooltip label tooltip
21205 * Create a new CheckBox
21206 * @param {Object} config The config object
21209 Roo.bootstrap.CheckBox = function(config){
21210 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
21215 * Fires when the element is checked or unchecked.
21216 * @param {Roo.bootstrap.CheckBox} this This input
21217 * @param {Boolean} checked The new checked value
21222 * Fires when the element is click.
21223 * @param {Roo.bootstrap.CheckBox} this This input
21230 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
21232 inputType: 'checkbox',
21241 // checkbox success does not make any sense really..
21246 getAutoCreate : function()
21248 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
21254 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
21257 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
21263 type : this.inputType,
21264 value : this.inputValue,
21265 cls : 'roo-' + this.inputType, //'form-box',
21266 placeholder : this.placeholder || ''
21270 if(this.inputType != 'radio'){
21274 cls : 'roo-hidden-value',
21275 value : this.checked ? this.inputValue : this.valueOff
21280 if (this.weight) { // Validity check?
21281 cfg.cls += " " + this.inputType + "-" + this.weight;
21284 if (this.disabled) {
21285 input.disabled=true;
21289 input.checked = this.checked;
21294 input.name = this.name;
21296 if(this.inputType != 'radio'){
21297 hidden.name = this.name;
21298 input.name = '_hidden_' + this.name;
21303 input.cls += ' input-' + this.size;
21308 ['xs','sm','md','lg'].map(function(size){
21309 if (settings[size]) {
21310 cfg.cls += ' col-' + size + '-' + settings[size];
21314 var inputblock = input;
21316 if (this.before || this.after) {
21319 cls : 'input-group',
21324 inputblock.cn.push({
21326 cls : 'input-group-addon',
21331 inputblock.cn.push(input);
21333 if(this.inputType != 'radio'){
21334 inputblock.cn.push(hidden);
21338 inputblock.cn.push({
21340 cls : 'input-group-addon',
21346 var boxLabelCfg = false;
21352 //'for': id, // box label is handled by onclick - so no for...
21354 html: this.boxLabel
21357 boxLabelCfg.tooltip = this.tooltip;
21363 if (align ==='left' && this.fieldLabel.length) {
21364 // Roo.log("left and has label");
21369 cls : 'control-label',
21370 html : this.fieldLabel
21381 cfg.cn[1].cn.push(boxLabelCfg);
21384 if(this.labelWidth > 12){
21385 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21388 if(this.labelWidth < 13 && this.labelmd == 0){
21389 this.labelmd = this.labelWidth;
21392 if(this.labellg > 0){
21393 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21394 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21397 if(this.labelmd > 0){
21398 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21399 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21402 if(this.labelsm > 0){
21403 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21404 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21407 if(this.labelxs > 0){
21408 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21409 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21412 } else if ( this.fieldLabel.length) {
21413 // Roo.log(" label");
21417 tag: this.boxLabel ? 'span' : 'label',
21419 cls: 'control-label box-input-label',
21420 //cls : 'input-group-addon',
21421 html : this.fieldLabel
21428 cfg.cn.push(boxLabelCfg);
21433 // Roo.log(" no label && no align");
21434 cfg.cn = [ inputblock ] ;
21436 cfg.cn.push(boxLabelCfg);
21444 if(this.inputType != 'radio'){
21445 cfg.cn.push(hidden);
21453 * return the real input element.
21455 inputEl: function ()
21457 return this.el.select('input.roo-' + this.inputType,true).first();
21459 hiddenEl: function ()
21461 return this.el.select('input.roo-hidden-value',true).first();
21464 labelEl: function()
21466 return this.el.select('label.control-label',true).first();
21468 /* depricated... */
21472 return this.labelEl();
21475 boxLabelEl: function()
21477 return this.el.select('label.box-label',true).first();
21480 initEvents : function()
21482 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21484 this.inputEl().on('click', this.onClick, this);
21486 if (this.boxLabel) {
21487 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
21490 this.startValue = this.getValue();
21493 Roo.bootstrap.CheckBox.register(this);
21497 onClick : function(e)
21499 if(this.fireEvent('click', this, e) !== false){
21500 this.setChecked(!this.checked);
21505 setChecked : function(state,suppressEvent)
21507 this.startValue = this.getValue();
21509 if(this.inputType == 'radio'){
21511 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21512 e.dom.checked = false;
21515 this.inputEl().dom.checked = true;
21517 this.inputEl().dom.value = this.inputValue;
21519 if(suppressEvent !== true){
21520 this.fireEvent('check', this, true);
21528 this.checked = state;
21530 this.inputEl().dom.checked = state;
21533 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21535 if(suppressEvent !== true){
21536 this.fireEvent('check', this, state);
21542 getValue : function()
21544 if(this.inputType == 'radio'){
21545 return this.getGroupValue();
21548 return this.hiddenEl().dom.value;
21552 getGroupValue : function()
21554 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21558 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21561 setValue : function(v,suppressEvent)
21563 if(this.inputType == 'radio'){
21564 this.setGroupValue(v, suppressEvent);
21568 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21573 setGroupValue : function(v, suppressEvent)
21575 this.startValue = this.getValue();
21577 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21578 e.dom.checked = false;
21580 if(e.dom.value == v){
21581 e.dom.checked = true;
21585 if(suppressEvent !== true){
21586 this.fireEvent('check', this, true);
21594 validate : function()
21596 if(this.getVisibilityEl().hasClass('hidden')){
21602 (this.inputType == 'radio' && this.validateRadio()) ||
21603 (this.inputType == 'checkbox' && this.validateCheckbox())
21609 this.markInvalid();
21613 validateRadio : function()
21615 if(this.getVisibilityEl().hasClass('hidden')){
21619 if(this.allowBlank){
21625 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21626 if(!e.dom.checked){
21638 validateCheckbox : function()
21641 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21642 //return (this.getValue() == this.inputValue) ? true : false;
21645 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21653 for(var i in group){
21654 if(group[i].el.isVisible(true)){
21662 for(var i in group){
21667 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21674 * Mark this field as valid
21676 markValid : function()
21680 this.fireEvent('valid', this);
21682 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21685 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21692 if(this.inputType == 'radio'){
21693 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21694 var fg = e.findParent('.form-group', false, true);
21695 if (Roo.bootstrap.version == 3) {
21696 fg.removeClass([_this.invalidClass, _this.validClass]);
21697 fg.addClass(_this.validClass);
21699 fg.removeClass(['is-valid', 'is-invalid']);
21700 fg.addClass('is-valid');
21708 var fg = this.el.findParent('.form-group', false, true);
21709 if (Roo.bootstrap.version == 3) {
21710 fg.removeClass([this.invalidClass, this.validClass]);
21711 fg.addClass(this.validClass);
21713 fg.removeClass(['is-valid', 'is-invalid']);
21714 fg.addClass('is-valid');
21719 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21725 for(var i in group){
21726 var fg = group[i].el.findParent('.form-group', false, true);
21727 if (Roo.bootstrap.version == 3) {
21728 fg.removeClass([this.invalidClass, this.validClass]);
21729 fg.addClass(this.validClass);
21731 fg.removeClass(['is-valid', 'is-invalid']);
21732 fg.addClass('is-valid');
21738 * Mark this field as invalid
21739 * @param {String} msg The validation message
21741 markInvalid : function(msg)
21743 if(this.allowBlank){
21749 this.fireEvent('invalid', this, msg);
21751 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21754 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21758 label.markInvalid();
21761 if(this.inputType == 'radio'){
21763 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21764 var fg = e.findParent('.form-group', false, true);
21765 if (Roo.bootstrap.version == 3) {
21766 fg.removeClass([_this.invalidClass, _this.validClass]);
21767 fg.addClass(_this.invalidClass);
21769 fg.removeClass(['is-invalid', 'is-valid']);
21770 fg.addClass('is-invalid');
21778 var fg = this.el.findParent('.form-group', false, true);
21779 if (Roo.bootstrap.version == 3) {
21780 fg.removeClass([_this.invalidClass, _this.validClass]);
21781 fg.addClass(_this.invalidClass);
21783 fg.removeClass(['is-invalid', 'is-valid']);
21784 fg.addClass('is-invalid');
21789 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21795 for(var i in group){
21796 var fg = group[i].el.findParent('.form-group', false, true);
21797 if (Roo.bootstrap.version == 3) {
21798 fg.removeClass([_this.invalidClass, _this.validClass]);
21799 fg.addClass(_this.invalidClass);
21801 fg.removeClass(['is-invalid', 'is-valid']);
21802 fg.addClass('is-invalid');
21808 clearInvalid : function()
21810 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21812 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21814 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21816 if (label && label.iconEl) {
21817 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21818 label.iconEl.removeClass(['is-invalid', 'is-valid']);
21822 disable : function()
21824 if(this.inputType != 'radio'){
21825 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21832 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21833 _this.getActionEl().addClass(this.disabledClass);
21834 e.dom.disabled = true;
21838 this.disabled = true;
21839 this.fireEvent("disable", this);
21843 enable : function()
21845 if(this.inputType != 'radio'){
21846 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21853 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21854 _this.getActionEl().removeClass(this.disabledClass);
21855 e.dom.disabled = false;
21859 this.disabled = false;
21860 this.fireEvent("enable", this);
21864 setBoxLabel : function(v)
21869 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21875 Roo.apply(Roo.bootstrap.CheckBox, {
21880 * register a CheckBox Group
21881 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21883 register : function(checkbox)
21885 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21886 this.groups[checkbox.groupId] = {};
21889 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21893 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21897 * fetch a CheckBox Group based on the group ID
21898 * @param {string} the group ID
21899 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21901 get: function(groupId) {
21902 if (typeof(this.groups[groupId]) == 'undefined') {
21906 return this.groups[groupId] ;
21919 * @class Roo.bootstrap.Radio
21920 * @extends Roo.bootstrap.Component
21921 * Bootstrap Radio class
21922 * @cfg {String} boxLabel - the label associated
21923 * @cfg {String} value - the value of radio
21926 * Create a new Radio
21927 * @param {Object} config The config object
21929 Roo.bootstrap.Radio = function(config){
21930 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21934 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21940 getAutoCreate : function()
21944 cls : 'form-group radio',
21949 html : this.boxLabel
21957 initEvents : function()
21959 this.parent().register(this);
21961 this.el.on('click', this.onClick, this);
21965 onClick : function(e)
21967 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21968 this.setChecked(true);
21972 setChecked : function(state, suppressEvent)
21974 this.parent().setValue(this.value, suppressEvent);
21978 setBoxLabel : function(v)
21983 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21998 * @class Roo.bootstrap.SecurePass
21999 * @extends Roo.bootstrap.Input
22000 * Bootstrap SecurePass class
22004 * Create a new SecurePass
22005 * @param {Object} config The config object
22008 Roo.bootstrap.SecurePass = function (config) {
22009 // these go here, so the translation tool can replace them..
22011 PwdEmpty: "Please type a password, and then retype it to confirm.",
22012 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22013 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22014 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22015 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22016 FNInPwd: "Your password can't contain your first name. Please type a different password.",
22017 LNInPwd: "Your password can't contain your last name. Please type a different password.",
22018 TooWeak: "Your password is Too Weak."
22020 this.meterLabel = "Password strength:";
22021 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
22022 this.meterClass = [
22023 "roo-password-meter-tooweak",
22024 "roo-password-meter-weak",
22025 "roo-password-meter-medium",
22026 "roo-password-meter-strong",
22027 "roo-password-meter-grey"
22032 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
22035 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
22037 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
22039 * PwdEmpty: "Please type a password, and then retype it to confirm.",
22040 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22041 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22042 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22043 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22044 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
22045 * LNInPwd: "Your password can't contain your last name. Please type a different password."
22055 * @cfg {String/Object} Label for the strength meter (defaults to
22056 * 'Password strength:')
22061 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
22062 * ['Weak', 'Medium', 'Strong'])
22065 pwdStrengths: false,
22078 initEvents: function ()
22080 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
22082 if (this.el.is('input[type=password]') && Roo.isSafari) {
22083 this.el.on('keydown', this.SafariOnKeyDown, this);
22086 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
22089 onRender: function (ct, position)
22091 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
22092 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
22093 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
22095 this.trigger.createChild({
22100 cls: 'roo-password-meter-grey col-xs-12',
22103 //width: this.meterWidth + 'px'
22107 cls: 'roo-password-meter-text'
22113 if (this.hideTrigger) {
22114 this.trigger.setDisplayed(false);
22116 this.setSize(this.width || '', this.height || '');
22119 onDestroy: function ()
22121 if (this.trigger) {
22122 this.trigger.removeAllListeners();
22123 this.trigger.remove();
22126 this.wrap.remove();
22128 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
22131 checkStrength: function ()
22133 var pwd = this.inputEl().getValue();
22134 if (pwd == this._lastPwd) {
22139 if (this.ClientSideStrongPassword(pwd)) {
22141 } else if (this.ClientSideMediumPassword(pwd)) {
22143 } else if (this.ClientSideWeakPassword(pwd)) {
22149 Roo.log('strength1: ' + strength);
22151 //var pm = this.trigger.child('div/div/div').dom;
22152 var pm = this.trigger.child('div/div');
22153 pm.removeClass(this.meterClass);
22154 pm.addClass(this.meterClass[strength]);
22157 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22159 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
22161 this._lastPwd = pwd;
22165 Roo.bootstrap.SecurePass.superclass.reset.call(this);
22167 this._lastPwd = '';
22169 var pm = this.trigger.child('div/div');
22170 pm.removeClass(this.meterClass);
22171 pm.addClass('roo-password-meter-grey');
22174 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22177 this.inputEl().dom.type='password';
22180 validateValue: function (value)
22183 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
22186 if (value.length == 0) {
22187 if (this.allowBlank) {
22188 this.clearInvalid();
22192 this.markInvalid(this.errors.PwdEmpty);
22193 this.errorMsg = this.errors.PwdEmpty;
22201 if ('[\x21-\x7e]*'.match(value)) {
22202 this.markInvalid(this.errors.PwdBadChar);
22203 this.errorMsg = this.errors.PwdBadChar;
22206 if (value.length < 6) {
22207 this.markInvalid(this.errors.PwdShort);
22208 this.errorMsg = this.errors.PwdShort;
22211 if (value.length > 16) {
22212 this.markInvalid(this.errors.PwdLong);
22213 this.errorMsg = this.errors.PwdLong;
22217 if (this.ClientSideStrongPassword(value)) {
22219 } else if (this.ClientSideMediumPassword(value)) {
22221 } else if (this.ClientSideWeakPassword(value)) {
22228 if (strength < 2) {
22229 //this.markInvalid(this.errors.TooWeak);
22230 this.errorMsg = this.errors.TooWeak;
22235 console.log('strength2: ' + strength);
22237 //var pm = this.trigger.child('div/div/div').dom;
22239 var pm = this.trigger.child('div/div');
22240 pm.removeClass(this.meterClass);
22241 pm.addClass(this.meterClass[strength]);
22243 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22245 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
22247 this.errorMsg = '';
22251 CharacterSetChecks: function (type)
22254 this.fResult = false;
22257 isctype: function (character, type)
22260 case this.kCapitalLetter:
22261 if (character >= 'A' && character <= 'Z') {
22266 case this.kSmallLetter:
22267 if (character >= 'a' && character <= 'z') {
22273 if (character >= '0' && character <= '9') {
22278 case this.kPunctuation:
22279 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
22290 IsLongEnough: function (pwd, size)
22292 return !(pwd == null || isNaN(size) || pwd.length < size);
22295 SpansEnoughCharacterSets: function (word, nb)
22297 if (!this.IsLongEnough(word, nb))
22302 var characterSetChecks = new Array(
22303 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22304 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22307 for (var index = 0; index < word.length; ++index) {
22308 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22309 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22310 characterSetChecks[nCharSet].fResult = true;
22317 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22318 if (characterSetChecks[nCharSet].fResult) {
22323 if (nCharSets < nb) {
22329 ClientSideStrongPassword: function (pwd)
22331 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22334 ClientSideMediumPassword: function (pwd)
22336 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22339 ClientSideWeakPassword: function (pwd)
22341 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22344 })//<script type="text/javascript">
22347 * Based Ext JS Library 1.1.1
22348 * Copyright(c) 2006-2007, Ext JS, LLC.
22354 * @class Roo.HtmlEditorCore
22355 * @extends Roo.Component
22356 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22358 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22361 Roo.HtmlEditorCore = function(config){
22364 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22369 * @event initialize
22370 * Fires when the editor is fully initialized (including the iframe)
22371 * @param {Roo.HtmlEditorCore} this
22376 * Fires when the editor is first receives the focus. Any insertion must wait
22377 * until after this event.
22378 * @param {Roo.HtmlEditorCore} this
22382 * @event beforesync
22383 * Fires before the textarea is updated with content from the editor iframe. Return false
22384 * to cancel the sync.
22385 * @param {Roo.HtmlEditorCore} this
22386 * @param {String} html
22390 * @event beforepush
22391 * Fires before the iframe editor is updated with content from the textarea. Return false
22392 * to cancel the push.
22393 * @param {Roo.HtmlEditorCore} this
22394 * @param {String} html
22399 * Fires when the textarea is updated with content from the editor iframe.
22400 * @param {Roo.HtmlEditorCore} this
22401 * @param {String} html
22406 * Fires when the iframe editor is updated with content from the textarea.
22407 * @param {Roo.HtmlEditorCore} this
22408 * @param {String} html
22413 * @event editorevent
22414 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22415 * @param {Roo.HtmlEditorCore} this
22421 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22423 // defaults : white / black...
22424 this.applyBlacklists();
22431 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
22435 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
22441 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22446 * @cfg {Number} height (in pixels)
22450 * @cfg {Number} width (in pixels)
22455 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22458 stylesheets: false,
22463 // private properties
22464 validationEvent : false,
22466 initialized : false,
22468 sourceEditMode : false,
22469 onFocus : Roo.emptyFn,
22471 hideMode:'offsets',
22475 // blacklist + whitelisted elements..
22482 * Protected method that will not generally be called directly. It
22483 * is called when the editor initializes the iframe with HTML contents. Override this method if you
22484 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22486 getDocMarkup : function(){
22490 // inherit styels from page...??
22491 if (this.stylesheets === false) {
22493 Roo.get(document.head).select('style').each(function(node) {
22494 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22497 Roo.get(document.head).select('link').each(function(node) {
22498 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22501 } else if (!this.stylesheets.length) {
22503 st = '<style type="text/css">' +
22504 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22507 st = '<style type="text/css">' +
22512 st += '<style type="text/css">' +
22513 'IMG { cursor: pointer } ' +
22516 var cls = 'roo-htmleditor-body';
22518 if(this.bodyCls.length){
22519 cls += ' ' + this.bodyCls;
22522 return '<html><head>' + st +
22523 //<style type="text/css">' +
22524 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22526 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
22530 onRender : function(ct, position)
22533 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22534 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22537 this.el.dom.style.border = '0 none';
22538 this.el.dom.setAttribute('tabIndex', -1);
22539 this.el.addClass('x-hidden hide');
22543 if(Roo.isIE){ // fix IE 1px bogus margin
22544 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22548 this.frameId = Roo.id();
22552 var iframe = this.owner.wrap.createChild({
22554 cls: 'form-control', // bootstrap..
22556 name: this.frameId,
22557 frameBorder : 'no',
22558 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
22563 this.iframe = iframe.dom;
22565 this.assignDocWin();
22567 this.doc.designMode = 'on';
22570 this.doc.write(this.getDocMarkup());
22574 var task = { // must defer to wait for browser to be ready
22576 //console.log("run task?" + this.doc.readyState);
22577 this.assignDocWin();
22578 if(this.doc.body || this.doc.readyState == 'complete'){
22580 this.doc.designMode="on";
22584 Roo.TaskMgr.stop(task);
22585 this.initEditor.defer(10, this);
22592 Roo.TaskMgr.start(task);
22597 onResize : function(w, h)
22599 Roo.log('resize: ' +w + ',' + h );
22600 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22604 if(typeof w == 'number'){
22606 this.iframe.style.width = w + 'px';
22608 if(typeof h == 'number'){
22610 this.iframe.style.height = h + 'px';
22612 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22619 * Toggles the editor between standard and source edit mode.
22620 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22622 toggleSourceEdit : function(sourceEditMode){
22624 this.sourceEditMode = sourceEditMode === true;
22626 if(this.sourceEditMode){
22628 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22631 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22632 //this.iframe.className = '';
22635 //this.setSize(this.owner.wrap.getSize());
22636 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22643 * Protected method that will not generally be called directly. If you need/want
22644 * custom HTML cleanup, this is the method you should override.
22645 * @param {String} html The HTML to be cleaned
22646 * return {String} The cleaned HTML
22648 cleanHtml : function(html){
22649 html = String(html);
22650 if(html.length > 5){
22651 if(Roo.isSafari){ // strip safari nonsense
22652 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22655 if(html == ' '){
22662 * HTML Editor -> Textarea
22663 * Protected method that will not generally be called directly. Syncs the contents
22664 * of the editor iframe with the textarea.
22666 syncValue : function(){
22667 if(this.initialized){
22668 var bd = (this.doc.body || this.doc.documentElement);
22669 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22670 var html = bd.innerHTML;
22672 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22673 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22675 html = '<div style="'+m[0]+'">' + html + '</div>';
22678 html = this.cleanHtml(html);
22679 // fix up the special chars.. normaly like back quotes in word...
22680 // however we do not want to do this with chinese..
22681 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
22683 var cc = match.charCodeAt();
22685 // Get the character value, handling surrogate pairs
22686 if (match.length == 2) {
22687 // It's a surrogate pair, calculate the Unicode code point
22688 var high = match.charCodeAt(0) - 0xD800;
22689 var low = match.charCodeAt(1) - 0xDC00;
22690 cc = (high * 0x400) + low + 0x10000;
22692 (cc >= 0x4E00 && cc < 0xA000 ) ||
22693 (cc >= 0x3400 && cc < 0x4E00 ) ||
22694 (cc >= 0xf900 && cc < 0xfb00 )
22699 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
22700 return "&#" + cc + ";";
22707 if(this.owner.fireEvent('beforesync', this, html) !== false){
22708 this.el.dom.value = html;
22709 this.owner.fireEvent('sync', this, html);
22715 * Protected method that will not generally be called directly. Pushes the value of the textarea
22716 * into the iframe editor.
22718 pushValue : function(){
22719 if(this.initialized){
22720 var v = this.el.dom.value.trim();
22722 // if(v.length < 1){
22726 if(this.owner.fireEvent('beforepush', this, v) !== false){
22727 var d = (this.doc.body || this.doc.documentElement);
22729 this.cleanUpPaste();
22730 this.el.dom.value = d.innerHTML;
22731 this.owner.fireEvent('push', this, v);
22737 deferFocus : function(){
22738 this.focus.defer(10, this);
22742 focus : function(){
22743 if(this.win && !this.sourceEditMode){
22750 assignDocWin: function()
22752 var iframe = this.iframe;
22755 this.doc = iframe.contentWindow.document;
22756 this.win = iframe.contentWindow;
22758 // if (!Roo.get(this.frameId)) {
22761 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22762 // this.win = Roo.get(this.frameId).dom.contentWindow;
22764 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22768 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22769 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22774 initEditor : function(){
22775 //console.log("INIT EDITOR");
22776 this.assignDocWin();
22780 this.doc.designMode="on";
22782 this.doc.write(this.getDocMarkup());
22785 var dbody = (this.doc.body || this.doc.documentElement);
22786 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22787 // this copies styles from the containing element into thsi one..
22788 // not sure why we need all of this..
22789 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22791 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22792 //ss['background-attachment'] = 'fixed'; // w3c
22793 dbody.bgProperties = 'fixed'; // ie
22794 //Roo.DomHelper.applyStyles(dbody, ss);
22795 Roo.EventManager.on(this.doc, {
22796 //'mousedown': this.onEditorEvent,
22797 'mouseup': this.onEditorEvent,
22798 'dblclick': this.onEditorEvent,
22799 'click': this.onEditorEvent,
22800 'keyup': this.onEditorEvent,
22805 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22807 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22808 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22810 this.initialized = true;
22812 this.owner.fireEvent('initialize', this);
22817 onDestroy : function(){
22823 //for (var i =0; i < this.toolbars.length;i++) {
22824 // // fixme - ask toolbars for heights?
22825 // this.toolbars[i].onDestroy();
22828 //this.wrap.dom.innerHTML = '';
22829 //this.wrap.remove();
22834 onFirstFocus : function(){
22836 this.assignDocWin();
22839 this.activated = true;
22842 if(Roo.isGecko){ // prevent silly gecko errors
22844 var s = this.win.getSelection();
22845 if(!s.focusNode || s.focusNode.nodeType != 3){
22846 var r = s.getRangeAt(0);
22847 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22852 this.execCmd('useCSS', true);
22853 this.execCmd('styleWithCSS', false);
22856 this.owner.fireEvent('activate', this);
22860 adjustFont: function(btn){
22861 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22862 //if(Roo.isSafari){ // safari
22865 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22866 if(Roo.isSafari){ // safari
22867 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22868 v = (v < 10) ? 10 : v;
22869 v = (v > 48) ? 48 : v;
22870 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22875 v = Math.max(1, v+adjust);
22877 this.execCmd('FontSize', v );
22880 onEditorEvent : function(e)
22882 this.owner.fireEvent('editorevent', this, e);
22883 // this.updateToolbar();
22884 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22887 insertTag : function(tg)
22889 // could be a bit smarter... -> wrap the current selected tRoo..
22890 if (tg.toLowerCase() == 'span' ||
22891 tg.toLowerCase() == 'code' ||
22892 tg.toLowerCase() == 'sup' ||
22893 tg.toLowerCase() == 'sub'
22896 range = this.createRange(this.getSelection());
22897 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22898 wrappingNode.appendChild(range.extractContents());
22899 range.insertNode(wrappingNode);
22906 this.execCmd("formatblock", tg);
22910 insertText : function(txt)
22914 var range = this.createRange();
22915 range.deleteContents();
22916 //alert(Sender.getAttribute('label'));
22918 range.insertNode(this.doc.createTextNode(txt));
22924 * Executes a Midas editor command on the editor document and performs necessary focus and
22925 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22926 * @param {String} cmd The Midas command
22927 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22929 relayCmd : function(cmd, value){
22931 this.execCmd(cmd, value);
22932 this.owner.fireEvent('editorevent', this);
22933 //this.updateToolbar();
22934 this.owner.deferFocus();
22938 * Executes a Midas editor command directly on the editor document.
22939 * For visual commands, you should use {@link #relayCmd} instead.
22940 * <b>This should only be called after the editor is initialized.</b>
22941 * @param {String} cmd The Midas command
22942 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22944 execCmd : function(cmd, value){
22945 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22952 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22954 * @param {String} text | dom node..
22956 insertAtCursor : function(text)
22959 if(!this.activated){
22965 var r = this.doc.selection.createRange();
22976 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22980 // from jquery ui (MIT licenced)
22982 var win = this.win;
22984 if (win.getSelection && win.getSelection().getRangeAt) {
22985 range = win.getSelection().getRangeAt(0);
22986 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22987 range.insertNode(node);
22988 } else if (win.document.selection && win.document.selection.createRange) {
22989 // no firefox support
22990 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22991 win.document.selection.createRange().pasteHTML(txt);
22993 // no firefox support
22994 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22995 this.execCmd('InsertHTML', txt);
23004 mozKeyPress : function(e){
23006 var c = e.getCharCode(), cmd;
23009 c = String.fromCharCode(c).toLowerCase();
23023 this.cleanUpPaste.defer(100, this);
23031 e.preventDefault();
23039 fixKeys : function(){ // load time branching for fastest keydown performance
23041 return function(e){
23042 var k = e.getKey(), r;
23045 r = this.doc.selection.createRange();
23048 r.pasteHTML('    ');
23055 r = this.doc.selection.createRange();
23057 var target = r.parentElement();
23058 if(!target || target.tagName.toLowerCase() != 'li'){
23060 r.pasteHTML('<br />');
23066 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23067 this.cleanUpPaste.defer(100, this);
23073 }else if(Roo.isOpera){
23074 return function(e){
23075 var k = e.getKey();
23079 this.execCmd('InsertHTML','    ');
23082 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23083 this.cleanUpPaste.defer(100, this);
23088 }else if(Roo.isSafari){
23089 return function(e){
23090 var k = e.getKey();
23094 this.execCmd('InsertText','\t');
23098 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23099 this.cleanUpPaste.defer(100, this);
23107 getAllAncestors: function()
23109 var p = this.getSelectedNode();
23112 a.push(p); // push blank onto stack..
23113 p = this.getParentElement();
23117 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
23121 a.push(this.doc.body);
23125 lastSelNode : false,
23128 getSelection : function()
23130 this.assignDocWin();
23131 return Roo.isIE ? this.doc.selection : this.win.getSelection();
23134 getSelectedNode: function()
23136 // this may only work on Gecko!!!
23138 // should we cache this!!!!
23143 var range = this.createRange(this.getSelection()).cloneRange();
23146 var parent = range.parentElement();
23148 var testRange = range.duplicate();
23149 testRange.moveToElementText(parent);
23150 if (testRange.inRange(range)) {
23153 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
23156 parent = parent.parentElement;
23161 // is ancestor a text element.
23162 var ac = range.commonAncestorContainer;
23163 if (ac.nodeType == 3) {
23164 ac = ac.parentNode;
23167 var ar = ac.childNodes;
23170 var other_nodes = [];
23171 var has_other_nodes = false;
23172 for (var i=0;i<ar.length;i++) {
23173 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
23176 // fullly contained node.
23178 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
23183 // probably selected..
23184 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
23185 other_nodes.push(ar[i]);
23189 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
23194 has_other_nodes = true;
23196 if (!nodes.length && other_nodes.length) {
23197 nodes= other_nodes;
23199 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
23205 createRange: function(sel)
23207 // this has strange effects when using with
23208 // top toolbar - not sure if it's a great idea.
23209 //this.editor.contentWindow.focus();
23210 if (typeof sel != "undefined") {
23212 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
23214 return this.doc.createRange();
23217 return this.doc.createRange();
23220 getParentElement: function()
23223 this.assignDocWin();
23224 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
23226 var range = this.createRange(sel);
23229 var p = range.commonAncestorContainer;
23230 while (p.nodeType == 3) { // text node
23241 * Range intersection.. the hard stuff...
23245 * [ -- selected range --- ]
23249 * if end is before start or hits it. fail.
23250 * if start is after end or hits it fail.
23252 * if either hits (but other is outside. - then it's not
23258 // @see http://www.thismuchiknow.co.uk/?p=64.
23259 rangeIntersectsNode : function(range, node)
23261 var nodeRange = node.ownerDocument.createRange();
23263 nodeRange.selectNode(node);
23265 nodeRange.selectNodeContents(node);
23268 var rangeStartRange = range.cloneRange();
23269 rangeStartRange.collapse(true);
23271 var rangeEndRange = range.cloneRange();
23272 rangeEndRange.collapse(false);
23274 var nodeStartRange = nodeRange.cloneRange();
23275 nodeStartRange.collapse(true);
23277 var nodeEndRange = nodeRange.cloneRange();
23278 nodeEndRange.collapse(false);
23280 return rangeStartRange.compareBoundaryPoints(
23281 Range.START_TO_START, nodeEndRange) == -1 &&
23282 rangeEndRange.compareBoundaryPoints(
23283 Range.START_TO_START, nodeStartRange) == 1;
23287 rangeCompareNode : function(range, node)
23289 var nodeRange = node.ownerDocument.createRange();
23291 nodeRange.selectNode(node);
23293 nodeRange.selectNodeContents(node);
23297 range.collapse(true);
23299 nodeRange.collapse(true);
23301 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23302 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
23304 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23306 var nodeIsBefore = ss == 1;
23307 var nodeIsAfter = ee == -1;
23309 if (nodeIsBefore && nodeIsAfter) {
23312 if (!nodeIsBefore && nodeIsAfter) {
23313 return 1; //right trailed.
23316 if (nodeIsBefore && !nodeIsAfter) {
23317 return 2; // left trailed.
23323 // private? - in a new class?
23324 cleanUpPaste : function()
23326 // cleans up the whole document..
23327 Roo.log('cleanuppaste');
23329 this.cleanUpChildren(this.doc.body);
23330 var clean = this.cleanWordChars(this.doc.body.innerHTML);
23331 if (clean != this.doc.body.innerHTML) {
23332 this.doc.body.innerHTML = clean;
23337 cleanWordChars : function(input) {// change the chars to hex code
23338 var he = Roo.HtmlEditorCore;
23340 var output = input;
23341 Roo.each(he.swapCodes, function(sw) {
23342 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23344 output = output.replace(swapper, sw[1]);
23351 cleanUpChildren : function (n)
23353 if (!n.childNodes.length) {
23356 for (var i = n.childNodes.length-1; i > -1 ; i--) {
23357 this.cleanUpChild(n.childNodes[i]);
23364 cleanUpChild : function (node)
23367 //console.log(node);
23368 if (node.nodeName == "#text") {
23369 // clean up silly Windows -- stuff?
23372 if (node.nodeName == "#comment") {
23373 node.parentNode.removeChild(node);
23374 // clean up silly Windows -- stuff?
23377 var lcname = node.tagName.toLowerCase();
23378 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23379 // whitelist of tags..
23381 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23383 node.parentNode.removeChild(node);
23388 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23390 // spans with no attributes - just remove them..
23391 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
23392 remove_keep_children = true;
23395 // remove <a name=....> as rendering on yahoo mailer is borked with this.
23396 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23398 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23399 // remove_keep_children = true;
23402 if (remove_keep_children) {
23403 this.cleanUpChildren(node);
23404 // inserts everything just before this node...
23405 while (node.childNodes.length) {
23406 var cn = node.childNodes[0];
23407 node.removeChild(cn);
23408 node.parentNode.insertBefore(cn, node);
23410 node.parentNode.removeChild(node);
23414 if (!node.attributes || !node.attributes.length) {
23419 this.cleanUpChildren(node);
23423 function cleanAttr(n,v)
23426 if (v.match(/^\./) || v.match(/^\//)) {
23429 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23432 if (v.match(/^#/)) {
23435 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23436 node.removeAttribute(n);
23440 var cwhite = this.cwhite;
23441 var cblack = this.cblack;
23443 function cleanStyle(n,v)
23445 if (v.match(/expression/)) { //XSS?? should we even bother..
23446 node.removeAttribute(n);
23450 var parts = v.split(/;/);
23453 Roo.each(parts, function(p) {
23454 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23458 var l = p.split(':').shift().replace(/\s+/g,'');
23459 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23461 if ( cwhite.length && cblack.indexOf(l) > -1) {
23462 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23463 //node.removeAttribute(n);
23467 // only allow 'c whitelisted system attributes'
23468 if ( cwhite.length && cwhite.indexOf(l) < 0) {
23469 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23470 //node.removeAttribute(n);
23480 if (clean.length) {
23481 node.setAttribute(n, clean.join(';'));
23483 node.removeAttribute(n);
23489 for (var i = node.attributes.length-1; i > -1 ; i--) {
23490 var a = node.attributes[i];
23493 if (a.name.toLowerCase().substr(0,2)=='on') {
23494 node.removeAttribute(a.name);
23497 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23498 node.removeAttribute(a.name);
23501 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23502 cleanAttr(a.name,a.value); // fixme..
23505 if (a.name == 'style') {
23506 cleanStyle(a.name,a.value);
23509 /// clean up MS crap..
23510 // tecnically this should be a list of valid class'es..
23513 if (a.name == 'class') {
23514 if (a.value.match(/^Mso/)) {
23515 node.removeAttribute('class');
23518 if (a.value.match(/^body$/)) {
23519 node.removeAttribute('class');
23530 this.cleanUpChildren(node);
23536 * Clean up MS wordisms...
23538 cleanWord : function(node)
23541 this.cleanWord(this.doc.body);
23546 node.nodeName == 'SPAN' &&
23547 !node.hasAttributes() &&
23548 node.childNodes.length == 1 &&
23549 node.firstChild.nodeName == "#text"
23551 var textNode = node.firstChild;
23552 node.removeChild(textNode);
23553 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
23554 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
23556 node.parentNode.insertBefore(textNode, node);
23557 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
23558 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
23560 node.parentNode.removeChild(node);
23563 if (node.nodeName == "#text") {
23564 // clean up silly Windows -- stuff?
23567 if (node.nodeName == "#comment") {
23568 node.parentNode.removeChild(node);
23569 // clean up silly Windows -- stuff?
23573 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23574 node.parentNode.removeChild(node);
23577 //Roo.log(node.tagName);
23578 // remove - but keep children..
23579 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
23580 //Roo.log('-- removed');
23581 while (node.childNodes.length) {
23582 var cn = node.childNodes[0];
23583 node.removeChild(cn);
23584 node.parentNode.insertBefore(cn, node);
23585 // move node to parent - and clean it..
23586 this.cleanWord(cn);
23588 node.parentNode.removeChild(node);
23589 /// no need to iterate chidlren = it's got none..
23590 //this.iterateChildren(node, this.cleanWord);
23594 if (node.className.length) {
23596 var cn = node.className.split(/\W+/);
23598 Roo.each(cn, function(cls) {
23599 if (cls.match(/Mso[a-zA-Z]+/)) {
23604 node.className = cna.length ? cna.join(' ') : '';
23606 node.removeAttribute("class");
23610 if (node.hasAttribute("lang")) {
23611 node.removeAttribute("lang");
23614 if (node.hasAttribute("style")) {
23616 var styles = node.getAttribute("style").split(";");
23618 Roo.each(styles, function(s) {
23619 if (!s.match(/:/)) {
23622 var kv = s.split(":");
23623 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23626 // what ever is left... we allow.
23629 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23630 if (!nstyle.length) {
23631 node.removeAttribute('style');
23634 this.iterateChildren(node, this.cleanWord);
23640 * iterateChildren of a Node, calling fn each time, using this as the scole..
23641 * @param {DomNode} node node to iterate children of.
23642 * @param {Function} fn method of this class to call on each item.
23644 iterateChildren : function(node, fn)
23646 if (!node.childNodes.length) {
23649 for (var i = node.childNodes.length-1; i > -1 ; i--) {
23650 fn.call(this, node.childNodes[i])
23656 * cleanTableWidths.
23658 * Quite often pasting from word etc.. results in tables with column and widths.
23659 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23662 cleanTableWidths : function(node)
23667 this.cleanTableWidths(this.doc.body);
23672 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23675 Roo.log(node.tagName);
23676 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23677 this.iterateChildren(node, this.cleanTableWidths);
23680 if (node.hasAttribute('width')) {
23681 node.removeAttribute('width');
23685 if (node.hasAttribute("style")) {
23688 var styles = node.getAttribute("style").split(";");
23690 Roo.each(styles, function(s) {
23691 if (!s.match(/:/)) {
23694 var kv = s.split(":");
23695 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23698 // what ever is left... we allow.
23701 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23702 if (!nstyle.length) {
23703 node.removeAttribute('style');
23707 this.iterateChildren(node, this.cleanTableWidths);
23715 domToHTML : function(currentElement, depth, nopadtext) {
23717 depth = depth || 0;
23718 nopadtext = nopadtext || false;
23720 if (!currentElement) {
23721 return this.domToHTML(this.doc.body);
23724 //Roo.log(currentElement);
23726 var allText = false;
23727 var nodeName = currentElement.nodeName;
23728 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23730 if (nodeName == '#text') {
23732 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23737 if (nodeName != 'BODY') {
23740 // Prints the node tagName, such as <A>, <IMG>, etc
23743 for(i = 0; i < currentElement.attributes.length;i++) {
23745 var aname = currentElement.attributes.item(i).name;
23746 if (!currentElement.attributes.item(i).value.length) {
23749 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23752 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23761 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23764 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23769 // Traverse the tree
23771 var currentElementChild = currentElement.childNodes.item(i);
23772 var allText = true;
23773 var innerHTML = '';
23775 while (currentElementChild) {
23776 // Formatting code (indent the tree so it looks nice on the screen)
23777 var nopad = nopadtext;
23778 if (lastnode == 'SPAN') {
23782 if (currentElementChild.nodeName == '#text') {
23783 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23784 toadd = nopadtext ? toadd : toadd.trim();
23785 if (!nopad && toadd.length > 80) {
23786 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23788 innerHTML += toadd;
23791 currentElementChild = currentElement.childNodes.item(i);
23797 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23799 // Recursively traverse the tree structure of the child node
23800 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23801 lastnode = currentElementChild.nodeName;
23803 currentElementChild=currentElement.childNodes.item(i);
23809 // The remaining code is mostly for formatting the tree
23810 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23815 ret+= "</"+tagName+">";
23821 applyBlacklists : function()
23823 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23824 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23828 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23829 if (b.indexOf(tag) > -1) {
23832 this.white.push(tag);
23836 Roo.each(w, function(tag) {
23837 if (b.indexOf(tag) > -1) {
23840 if (this.white.indexOf(tag) > -1) {
23843 this.white.push(tag);
23848 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23849 if (w.indexOf(tag) > -1) {
23852 this.black.push(tag);
23856 Roo.each(b, function(tag) {
23857 if (w.indexOf(tag) > -1) {
23860 if (this.black.indexOf(tag) > -1) {
23863 this.black.push(tag);
23868 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23869 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23873 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23874 if (b.indexOf(tag) > -1) {
23877 this.cwhite.push(tag);
23881 Roo.each(w, function(tag) {
23882 if (b.indexOf(tag) > -1) {
23885 if (this.cwhite.indexOf(tag) > -1) {
23888 this.cwhite.push(tag);
23893 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23894 if (w.indexOf(tag) > -1) {
23897 this.cblack.push(tag);
23901 Roo.each(b, function(tag) {
23902 if (w.indexOf(tag) > -1) {
23905 if (this.cblack.indexOf(tag) > -1) {
23908 this.cblack.push(tag);
23913 setStylesheets : function(stylesheets)
23915 if(typeof(stylesheets) == 'string'){
23916 Roo.get(this.iframe.contentDocument.head).createChild({
23918 rel : 'stylesheet',
23927 Roo.each(stylesheets, function(s) {
23932 Roo.get(_this.iframe.contentDocument.head).createChild({
23934 rel : 'stylesheet',
23943 removeStylesheets : function()
23947 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23952 setStyle : function(style)
23954 Roo.get(this.iframe.contentDocument.head).createChild({
23963 // hide stuff that is not compatible
23977 * @event specialkey
23981 * @cfg {String} fieldClass @hide
23984 * @cfg {String} focusClass @hide
23987 * @cfg {String} autoCreate @hide
23990 * @cfg {String} inputType @hide
23993 * @cfg {String} invalidClass @hide
23996 * @cfg {String} invalidText @hide
23999 * @cfg {String} msgFx @hide
24002 * @cfg {String} validateOnBlur @hide
24006 Roo.HtmlEditorCore.white = [
24007 'area', 'br', 'img', 'input', 'hr', 'wbr',
24009 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
24010 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
24011 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
24012 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
24013 'table', 'ul', 'xmp',
24015 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
24018 'dir', 'menu', 'ol', 'ul', 'dl',
24024 Roo.HtmlEditorCore.black = [
24025 // 'embed', 'object', // enable - backend responsiblity to clean thiese
24027 'base', 'basefont', 'bgsound', 'blink', 'body',
24028 'frame', 'frameset', 'head', 'html', 'ilayer',
24029 'iframe', 'layer', 'link', 'meta', 'object',
24030 'script', 'style' ,'title', 'xml' // clean later..
24032 Roo.HtmlEditorCore.clean = [
24033 'script', 'style', 'title', 'xml'
24035 Roo.HtmlEditorCore.remove = [
24040 Roo.HtmlEditorCore.ablack = [
24044 Roo.HtmlEditorCore.aclean = [
24045 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24049 Roo.HtmlEditorCore.pwhite= [
24050 'http', 'https', 'mailto'
24053 // white listed style attributes.
24054 Roo.HtmlEditorCore.cwhite= [
24055 // 'text-align', /// default is to allow most things..
24061 // black listed style attributes.
24062 Roo.HtmlEditorCore.cblack= [
24063 // 'font-size' -- this can be set by the project
24067 Roo.HtmlEditorCore.swapCodes =[
24086 * @class Roo.bootstrap.HtmlEditor
24087 * @extends Roo.bootstrap.TextArea
24088 * Bootstrap HtmlEditor class
24091 * Create a new HtmlEditor
24092 * @param {Object} config The config object
24095 Roo.bootstrap.HtmlEditor = function(config){
24096 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
24097 if (!this.toolbars) {
24098 this.toolbars = [];
24101 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
24104 * @event initialize
24105 * Fires when the editor is fully initialized (including the iframe)
24106 * @param {HtmlEditor} this
24111 * Fires when the editor is first receives the focus. Any insertion must wait
24112 * until after this event.
24113 * @param {HtmlEditor} this
24117 * @event beforesync
24118 * Fires before the textarea is updated with content from the editor iframe. Return false
24119 * to cancel the sync.
24120 * @param {HtmlEditor} this
24121 * @param {String} html
24125 * @event beforepush
24126 * Fires before the iframe editor is updated with content from the textarea. Return false
24127 * to cancel the push.
24128 * @param {HtmlEditor} this
24129 * @param {String} html
24134 * Fires when the textarea is updated with content from the editor iframe.
24135 * @param {HtmlEditor} this
24136 * @param {String} html
24141 * Fires when the iframe editor is updated with content from the textarea.
24142 * @param {HtmlEditor} this
24143 * @param {String} html
24147 * @event editmodechange
24148 * Fires when the editor switches edit modes
24149 * @param {HtmlEditor} this
24150 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24152 editmodechange: true,
24154 * @event editorevent
24155 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24156 * @param {HtmlEditor} this
24160 * @event firstfocus
24161 * Fires when on first focus - needed by toolbars..
24162 * @param {HtmlEditor} this
24167 * Auto save the htmlEditor value as a file into Events
24168 * @param {HtmlEditor} this
24172 * @event savedpreview
24173 * preview the saved version of htmlEditor
24174 * @param {HtmlEditor} this
24181 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
24185 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24190 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
24195 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24200 * @cfg {Number} height (in pixels)
24204 * @cfg {Number} width (in pixels)
24209 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24212 stylesheets: false,
24217 // private properties
24218 validationEvent : false,
24220 initialized : false,
24223 onFocus : Roo.emptyFn,
24225 hideMode:'offsets',
24227 tbContainer : false,
24231 toolbarContainer :function() {
24232 return this.wrap.select('.x-html-editor-tb',true).first();
24236 * Protected method that will not generally be called directly. It
24237 * is called when the editor creates its toolbar. Override this method if you need to
24238 * add custom toolbar buttons.
24239 * @param {HtmlEditor} editor
24241 createToolbar : function(){
24242 Roo.log('renewing');
24243 Roo.log("create toolbars");
24245 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
24246 this.toolbars[0].render(this.toolbarContainer());
24250 // if (!editor.toolbars || !editor.toolbars.length) {
24251 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
24254 // for (var i =0 ; i < editor.toolbars.length;i++) {
24255 // editor.toolbars[i] = Roo.factory(
24256 // typeof(editor.toolbars[i]) == 'string' ?
24257 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
24258 // Roo.bootstrap.HtmlEditor);
24259 // editor.toolbars[i].init(editor);
24265 onRender : function(ct, position)
24267 // Roo.log("Call onRender: " + this.xtype);
24269 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
24271 this.wrap = this.inputEl().wrap({
24272 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24275 this.editorcore.onRender(ct, position);
24277 if (this.resizable) {
24278 this.resizeEl = new Roo.Resizable(this.wrap, {
24282 minHeight : this.height,
24283 height: this.height,
24284 handles : this.resizable,
24287 resize : function(r, w, h) {
24288 _t.onResize(w,h); // -something
24294 this.createToolbar(this);
24297 if(!this.width && this.resizable){
24298 this.setSize(this.wrap.getSize());
24300 if (this.resizeEl) {
24301 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24302 // should trigger onReize..
24308 onResize : function(w, h)
24310 Roo.log('resize: ' +w + ',' + h );
24311 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
24315 if(this.inputEl() ){
24316 if(typeof w == 'number'){
24317 var aw = w - this.wrap.getFrameWidth('lr');
24318 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24321 if(typeof h == 'number'){
24322 var tbh = -11; // fixme it needs to tool bar size!
24323 for (var i =0; i < this.toolbars.length;i++) {
24324 // fixme - ask toolbars for heights?
24325 tbh += this.toolbars[i].el.getHeight();
24326 //if (this.toolbars[i].footer) {
24327 // tbh += this.toolbars[i].footer.el.getHeight();
24335 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24336 ah -= 5; // knock a few pixes off for look..
24337 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24341 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24342 this.editorcore.onResize(ew,eh);
24347 * Toggles the editor between standard and source edit mode.
24348 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24350 toggleSourceEdit : function(sourceEditMode)
24352 this.editorcore.toggleSourceEdit(sourceEditMode);
24354 if(this.editorcore.sourceEditMode){
24355 Roo.log('editor - showing textarea');
24358 // Roo.log(this.syncValue());
24360 this.inputEl().removeClass(['hide', 'x-hidden']);
24361 this.inputEl().dom.removeAttribute('tabIndex');
24362 this.inputEl().focus();
24364 Roo.log('editor - hiding textarea');
24366 // Roo.log(this.pushValue());
24369 this.inputEl().addClass(['hide', 'x-hidden']);
24370 this.inputEl().dom.setAttribute('tabIndex', -1);
24371 //this.deferFocus();
24374 if(this.resizable){
24375 this.setSize(this.wrap.getSize());
24378 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24381 // private (for BoxComponent)
24382 adjustSize : Roo.BoxComponent.prototype.adjustSize,
24384 // private (for BoxComponent)
24385 getResizeEl : function(){
24389 // private (for BoxComponent)
24390 getPositionEl : function(){
24395 initEvents : function(){
24396 this.originalValue = this.getValue();
24400 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24403 // markInvalid : Roo.emptyFn,
24405 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24408 // clearInvalid : Roo.emptyFn,
24410 setValue : function(v){
24411 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24412 this.editorcore.pushValue();
24417 deferFocus : function(){
24418 this.focus.defer(10, this);
24422 focus : function(){
24423 this.editorcore.focus();
24429 onDestroy : function(){
24435 for (var i =0; i < this.toolbars.length;i++) {
24436 // fixme - ask toolbars for heights?
24437 this.toolbars[i].onDestroy();
24440 this.wrap.dom.innerHTML = '';
24441 this.wrap.remove();
24446 onFirstFocus : function(){
24447 //Roo.log("onFirstFocus");
24448 this.editorcore.onFirstFocus();
24449 for (var i =0; i < this.toolbars.length;i++) {
24450 this.toolbars[i].onFirstFocus();
24456 syncValue : function()
24458 this.editorcore.syncValue();
24461 pushValue : function()
24463 this.editorcore.pushValue();
24467 // hide stuff that is not compatible
24481 * @event specialkey
24485 * @cfg {String} fieldClass @hide
24488 * @cfg {String} focusClass @hide
24491 * @cfg {String} autoCreate @hide
24494 * @cfg {String} inputType @hide
24498 * @cfg {String} invalidText @hide
24501 * @cfg {String} msgFx @hide
24504 * @cfg {String} validateOnBlur @hide
24513 Roo.namespace('Roo.bootstrap.htmleditor');
24515 * @class Roo.bootstrap.HtmlEditorToolbar1
24521 new Roo.bootstrap.HtmlEditor({
24524 new Roo.bootstrap.HtmlEditorToolbar1({
24525 disable : { fonts: 1 , format: 1, ..., ... , ...],
24531 * @cfg {Object} disable List of elements to disable..
24532 * @cfg {Array} btns List of additional buttons.
24536 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24539 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24542 Roo.apply(this, config);
24544 // default disabled, based on 'good practice'..
24545 this.disable = this.disable || {};
24546 Roo.applyIf(this.disable, {
24549 specialElements : true
24551 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24553 this.editor = config.editor;
24554 this.editorcore = config.editor.editorcore;
24556 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24558 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24559 // dont call parent... till later.
24561 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
24566 editorcore : false,
24571 "h1","h2","h3","h4","h5","h6",
24573 "abbr", "acronym", "address", "cite", "samp", "var",
24577 onRender : function(ct, position)
24579 // Roo.log("Call onRender: " + this.xtype);
24581 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24583 this.el.dom.style.marginBottom = '0';
24585 var editorcore = this.editorcore;
24586 var editor= this.editor;
24589 var btn = function(id,cmd , toggle, handler, html){
24591 var event = toggle ? 'toggle' : 'click';
24596 xns: Roo.bootstrap,
24600 enableToggle:toggle !== false,
24602 pressed : toggle ? false : null,
24605 a.listeners[toggle ? 'toggle' : 'click'] = function() {
24606 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
24612 // var cb_box = function...
24617 xns: Roo.bootstrap,
24622 xns: Roo.bootstrap,
24626 Roo.each(this.formats, function(f) {
24627 style.menu.items.push({
24629 xns: Roo.bootstrap,
24630 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24635 editorcore.insertTag(this.tagname);
24642 children.push(style);
24644 btn('bold',false,true);
24645 btn('italic',false,true);
24646 btn('align-left', 'justifyleft',true);
24647 btn('align-center', 'justifycenter',true);
24648 btn('align-right' , 'justifyright',true);
24649 btn('link', false, false, function(btn) {
24650 //Roo.log("create link?");
24651 var url = prompt(this.createLinkText, this.defaultLinkValue);
24652 if(url && url != 'http:/'+'/'){
24653 this.editorcore.relayCmd('createlink', url);
24656 btn('list','insertunorderedlist',true);
24657 btn('pencil', false,true, function(btn){
24659 this.toggleSourceEdit(btn.pressed);
24662 if (this.editor.btns.length > 0) {
24663 for (var i = 0; i<this.editor.btns.length; i++) {
24664 children.push(this.editor.btns[i]);
24672 xns: Roo.bootstrap,
24677 xns: Roo.bootstrap,
24682 cog.menu.items.push({
24684 xns: Roo.bootstrap,
24685 html : Clean styles,
24690 editorcore.insertTag(this.tagname);
24699 this.xtype = 'NavSimplebar';
24701 for(var i=0;i< children.length;i++) {
24703 this.buttons.add(this.addxtypeChild(children[i]));
24707 editor.on('editorevent', this.updateToolbar, this);
24709 onBtnClick : function(id)
24711 this.editorcore.relayCmd(id);
24712 this.editorcore.focus();
24716 * Protected method that will not generally be called directly. It triggers
24717 * a toolbar update by reading the markup state of the current selection in the editor.
24719 updateToolbar: function(){
24721 if(!this.editorcore.activated){
24722 this.editor.onFirstFocus(); // is this neeed?
24726 var btns = this.buttons;
24727 var doc = this.editorcore.doc;
24728 btns.get('bold').setActive(doc.queryCommandState('bold'));
24729 btns.get('italic').setActive(doc.queryCommandState('italic'));
24730 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24732 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24733 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24734 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24736 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24737 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24740 var ans = this.editorcore.getAllAncestors();
24741 if (this.formatCombo) {
24744 var store = this.formatCombo.store;
24745 this.formatCombo.setValue("");
24746 for (var i =0; i < ans.length;i++) {
24747 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24749 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24757 // hides menus... - so this cant be on a menu...
24758 Roo.bootstrap.MenuMgr.hideAll();
24760 Roo.bootstrap.MenuMgr.hideAll();
24761 //this.editorsyncValue();
24763 onFirstFocus: function() {
24764 this.buttons.each(function(item){
24768 toggleSourceEdit : function(sourceEditMode){
24771 if(sourceEditMode){
24772 Roo.log("disabling buttons");
24773 this.buttons.each( function(item){
24774 if(item.cmd != 'pencil'){
24780 Roo.log("enabling buttons");
24781 if(this.editorcore.initialized){
24782 this.buttons.each( function(item){
24788 Roo.log("calling toggole on editor");
24789 // tell the editor that it's been pressed..
24790 this.editor.toggleSourceEdit(sourceEditMode);
24800 * @class Roo.bootstrap.Table.AbstractSelectionModel
24801 * @extends Roo.util.Observable
24802 * Abstract base class for grid SelectionModels. It provides the interface that should be
24803 * implemented by descendant classes. This class should not be directly instantiated.
24806 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24807 this.locked = false;
24808 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24812 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24813 /** @ignore Called by the grid automatically. Do not call directly. */
24814 init : function(grid){
24820 * Locks the selections.
24823 this.locked = true;
24827 * Unlocks the selections.
24829 unlock : function(){
24830 this.locked = false;
24834 * Returns true if the selections are locked.
24835 * @return {Boolean}
24837 isLocked : function(){
24838 return this.locked;
24842 initEvents : function ()
24848 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24849 * @class Roo.bootstrap.Table.RowSelectionModel
24850 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24851 * It supports multiple selections and keyboard selection/navigation.
24853 * @param {Object} config
24856 Roo.bootstrap.Table.RowSelectionModel = function(config){
24857 Roo.apply(this, config);
24858 this.selections = new Roo.util.MixedCollection(false, function(o){
24863 this.lastActive = false;
24867 * @event selectionchange
24868 * Fires when the selection changes
24869 * @param {SelectionModel} this
24871 "selectionchange" : true,
24873 * @event afterselectionchange
24874 * Fires after the selection changes (eg. by key press or clicking)
24875 * @param {SelectionModel} this
24877 "afterselectionchange" : true,
24879 * @event beforerowselect
24880 * Fires when a row is selected being selected, return false to cancel.
24881 * @param {SelectionModel} this
24882 * @param {Number} rowIndex The selected index
24883 * @param {Boolean} keepExisting False if other selections will be cleared
24885 "beforerowselect" : true,
24888 * Fires when a row is selected.
24889 * @param {SelectionModel} this
24890 * @param {Number} rowIndex The selected index
24891 * @param {Roo.data.Record} r The record
24893 "rowselect" : true,
24895 * @event rowdeselect
24896 * Fires when a row is deselected.
24897 * @param {SelectionModel} this
24898 * @param {Number} rowIndex The selected index
24900 "rowdeselect" : true
24902 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24903 this.locked = false;
24906 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24908 * @cfg {Boolean} singleSelect
24909 * True to allow selection of only one row at a time (defaults to false)
24911 singleSelect : false,
24914 initEvents : function()
24917 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24918 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24919 //}else{ // allow click to work like normal
24920 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24922 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24923 this.grid.on("rowclick", this.handleMouseDown, this);
24925 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24926 "up" : function(e){
24928 this.selectPrevious(e.shiftKey);
24929 }else if(this.last !== false && this.lastActive !== false){
24930 var last = this.last;
24931 this.selectRange(this.last, this.lastActive-1);
24932 this.grid.getView().focusRow(this.lastActive);
24933 if(last !== false){
24937 this.selectFirstRow();
24939 this.fireEvent("afterselectionchange", this);
24941 "down" : function(e){
24943 this.selectNext(e.shiftKey);
24944 }else if(this.last !== false && this.lastActive !== false){
24945 var last = this.last;
24946 this.selectRange(this.last, this.lastActive+1);
24947 this.grid.getView().focusRow(this.lastActive);
24948 if(last !== false){
24952 this.selectFirstRow();
24954 this.fireEvent("afterselectionchange", this);
24958 this.grid.store.on('load', function(){
24959 this.selections.clear();
24962 var view = this.grid.view;
24963 view.on("refresh", this.onRefresh, this);
24964 view.on("rowupdated", this.onRowUpdated, this);
24965 view.on("rowremoved", this.onRemove, this);
24970 onRefresh : function()
24972 var ds = this.grid.store, i, v = this.grid.view;
24973 var s = this.selections;
24974 s.each(function(r){
24975 if((i = ds.indexOfId(r.id)) != -1){
24984 onRemove : function(v, index, r){
24985 this.selections.remove(r);
24989 onRowUpdated : function(v, index, r){
24990 if(this.isSelected(r)){
24991 v.onRowSelect(index);
24997 * @param {Array} records The records to select
24998 * @param {Boolean} keepExisting (optional) True to keep existing selections
25000 selectRecords : function(records, keepExisting)
25003 this.clearSelections();
25005 var ds = this.grid.store;
25006 for(var i = 0, len = records.length; i < len; i++){
25007 this.selectRow(ds.indexOf(records[i]), true);
25012 * Gets the number of selected rows.
25015 getCount : function(){
25016 return this.selections.length;
25020 * Selects the first row in the grid.
25022 selectFirstRow : function(){
25027 * Select the last row.
25028 * @param {Boolean} keepExisting (optional) True to keep existing selections
25030 selectLastRow : function(keepExisting){
25031 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
25032 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
25036 * Selects the row immediately following the last selected row.
25037 * @param {Boolean} keepExisting (optional) True to keep existing selections
25039 selectNext : function(keepExisting)
25041 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
25042 this.selectRow(this.last+1, keepExisting);
25043 this.grid.getView().focusRow(this.last);
25048 * Selects the row that precedes the last selected row.
25049 * @param {Boolean} keepExisting (optional) True to keep existing selections
25051 selectPrevious : function(keepExisting){
25053 this.selectRow(this.last-1, keepExisting);
25054 this.grid.getView().focusRow(this.last);
25059 * Returns the selected records
25060 * @return {Array} Array of selected records
25062 getSelections : function(){
25063 return [].concat(this.selections.items);
25067 * Returns the first selected record.
25070 getSelected : function(){
25071 return this.selections.itemAt(0);
25076 * Clears all selections.
25078 clearSelections : function(fast)
25084 var ds = this.grid.store;
25085 var s = this.selections;
25086 s.each(function(r){
25087 this.deselectRow(ds.indexOfId(r.id));
25091 this.selections.clear();
25098 * Selects all rows.
25100 selectAll : function(){
25104 this.selections.clear();
25105 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
25106 this.selectRow(i, true);
25111 * Returns True if there is a selection.
25112 * @return {Boolean}
25114 hasSelection : function(){
25115 return this.selections.length > 0;
25119 * Returns True if the specified row is selected.
25120 * @param {Number/Record} record The record or index of the record to check
25121 * @return {Boolean}
25123 isSelected : function(index){
25124 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
25125 return (r && this.selections.key(r.id) ? true : false);
25129 * Returns True if the specified record id is selected.
25130 * @param {String} id The id of record to check
25131 * @return {Boolean}
25133 isIdSelected : function(id){
25134 return (this.selections.key(id) ? true : false);
25139 handleMouseDBClick : function(e, t){
25143 handleMouseDown : function(e, t)
25145 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
25146 if(this.isLocked() || rowIndex < 0 ){
25149 if(e.shiftKey && this.last !== false){
25150 var last = this.last;
25151 this.selectRange(last, rowIndex, e.ctrlKey);
25152 this.last = last; // reset the last
25156 var isSelected = this.isSelected(rowIndex);
25157 //Roo.log("select row:" + rowIndex);
25159 this.deselectRow(rowIndex);
25161 this.selectRow(rowIndex, true);
25165 if(e.button !== 0 && isSelected){
25166 alert('rowIndex 2: ' + rowIndex);
25167 view.focusRow(rowIndex);
25168 }else if(e.ctrlKey && isSelected){
25169 this.deselectRow(rowIndex);
25170 }else if(!isSelected){
25171 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
25172 view.focusRow(rowIndex);
25176 this.fireEvent("afterselectionchange", this);
25179 handleDragableRowClick : function(grid, rowIndex, e)
25181 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
25182 this.selectRow(rowIndex, false);
25183 grid.view.focusRow(rowIndex);
25184 this.fireEvent("afterselectionchange", this);
25189 * Selects multiple rows.
25190 * @param {Array} rows Array of the indexes of the row to select
25191 * @param {Boolean} keepExisting (optional) True to keep existing selections
25193 selectRows : function(rows, keepExisting){
25195 this.clearSelections();
25197 for(var i = 0, len = rows.length; i < len; i++){
25198 this.selectRow(rows[i], true);
25203 * Selects a range of rows. All rows in between startRow and endRow are also selected.
25204 * @param {Number} startRow The index of the first row in the range
25205 * @param {Number} endRow The index of the last row in the range
25206 * @param {Boolean} keepExisting (optional) True to retain existing selections
25208 selectRange : function(startRow, endRow, keepExisting){
25213 this.clearSelections();
25215 if(startRow <= endRow){
25216 for(var i = startRow; i <= endRow; i++){
25217 this.selectRow(i, true);
25220 for(var i = startRow; i >= endRow; i--){
25221 this.selectRow(i, true);
25227 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
25228 * @param {Number} startRow The index of the first row in the range
25229 * @param {Number} endRow The index of the last row in the range
25231 deselectRange : function(startRow, endRow, preventViewNotify){
25235 for(var i = startRow; i <= endRow; i++){
25236 this.deselectRow(i, preventViewNotify);
25242 * @param {Number} row The index of the row to select
25243 * @param {Boolean} keepExisting (optional) True to keep existing selections
25245 selectRow : function(index, keepExisting, preventViewNotify)
25247 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
25250 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
25251 if(!keepExisting || this.singleSelect){
25252 this.clearSelections();
25255 var r = this.grid.store.getAt(index);
25256 //console.log('selectRow - record id :' + r.id);
25258 this.selections.add(r);
25259 this.last = this.lastActive = index;
25260 if(!preventViewNotify){
25261 var proxy = new Roo.Element(
25262 this.grid.getRowDom(index)
25264 proxy.addClass('bg-info info');
25266 this.fireEvent("rowselect", this, index, r);
25267 this.fireEvent("selectionchange", this);
25273 * @param {Number} row The index of the row to deselect
25275 deselectRow : function(index, preventViewNotify)
25280 if(this.last == index){
25283 if(this.lastActive == index){
25284 this.lastActive = false;
25287 var r = this.grid.store.getAt(index);
25292 this.selections.remove(r);
25293 //.console.log('deselectRow - record id :' + r.id);
25294 if(!preventViewNotify){
25296 var proxy = new Roo.Element(
25297 this.grid.getRowDom(index)
25299 proxy.removeClass('bg-info info');
25301 this.fireEvent("rowdeselect", this, index);
25302 this.fireEvent("selectionchange", this);
25306 restoreLast : function(){
25308 this.last = this._last;
25313 acceptsNav : function(row, col, cm){
25314 return !cm.isHidden(col) && cm.isCellEditable(col, row);
25318 onEditorKey : function(field, e){
25319 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25324 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25326 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25328 }else if(k == e.ENTER && !e.ctrlKey){
25332 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25334 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25336 }else if(k == e.ESC){
25340 g.startEditing(newCell[0], newCell[1]);
25346 * Ext JS Library 1.1.1
25347 * Copyright(c) 2006-2007, Ext JS, LLC.
25349 * Originally Released Under LGPL - original licence link has changed is not relivant.
25352 * <script type="text/javascript">
25356 * @class Roo.bootstrap.PagingToolbar
25357 * @extends Roo.bootstrap.NavSimplebar
25358 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25360 * Create a new PagingToolbar
25361 * @param {Object} config The config object
25362 * @param {Roo.data.Store} store
25364 Roo.bootstrap.PagingToolbar = function(config)
25366 // old args format still supported... - xtype is prefered..
25367 // created from xtype...
25369 this.ds = config.dataSource;
25371 if (config.store && !this.ds) {
25372 this.store= Roo.factory(config.store, Roo.data);
25373 this.ds = this.store;
25374 this.ds.xmodule = this.xmodule || false;
25377 this.toolbarItems = [];
25378 if (config.items) {
25379 this.toolbarItems = config.items;
25382 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25387 this.bind(this.ds);
25390 if (Roo.bootstrap.version == 4) {
25391 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25393 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25398 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25400 * @cfg {Roo.data.Store} dataSource
25401 * The underlying data store providing the paged data
25404 * @cfg {String/HTMLElement/Element} container
25405 * container The id or element that will contain the toolbar
25408 * @cfg {Boolean} displayInfo
25409 * True to display the displayMsg (defaults to false)
25412 * @cfg {Number} pageSize
25413 * The number of records to display per page (defaults to 20)
25417 * @cfg {String} displayMsg
25418 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25420 displayMsg : 'Displaying {0} - {1} of {2}',
25422 * @cfg {String} emptyMsg
25423 * The message to display when no records are found (defaults to "No data to display")
25425 emptyMsg : 'No data to display',
25427 * Customizable piece of the default paging text (defaults to "Page")
25430 beforePageText : "Page",
25432 * Customizable piece of the default paging text (defaults to "of %0")
25435 afterPageText : "of {0}",
25437 * Customizable piece of the default paging text (defaults to "First Page")
25440 firstText : "First Page",
25442 * Customizable piece of the default paging text (defaults to "Previous Page")
25445 prevText : "Previous Page",
25447 * Customizable piece of the default paging text (defaults to "Next Page")
25450 nextText : "Next Page",
25452 * Customizable piece of the default paging text (defaults to "Last Page")
25455 lastText : "Last Page",
25457 * Customizable piece of the default paging text (defaults to "Refresh")
25460 refreshText : "Refresh",
25464 onRender : function(ct, position)
25466 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25467 this.navgroup.parentId = this.id;
25468 this.navgroup.onRender(this.el, null);
25469 // add the buttons to the navgroup
25471 if(this.displayInfo){
25472 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25473 this.displayEl = this.el.select('.x-paging-info', true).first();
25474 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25475 // this.displayEl = navel.el.select('span',true).first();
25481 Roo.each(_this.buttons, function(e){ // this might need to use render????
25482 Roo.factory(e).render(_this.el);
25486 Roo.each(_this.toolbarItems, function(e) {
25487 _this.navgroup.addItem(e);
25491 this.first = this.navgroup.addItem({
25492 tooltip: this.firstText,
25493 cls: "prev btn-outline-secondary",
25494 html : ' <i class="fa fa-step-backward"></i>',
25496 preventDefault: true,
25497 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25500 this.prev = this.navgroup.addItem({
25501 tooltip: this.prevText,
25502 cls: "prev btn-outline-secondary",
25503 html : ' <i class="fa fa-backward"></i>',
25505 preventDefault: true,
25506 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
25508 //this.addSeparator();
25511 var field = this.navgroup.addItem( {
25513 cls : 'x-paging-position btn-outline-secondary',
25515 html : this.beforePageText +
25516 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25517 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
25520 this.field = field.el.select('input', true).first();
25521 this.field.on("keydown", this.onPagingKeydown, this);
25522 this.field.on("focus", function(){this.dom.select();});
25525 this.afterTextEl = field.el.select('.x-paging-after',true).first();
25526 //this.field.setHeight(18);
25527 //this.addSeparator();
25528 this.next = this.navgroup.addItem({
25529 tooltip: this.nextText,
25530 cls: "next btn-outline-secondary",
25531 html : ' <i class="fa fa-forward"></i>',
25533 preventDefault: true,
25534 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
25536 this.last = this.navgroup.addItem({
25537 tooltip: this.lastText,
25538 html : ' <i class="fa fa-step-forward"></i>',
25539 cls: "next btn-outline-secondary",
25541 preventDefault: true,
25542 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
25544 //this.addSeparator();
25545 this.loading = this.navgroup.addItem({
25546 tooltip: this.refreshText,
25547 cls: "btn-outline-secondary",
25548 html : ' <i class="fa fa-refresh"></i>',
25549 preventDefault: true,
25550 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25556 updateInfo : function(){
25557 if(this.displayEl){
25558 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25559 var msg = count == 0 ?
25563 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
25565 this.displayEl.update(msg);
25570 onLoad : function(ds, r, o)
25572 this.cursor = o.params.start ? o.params.start : 0;
25574 var d = this.getPageData(),
25579 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25580 this.field.dom.value = ap;
25581 this.first.setDisabled(ap == 1);
25582 this.prev.setDisabled(ap == 1);
25583 this.next.setDisabled(ap == ps);
25584 this.last.setDisabled(ap == ps);
25585 this.loading.enable();
25590 getPageData : function(){
25591 var total = this.ds.getTotalCount();
25594 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25595 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25600 onLoadError : function(){
25601 this.loading.enable();
25605 onPagingKeydown : function(e){
25606 var k = e.getKey();
25607 var d = this.getPageData();
25609 var v = this.field.dom.value, pageNum;
25610 if(!v || isNaN(pageNum = parseInt(v, 10))){
25611 this.field.dom.value = d.activePage;
25614 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25615 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25618 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))
25620 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25621 this.field.dom.value = pageNum;
25622 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25625 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25627 var v = this.field.dom.value, pageNum;
25628 var increment = (e.shiftKey) ? 10 : 1;
25629 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25632 if(!v || isNaN(pageNum = parseInt(v, 10))) {
25633 this.field.dom.value = d.activePage;
25636 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25638 this.field.dom.value = parseInt(v, 10) + increment;
25639 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25640 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25647 beforeLoad : function(){
25649 this.loading.disable();
25654 onClick : function(which){
25663 ds.load({params:{start: 0, limit: this.pageSize}});
25666 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25669 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25672 var total = ds.getTotalCount();
25673 var extra = total % this.pageSize;
25674 var lastStart = extra ? (total - extra) : total-this.pageSize;
25675 ds.load({params:{start: lastStart, limit: this.pageSize}});
25678 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25684 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25685 * @param {Roo.data.Store} store The data store to unbind
25687 unbind : function(ds){
25688 ds.un("beforeload", this.beforeLoad, this);
25689 ds.un("load", this.onLoad, this);
25690 ds.un("loadexception", this.onLoadError, this);
25691 ds.un("remove", this.updateInfo, this);
25692 ds.un("add", this.updateInfo, this);
25693 this.ds = undefined;
25697 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25698 * @param {Roo.data.Store} store The data store to bind
25700 bind : function(ds){
25701 ds.on("beforeload", this.beforeLoad, this);
25702 ds.on("load", this.onLoad, this);
25703 ds.on("loadexception", this.onLoadError, this);
25704 ds.on("remove", this.updateInfo, this);
25705 ds.on("add", this.updateInfo, this);
25716 * @class Roo.bootstrap.MessageBar
25717 * @extends Roo.bootstrap.Component
25718 * Bootstrap MessageBar class
25719 * @cfg {String} html contents of the MessageBar
25720 * @cfg {String} weight (info | success | warning | danger) default info
25721 * @cfg {String} beforeClass insert the bar before the given class
25722 * @cfg {Boolean} closable (true | false) default false
25723 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25726 * Create a new Element
25727 * @param {Object} config The config object
25730 Roo.bootstrap.MessageBar = function(config){
25731 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25734 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25740 beforeClass: 'bootstrap-sticky-wrap',
25742 getAutoCreate : function(){
25746 cls: 'alert alert-dismissable alert-' + this.weight,
25751 html: this.html || ''
25757 cfg.cls += ' alert-messages-fixed';
25771 onRender : function(ct, position)
25773 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25776 var cfg = Roo.apply({}, this.getAutoCreate());
25780 cfg.cls += ' ' + this.cls;
25783 cfg.style = this.style;
25785 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25787 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25790 this.el.select('>button.close').on('click', this.hide, this);
25796 if (!this.rendered) {
25802 this.fireEvent('show', this);
25808 if (!this.rendered) {
25814 this.fireEvent('hide', this);
25817 update : function()
25819 // var e = this.el.dom.firstChild;
25821 // if(this.closable){
25822 // e = e.nextSibling;
25825 // e.data = this.html || '';
25827 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25843 * @class Roo.bootstrap.Graph
25844 * @extends Roo.bootstrap.Component
25845 * Bootstrap Graph class
25849 @cfg {String} graphtype bar | vbar | pie
25850 @cfg {number} g_x coodinator | centre x (pie)
25851 @cfg {number} g_y coodinator | centre y (pie)
25852 @cfg {number} g_r radius (pie)
25853 @cfg {number} g_height height of the chart (respected by all elements in the set)
25854 @cfg {number} g_width width of the chart (respected by all elements in the set)
25855 @cfg {Object} title The title of the chart
25858 -opts (object) options for the chart
25860 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25861 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25863 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.
25864 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25866 o stretch (boolean)
25868 -opts (object) options for the pie
25871 o startAngle (number)
25872 o endAngle (number)
25876 * Create a new Input
25877 * @param {Object} config The config object
25880 Roo.bootstrap.Graph = function(config){
25881 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25887 * The img click event for the img.
25888 * @param {Roo.EventObject} e
25894 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25905 //g_colors: this.colors,
25912 getAutoCreate : function(){
25923 onRender : function(ct,position){
25926 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25928 if (typeof(Raphael) == 'undefined') {
25929 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25933 this.raphael = Raphael(this.el.dom);
25935 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25936 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25937 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25938 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25940 r.text(160, 10, "Single Series Chart").attr(txtattr);
25941 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25942 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25943 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25945 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25946 r.barchart(330, 10, 300, 220, data1);
25947 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25948 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25951 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25952 // r.barchart(30, 30, 560, 250, xdata, {
25953 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25954 // axis : "0 0 1 1",
25955 // axisxlabels : xdata
25956 // //yvalues : cols,
25959 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25961 // this.load(null,xdata,{
25962 // axis : "0 0 1 1",
25963 // axisxlabels : xdata
25968 load : function(graphtype,xdata,opts)
25970 this.raphael.clear();
25972 graphtype = this.graphtype;
25977 var r = this.raphael,
25978 fin = function () {
25979 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25981 fout = function () {
25982 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25984 pfin = function() {
25985 this.sector.stop();
25986 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25989 this.label[0].stop();
25990 this.label[0].attr({ r: 7.5 });
25991 this.label[1].attr({ "font-weight": 800 });
25994 pfout = function() {
25995 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25998 this.label[0].animate({ r: 5 }, 500, "bounce");
25999 this.label[1].attr({ "font-weight": 400 });
26005 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26008 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26011 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
26012 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
26014 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
26021 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
26026 setTitle: function(o)
26031 initEvents: function() {
26034 this.el.on('click', this.onClick, this);
26038 onClick : function(e)
26040 Roo.log('img onclick');
26041 this.fireEvent('click', this, e);
26053 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26056 * @class Roo.bootstrap.dash.NumberBox
26057 * @extends Roo.bootstrap.Component
26058 * Bootstrap NumberBox class
26059 * @cfg {String} headline Box headline
26060 * @cfg {String} content Box content
26061 * @cfg {String} icon Box icon
26062 * @cfg {String} footer Footer text
26063 * @cfg {String} fhref Footer href
26066 * Create a new NumberBox
26067 * @param {Object} config The config object
26071 Roo.bootstrap.dash.NumberBox = function(config){
26072 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
26076 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
26085 getAutoCreate : function(){
26089 cls : 'small-box ',
26097 cls : 'roo-headline',
26098 html : this.headline
26102 cls : 'roo-content',
26103 html : this.content
26117 cls : 'ion ' + this.icon
26126 cls : 'small-box-footer',
26127 href : this.fhref || '#',
26131 cfg.cn.push(footer);
26138 onRender : function(ct,position){
26139 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
26146 setHeadline: function (value)
26148 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
26151 setFooter: function (value, href)
26153 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
26156 this.el.select('a.small-box-footer',true).first().attr('href', href);
26161 setContent: function (value)
26163 this.el.select('.roo-content',true).first().dom.innerHTML = value;
26166 initEvents: function()
26180 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26183 * @class Roo.bootstrap.dash.TabBox
26184 * @extends Roo.bootstrap.Component
26185 * Bootstrap TabBox class
26186 * @cfg {String} title Title of the TabBox
26187 * @cfg {String} icon Icon of the TabBox
26188 * @cfg {Boolean} showtabs (true|false) show the tabs default true
26189 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
26192 * Create a new TabBox
26193 * @param {Object} config The config object
26197 Roo.bootstrap.dash.TabBox = function(config){
26198 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
26203 * When a pane is added
26204 * @param {Roo.bootstrap.dash.TabPane} pane
26208 * @event activatepane
26209 * When a pane is activated
26210 * @param {Roo.bootstrap.dash.TabPane} pane
26212 "activatepane" : true
26220 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
26225 tabScrollable : false,
26227 getChildContainer : function()
26229 return this.el.select('.tab-content', true).first();
26232 getAutoCreate : function(){
26236 cls: 'pull-left header',
26244 cls: 'fa ' + this.icon
26250 cls: 'nav nav-tabs pull-right',
26256 if(this.tabScrollable){
26263 cls: 'nav nav-tabs pull-right',
26274 cls: 'nav-tabs-custom',
26279 cls: 'tab-content no-padding',
26287 initEvents : function()
26289 //Roo.log('add add pane handler');
26290 this.on('addpane', this.onAddPane, this);
26293 * Updates the box title
26294 * @param {String} html to set the title to.
26296 setTitle : function(value)
26298 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
26300 onAddPane : function(pane)
26302 this.panes.push(pane);
26303 //Roo.log('addpane');
26305 // tabs are rendere left to right..
26306 if(!this.showtabs){
26310 var ctr = this.el.select('.nav-tabs', true).first();
26313 var existing = ctr.select('.nav-tab',true);
26314 var qty = existing.getCount();;
26317 var tab = ctr.createChild({
26319 cls : 'nav-tab' + (qty ? '' : ' active'),
26327 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26330 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26332 pane.el.addClass('active');
26337 onTabClick : function(ev,un,ob,pane)
26339 //Roo.log('tab - prev default');
26340 ev.preventDefault();
26343 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26344 pane.tab.addClass('active');
26345 //Roo.log(pane.title);
26346 this.getChildContainer().select('.tab-pane',true).removeClass('active');
26347 // technically we should have a deactivate event.. but maybe add later.
26348 // and it should not de-activate the selected tab...
26349 this.fireEvent('activatepane', pane);
26350 pane.el.addClass('active');
26351 pane.fireEvent('activate');
26356 getActivePane : function()
26359 Roo.each(this.panes, function(p) {
26360 if(p.el.hasClass('active')){
26381 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26383 * @class Roo.bootstrap.TabPane
26384 * @extends Roo.bootstrap.Component
26385 * Bootstrap TabPane class
26386 * @cfg {Boolean} active (false | true) Default false
26387 * @cfg {String} title title of panel
26391 * Create a new TabPane
26392 * @param {Object} config The config object
26395 Roo.bootstrap.dash.TabPane = function(config){
26396 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26402 * When a pane is activated
26403 * @param {Roo.bootstrap.dash.TabPane} pane
26410 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
26415 // the tabBox that this is attached to.
26418 getAutoCreate : function()
26426 cfg.cls += ' active';
26431 initEvents : function()
26433 //Roo.log('trigger add pane handler');
26434 this.parent().fireEvent('addpane', this)
26438 * Updates the tab title
26439 * @param {String} html to set the title to.
26441 setTitle: function(str)
26447 this.tab.select('a', true).first().dom.innerHTML = str;
26464 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26467 * @class Roo.bootstrap.menu.Menu
26468 * @extends Roo.bootstrap.Component
26469 * Bootstrap Menu class - container for Menu
26470 * @cfg {String} html Text of the menu
26471 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26472 * @cfg {String} icon Font awesome icon
26473 * @cfg {String} pos Menu align to (top | bottom) default bottom
26477 * Create a new Menu
26478 * @param {Object} config The config object
26482 Roo.bootstrap.menu.Menu = function(config){
26483 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26487 * @event beforeshow
26488 * Fires before this menu is displayed
26489 * @param {Roo.bootstrap.menu.Menu} this
26493 * @event beforehide
26494 * Fires before this menu is hidden
26495 * @param {Roo.bootstrap.menu.Menu} this
26500 * Fires after this menu is displayed
26501 * @param {Roo.bootstrap.menu.Menu} this
26506 * Fires after this menu is hidden
26507 * @param {Roo.bootstrap.menu.Menu} this
26512 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26513 * @param {Roo.bootstrap.menu.Menu} this
26514 * @param {Roo.EventObject} e
26521 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
26525 weight : 'default',
26530 getChildContainer : function() {
26531 if(this.isSubMenu){
26535 return this.el.select('ul.dropdown-menu', true).first();
26538 getAutoCreate : function()
26543 cls : 'roo-menu-text',
26551 cls : 'fa ' + this.icon
26562 cls : 'dropdown-button btn btn-' + this.weight,
26567 cls : 'dropdown-toggle btn btn-' + this.weight,
26577 cls : 'dropdown-menu'
26583 if(this.pos == 'top'){
26584 cfg.cls += ' dropup';
26587 if(this.isSubMenu){
26590 cls : 'dropdown-menu'
26597 onRender : function(ct, position)
26599 this.isSubMenu = ct.hasClass('dropdown-submenu');
26601 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26604 initEvents : function()
26606 if(this.isSubMenu){
26610 this.hidden = true;
26612 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26613 this.triggerEl.on('click', this.onTriggerPress, this);
26615 this.buttonEl = this.el.select('button.dropdown-button', true).first();
26616 this.buttonEl.on('click', this.onClick, this);
26622 if(this.isSubMenu){
26626 return this.el.select('ul.dropdown-menu', true).first();
26629 onClick : function(e)
26631 this.fireEvent("click", this, e);
26634 onTriggerPress : function(e)
26636 if (this.isVisible()) {
26643 isVisible : function(){
26644 return !this.hidden;
26649 this.fireEvent("beforeshow", this);
26651 this.hidden = false;
26652 this.el.addClass('open');
26654 Roo.get(document).on("mouseup", this.onMouseUp, this);
26656 this.fireEvent("show", this);
26663 this.fireEvent("beforehide", this);
26665 this.hidden = true;
26666 this.el.removeClass('open');
26668 Roo.get(document).un("mouseup", this.onMouseUp);
26670 this.fireEvent("hide", this);
26673 onMouseUp : function()
26687 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26690 * @class Roo.bootstrap.menu.Item
26691 * @extends Roo.bootstrap.Component
26692 * Bootstrap MenuItem class
26693 * @cfg {Boolean} submenu (true | false) default false
26694 * @cfg {String} html text of the item
26695 * @cfg {String} href the link
26696 * @cfg {Boolean} disable (true | false) default false
26697 * @cfg {Boolean} preventDefault (true | false) default true
26698 * @cfg {String} icon Font awesome icon
26699 * @cfg {String} pos Submenu align to (left | right) default right
26703 * Create a new Item
26704 * @param {Object} config The config object
26708 Roo.bootstrap.menu.Item = function(config){
26709 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26713 * Fires when the mouse is hovering over this menu
26714 * @param {Roo.bootstrap.menu.Item} this
26715 * @param {Roo.EventObject} e
26720 * Fires when the mouse exits this menu
26721 * @param {Roo.bootstrap.menu.Item} this
26722 * @param {Roo.EventObject} e
26728 * The raw click event for the entire grid.
26729 * @param {Roo.EventObject} e
26735 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26740 preventDefault: true,
26745 getAutoCreate : function()
26750 cls : 'roo-menu-item-text',
26758 cls : 'fa ' + this.icon
26767 href : this.href || '#',
26774 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26778 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26780 if(this.pos == 'left'){
26781 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26788 initEvents : function()
26790 this.el.on('mouseover', this.onMouseOver, this);
26791 this.el.on('mouseout', this.onMouseOut, this);
26793 this.el.select('a', true).first().on('click', this.onClick, this);
26797 onClick : function(e)
26799 if(this.preventDefault){
26800 e.preventDefault();
26803 this.fireEvent("click", this, e);
26806 onMouseOver : function(e)
26808 if(this.submenu && this.pos == 'left'){
26809 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26812 this.fireEvent("mouseover", this, e);
26815 onMouseOut : function(e)
26817 this.fireEvent("mouseout", this, e);
26829 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26832 * @class Roo.bootstrap.menu.Separator
26833 * @extends Roo.bootstrap.Component
26834 * Bootstrap Separator class
26837 * Create a new Separator
26838 * @param {Object} config The config object
26842 Roo.bootstrap.menu.Separator = function(config){
26843 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26846 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26848 getAutoCreate : function(){
26869 * @class Roo.bootstrap.Tooltip
26870 * Bootstrap Tooltip class
26871 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26872 * to determine which dom element triggers the tooltip.
26874 * It needs to add support for additional attributes like tooltip-position
26877 * Create a new Toolti
26878 * @param {Object} config The config object
26881 Roo.bootstrap.Tooltip = function(config){
26882 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26884 this.alignment = Roo.bootstrap.Tooltip.alignment;
26886 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26887 this.alignment = config.alignment;
26892 Roo.apply(Roo.bootstrap.Tooltip, {
26894 * @function init initialize tooltip monitoring.
26898 currentTip : false,
26899 currentRegion : false,
26905 Roo.get(document).on('mouseover', this.enter ,this);
26906 Roo.get(document).on('mouseout', this.leave, this);
26909 this.currentTip = new Roo.bootstrap.Tooltip();
26912 enter : function(ev)
26914 var dom = ev.getTarget();
26916 //Roo.log(['enter',dom]);
26917 var el = Roo.fly(dom);
26918 if (this.currentEl) {
26920 //Roo.log(this.currentEl);
26921 //Roo.log(this.currentEl.contains(dom));
26922 if (this.currentEl == el) {
26925 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26931 if (this.currentTip.el) {
26932 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26936 if(!el || el.dom == document){
26942 // you can not look for children, as if el is the body.. then everythign is the child..
26943 if (!el.attr('tooltip')) { //
26944 if (!el.select("[tooltip]").elements.length) {
26947 // is the mouse over this child...?
26948 bindEl = el.select("[tooltip]").first();
26949 var xy = ev.getXY();
26950 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26951 //Roo.log("not in region.");
26954 //Roo.log("child element over..");
26957 this.currentEl = bindEl;
26958 this.currentTip.bind(bindEl);
26959 this.currentRegion = Roo.lib.Region.getRegion(dom);
26960 this.currentTip.enter();
26963 leave : function(ev)
26965 var dom = ev.getTarget();
26966 //Roo.log(['leave',dom]);
26967 if (!this.currentEl) {
26972 if (dom != this.currentEl.dom) {
26975 var xy = ev.getXY();
26976 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26979 // only activate leave if mouse cursor is outside... bounding box..
26984 if (this.currentTip) {
26985 this.currentTip.leave();
26987 //Roo.log('clear currentEl');
26988 this.currentEl = false;
26993 'left' : ['r-l', [-2,0], 'right'],
26994 'right' : ['l-r', [2,0], 'left'],
26995 'bottom' : ['t-b', [0,2], 'top'],
26996 'top' : [ 'b-t', [0,-2], 'bottom']
27002 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
27007 delay : null, // can be { show : 300 , hide: 500}
27011 hoverState : null, //???
27013 placement : 'bottom',
27017 getAutoCreate : function(){
27024 cls : 'tooltip-arrow'
27027 cls : 'tooltip-inner'
27034 bind : function(el)
27040 enter : function () {
27042 if (this.timeout != null) {
27043 clearTimeout(this.timeout);
27046 this.hoverState = 'in';
27047 //Roo.log("enter - show");
27048 if (!this.delay || !this.delay.show) {
27053 this.timeout = setTimeout(function () {
27054 if (_t.hoverState == 'in') {
27057 }, this.delay.show);
27061 clearTimeout(this.timeout);
27063 this.hoverState = 'out';
27064 if (!this.delay || !this.delay.hide) {
27070 this.timeout = setTimeout(function () {
27071 //Roo.log("leave - timeout");
27073 if (_t.hoverState == 'out') {
27075 Roo.bootstrap.Tooltip.currentEl = false;
27080 show : function (msg)
27083 this.render(document.body);
27086 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
27088 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
27090 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
27092 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
27094 var placement = typeof this.placement == 'function' ?
27095 this.placement.call(this, this.el, on_el) :
27098 var autoToken = /\s?auto?\s?/i;
27099 var autoPlace = autoToken.test(placement);
27101 placement = placement.replace(autoToken, '') || 'top';
27105 //this.el.setXY([0,0]);
27107 //this.el.dom.style.display='block';
27109 //this.el.appendTo(on_el);
27111 var p = this.getPosition();
27112 var box = this.el.getBox();
27118 var align = this.alignment[placement];
27120 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
27122 if(placement == 'top' || placement == 'bottom'){
27124 placement = 'right';
27127 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
27128 placement = 'left';
27131 var scroll = Roo.select('body', true).first().getScroll();
27133 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
27137 align = this.alignment[placement];
27140 this.el.alignTo(this.bindEl, align[0],align[1]);
27141 //var arrow = this.el.select('.arrow',true).first();
27142 //arrow.set(align[2],
27144 this.el.addClass(placement);
27146 this.el.addClass('in fade');
27148 this.hoverState = null;
27150 if (this.el.hasClass('fade')) {
27161 //this.el.setXY([0,0]);
27162 this.el.removeClass('in');
27178 * @class Roo.bootstrap.LocationPicker
27179 * @extends Roo.bootstrap.Component
27180 * Bootstrap LocationPicker class
27181 * @cfg {Number} latitude Position when init default 0
27182 * @cfg {Number} longitude Position when init default 0
27183 * @cfg {Number} zoom default 15
27184 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
27185 * @cfg {Boolean} mapTypeControl default false
27186 * @cfg {Boolean} disableDoubleClickZoom default false
27187 * @cfg {Boolean} scrollwheel default true
27188 * @cfg {Boolean} streetViewControl default false
27189 * @cfg {Number} radius default 0
27190 * @cfg {String} locationName
27191 * @cfg {Boolean} draggable default true
27192 * @cfg {Boolean} enableAutocomplete default false
27193 * @cfg {Boolean} enableReverseGeocode default true
27194 * @cfg {String} markerTitle
27197 * Create a new LocationPicker
27198 * @param {Object} config The config object
27202 Roo.bootstrap.LocationPicker = function(config){
27204 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
27209 * Fires when the picker initialized.
27210 * @param {Roo.bootstrap.LocationPicker} this
27211 * @param {Google Location} location
27215 * @event positionchanged
27216 * Fires when the picker position changed.
27217 * @param {Roo.bootstrap.LocationPicker} this
27218 * @param {Google Location} location
27220 positionchanged : true,
27223 * Fires when the map resize.
27224 * @param {Roo.bootstrap.LocationPicker} this
27229 * Fires when the map show.
27230 * @param {Roo.bootstrap.LocationPicker} this
27235 * Fires when the map hide.
27236 * @param {Roo.bootstrap.LocationPicker} this
27241 * Fires when click the map.
27242 * @param {Roo.bootstrap.LocationPicker} this
27243 * @param {Map event} e
27247 * @event mapRightClick
27248 * Fires when right click the map.
27249 * @param {Roo.bootstrap.LocationPicker} this
27250 * @param {Map event} e
27252 mapRightClick : true,
27254 * @event markerClick
27255 * Fires when click the marker.
27256 * @param {Roo.bootstrap.LocationPicker} this
27257 * @param {Map event} e
27259 markerClick : true,
27261 * @event markerRightClick
27262 * Fires when right click the marker.
27263 * @param {Roo.bootstrap.LocationPicker} this
27264 * @param {Map event} e
27266 markerRightClick : true,
27268 * @event OverlayViewDraw
27269 * Fires when OverlayView Draw
27270 * @param {Roo.bootstrap.LocationPicker} this
27272 OverlayViewDraw : true,
27274 * @event OverlayViewOnAdd
27275 * Fires when OverlayView Draw
27276 * @param {Roo.bootstrap.LocationPicker} this
27278 OverlayViewOnAdd : true,
27280 * @event OverlayViewOnRemove
27281 * Fires when OverlayView Draw
27282 * @param {Roo.bootstrap.LocationPicker} this
27284 OverlayViewOnRemove : true,
27286 * @event OverlayViewShow
27287 * Fires when OverlayView Draw
27288 * @param {Roo.bootstrap.LocationPicker} this
27289 * @param {Pixel} cpx
27291 OverlayViewShow : true,
27293 * @event OverlayViewHide
27294 * Fires when OverlayView Draw
27295 * @param {Roo.bootstrap.LocationPicker} this
27297 OverlayViewHide : true,
27299 * @event loadexception
27300 * Fires when load google lib failed.
27301 * @param {Roo.bootstrap.LocationPicker} this
27303 loadexception : true
27308 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
27310 gMapContext: false,
27316 mapTypeControl: false,
27317 disableDoubleClickZoom: false,
27319 streetViewControl: false,
27323 enableAutocomplete: false,
27324 enableReverseGeocode: true,
27327 getAutoCreate: function()
27332 cls: 'roo-location-picker'
27338 initEvents: function(ct, position)
27340 if(!this.el.getWidth() || this.isApplied()){
27344 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27349 initial: function()
27351 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27352 this.fireEvent('loadexception', this);
27356 if(!this.mapTypeId){
27357 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27360 this.gMapContext = this.GMapContext();
27362 this.initOverlayView();
27364 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27368 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27369 _this.setPosition(_this.gMapContext.marker.position);
27372 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27373 _this.fireEvent('mapClick', this, event);
27377 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27378 _this.fireEvent('mapRightClick', this, event);
27382 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27383 _this.fireEvent('markerClick', this, event);
27387 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27388 _this.fireEvent('markerRightClick', this, event);
27392 this.setPosition(this.gMapContext.location);
27394 this.fireEvent('initial', this, this.gMapContext.location);
27397 initOverlayView: function()
27401 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27405 _this.fireEvent('OverlayViewDraw', _this);
27410 _this.fireEvent('OverlayViewOnAdd', _this);
27413 onRemove: function()
27415 _this.fireEvent('OverlayViewOnRemove', _this);
27418 show: function(cpx)
27420 _this.fireEvent('OverlayViewShow', _this, cpx);
27425 _this.fireEvent('OverlayViewHide', _this);
27431 fromLatLngToContainerPixel: function(event)
27433 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27436 isApplied: function()
27438 return this.getGmapContext() == false ? false : true;
27441 getGmapContext: function()
27443 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27446 GMapContext: function()
27448 var position = new google.maps.LatLng(this.latitude, this.longitude);
27450 var _map = new google.maps.Map(this.el.dom, {
27453 mapTypeId: this.mapTypeId,
27454 mapTypeControl: this.mapTypeControl,
27455 disableDoubleClickZoom: this.disableDoubleClickZoom,
27456 scrollwheel: this.scrollwheel,
27457 streetViewControl: this.streetViewControl,
27458 locationName: this.locationName,
27459 draggable: this.draggable,
27460 enableAutocomplete: this.enableAutocomplete,
27461 enableReverseGeocode: this.enableReverseGeocode
27464 var _marker = new google.maps.Marker({
27465 position: position,
27467 title: this.markerTitle,
27468 draggable: this.draggable
27475 location: position,
27476 radius: this.radius,
27477 locationName: this.locationName,
27478 addressComponents: {
27479 formatted_address: null,
27480 addressLine1: null,
27481 addressLine2: null,
27483 streetNumber: null,
27487 stateOrProvince: null
27490 domContainer: this.el.dom,
27491 geodecoder: new google.maps.Geocoder()
27495 drawCircle: function(center, radius, options)
27497 if (this.gMapContext.circle != null) {
27498 this.gMapContext.circle.setMap(null);
27502 options = Roo.apply({}, options, {
27503 strokeColor: "#0000FF",
27504 strokeOpacity: .35,
27506 fillColor: "#0000FF",
27510 options.map = this.gMapContext.map;
27511 options.radius = radius;
27512 options.center = center;
27513 this.gMapContext.circle = new google.maps.Circle(options);
27514 return this.gMapContext.circle;
27520 setPosition: function(location)
27522 this.gMapContext.location = location;
27523 this.gMapContext.marker.setPosition(location);
27524 this.gMapContext.map.panTo(location);
27525 this.drawCircle(location, this.gMapContext.radius, {});
27529 if (this.gMapContext.settings.enableReverseGeocode) {
27530 this.gMapContext.geodecoder.geocode({
27531 latLng: this.gMapContext.location
27532 }, function(results, status) {
27534 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27535 _this.gMapContext.locationName = results[0].formatted_address;
27536 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27538 _this.fireEvent('positionchanged', this, location);
27545 this.fireEvent('positionchanged', this, location);
27550 google.maps.event.trigger(this.gMapContext.map, "resize");
27552 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27554 this.fireEvent('resize', this);
27557 setPositionByLatLng: function(latitude, longitude)
27559 this.setPosition(new google.maps.LatLng(latitude, longitude));
27562 getCurrentPosition: function()
27565 latitude: this.gMapContext.location.lat(),
27566 longitude: this.gMapContext.location.lng()
27570 getAddressName: function()
27572 return this.gMapContext.locationName;
27575 getAddressComponents: function()
27577 return this.gMapContext.addressComponents;
27580 address_component_from_google_geocode: function(address_components)
27584 for (var i = 0; i < address_components.length; i++) {
27585 var component = address_components[i];
27586 if (component.types.indexOf("postal_code") >= 0) {
27587 result.postalCode = component.short_name;
27588 } else if (component.types.indexOf("street_number") >= 0) {
27589 result.streetNumber = component.short_name;
27590 } else if (component.types.indexOf("route") >= 0) {
27591 result.streetName = component.short_name;
27592 } else if (component.types.indexOf("neighborhood") >= 0) {
27593 result.city = component.short_name;
27594 } else if (component.types.indexOf("locality") >= 0) {
27595 result.city = component.short_name;
27596 } else if (component.types.indexOf("sublocality") >= 0) {
27597 result.district = component.short_name;
27598 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27599 result.stateOrProvince = component.short_name;
27600 } else if (component.types.indexOf("country") >= 0) {
27601 result.country = component.short_name;
27605 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27606 result.addressLine2 = "";
27610 setZoomLevel: function(zoom)
27612 this.gMapContext.map.setZoom(zoom);
27625 this.fireEvent('show', this);
27636 this.fireEvent('hide', this);
27641 Roo.apply(Roo.bootstrap.LocationPicker, {
27643 OverlayView : function(map, options)
27645 options = options || {};
27652 * @class Roo.bootstrap.Alert
27653 * @extends Roo.bootstrap.Component
27654 * Bootstrap Alert class - shows an alert area box
27656 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27657 Enter a valid email address
27660 * @cfg {String} title The title of alert
27661 * @cfg {String} html The content of alert
27662 * @cfg {String} weight ( success | info | warning | danger )
27663 * @cfg {String} faicon font-awesomeicon
27666 * Create a new alert
27667 * @param {Object} config The config object
27671 Roo.bootstrap.Alert = function(config){
27672 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27676 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27683 getAutoCreate : function()
27692 cls : 'roo-alert-icon'
27697 cls : 'roo-alert-title',
27702 cls : 'roo-alert-text',
27709 cfg.cn[0].cls += ' fa ' + this.faicon;
27713 cfg.cls += ' alert-' + this.weight;
27719 initEvents: function()
27721 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27724 setTitle : function(str)
27726 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27729 setText : function(str)
27731 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27734 setWeight : function(weight)
27737 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27740 this.weight = weight;
27742 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27745 setIcon : function(icon)
27748 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27751 this.faicon = icon;
27753 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27774 * @class Roo.bootstrap.UploadCropbox
27775 * @extends Roo.bootstrap.Component
27776 * Bootstrap UploadCropbox class
27777 * @cfg {String} emptyText show when image has been loaded
27778 * @cfg {String} rotateNotify show when image too small to rotate
27779 * @cfg {Number} errorTimeout default 3000
27780 * @cfg {Number} minWidth default 300
27781 * @cfg {Number} minHeight default 300
27782 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27783 * @cfg {Boolean} isDocument (true|false) default false
27784 * @cfg {String} url action url
27785 * @cfg {String} paramName default 'imageUpload'
27786 * @cfg {String} method default POST
27787 * @cfg {Boolean} loadMask (true|false) default true
27788 * @cfg {Boolean} loadingText default 'Loading...'
27791 * Create a new UploadCropbox
27792 * @param {Object} config The config object
27795 Roo.bootstrap.UploadCropbox = function(config){
27796 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27800 * @event beforeselectfile
27801 * Fire before select file
27802 * @param {Roo.bootstrap.UploadCropbox} this
27804 "beforeselectfile" : true,
27807 * Fire after initEvent
27808 * @param {Roo.bootstrap.UploadCropbox} this
27813 * Fire after initEvent
27814 * @param {Roo.bootstrap.UploadCropbox} this
27815 * @param {String} data
27820 * Fire when preparing the file data
27821 * @param {Roo.bootstrap.UploadCropbox} this
27822 * @param {Object} file
27827 * Fire when get exception
27828 * @param {Roo.bootstrap.UploadCropbox} this
27829 * @param {XMLHttpRequest} xhr
27831 "exception" : true,
27833 * @event beforeloadcanvas
27834 * Fire before load the canvas
27835 * @param {Roo.bootstrap.UploadCropbox} this
27836 * @param {String} src
27838 "beforeloadcanvas" : true,
27841 * Fire when trash image
27842 * @param {Roo.bootstrap.UploadCropbox} this
27847 * Fire when download the image
27848 * @param {Roo.bootstrap.UploadCropbox} this
27852 * @event footerbuttonclick
27853 * Fire when footerbuttonclick
27854 * @param {Roo.bootstrap.UploadCropbox} this
27855 * @param {String} type
27857 "footerbuttonclick" : true,
27861 * @param {Roo.bootstrap.UploadCropbox} this
27866 * Fire when rotate the image
27867 * @param {Roo.bootstrap.UploadCropbox} this
27868 * @param {String} pos
27873 * Fire when inspect the file
27874 * @param {Roo.bootstrap.UploadCropbox} this
27875 * @param {Object} file
27880 * Fire when xhr upload the file
27881 * @param {Roo.bootstrap.UploadCropbox} this
27882 * @param {Object} data
27887 * Fire when arrange the file data
27888 * @param {Roo.bootstrap.UploadCropbox} this
27889 * @param {Object} formData
27894 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27897 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27899 emptyText : 'Click to upload image',
27900 rotateNotify : 'Image is too small to rotate',
27901 errorTimeout : 3000,
27915 cropType : 'image/jpeg',
27917 canvasLoaded : false,
27918 isDocument : false,
27920 paramName : 'imageUpload',
27922 loadingText : 'Loading...',
27925 getAutoCreate : function()
27929 cls : 'roo-upload-cropbox',
27933 cls : 'roo-upload-cropbox-selector',
27938 cls : 'roo-upload-cropbox-body',
27939 style : 'cursor:pointer',
27943 cls : 'roo-upload-cropbox-preview'
27947 cls : 'roo-upload-cropbox-thumb'
27951 cls : 'roo-upload-cropbox-empty-notify',
27952 html : this.emptyText
27956 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27957 html : this.rotateNotify
27963 cls : 'roo-upload-cropbox-footer',
27966 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27976 onRender : function(ct, position)
27978 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27980 if (this.buttons.length) {
27982 Roo.each(this.buttons, function(bb) {
27984 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27986 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27992 this.maskEl = this.el;
27996 initEvents : function()
27998 this.urlAPI = (window.createObjectURL && window) ||
27999 (window.URL && URL.revokeObjectURL && URL) ||
28000 (window.webkitURL && webkitURL);
28002 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
28003 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28005 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
28006 this.selectorEl.hide();
28008 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
28009 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28011 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
28012 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28013 this.thumbEl.hide();
28015 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
28016 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28018 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
28019 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28020 this.errorEl.hide();
28022 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
28023 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28024 this.footerEl.hide();
28026 this.setThumbBoxSize();
28032 this.fireEvent('initial', this);
28039 window.addEventListener("resize", function() { _this.resize(); } );
28041 this.bodyEl.on('click', this.beforeSelectFile, this);
28044 this.bodyEl.on('touchstart', this.onTouchStart, this);
28045 this.bodyEl.on('touchmove', this.onTouchMove, this);
28046 this.bodyEl.on('touchend', this.onTouchEnd, this);
28050 this.bodyEl.on('mousedown', this.onMouseDown, this);
28051 this.bodyEl.on('mousemove', this.onMouseMove, this);
28052 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
28053 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
28054 Roo.get(document).on('mouseup', this.onMouseUp, this);
28057 this.selectorEl.on('change', this.onFileSelected, this);
28063 this.baseScale = 1;
28065 this.baseRotate = 1;
28066 this.dragable = false;
28067 this.pinching = false;
28070 this.cropData = false;
28071 this.notifyEl.dom.innerHTML = this.emptyText;
28073 this.selectorEl.dom.value = '';
28077 resize : function()
28079 if(this.fireEvent('resize', this) != false){
28080 this.setThumbBoxPosition();
28081 this.setCanvasPosition();
28085 onFooterButtonClick : function(e, el, o, type)
28088 case 'rotate-left' :
28089 this.onRotateLeft(e);
28091 case 'rotate-right' :
28092 this.onRotateRight(e);
28095 this.beforeSelectFile(e);
28110 this.fireEvent('footerbuttonclick', this, type);
28113 beforeSelectFile : function(e)
28115 e.preventDefault();
28117 if(this.fireEvent('beforeselectfile', this) != false){
28118 this.selectorEl.dom.click();
28122 onFileSelected : function(e)
28124 e.preventDefault();
28126 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28130 var file = this.selectorEl.dom.files[0];
28132 if(this.fireEvent('inspect', this, file) != false){
28133 this.prepare(file);
28138 trash : function(e)
28140 this.fireEvent('trash', this);
28143 download : function(e)
28145 this.fireEvent('download', this);
28148 loadCanvas : function(src)
28150 if(this.fireEvent('beforeloadcanvas', this, src) != false){
28154 this.imageEl = document.createElement('img');
28158 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
28160 this.imageEl.src = src;
28164 onLoadCanvas : function()
28166 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
28167 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
28169 this.bodyEl.un('click', this.beforeSelectFile, this);
28171 this.notifyEl.hide();
28172 this.thumbEl.show();
28173 this.footerEl.show();
28175 this.baseRotateLevel();
28177 if(this.isDocument){
28178 this.setThumbBoxSize();
28181 this.setThumbBoxPosition();
28183 this.baseScaleLevel();
28189 this.canvasLoaded = true;
28192 this.maskEl.unmask();
28197 setCanvasPosition : function()
28199 if(!this.canvasEl){
28203 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
28204 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
28206 this.previewEl.setLeft(pw);
28207 this.previewEl.setTop(ph);
28211 onMouseDown : function(e)
28215 this.dragable = true;
28216 this.pinching = false;
28218 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
28219 this.dragable = false;
28223 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28224 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28228 onMouseMove : function(e)
28232 if(!this.canvasLoaded){
28236 if (!this.dragable){
28240 var minX = Math.ceil(this.thumbEl.getLeft(true));
28241 var minY = Math.ceil(this.thumbEl.getTop(true));
28243 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
28244 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
28246 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28247 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28249 x = x - this.mouseX;
28250 y = y - this.mouseY;
28252 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
28253 var bgY = Math.ceil(y + this.previewEl.getTop(true));
28255 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
28256 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
28258 this.previewEl.setLeft(bgX);
28259 this.previewEl.setTop(bgY);
28261 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28262 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28265 onMouseUp : function(e)
28269 this.dragable = false;
28272 onMouseWheel : function(e)
28276 this.startScale = this.scale;
28278 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
28280 if(!this.zoomable()){
28281 this.scale = this.startScale;
28290 zoomable : function()
28292 var minScale = this.thumbEl.getWidth() / this.minWidth;
28294 if(this.minWidth < this.minHeight){
28295 minScale = this.thumbEl.getHeight() / this.minHeight;
28298 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
28299 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
28303 (this.rotate == 0 || this.rotate == 180) &&
28305 width > this.imageEl.OriginWidth ||
28306 height > this.imageEl.OriginHeight ||
28307 (width < this.minWidth && height < this.minHeight)
28315 (this.rotate == 90 || this.rotate == 270) &&
28317 width > this.imageEl.OriginWidth ||
28318 height > this.imageEl.OriginHeight ||
28319 (width < this.minHeight && height < this.minWidth)
28326 !this.isDocument &&
28327 (this.rotate == 0 || this.rotate == 180) &&
28329 width < this.minWidth ||
28330 width > this.imageEl.OriginWidth ||
28331 height < this.minHeight ||
28332 height > this.imageEl.OriginHeight
28339 !this.isDocument &&
28340 (this.rotate == 90 || this.rotate == 270) &&
28342 width < this.minHeight ||
28343 width > this.imageEl.OriginWidth ||
28344 height < this.minWidth ||
28345 height > this.imageEl.OriginHeight
28355 onRotateLeft : function(e)
28357 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28359 var minScale = this.thumbEl.getWidth() / this.minWidth;
28361 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28362 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28364 this.startScale = this.scale;
28366 while (this.getScaleLevel() < minScale){
28368 this.scale = this.scale + 1;
28370 if(!this.zoomable()){
28375 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28376 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28381 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28388 this.scale = this.startScale;
28390 this.onRotateFail();
28395 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28397 if(this.isDocument){
28398 this.setThumbBoxSize();
28399 this.setThumbBoxPosition();
28400 this.setCanvasPosition();
28405 this.fireEvent('rotate', this, 'left');
28409 onRotateRight : function(e)
28411 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28413 var minScale = this.thumbEl.getWidth() / this.minWidth;
28415 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28416 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28418 this.startScale = this.scale;
28420 while (this.getScaleLevel() < minScale){
28422 this.scale = this.scale + 1;
28424 if(!this.zoomable()){
28429 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28430 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28435 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28442 this.scale = this.startScale;
28444 this.onRotateFail();
28449 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28451 if(this.isDocument){
28452 this.setThumbBoxSize();
28453 this.setThumbBoxPosition();
28454 this.setCanvasPosition();
28459 this.fireEvent('rotate', this, 'right');
28462 onRotateFail : function()
28464 this.errorEl.show(true);
28468 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28473 this.previewEl.dom.innerHTML = '';
28475 var canvasEl = document.createElement("canvas");
28477 var contextEl = canvasEl.getContext("2d");
28479 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28480 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28481 var center = this.imageEl.OriginWidth / 2;
28483 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28484 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28485 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28486 center = this.imageEl.OriginHeight / 2;
28489 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28491 contextEl.translate(center, center);
28492 contextEl.rotate(this.rotate * Math.PI / 180);
28494 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28496 this.canvasEl = document.createElement("canvas");
28498 this.contextEl = this.canvasEl.getContext("2d");
28500 switch (this.rotate) {
28503 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28504 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28506 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28511 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28512 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28514 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28515 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);
28519 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28524 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28525 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28527 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28528 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);
28532 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);
28537 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28538 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28540 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28541 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28545 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);
28552 this.previewEl.appendChild(this.canvasEl);
28554 this.setCanvasPosition();
28559 if(!this.canvasLoaded){
28563 var imageCanvas = document.createElement("canvas");
28565 var imageContext = imageCanvas.getContext("2d");
28567 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28568 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28570 var center = imageCanvas.width / 2;
28572 imageContext.translate(center, center);
28574 imageContext.rotate(this.rotate * Math.PI / 180);
28576 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28578 var canvas = document.createElement("canvas");
28580 var context = canvas.getContext("2d");
28582 canvas.width = this.minWidth;
28583 canvas.height = this.minHeight;
28585 switch (this.rotate) {
28588 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28589 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28591 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28592 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28594 var targetWidth = this.minWidth - 2 * x;
28595 var targetHeight = this.minHeight - 2 * y;
28599 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28600 scale = targetWidth / width;
28603 if(x > 0 && y == 0){
28604 scale = targetHeight / height;
28607 if(x > 0 && y > 0){
28608 scale = targetWidth / width;
28610 if(width < height){
28611 scale = targetHeight / height;
28615 context.scale(scale, scale);
28617 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28618 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28620 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28621 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28623 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28628 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28629 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28631 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28632 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28634 var targetWidth = this.minWidth - 2 * x;
28635 var targetHeight = this.minHeight - 2 * y;
28639 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28640 scale = targetWidth / width;
28643 if(x > 0 && y == 0){
28644 scale = targetHeight / height;
28647 if(x > 0 && y > 0){
28648 scale = targetWidth / width;
28650 if(width < height){
28651 scale = targetHeight / height;
28655 context.scale(scale, scale);
28657 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28658 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28660 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28661 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28663 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28665 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28670 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28671 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28673 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28674 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28676 var targetWidth = this.minWidth - 2 * x;
28677 var targetHeight = this.minHeight - 2 * y;
28681 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28682 scale = targetWidth / width;
28685 if(x > 0 && y == 0){
28686 scale = targetHeight / height;
28689 if(x > 0 && y > 0){
28690 scale = targetWidth / width;
28692 if(width < height){
28693 scale = targetHeight / height;
28697 context.scale(scale, scale);
28699 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28700 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28702 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28703 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28705 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28706 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28708 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28713 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28714 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28716 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28717 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28719 var targetWidth = this.minWidth - 2 * x;
28720 var targetHeight = this.minHeight - 2 * y;
28724 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28725 scale = targetWidth / width;
28728 if(x > 0 && y == 0){
28729 scale = targetHeight / height;
28732 if(x > 0 && y > 0){
28733 scale = targetWidth / width;
28735 if(width < height){
28736 scale = targetHeight / height;
28740 context.scale(scale, scale);
28742 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28743 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28745 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28746 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28748 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28750 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28757 this.cropData = canvas.toDataURL(this.cropType);
28759 if(this.fireEvent('crop', this, this.cropData) !== false){
28760 this.process(this.file, this.cropData);
28767 setThumbBoxSize : function()
28771 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28772 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28773 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28775 this.minWidth = width;
28776 this.minHeight = height;
28778 if(this.rotate == 90 || this.rotate == 270){
28779 this.minWidth = height;
28780 this.minHeight = width;
28785 width = Math.ceil(this.minWidth * height / this.minHeight);
28787 if(this.minWidth > this.minHeight){
28789 height = Math.ceil(this.minHeight * width / this.minWidth);
28792 this.thumbEl.setStyle({
28793 width : width + 'px',
28794 height : height + 'px'
28801 setThumbBoxPosition : function()
28803 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28804 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28806 this.thumbEl.setLeft(x);
28807 this.thumbEl.setTop(y);
28811 baseRotateLevel : function()
28813 this.baseRotate = 1;
28816 typeof(this.exif) != 'undefined' &&
28817 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28818 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28820 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28823 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28827 baseScaleLevel : function()
28831 if(this.isDocument){
28833 if(this.baseRotate == 6 || this.baseRotate == 8){
28835 height = this.thumbEl.getHeight();
28836 this.baseScale = height / this.imageEl.OriginWidth;
28838 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28839 width = this.thumbEl.getWidth();
28840 this.baseScale = width / this.imageEl.OriginHeight;
28846 height = this.thumbEl.getHeight();
28847 this.baseScale = height / this.imageEl.OriginHeight;
28849 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28850 width = this.thumbEl.getWidth();
28851 this.baseScale = width / this.imageEl.OriginWidth;
28857 if(this.baseRotate == 6 || this.baseRotate == 8){
28859 width = this.thumbEl.getHeight();
28860 this.baseScale = width / this.imageEl.OriginHeight;
28862 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28863 height = this.thumbEl.getWidth();
28864 this.baseScale = height / this.imageEl.OriginHeight;
28867 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28868 height = this.thumbEl.getWidth();
28869 this.baseScale = height / this.imageEl.OriginHeight;
28871 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28872 width = this.thumbEl.getHeight();
28873 this.baseScale = width / this.imageEl.OriginWidth;
28880 width = this.thumbEl.getWidth();
28881 this.baseScale = width / this.imageEl.OriginWidth;
28883 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28884 height = this.thumbEl.getHeight();
28885 this.baseScale = height / this.imageEl.OriginHeight;
28888 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28890 height = this.thumbEl.getHeight();
28891 this.baseScale = height / this.imageEl.OriginHeight;
28893 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28894 width = this.thumbEl.getWidth();
28895 this.baseScale = width / this.imageEl.OriginWidth;
28903 getScaleLevel : function()
28905 return this.baseScale * Math.pow(1.1, this.scale);
28908 onTouchStart : function(e)
28910 if(!this.canvasLoaded){
28911 this.beforeSelectFile(e);
28915 var touches = e.browserEvent.touches;
28921 if(touches.length == 1){
28922 this.onMouseDown(e);
28926 if(touches.length != 2){
28932 for(var i = 0, finger; finger = touches[i]; i++){
28933 coords.push(finger.pageX, finger.pageY);
28936 var x = Math.pow(coords[0] - coords[2], 2);
28937 var y = Math.pow(coords[1] - coords[3], 2);
28939 this.startDistance = Math.sqrt(x + y);
28941 this.startScale = this.scale;
28943 this.pinching = true;
28944 this.dragable = false;
28948 onTouchMove : function(e)
28950 if(!this.pinching && !this.dragable){
28954 var touches = e.browserEvent.touches;
28961 this.onMouseMove(e);
28967 for(var i = 0, finger; finger = touches[i]; i++){
28968 coords.push(finger.pageX, finger.pageY);
28971 var x = Math.pow(coords[0] - coords[2], 2);
28972 var y = Math.pow(coords[1] - coords[3], 2);
28974 this.endDistance = Math.sqrt(x + y);
28976 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28978 if(!this.zoomable()){
28979 this.scale = this.startScale;
28987 onTouchEnd : function(e)
28989 this.pinching = false;
28990 this.dragable = false;
28994 process : function(file, crop)
28997 this.maskEl.mask(this.loadingText);
29000 this.xhr = new XMLHttpRequest();
29002 file.xhr = this.xhr;
29004 this.xhr.open(this.method, this.url, true);
29007 "Accept": "application/json",
29008 "Cache-Control": "no-cache",
29009 "X-Requested-With": "XMLHttpRequest"
29012 for (var headerName in headers) {
29013 var headerValue = headers[headerName];
29015 this.xhr.setRequestHeader(headerName, headerValue);
29021 this.xhr.onload = function()
29023 _this.xhrOnLoad(_this.xhr);
29026 this.xhr.onerror = function()
29028 _this.xhrOnError(_this.xhr);
29031 var formData = new FormData();
29033 formData.append('returnHTML', 'NO');
29036 formData.append('crop', crop);
29039 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
29040 formData.append(this.paramName, file, file.name);
29043 if(typeof(file.filename) != 'undefined'){
29044 formData.append('filename', file.filename);
29047 if(typeof(file.mimetype) != 'undefined'){
29048 formData.append('mimetype', file.mimetype);
29051 if(this.fireEvent('arrange', this, formData) != false){
29052 this.xhr.send(formData);
29056 xhrOnLoad : function(xhr)
29059 this.maskEl.unmask();
29062 if (xhr.readyState !== 4) {
29063 this.fireEvent('exception', this, xhr);
29067 var response = Roo.decode(xhr.responseText);
29069 if(!response.success){
29070 this.fireEvent('exception', this, xhr);
29074 var response = Roo.decode(xhr.responseText);
29076 this.fireEvent('upload', this, response);
29080 xhrOnError : function()
29083 this.maskEl.unmask();
29086 Roo.log('xhr on error');
29088 var response = Roo.decode(xhr.responseText);
29094 prepare : function(file)
29097 this.maskEl.mask(this.loadingText);
29103 if(typeof(file) === 'string'){
29104 this.loadCanvas(file);
29108 if(!file || !this.urlAPI){
29113 this.cropType = file.type;
29117 if(this.fireEvent('prepare', this, this.file) != false){
29119 var reader = new FileReader();
29121 reader.onload = function (e) {
29122 if (e.target.error) {
29123 Roo.log(e.target.error);
29127 var buffer = e.target.result,
29128 dataView = new DataView(buffer),
29130 maxOffset = dataView.byteLength - 4,
29134 if (dataView.getUint16(0) === 0xffd8) {
29135 while (offset < maxOffset) {
29136 markerBytes = dataView.getUint16(offset);
29138 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
29139 markerLength = dataView.getUint16(offset + 2) + 2;
29140 if (offset + markerLength > dataView.byteLength) {
29141 Roo.log('Invalid meta data: Invalid segment size.');
29145 if(markerBytes == 0xffe1){
29146 _this.parseExifData(
29153 offset += markerLength;
29163 var url = _this.urlAPI.createObjectURL(_this.file);
29165 _this.loadCanvas(url);
29170 reader.readAsArrayBuffer(this.file);
29176 parseExifData : function(dataView, offset, length)
29178 var tiffOffset = offset + 10,
29182 if (dataView.getUint32(offset + 4) !== 0x45786966) {
29183 // No Exif data, might be XMP data instead
29187 // Check for the ASCII code for "Exif" (0x45786966):
29188 if (dataView.getUint32(offset + 4) !== 0x45786966) {
29189 // No Exif data, might be XMP data instead
29192 if (tiffOffset + 8 > dataView.byteLength) {
29193 Roo.log('Invalid Exif data: Invalid segment size.');
29196 // Check for the two null bytes:
29197 if (dataView.getUint16(offset + 8) !== 0x0000) {
29198 Roo.log('Invalid Exif data: Missing byte alignment offset.');
29201 // Check the byte alignment:
29202 switch (dataView.getUint16(tiffOffset)) {
29204 littleEndian = true;
29207 littleEndian = false;
29210 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
29213 // Check for the TIFF tag marker (0x002A):
29214 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
29215 Roo.log('Invalid Exif data: Missing TIFF marker.');
29218 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
29219 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
29221 this.parseExifTags(
29224 tiffOffset + dirOffset,
29229 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
29234 if (dirOffset + 6 > dataView.byteLength) {
29235 Roo.log('Invalid Exif data: Invalid directory offset.');
29238 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
29239 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
29240 if (dirEndOffset + 4 > dataView.byteLength) {
29241 Roo.log('Invalid Exif data: Invalid directory size.');
29244 for (i = 0; i < tagsNumber; i += 1) {
29248 dirOffset + 2 + 12 * i, // tag offset
29252 // Return the offset to the next directory:
29253 return dataView.getUint32(dirEndOffset, littleEndian);
29256 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
29258 var tag = dataView.getUint16(offset, littleEndian);
29260 this.exif[tag] = this.getExifValue(
29264 dataView.getUint16(offset + 2, littleEndian), // tag type
29265 dataView.getUint32(offset + 4, littleEndian), // tag length
29270 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
29272 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
29281 Roo.log('Invalid Exif data: Invalid tag type.');
29285 tagSize = tagType.size * length;
29286 // Determine if the value is contained in the dataOffset bytes,
29287 // or if the value at the dataOffset is a pointer to the actual data:
29288 dataOffset = tagSize > 4 ?
29289 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
29290 if (dataOffset + tagSize > dataView.byteLength) {
29291 Roo.log('Invalid Exif data: Invalid data offset.');
29294 if (length === 1) {
29295 return tagType.getValue(dataView, dataOffset, littleEndian);
29298 for (i = 0; i < length; i += 1) {
29299 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
29302 if (tagType.ascii) {
29304 // Concatenate the chars:
29305 for (i = 0; i < values.length; i += 1) {
29307 // Ignore the terminating NULL byte(s):
29308 if (c === '\u0000') {
29320 Roo.apply(Roo.bootstrap.UploadCropbox, {
29322 'Orientation': 0x0112
29326 1: 0, //'top-left',
29328 3: 180, //'bottom-right',
29329 // 4: 'bottom-left',
29331 6: 90, //'right-top',
29332 // 7: 'right-bottom',
29333 8: 270 //'left-bottom'
29337 // byte, 8-bit unsigned int:
29339 getValue: function (dataView, dataOffset) {
29340 return dataView.getUint8(dataOffset);
29344 // ascii, 8-bit byte:
29346 getValue: function (dataView, dataOffset) {
29347 return String.fromCharCode(dataView.getUint8(dataOffset));
29352 // short, 16 bit int:
29354 getValue: function (dataView, dataOffset, littleEndian) {
29355 return dataView.getUint16(dataOffset, littleEndian);
29359 // long, 32 bit int:
29361 getValue: function (dataView, dataOffset, littleEndian) {
29362 return dataView.getUint32(dataOffset, littleEndian);
29366 // rational = two long values, first is numerator, second is denominator:
29368 getValue: function (dataView, dataOffset, littleEndian) {
29369 return dataView.getUint32(dataOffset, littleEndian) /
29370 dataView.getUint32(dataOffset + 4, littleEndian);
29374 // slong, 32 bit signed int:
29376 getValue: function (dataView, dataOffset, littleEndian) {
29377 return dataView.getInt32(dataOffset, littleEndian);
29381 // srational, two slongs, first is numerator, second is denominator:
29383 getValue: function (dataView, dataOffset, littleEndian) {
29384 return dataView.getInt32(dataOffset, littleEndian) /
29385 dataView.getInt32(dataOffset + 4, littleEndian);
29395 cls : 'btn-group roo-upload-cropbox-rotate-left',
29396 action : 'rotate-left',
29400 cls : 'btn btn-default',
29401 html : '<i class="fa fa-undo"></i>'
29407 cls : 'btn-group roo-upload-cropbox-picture',
29408 action : 'picture',
29412 cls : 'btn btn-default',
29413 html : '<i class="fa fa-picture-o"></i>'
29419 cls : 'btn-group roo-upload-cropbox-rotate-right',
29420 action : 'rotate-right',
29424 cls : 'btn btn-default',
29425 html : '<i class="fa fa-repeat"></i>'
29433 cls : 'btn-group roo-upload-cropbox-rotate-left',
29434 action : 'rotate-left',
29438 cls : 'btn btn-default',
29439 html : '<i class="fa fa-undo"></i>'
29445 cls : 'btn-group roo-upload-cropbox-download',
29446 action : 'download',
29450 cls : 'btn btn-default',
29451 html : '<i class="fa fa-download"></i>'
29457 cls : 'btn-group roo-upload-cropbox-crop',
29462 cls : 'btn btn-default',
29463 html : '<i class="fa fa-crop"></i>'
29469 cls : 'btn-group roo-upload-cropbox-trash',
29474 cls : 'btn btn-default',
29475 html : '<i class="fa fa-trash"></i>'
29481 cls : 'btn-group roo-upload-cropbox-rotate-right',
29482 action : 'rotate-right',
29486 cls : 'btn btn-default',
29487 html : '<i class="fa fa-repeat"></i>'
29495 cls : 'btn-group roo-upload-cropbox-rotate-left',
29496 action : 'rotate-left',
29500 cls : 'btn btn-default',
29501 html : '<i class="fa fa-undo"></i>'
29507 cls : 'btn-group roo-upload-cropbox-rotate-right',
29508 action : 'rotate-right',
29512 cls : 'btn btn-default',
29513 html : '<i class="fa fa-repeat"></i>'
29526 * @class Roo.bootstrap.DocumentManager
29527 * @extends Roo.bootstrap.Component
29528 * Bootstrap DocumentManager class
29529 * @cfg {String} paramName default 'imageUpload'
29530 * @cfg {String} toolTipName default 'filename'
29531 * @cfg {String} method default POST
29532 * @cfg {String} url action url
29533 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29534 * @cfg {Boolean} multiple multiple upload default true
29535 * @cfg {Number} thumbSize default 300
29536 * @cfg {String} fieldLabel
29537 * @cfg {Number} labelWidth default 4
29538 * @cfg {String} labelAlign (left|top) default left
29539 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29540 * @cfg {Number} labellg set the width of label (1-12)
29541 * @cfg {Number} labelmd set the width of label (1-12)
29542 * @cfg {Number} labelsm set the width of label (1-12)
29543 * @cfg {Number} labelxs set the width of label (1-12)
29546 * Create a new DocumentManager
29547 * @param {Object} config The config object
29550 Roo.bootstrap.DocumentManager = function(config){
29551 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29554 this.delegates = [];
29559 * Fire when initial the DocumentManager
29560 * @param {Roo.bootstrap.DocumentManager} this
29565 * inspect selected file
29566 * @param {Roo.bootstrap.DocumentManager} this
29567 * @param {File} file
29572 * Fire when xhr load exception
29573 * @param {Roo.bootstrap.DocumentManager} this
29574 * @param {XMLHttpRequest} xhr
29576 "exception" : true,
29578 * @event afterupload
29579 * Fire when xhr load exception
29580 * @param {Roo.bootstrap.DocumentManager} this
29581 * @param {XMLHttpRequest} xhr
29583 "afterupload" : true,
29586 * prepare the form data
29587 * @param {Roo.bootstrap.DocumentManager} this
29588 * @param {Object} formData
29593 * Fire when remove the file
29594 * @param {Roo.bootstrap.DocumentManager} this
29595 * @param {Object} file
29600 * Fire after refresh the file
29601 * @param {Roo.bootstrap.DocumentManager} this
29606 * Fire after click the image
29607 * @param {Roo.bootstrap.DocumentManager} this
29608 * @param {Object} file
29613 * Fire when upload a image and editable set to true
29614 * @param {Roo.bootstrap.DocumentManager} this
29615 * @param {Object} file
29619 * @event beforeselectfile
29620 * Fire before select file
29621 * @param {Roo.bootstrap.DocumentManager} this
29623 "beforeselectfile" : true,
29626 * Fire before process file
29627 * @param {Roo.bootstrap.DocumentManager} this
29628 * @param {Object} file
29632 * @event previewrendered
29633 * Fire when preview rendered
29634 * @param {Roo.bootstrap.DocumentManager} this
29635 * @param {Object} file
29637 "previewrendered" : true,
29640 "previewResize" : true
29645 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
29654 paramName : 'imageUpload',
29655 toolTipName : 'filename',
29658 labelAlign : 'left',
29668 getAutoCreate : function()
29670 var managerWidget = {
29672 cls : 'roo-document-manager',
29676 cls : 'roo-document-manager-selector',
29681 cls : 'roo-document-manager-uploader',
29685 cls : 'roo-document-manager-upload-btn',
29686 html : '<i class="fa fa-plus"></i>'
29697 cls : 'column col-md-12',
29702 if(this.fieldLabel.length){
29707 cls : 'column col-md-12',
29708 html : this.fieldLabel
29712 cls : 'column col-md-12',
29717 if(this.labelAlign == 'left'){
29722 html : this.fieldLabel
29731 if(this.labelWidth > 12){
29732 content[0].style = "width: " + this.labelWidth + 'px';
29735 if(this.labelWidth < 13 && this.labelmd == 0){
29736 this.labelmd = this.labelWidth;
29739 if(this.labellg > 0){
29740 content[0].cls += ' col-lg-' + this.labellg;
29741 content[1].cls += ' col-lg-' + (12 - this.labellg);
29744 if(this.labelmd > 0){
29745 content[0].cls += ' col-md-' + this.labelmd;
29746 content[1].cls += ' col-md-' + (12 - this.labelmd);
29749 if(this.labelsm > 0){
29750 content[0].cls += ' col-sm-' + this.labelsm;
29751 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29754 if(this.labelxs > 0){
29755 content[0].cls += ' col-xs-' + this.labelxs;
29756 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29764 cls : 'row clearfix',
29772 initEvents : function()
29774 this.managerEl = this.el.select('.roo-document-manager', true).first();
29775 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29777 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29778 this.selectorEl.hide();
29781 this.selectorEl.attr('multiple', 'multiple');
29784 this.selectorEl.on('change', this.onFileSelected, this);
29786 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29787 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29789 this.uploader.on('click', this.onUploaderClick, this);
29791 this.renderProgressDialog();
29795 window.addEventListener("resize", function() { _this.refresh(); } );
29797 this.fireEvent('initial', this);
29800 renderProgressDialog : function()
29804 this.progressDialog = new Roo.bootstrap.Modal({
29805 cls : 'roo-document-manager-progress-dialog',
29806 allow_close : false,
29817 btnclick : function() {
29818 _this.uploadCancel();
29824 this.progressDialog.render(Roo.get(document.body));
29826 this.progress = new Roo.bootstrap.Progress({
29827 cls : 'roo-document-manager-progress',
29832 this.progress.render(this.progressDialog.getChildContainer());
29834 this.progressBar = new Roo.bootstrap.ProgressBar({
29835 cls : 'roo-document-manager-progress-bar',
29838 aria_valuemax : 12,
29842 this.progressBar.render(this.progress.getChildContainer());
29845 onUploaderClick : function(e)
29847 e.preventDefault();
29849 if(this.fireEvent('beforeselectfile', this) != false){
29850 this.selectorEl.dom.click();
29855 onFileSelected : function(e)
29857 e.preventDefault();
29859 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29863 Roo.each(this.selectorEl.dom.files, function(file){
29864 if(this.fireEvent('inspect', this, file) != false){
29865 this.files.push(file);
29875 this.selectorEl.dom.value = '';
29877 if(!this.files || !this.files.length){
29881 if(this.boxes > 0 && this.files.length > this.boxes){
29882 this.files = this.files.slice(0, this.boxes);
29885 this.uploader.show();
29887 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29888 this.uploader.hide();
29897 Roo.each(this.files, function(file){
29899 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29900 var f = this.renderPreview(file);
29905 if(file.type.indexOf('image') != -1){
29906 this.delegates.push(
29908 _this.process(file);
29909 }).createDelegate(this)
29917 _this.process(file);
29918 }).createDelegate(this)
29923 this.files = files;
29925 this.delegates = this.delegates.concat(docs);
29927 if(!this.delegates.length){
29932 this.progressBar.aria_valuemax = this.delegates.length;
29939 arrange : function()
29941 if(!this.delegates.length){
29942 this.progressDialog.hide();
29947 var delegate = this.delegates.shift();
29949 this.progressDialog.show();
29951 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29953 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29958 refresh : function()
29960 this.uploader.show();
29962 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29963 this.uploader.hide();
29966 Roo.isTouch ? this.closable(false) : this.closable(true);
29968 this.fireEvent('refresh', this);
29971 onRemove : function(e, el, o)
29973 e.preventDefault();
29975 this.fireEvent('remove', this, o);
29979 remove : function(o)
29983 Roo.each(this.files, function(file){
29984 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29993 this.files = files;
30000 Roo.each(this.files, function(file){
30005 file.target.remove();
30014 onClick : function(e, el, o)
30016 e.preventDefault();
30018 this.fireEvent('click', this, o);
30022 closable : function(closable)
30024 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
30026 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30038 xhrOnLoad : function(xhr)
30040 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30044 if (xhr.readyState !== 4) {
30046 this.fireEvent('exception', this, xhr);
30050 var response = Roo.decode(xhr.responseText);
30052 if(!response.success){
30054 this.fireEvent('exception', this, xhr);
30058 var file = this.renderPreview(response.data);
30060 this.files.push(file);
30064 this.fireEvent('afterupload', this, xhr);
30068 xhrOnError : function(xhr)
30070 Roo.log('xhr on error');
30072 var response = Roo.decode(xhr.responseText);
30079 process : function(file)
30081 if(this.fireEvent('process', this, file) !== false){
30082 if(this.editable && file.type.indexOf('image') != -1){
30083 this.fireEvent('edit', this, file);
30087 this.uploadStart(file, false);
30094 uploadStart : function(file, crop)
30096 this.xhr = new XMLHttpRequest();
30098 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30103 file.xhr = this.xhr;
30105 this.managerEl.createChild({
30107 cls : 'roo-document-manager-loading',
30111 tooltip : file.name,
30112 cls : 'roo-document-manager-thumb',
30113 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30119 this.xhr.open(this.method, this.url, true);
30122 "Accept": "application/json",
30123 "Cache-Control": "no-cache",
30124 "X-Requested-With": "XMLHttpRequest"
30127 for (var headerName in headers) {
30128 var headerValue = headers[headerName];
30130 this.xhr.setRequestHeader(headerName, headerValue);
30136 this.xhr.onload = function()
30138 _this.xhrOnLoad(_this.xhr);
30141 this.xhr.onerror = function()
30143 _this.xhrOnError(_this.xhr);
30146 var formData = new FormData();
30148 formData.append('returnHTML', 'NO');
30151 formData.append('crop', crop);
30154 formData.append(this.paramName, file, file.name);
30161 if(this.fireEvent('prepare', this, formData, options) != false){
30163 if(options.manually){
30167 this.xhr.send(formData);
30171 this.uploadCancel();
30174 uploadCancel : function()
30180 this.delegates = [];
30182 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30189 renderPreview : function(file)
30191 if(typeof(file.target) != 'undefined' && file.target){
30195 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
30197 var previewEl = this.managerEl.createChild({
30199 cls : 'roo-document-manager-preview',
30203 tooltip : file[this.toolTipName],
30204 cls : 'roo-document-manager-thumb',
30205 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
30210 html : '<i class="fa fa-times-circle"></i>'
30215 var close = previewEl.select('button.close', true).first();
30217 close.on('click', this.onRemove, this, file);
30219 file.target = previewEl;
30221 var image = previewEl.select('img', true).first();
30225 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
30227 image.on('click', this.onClick, this, file);
30229 this.fireEvent('previewrendered', this, file);
30235 onPreviewLoad : function(file, image)
30237 if(typeof(file.target) == 'undefined' || !file.target){
30241 var width = image.dom.naturalWidth || image.dom.width;
30242 var height = image.dom.naturalHeight || image.dom.height;
30244 if(!this.previewResize) {
30248 if(width > height){
30249 file.target.addClass('wide');
30253 file.target.addClass('tall');
30258 uploadFromSource : function(file, crop)
30260 this.xhr = new XMLHttpRequest();
30262 this.managerEl.createChild({
30264 cls : 'roo-document-manager-loading',
30268 tooltip : file.name,
30269 cls : 'roo-document-manager-thumb',
30270 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30276 this.xhr.open(this.method, this.url, true);
30279 "Accept": "application/json",
30280 "Cache-Control": "no-cache",
30281 "X-Requested-With": "XMLHttpRequest"
30284 for (var headerName in headers) {
30285 var headerValue = headers[headerName];
30287 this.xhr.setRequestHeader(headerName, headerValue);
30293 this.xhr.onload = function()
30295 _this.xhrOnLoad(_this.xhr);
30298 this.xhr.onerror = function()
30300 _this.xhrOnError(_this.xhr);
30303 var formData = new FormData();
30305 formData.append('returnHTML', 'NO');
30307 formData.append('crop', crop);
30309 if(typeof(file.filename) != 'undefined'){
30310 formData.append('filename', file.filename);
30313 if(typeof(file.mimetype) != 'undefined'){
30314 formData.append('mimetype', file.mimetype);
30319 if(this.fireEvent('prepare', this, formData) != false){
30320 this.xhr.send(formData);
30330 * @class Roo.bootstrap.DocumentViewer
30331 * @extends Roo.bootstrap.Component
30332 * Bootstrap DocumentViewer class
30333 * @cfg {Boolean} showDownload (true|false) show download button (default true)
30334 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30337 * Create a new DocumentViewer
30338 * @param {Object} config The config object
30341 Roo.bootstrap.DocumentViewer = function(config){
30342 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30347 * Fire after initEvent
30348 * @param {Roo.bootstrap.DocumentViewer} this
30354 * @param {Roo.bootstrap.DocumentViewer} this
30359 * Fire after download button
30360 * @param {Roo.bootstrap.DocumentViewer} this
30365 * Fire after trash button
30366 * @param {Roo.bootstrap.DocumentViewer} this
30373 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
30375 showDownload : true,
30379 getAutoCreate : function()
30383 cls : 'roo-document-viewer',
30387 cls : 'roo-document-viewer-body',
30391 cls : 'roo-document-viewer-thumb',
30395 cls : 'roo-document-viewer-image'
30403 cls : 'roo-document-viewer-footer',
30406 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30410 cls : 'btn-group roo-document-viewer-download',
30414 cls : 'btn btn-default',
30415 html : '<i class="fa fa-download"></i>'
30421 cls : 'btn-group roo-document-viewer-trash',
30425 cls : 'btn btn-default',
30426 html : '<i class="fa fa-trash"></i>'
30439 initEvents : function()
30441 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30442 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30444 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30445 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30447 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30448 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30450 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30451 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30453 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30454 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30456 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30457 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30459 this.bodyEl.on('click', this.onClick, this);
30460 this.downloadBtn.on('click', this.onDownload, this);
30461 this.trashBtn.on('click', this.onTrash, this);
30463 this.downloadBtn.hide();
30464 this.trashBtn.hide();
30466 if(this.showDownload){
30467 this.downloadBtn.show();
30470 if(this.showTrash){
30471 this.trashBtn.show();
30474 if(!this.showDownload && !this.showTrash) {
30475 this.footerEl.hide();
30480 initial : function()
30482 this.fireEvent('initial', this);
30486 onClick : function(e)
30488 e.preventDefault();
30490 this.fireEvent('click', this);
30493 onDownload : function(e)
30495 e.preventDefault();
30497 this.fireEvent('download', this);
30500 onTrash : function(e)
30502 e.preventDefault();
30504 this.fireEvent('trash', this);
30516 * @class Roo.bootstrap.NavProgressBar
30517 * @extends Roo.bootstrap.Component
30518 * Bootstrap NavProgressBar class
30521 * Create a new nav progress bar
30522 * @param {Object} config The config object
30525 Roo.bootstrap.NavProgressBar = function(config){
30526 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30528 this.bullets = this.bullets || [];
30530 // Roo.bootstrap.NavProgressBar.register(this);
30534 * Fires when the active item changes
30535 * @param {Roo.bootstrap.NavProgressBar} this
30536 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30537 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
30544 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
30549 getAutoCreate : function()
30551 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30555 cls : 'roo-navigation-bar-group',
30559 cls : 'roo-navigation-top-bar'
30563 cls : 'roo-navigation-bullets-bar',
30567 cls : 'roo-navigation-bar'
30574 cls : 'roo-navigation-bottom-bar'
30584 initEvents: function()
30589 onRender : function(ct, position)
30591 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30593 if(this.bullets.length){
30594 Roo.each(this.bullets, function(b){
30603 addItem : function(cfg)
30605 var item = new Roo.bootstrap.NavProgressItem(cfg);
30607 item.parentId = this.id;
30608 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30611 var top = new Roo.bootstrap.Element({
30613 cls : 'roo-navigation-bar-text'
30616 var bottom = new Roo.bootstrap.Element({
30618 cls : 'roo-navigation-bar-text'
30621 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30622 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30624 var topText = new Roo.bootstrap.Element({
30626 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30629 var bottomText = new Roo.bootstrap.Element({
30631 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30634 topText.onRender(top.el, null);
30635 bottomText.onRender(bottom.el, null);
30638 item.bottomEl = bottom;
30641 this.barItems.push(item);
30646 getActive : function()
30648 var active = false;
30650 Roo.each(this.barItems, function(v){
30652 if (!v.isActive()) {
30664 setActiveItem : function(item)
30668 Roo.each(this.barItems, function(v){
30669 if (v.rid == item.rid) {
30673 if (v.isActive()) {
30674 v.setActive(false);
30679 item.setActive(true);
30681 this.fireEvent('changed', this, item, prev);
30684 getBarItem: function(rid)
30688 Roo.each(this.barItems, function(e) {
30689 if (e.rid != rid) {
30700 indexOfItem : function(item)
30704 Roo.each(this.barItems, function(v, i){
30706 if (v.rid != item.rid) {
30717 setActiveNext : function()
30719 var i = this.indexOfItem(this.getActive());
30721 if (i > this.barItems.length) {
30725 this.setActiveItem(this.barItems[i+1]);
30728 setActivePrev : function()
30730 var i = this.indexOfItem(this.getActive());
30736 this.setActiveItem(this.barItems[i-1]);
30739 format : function()
30741 if(!this.barItems.length){
30745 var width = 100 / this.barItems.length;
30747 Roo.each(this.barItems, function(i){
30748 i.el.setStyle('width', width + '%');
30749 i.topEl.el.setStyle('width', width + '%');
30750 i.bottomEl.el.setStyle('width', width + '%');
30759 * Nav Progress Item
30764 * @class Roo.bootstrap.NavProgressItem
30765 * @extends Roo.bootstrap.Component
30766 * Bootstrap NavProgressItem class
30767 * @cfg {String} rid the reference id
30768 * @cfg {Boolean} active (true|false) Is item active default false
30769 * @cfg {Boolean} disabled (true|false) Is item active default false
30770 * @cfg {String} html
30771 * @cfg {String} position (top|bottom) text position default bottom
30772 * @cfg {String} icon show icon instead of number
30775 * Create a new NavProgressItem
30776 * @param {Object} config The config object
30778 Roo.bootstrap.NavProgressItem = function(config){
30779 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30784 * The raw click event for the entire grid.
30785 * @param {Roo.bootstrap.NavProgressItem} this
30786 * @param {Roo.EventObject} e
30793 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30799 position : 'bottom',
30802 getAutoCreate : function()
30804 var iconCls = 'roo-navigation-bar-item-icon';
30806 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30810 cls: 'roo-navigation-bar-item',
30820 cfg.cls += ' active';
30823 cfg.cls += ' disabled';
30829 disable : function()
30831 this.setDisabled(true);
30834 enable : function()
30836 this.setDisabled(false);
30839 initEvents: function()
30841 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30843 this.iconEl.on('click', this.onClick, this);
30846 onClick : function(e)
30848 e.preventDefault();
30854 if(this.fireEvent('click', this, e) === false){
30858 this.parent().setActiveItem(this);
30861 isActive: function ()
30863 return this.active;
30866 setActive : function(state)
30868 if(this.active == state){
30872 this.active = state;
30875 this.el.addClass('active');
30879 this.el.removeClass('active');
30884 setDisabled : function(state)
30886 if(this.disabled == state){
30890 this.disabled = state;
30893 this.el.addClass('disabled');
30897 this.el.removeClass('disabled');
30900 tooltipEl : function()
30902 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30915 * @class Roo.bootstrap.FieldLabel
30916 * @extends Roo.bootstrap.Component
30917 * Bootstrap FieldLabel class
30918 * @cfg {String} html contents of the element
30919 * @cfg {String} tag tag of the element default label
30920 * @cfg {String} cls class of the element
30921 * @cfg {String} target label target
30922 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30923 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30924 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30925 * @cfg {String} iconTooltip default "This field is required"
30926 * @cfg {String} indicatorpos (left|right) default left
30929 * Create a new FieldLabel
30930 * @param {Object} config The config object
30933 Roo.bootstrap.FieldLabel = function(config){
30934 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30939 * Fires after the field has been marked as invalid.
30940 * @param {Roo.form.FieldLabel} this
30941 * @param {String} msg The validation message
30946 * Fires after the field has been validated with no errors.
30947 * @param {Roo.form.FieldLabel} this
30953 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30960 invalidClass : 'has-warning',
30961 validClass : 'has-success',
30962 iconTooltip : 'This field is required',
30963 indicatorpos : 'left',
30965 getAutoCreate : function(){
30968 if (!this.allowBlank) {
30974 cls : 'roo-bootstrap-field-label ' + this.cls,
30979 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30980 tooltip : this.iconTooltip
30989 if(this.indicatorpos == 'right'){
30992 cls : 'roo-bootstrap-field-label ' + this.cls,
31001 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
31002 tooltip : this.iconTooltip
31011 initEvents: function()
31013 Roo.bootstrap.Element.superclass.initEvents.call(this);
31015 this.indicator = this.indicatorEl();
31017 if(this.indicator){
31018 this.indicator.removeClass('visible');
31019 this.indicator.addClass('invisible');
31022 Roo.bootstrap.FieldLabel.register(this);
31025 indicatorEl : function()
31027 var indicator = this.el.select('i.roo-required-indicator',true).first();
31038 * Mark this field as valid
31040 markValid : function()
31042 if(this.indicator){
31043 this.indicator.removeClass('visible');
31044 this.indicator.addClass('invisible');
31046 if (Roo.bootstrap.version == 3) {
31047 this.el.removeClass(this.invalidClass);
31048 this.el.addClass(this.validClass);
31050 this.el.removeClass('is-invalid');
31051 this.el.addClass('is-valid');
31055 this.fireEvent('valid', this);
31059 * Mark this field as invalid
31060 * @param {String} msg The validation message
31062 markInvalid : function(msg)
31064 if(this.indicator){
31065 this.indicator.removeClass('invisible');
31066 this.indicator.addClass('visible');
31068 if (Roo.bootstrap.version == 3) {
31069 this.el.removeClass(this.validClass);
31070 this.el.addClass(this.invalidClass);
31072 this.el.removeClass('is-valid');
31073 this.el.addClass('is-invalid');
31077 this.fireEvent('invalid', this, msg);
31083 Roo.apply(Roo.bootstrap.FieldLabel, {
31088 * register a FieldLabel Group
31089 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
31091 register : function(label)
31093 if(this.groups.hasOwnProperty(label.target)){
31097 this.groups[label.target] = label;
31101 * fetch a FieldLabel Group based on the target
31102 * @param {string} target
31103 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
31105 get: function(target) {
31106 if (typeof(this.groups[target]) == 'undefined') {
31110 return this.groups[target] ;
31119 * page DateSplitField.
31125 * @class Roo.bootstrap.DateSplitField
31126 * @extends Roo.bootstrap.Component
31127 * Bootstrap DateSplitField class
31128 * @cfg {string} fieldLabel - the label associated
31129 * @cfg {Number} labelWidth set the width of label (0-12)
31130 * @cfg {String} labelAlign (top|left)
31131 * @cfg {Boolean} dayAllowBlank (true|false) default false
31132 * @cfg {Boolean} monthAllowBlank (true|false) default false
31133 * @cfg {Boolean} yearAllowBlank (true|false) default false
31134 * @cfg {string} dayPlaceholder
31135 * @cfg {string} monthPlaceholder
31136 * @cfg {string} yearPlaceholder
31137 * @cfg {string} dayFormat default 'd'
31138 * @cfg {string} monthFormat default 'm'
31139 * @cfg {string} yearFormat default 'Y'
31140 * @cfg {Number} labellg set the width of label (1-12)
31141 * @cfg {Number} labelmd set the width of label (1-12)
31142 * @cfg {Number} labelsm set the width of label (1-12)
31143 * @cfg {Number} labelxs set the width of label (1-12)
31147 * Create a new DateSplitField
31148 * @param {Object} config The config object
31151 Roo.bootstrap.DateSplitField = function(config){
31152 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
31158 * getting the data of years
31159 * @param {Roo.bootstrap.DateSplitField} this
31160 * @param {Object} years
31165 * getting the data of days
31166 * @param {Roo.bootstrap.DateSplitField} this
31167 * @param {Object} days
31172 * Fires after the field has been marked as invalid.
31173 * @param {Roo.form.Field} this
31174 * @param {String} msg The validation message
31179 * Fires after the field has been validated with no errors.
31180 * @param {Roo.form.Field} this
31186 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
31189 labelAlign : 'top',
31191 dayAllowBlank : false,
31192 monthAllowBlank : false,
31193 yearAllowBlank : false,
31194 dayPlaceholder : '',
31195 monthPlaceholder : '',
31196 yearPlaceholder : '',
31200 isFormField : true,
31206 getAutoCreate : function()
31210 cls : 'row roo-date-split-field-group',
31215 cls : 'form-hidden-field roo-date-split-field-group-value',
31221 var labelCls = 'col-md-12';
31222 var contentCls = 'col-md-4';
31224 if(this.fieldLabel){
31228 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
31232 html : this.fieldLabel
31237 if(this.labelAlign == 'left'){
31239 if(this.labelWidth > 12){
31240 label.style = "width: " + this.labelWidth + 'px';
31243 if(this.labelWidth < 13 && this.labelmd == 0){
31244 this.labelmd = this.labelWidth;
31247 if(this.labellg > 0){
31248 labelCls = ' col-lg-' + this.labellg;
31249 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
31252 if(this.labelmd > 0){
31253 labelCls = ' col-md-' + this.labelmd;
31254 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
31257 if(this.labelsm > 0){
31258 labelCls = ' col-sm-' + this.labelsm;
31259 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
31262 if(this.labelxs > 0){
31263 labelCls = ' col-xs-' + this.labelxs;
31264 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
31268 label.cls += ' ' + labelCls;
31270 cfg.cn.push(label);
31273 Roo.each(['day', 'month', 'year'], function(t){
31276 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
31283 inputEl: function ()
31285 return this.el.select('.roo-date-split-field-group-value', true).first();
31288 onRender : function(ct, position)
31292 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31294 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
31296 this.dayField = new Roo.bootstrap.ComboBox({
31297 allowBlank : this.dayAllowBlank,
31298 alwaysQuery : true,
31299 displayField : 'value',
31302 forceSelection : true,
31304 placeholder : this.dayPlaceholder,
31305 selectOnFocus : true,
31306 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31307 triggerAction : 'all',
31309 valueField : 'value',
31310 store : new Roo.data.SimpleStore({
31311 data : (function() {
31313 _this.fireEvent('days', _this, days);
31316 fields : [ 'value' ]
31319 select : function (_self, record, index)
31321 _this.setValue(_this.getValue());
31326 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31328 this.monthField = new Roo.bootstrap.MonthField({
31329 after : '<i class=\"fa fa-calendar\"></i>',
31330 allowBlank : this.monthAllowBlank,
31331 placeholder : this.monthPlaceholder,
31334 render : function (_self)
31336 this.el.select('span.input-group-addon', true).first().on('click', function(e){
31337 e.preventDefault();
31341 select : function (_self, oldvalue, newvalue)
31343 _this.setValue(_this.getValue());
31348 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31350 this.yearField = new Roo.bootstrap.ComboBox({
31351 allowBlank : this.yearAllowBlank,
31352 alwaysQuery : true,
31353 displayField : 'value',
31356 forceSelection : true,
31358 placeholder : this.yearPlaceholder,
31359 selectOnFocus : true,
31360 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31361 triggerAction : 'all',
31363 valueField : 'value',
31364 store : new Roo.data.SimpleStore({
31365 data : (function() {
31367 _this.fireEvent('years', _this, years);
31370 fields : [ 'value' ]
31373 select : function (_self, record, index)
31375 _this.setValue(_this.getValue());
31380 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31383 setValue : function(v, format)
31385 this.inputEl.dom.value = v;
31387 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31389 var d = Date.parseDate(v, f);
31396 this.setDay(d.format(this.dayFormat));
31397 this.setMonth(d.format(this.monthFormat));
31398 this.setYear(d.format(this.yearFormat));
31405 setDay : function(v)
31407 this.dayField.setValue(v);
31408 this.inputEl.dom.value = this.getValue();
31413 setMonth : function(v)
31415 this.monthField.setValue(v, true);
31416 this.inputEl.dom.value = this.getValue();
31421 setYear : function(v)
31423 this.yearField.setValue(v);
31424 this.inputEl.dom.value = this.getValue();
31429 getDay : function()
31431 return this.dayField.getValue();
31434 getMonth : function()
31436 return this.monthField.getValue();
31439 getYear : function()
31441 return this.yearField.getValue();
31444 getValue : function()
31446 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31448 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31458 this.inputEl.dom.value = '';
31463 validate : function()
31465 var d = this.dayField.validate();
31466 var m = this.monthField.validate();
31467 var y = this.yearField.validate();
31472 (!this.dayAllowBlank && !d) ||
31473 (!this.monthAllowBlank && !m) ||
31474 (!this.yearAllowBlank && !y)
31479 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31488 this.markInvalid();
31493 markValid : function()
31496 var label = this.el.select('label', true).first();
31497 var icon = this.el.select('i.fa-star', true).first();
31503 this.fireEvent('valid', this);
31507 * Mark this field as invalid
31508 * @param {String} msg The validation message
31510 markInvalid : function(msg)
31513 var label = this.el.select('label', true).first();
31514 var icon = this.el.select('i.fa-star', true).first();
31516 if(label && !icon){
31517 this.el.select('.roo-date-split-field-label', true).createChild({
31519 cls : 'text-danger fa fa-lg fa-star',
31520 tooltip : 'This field is required',
31521 style : 'margin-right:5px;'
31525 this.fireEvent('invalid', this, msg);
31528 clearInvalid : function()
31530 var label = this.el.select('label', true).first();
31531 var icon = this.el.select('i.fa-star', true).first();
31537 this.fireEvent('valid', this);
31540 getName: function()
31550 * http://masonry.desandro.com
31552 * The idea is to render all the bricks based on vertical width...
31554 * The original code extends 'outlayer' - we might need to use that....
31560 * @class Roo.bootstrap.LayoutMasonry
31561 * @extends Roo.bootstrap.Component
31562 * Bootstrap Layout Masonry class
31565 * Create a new Element
31566 * @param {Object} config The config object
31569 Roo.bootstrap.LayoutMasonry = function(config){
31571 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31575 Roo.bootstrap.LayoutMasonry.register(this);
31581 * Fire after layout the items
31582 * @param {Roo.bootstrap.LayoutMasonry} this
31583 * @param {Roo.EventObject} e
31590 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
31593 * @cfg {Boolean} isLayoutInstant = no animation?
31595 isLayoutInstant : false, // needed?
31598 * @cfg {Number} boxWidth width of the columns
31603 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
31608 * @cfg {Number} padWidth padding below box..
31613 * @cfg {Number} gutter gutter width..
31618 * @cfg {Number} maxCols maximum number of columns
31624 * @cfg {Boolean} isAutoInitial defalut true
31626 isAutoInitial : true,
31631 * @cfg {Boolean} isHorizontal defalut false
31633 isHorizontal : false,
31635 currentSize : null,
31641 bricks: null, //CompositeElement
31645 _isLayoutInited : false,
31647 // isAlternative : false, // only use for vertical layout...
31650 * @cfg {Number} alternativePadWidth padding below box..
31652 alternativePadWidth : 50,
31654 selectedBrick : [],
31656 getAutoCreate : function(){
31658 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31662 cls: 'blog-masonary-wrapper ' + this.cls,
31664 cls : 'mas-boxes masonary'
31671 getChildContainer: function( )
31673 if (this.boxesEl) {
31674 return this.boxesEl;
31677 this.boxesEl = this.el.select('.mas-boxes').first();
31679 return this.boxesEl;
31683 initEvents : function()
31687 if(this.isAutoInitial){
31688 Roo.log('hook children rendered');
31689 this.on('childrenrendered', function() {
31690 Roo.log('children rendered');
31696 initial : function()
31698 this.selectedBrick = [];
31700 this.currentSize = this.el.getBox(true);
31702 Roo.EventManager.onWindowResize(this.resize, this);
31704 if(!this.isAutoInitial){
31712 //this.layout.defer(500,this);
31716 resize : function()
31718 var cs = this.el.getBox(true);
31721 this.currentSize.width == cs.width &&
31722 this.currentSize.x == cs.x &&
31723 this.currentSize.height == cs.height &&
31724 this.currentSize.y == cs.y
31726 Roo.log("no change in with or X or Y");
31730 this.currentSize = cs;
31736 layout : function()
31738 this._resetLayout();
31740 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31742 this.layoutItems( isInstant );
31744 this._isLayoutInited = true;
31746 this.fireEvent('layout', this);
31750 _resetLayout : function()
31752 if(this.isHorizontal){
31753 this.horizontalMeasureColumns();
31757 this.verticalMeasureColumns();
31761 verticalMeasureColumns : function()
31763 this.getContainerWidth();
31765 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31766 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31770 var boxWidth = this.boxWidth + this.padWidth;
31772 if(this.containerWidth < this.boxWidth){
31773 boxWidth = this.containerWidth
31776 var containerWidth = this.containerWidth;
31778 var cols = Math.floor(containerWidth / boxWidth);
31780 this.cols = Math.max( cols, 1 );
31782 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31784 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31786 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31788 this.colWidth = boxWidth + avail - this.padWidth;
31790 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31791 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31794 horizontalMeasureColumns : function()
31796 this.getContainerWidth();
31798 var boxWidth = this.boxWidth;
31800 if(this.containerWidth < boxWidth){
31801 boxWidth = this.containerWidth;
31804 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31806 this.el.setHeight(boxWidth);
31810 getContainerWidth : function()
31812 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31815 layoutItems : function( isInstant )
31817 Roo.log(this.bricks);
31819 var items = Roo.apply([], this.bricks);
31821 if(this.isHorizontal){
31822 this._horizontalLayoutItems( items , isInstant );
31826 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31827 // this._verticalAlternativeLayoutItems( items , isInstant );
31831 this._verticalLayoutItems( items , isInstant );
31835 _verticalLayoutItems : function ( items , isInstant)
31837 if ( !items || !items.length ) {
31842 ['xs', 'xs', 'xs', 'tall'],
31843 ['xs', 'xs', 'tall'],
31844 ['xs', 'xs', 'sm'],
31845 ['xs', 'xs', 'xs'],
31851 ['sm', 'xs', 'xs'],
31855 ['tall', 'xs', 'xs', 'xs'],
31856 ['tall', 'xs', 'xs'],
31868 Roo.each(items, function(item, k){
31870 switch (item.size) {
31871 // these layouts take up a full box,
31882 boxes.push([item]);
31905 var filterPattern = function(box, length)
31913 var pattern = box.slice(0, length);
31917 Roo.each(pattern, function(i){
31918 format.push(i.size);
31921 Roo.each(standard, function(s){
31923 if(String(s) != String(format)){
31932 if(!match && length == 1){
31937 filterPattern(box, length - 1);
31941 queue.push(pattern);
31943 box = box.slice(length, box.length);
31945 filterPattern(box, 4);
31951 Roo.each(boxes, function(box, k){
31957 if(box.length == 1){
31962 filterPattern(box, 4);
31966 this._processVerticalLayoutQueue( queue, isInstant );
31970 // _verticalAlternativeLayoutItems : function( items , isInstant )
31972 // if ( !items || !items.length ) {
31976 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31980 _horizontalLayoutItems : function ( items , isInstant)
31982 if ( !items || !items.length || items.length < 3) {
31988 var eItems = items.slice(0, 3);
31990 items = items.slice(3, items.length);
31993 ['xs', 'xs', 'xs', 'wide'],
31994 ['xs', 'xs', 'wide'],
31995 ['xs', 'xs', 'sm'],
31996 ['xs', 'xs', 'xs'],
32002 ['sm', 'xs', 'xs'],
32006 ['wide', 'xs', 'xs', 'xs'],
32007 ['wide', 'xs', 'xs'],
32020 Roo.each(items, function(item, k){
32022 switch (item.size) {
32033 boxes.push([item]);
32057 var filterPattern = function(box, length)
32065 var pattern = box.slice(0, length);
32069 Roo.each(pattern, function(i){
32070 format.push(i.size);
32073 Roo.each(standard, function(s){
32075 if(String(s) != String(format)){
32084 if(!match && length == 1){
32089 filterPattern(box, length - 1);
32093 queue.push(pattern);
32095 box = box.slice(length, box.length);
32097 filterPattern(box, 4);
32103 Roo.each(boxes, function(box, k){
32109 if(box.length == 1){
32114 filterPattern(box, 4);
32121 var pos = this.el.getBox(true);
32125 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32127 var hit_end = false;
32129 Roo.each(queue, function(box){
32133 Roo.each(box, function(b){
32135 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32145 Roo.each(box, function(b){
32147 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32150 mx = Math.max(mx, b.x);
32154 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
32158 Roo.each(box, function(b){
32160 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32174 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
32177 /** Sets position of item in DOM
32178 * @param {Element} item
32179 * @param {Number} x - horizontal position
32180 * @param {Number} y - vertical position
32181 * @param {Boolean} isInstant - disables transitions
32183 _processVerticalLayoutQueue : function( queue, isInstant )
32185 var pos = this.el.getBox(true);
32190 for (var i = 0; i < this.cols; i++){
32194 Roo.each(queue, function(box, k){
32196 var col = k % this.cols;
32198 Roo.each(box, function(b,kk){
32200 b.el.position('absolute');
32202 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32203 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32205 if(b.size == 'md-left' || b.size == 'md-right'){
32206 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32207 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32210 b.el.setWidth(width);
32211 b.el.setHeight(height);
32213 b.el.select('iframe',true).setSize(width,height);
32217 for (var i = 0; i < this.cols; i++){
32219 if(maxY[i] < maxY[col]){
32224 col = Math.min(col, i);
32228 x = pos.x + col * (this.colWidth + this.padWidth);
32232 var positions = [];
32234 switch (box.length){
32236 positions = this.getVerticalOneBoxColPositions(x, y, box);
32239 positions = this.getVerticalTwoBoxColPositions(x, y, box);
32242 positions = this.getVerticalThreeBoxColPositions(x, y, box);
32245 positions = this.getVerticalFourBoxColPositions(x, y, box);
32251 Roo.each(box, function(b,kk){
32253 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32255 var sz = b.el.getSize();
32257 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
32265 for (var i = 0; i < this.cols; i++){
32266 mY = Math.max(mY, maxY[i]);
32269 this.el.setHeight(mY - pos.y);
32273 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
32275 // var pos = this.el.getBox(true);
32278 // var maxX = pos.right;
32280 // var maxHeight = 0;
32282 // Roo.each(items, function(item, k){
32286 // item.el.position('absolute');
32288 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
32290 // item.el.setWidth(width);
32292 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
32294 // item.el.setHeight(height);
32297 // item.el.setXY([x, y], isInstant ? false : true);
32299 // item.el.setXY([maxX - width, y], isInstant ? false : true);
32302 // y = y + height + this.alternativePadWidth;
32304 // maxHeight = maxHeight + height + this.alternativePadWidth;
32308 // this.el.setHeight(maxHeight);
32312 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32314 var pos = this.el.getBox(true);
32319 var maxX = pos.right;
32321 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32323 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32325 Roo.each(queue, function(box, k){
32327 Roo.each(box, function(b, kk){
32329 b.el.position('absolute');
32331 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32332 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32334 if(b.size == 'md-left' || b.size == 'md-right'){
32335 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32336 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32339 b.el.setWidth(width);
32340 b.el.setHeight(height);
32348 var positions = [];
32350 switch (box.length){
32352 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32355 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32358 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32361 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32367 Roo.each(box, function(b,kk){
32369 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32371 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32379 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32381 Roo.each(eItems, function(b,k){
32383 b.size = (k == 0) ? 'sm' : 'xs';
32384 b.x = (k == 0) ? 2 : 1;
32385 b.y = (k == 0) ? 2 : 1;
32387 b.el.position('absolute');
32389 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32391 b.el.setWidth(width);
32393 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32395 b.el.setHeight(height);
32399 var positions = [];
32402 x : maxX - this.unitWidth * 2 - this.gutter,
32407 x : maxX - this.unitWidth,
32408 y : minY + (this.unitWidth + this.gutter) * 2
32412 x : maxX - this.unitWidth * 3 - this.gutter * 2,
32416 Roo.each(eItems, function(b,k){
32418 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32424 getVerticalOneBoxColPositions : function(x, y, box)
32428 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32430 if(box[0].size == 'md-left'){
32434 if(box[0].size == 'md-right'){
32439 x : x + (this.unitWidth + this.gutter) * rand,
32446 getVerticalTwoBoxColPositions : function(x, y, box)
32450 if(box[0].size == 'xs'){
32454 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32458 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32472 x : x + (this.unitWidth + this.gutter) * 2,
32473 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32480 getVerticalThreeBoxColPositions : function(x, y, box)
32484 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32492 x : x + (this.unitWidth + this.gutter) * 1,
32497 x : x + (this.unitWidth + this.gutter) * 2,
32505 if(box[0].size == 'xs' && box[1].size == 'xs'){
32514 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32518 x : x + (this.unitWidth + this.gutter) * 1,
32532 x : x + (this.unitWidth + this.gutter) * 2,
32537 x : x + (this.unitWidth + this.gutter) * 2,
32538 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32545 getVerticalFourBoxColPositions : function(x, y, box)
32549 if(box[0].size == 'xs'){
32558 y : y + (this.unitHeight + this.gutter) * 1
32563 y : y + (this.unitHeight + this.gutter) * 2
32567 x : x + (this.unitWidth + this.gutter) * 1,
32581 x : x + (this.unitWidth + this.gutter) * 2,
32586 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32587 y : y + (this.unitHeight + this.gutter) * 1
32591 x : x + (this.unitWidth + this.gutter) * 2,
32592 y : y + (this.unitWidth + this.gutter) * 2
32599 getHorizontalOneBoxColPositions : function(maxX, minY, box)
32603 if(box[0].size == 'md-left'){
32605 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32612 if(box[0].size == 'md-right'){
32614 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32615 y : minY + (this.unitWidth + this.gutter) * 1
32621 var rand = Math.floor(Math.random() * (4 - box[0].y));
32624 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32625 y : minY + (this.unitWidth + this.gutter) * rand
32632 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32636 if(box[0].size == 'xs'){
32639 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32644 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32645 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32653 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32658 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32659 y : minY + (this.unitWidth + this.gutter) * 2
32666 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32670 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32673 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32678 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32679 y : minY + (this.unitWidth + this.gutter) * 1
32683 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32684 y : minY + (this.unitWidth + this.gutter) * 2
32691 if(box[0].size == 'xs' && box[1].size == 'xs'){
32694 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32699 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32704 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32705 y : minY + (this.unitWidth + this.gutter) * 1
32713 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32718 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32719 y : minY + (this.unitWidth + this.gutter) * 2
32723 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32724 y : minY + (this.unitWidth + this.gutter) * 2
32731 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32735 if(box[0].size == 'xs'){
32738 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32743 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32748 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),
32753 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32754 y : minY + (this.unitWidth + this.gutter) * 1
32762 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32767 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32768 y : minY + (this.unitWidth + this.gutter) * 2
32772 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32773 y : minY + (this.unitWidth + this.gutter) * 2
32777 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),
32778 y : minY + (this.unitWidth + this.gutter) * 2
32786 * remove a Masonry Brick
32787 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32789 removeBrick : function(brick_id)
32795 for (var i = 0; i<this.bricks.length; i++) {
32796 if (this.bricks[i].id == brick_id) {
32797 this.bricks.splice(i,1);
32798 this.el.dom.removeChild(Roo.get(brick_id).dom);
32805 * adds a Masonry Brick
32806 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32808 addBrick : function(cfg)
32810 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32811 //this.register(cn);
32812 cn.parentId = this.id;
32813 cn.render(this.el);
32818 * register a Masonry Brick
32819 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32822 register : function(brick)
32824 this.bricks.push(brick);
32825 brick.masonryId = this.id;
32829 * clear all the Masonry Brick
32831 clearAll : function()
32834 //this.getChildContainer().dom.innerHTML = "";
32835 this.el.dom.innerHTML = '';
32838 getSelected : function()
32840 if (!this.selectedBrick) {
32844 return this.selectedBrick;
32848 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32852 * register a Masonry Layout
32853 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32856 register : function(layout)
32858 this.groups[layout.id] = layout;
32861 * fetch a Masonry Layout based on the masonry layout ID
32862 * @param {string} the masonry layout to add
32863 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32866 get: function(layout_id) {
32867 if (typeof(this.groups[layout_id]) == 'undefined') {
32870 return this.groups[layout_id] ;
32882 * http://masonry.desandro.com
32884 * The idea is to render all the bricks based on vertical width...
32886 * The original code extends 'outlayer' - we might need to use that....
32892 * @class Roo.bootstrap.LayoutMasonryAuto
32893 * @extends Roo.bootstrap.Component
32894 * Bootstrap Layout Masonry class
32897 * Create a new Element
32898 * @param {Object} config The config object
32901 Roo.bootstrap.LayoutMasonryAuto = function(config){
32902 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32905 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32908 * @cfg {Boolean} isFitWidth - resize the width..
32910 isFitWidth : false, // options..
32912 * @cfg {Boolean} isOriginLeft = left align?
32914 isOriginLeft : true,
32916 * @cfg {Boolean} isOriginTop = top align?
32918 isOriginTop : false,
32920 * @cfg {Boolean} isLayoutInstant = no animation?
32922 isLayoutInstant : false, // needed?
32924 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32926 isResizingContainer : true,
32928 * @cfg {Number} columnWidth width of the columns
32934 * @cfg {Number} maxCols maximum number of columns
32939 * @cfg {Number} padHeight padding below box..
32945 * @cfg {Boolean} isAutoInitial defalut true
32948 isAutoInitial : true,
32954 initialColumnWidth : 0,
32955 currentSize : null,
32957 colYs : null, // array.
32964 bricks: null, //CompositeElement
32965 cols : 0, // array?
32966 // element : null, // wrapped now this.el
32967 _isLayoutInited : null,
32970 getAutoCreate : function(){
32974 cls: 'blog-masonary-wrapper ' + this.cls,
32976 cls : 'mas-boxes masonary'
32983 getChildContainer: function( )
32985 if (this.boxesEl) {
32986 return this.boxesEl;
32989 this.boxesEl = this.el.select('.mas-boxes').first();
32991 return this.boxesEl;
32995 initEvents : function()
32999 if(this.isAutoInitial){
33000 Roo.log('hook children rendered');
33001 this.on('childrenrendered', function() {
33002 Roo.log('children rendered');
33009 initial : function()
33011 this.reloadItems();
33013 this.currentSize = this.el.getBox(true);
33015 /// was window resize... - let's see if this works..
33016 Roo.EventManager.onWindowResize(this.resize, this);
33018 if(!this.isAutoInitial){
33023 this.layout.defer(500,this);
33026 reloadItems: function()
33028 this.bricks = this.el.select('.masonry-brick', true);
33030 this.bricks.each(function(b) {
33031 //Roo.log(b.getSize());
33032 if (!b.attr('originalwidth')) {
33033 b.attr('originalwidth', b.getSize().width);
33038 Roo.log(this.bricks.elements.length);
33041 resize : function()
33044 var cs = this.el.getBox(true);
33046 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
33047 Roo.log("no change in with or X");
33050 this.currentSize = cs;
33054 layout : function()
33057 this._resetLayout();
33058 //this._manageStamps();
33060 // don't animate first layout
33061 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33062 this.layoutItems( isInstant );
33064 // flag for initalized
33065 this._isLayoutInited = true;
33068 layoutItems : function( isInstant )
33070 //var items = this._getItemsForLayout( this.items );
33071 // original code supports filtering layout items.. we just ignore it..
33073 this._layoutItems( this.bricks , isInstant );
33075 this._postLayout();
33077 _layoutItems : function ( items , isInstant)
33079 //this.fireEvent( 'layout', this, items );
33082 if ( !items || !items.elements.length ) {
33083 // no items, emit event with empty array
33088 items.each(function(item) {
33089 Roo.log("layout item");
33091 // get x/y object from method
33092 var position = this._getItemLayoutPosition( item );
33094 position.item = item;
33095 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
33096 queue.push( position );
33099 this._processLayoutQueue( queue );
33101 /** Sets position of item in DOM
33102 * @param {Element} item
33103 * @param {Number} x - horizontal position
33104 * @param {Number} y - vertical position
33105 * @param {Boolean} isInstant - disables transitions
33107 _processLayoutQueue : function( queue )
33109 for ( var i=0, len = queue.length; i < len; i++ ) {
33110 var obj = queue[i];
33111 obj.item.position('absolute');
33112 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
33118 * Any logic you want to do after each layout,
33119 * i.e. size the container
33121 _postLayout : function()
33123 this.resizeContainer();
33126 resizeContainer : function()
33128 if ( !this.isResizingContainer ) {
33131 var size = this._getContainerSize();
33133 this.el.setSize(size.width,size.height);
33134 this.boxesEl.setSize(size.width,size.height);
33140 _resetLayout : function()
33142 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
33143 this.colWidth = this.el.getWidth();
33144 //this.gutter = this.el.getWidth();
33146 this.measureColumns();
33152 this.colYs.push( 0 );
33158 measureColumns : function()
33160 this.getContainerWidth();
33161 // if columnWidth is 0, default to outerWidth of first item
33162 if ( !this.columnWidth ) {
33163 var firstItem = this.bricks.first();
33164 Roo.log(firstItem);
33165 this.columnWidth = this.containerWidth;
33166 if (firstItem && firstItem.attr('originalwidth') ) {
33167 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
33169 // columnWidth fall back to item of first element
33170 Roo.log("set column width?");
33171 this.initialColumnWidth = this.columnWidth ;
33173 // if first elem has no width, default to size of container
33178 if (this.initialColumnWidth) {
33179 this.columnWidth = this.initialColumnWidth;
33184 // column width is fixed at the top - however if container width get's smaller we should
33187 // this bit calcs how man columns..
33189 var columnWidth = this.columnWidth += this.gutter;
33191 // calculate columns
33192 var containerWidth = this.containerWidth + this.gutter;
33194 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
33195 // fix rounding errors, typically with gutters
33196 var excess = columnWidth - containerWidth % columnWidth;
33199 // if overshoot is less than a pixel, round up, otherwise floor it
33200 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
33201 cols = Math[ mathMethod ]( cols );
33202 this.cols = Math.max( cols, 1 );
33203 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33205 // padding positioning..
33206 var totalColWidth = this.cols * this.columnWidth;
33207 var padavail = this.containerWidth - totalColWidth;
33208 // so for 2 columns - we need 3 'pads'
33210 var padNeeded = (1+this.cols) * this.padWidth;
33212 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
33214 this.columnWidth += padExtra
33215 //this.padWidth = Math.floor(padavail / ( this.cols));
33217 // adjust colum width so that padding is fixed??
33219 // we have 3 columns ... total = width * 3
33220 // we have X left over... that should be used by
33222 //if (this.expandC) {
33230 getContainerWidth : function()
33232 /* // container is parent if fit width
33233 var container = this.isFitWidth ? this.element.parentNode : this.element;
33234 // check that this.size and size are there
33235 // IE8 triggers resize on body size change, so they might not be
33237 var size = getSize( container ); //FIXME
33238 this.containerWidth = size && size.innerWidth; //FIXME
33241 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33245 _getItemLayoutPosition : function( item ) // what is item?
33247 // we resize the item to our columnWidth..
33249 item.setWidth(this.columnWidth);
33250 item.autoBoxAdjust = false;
33252 var sz = item.getSize();
33254 // how many columns does this brick span
33255 var remainder = this.containerWidth % this.columnWidth;
33257 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
33258 // round if off by 1 pixel, otherwise use ceil
33259 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
33260 colSpan = Math.min( colSpan, this.cols );
33262 // normally this should be '1' as we dont' currently allow multi width columns..
33264 var colGroup = this._getColGroup( colSpan );
33265 // get the minimum Y value from the columns
33266 var minimumY = Math.min.apply( Math, colGroup );
33267 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
33269 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
33271 // position the brick
33273 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
33274 y: this.currentSize.y + minimumY + this.padHeight
33278 // apply setHeight to necessary columns
33279 var setHeight = minimumY + sz.height + this.padHeight;
33280 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
33282 var setSpan = this.cols + 1 - colGroup.length;
33283 for ( var i = 0; i < setSpan; i++ ) {
33284 this.colYs[ shortColIndex + i ] = setHeight ;
33291 * @param {Number} colSpan - number of columns the element spans
33292 * @returns {Array} colGroup
33294 _getColGroup : function( colSpan )
33296 if ( colSpan < 2 ) {
33297 // if brick spans only one column, use all the column Ys
33302 // how many different places could this brick fit horizontally
33303 var groupCount = this.cols + 1 - colSpan;
33304 // for each group potential horizontal position
33305 for ( var i = 0; i < groupCount; i++ ) {
33306 // make an array of colY values for that one group
33307 var groupColYs = this.colYs.slice( i, i + colSpan );
33308 // and get the max value of the array
33309 colGroup[i] = Math.max.apply( Math, groupColYs );
33314 _manageStamp : function( stamp )
33316 var stampSize = stamp.getSize();
33317 var offset = stamp.getBox();
33318 // get the columns that this stamp affects
33319 var firstX = this.isOriginLeft ? offset.x : offset.right;
33320 var lastX = firstX + stampSize.width;
33321 var firstCol = Math.floor( firstX / this.columnWidth );
33322 firstCol = Math.max( 0, firstCol );
33324 var lastCol = Math.floor( lastX / this.columnWidth );
33325 // lastCol should not go over if multiple of columnWidth #425
33326 lastCol -= lastX % this.columnWidth ? 0 : 1;
33327 lastCol = Math.min( this.cols - 1, lastCol );
33329 // set colYs to bottom of the stamp
33330 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33333 for ( var i = firstCol; i <= lastCol; i++ ) {
33334 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33339 _getContainerSize : function()
33341 this.maxY = Math.max.apply( Math, this.colYs );
33346 if ( this.isFitWidth ) {
33347 size.width = this._getContainerFitWidth();
33353 _getContainerFitWidth : function()
33355 var unusedCols = 0;
33356 // count unused columns
33359 if ( this.colYs[i] !== 0 ) {
33364 // fit container to columns that have been used
33365 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33368 needsResizeLayout : function()
33370 var previousWidth = this.containerWidth;
33371 this.getContainerWidth();
33372 return previousWidth !== this.containerWidth;
33387 * @class Roo.bootstrap.MasonryBrick
33388 * @extends Roo.bootstrap.Component
33389 * Bootstrap MasonryBrick class
33392 * Create a new MasonryBrick
33393 * @param {Object} config The config object
33396 Roo.bootstrap.MasonryBrick = function(config){
33398 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33400 Roo.bootstrap.MasonryBrick.register(this);
33406 * When a MasonryBrick is clcik
33407 * @param {Roo.bootstrap.MasonryBrick} this
33408 * @param {Roo.EventObject} e
33414 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
33417 * @cfg {String} title
33421 * @cfg {String} html
33425 * @cfg {String} bgimage
33429 * @cfg {String} videourl
33433 * @cfg {String} cls
33437 * @cfg {String} href
33441 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33446 * @cfg {String} placetitle (center|bottom)
33451 * @cfg {Boolean} isFitContainer defalut true
33453 isFitContainer : true,
33456 * @cfg {Boolean} preventDefault defalut false
33458 preventDefault : false,
33461 * @cfg {Boolean} inverse defalut false
33463 maskInverse : false,
33465 getAutoCreate : function()
33467 if(!this.isFitContainer){
33468 return this.getSplitAutoCreate();
33471 var cls = 'masonry-brick masonry-brick-full';
33473 if(this.href.length){
33474 cls += ' masonry-brick-link';
33477 if(this.bgimage.length){
33478 cls += ' masonry-brick-image';
33481 if(this.maskInverse){
33482 cls += ' mask-inverse';
33485 if(!this.html.length && !this.maskInverse && !this.videourl.length){
33486 cls += ' enable-mask';
33490 cls += ' masonry-' + this.size + '-brick';
33493 if(this.placetitle.length){
33495 switch (this.placetitle) {
33497 cls += ' masonry-center-title';
33500 cls += ' masonry-bottom-title';
33507 if(!this.html.length && !this.bgimage.length){
33508 cls += ' masonry-center-title';
33511 if(!this.html.length && this.bgimage.length){
33512 cls += ' masonry-bottom-title';
33517 cls += ' ' + this.cls;
33521 tag: (this.href.length) ? 'a' : 'div',
33526 cls: 'masonry-brick-mask'
33530 cls: 'masonry-brick-paragraph',
33536 if(this.href.length){
33537 cfg.href = this.href;
33540 var cn = cfg.cn[1].cn;
33542 if(this.title.length){
33545 cls: 'masonry-brick-title',
33550 if(this.html.length){
33553 cls: 'masonry-brick-text',
33558 if (!this.title.length && !this.html.length) {
33559 cfg.cn[1].cls += ' hide';
33562 if(this.bgimage.length){
33565 cls: 'masonry-brick-image-view',
33570 if(this.videourl.length){
33571 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33572 // youtube support only?
33575 cls: 'masonry-brick-image-view',
33578 allowfullscreen : true
33586 getSplitAutoCreate : function()
33588 var cls = 'masonry-brick masonry-brick-split';
33590 if(this.href.length){
33591 cls += ' masonry-brick-link';
33594 if(this.bgimage.length){
33595 cls += ' masonry-brick-image';
33599 cls += ' masonry-' + this.size + '-brick';
33602 switch (this.placetitle) {
33604 cls += ' masonry-center-title';
33607 cls += ' masonry-bottom-title';
33610 if(!this.bgimage.length){
33611 cls += ' masonry-center-title';
33614 if(this.bgimage.length){
33615 cls += ' masonry-bottom-title';
33621 cls += ' ' + this.cls;
33625 tag: (this.href.length) ? 'a' : 'div',
33630 cls: 'masonry-brick-split-head',
33634 cls: 'masonry-brick-paragraph',
33641 cls: 'masonry-brick-split-body',
33647 if(this.href.length){
33648 cfg.href = this.href;
33651 if(this.title.length){
33652 cfg.cn[0].cn[0].cn.push({
33654 cls: 'masonry-brick-title',
33659 if(this.html.length){
33660 cfg.cn[1].cn.push({
33662 cls: 'masonry-brick-text',
33667 if(this.bgimage.length){
33668 cfg.cn[0].cn.push({
33670 cls: 'masonry-brick-image-view',
33675 if(this.videourl.length){
33676 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33677 // youtube support only?
33678 cfg.cn[0].cn.cn.push({
33680 cls: 'masonry-brick-image-view',
33683 allowfullscreen : true
33690 initEvents: function()
33692 switch (this.size) {
33725 this.el.on('touchstart', this.onTouchStart, this);
33726 this.el.on('touchmove', this.onTouchMove, this);
33727 this.el.on('touchend', this.onTouchEnd, this);
33728 this.el.on('contextmenu', this.onContextMenu, this);
33730 this.el.on('mouseenter' ,this.enter, this);
33731 this.el.on('mouseleave', this.leave, this);
33732 this.el.on('click', this.onClick, this);
33735 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33736 this.parent().bricks.push(this);
33741 onClick: function(e, el)
33743 var time = this.endTimer - this.startTimer;
33744 // Roo.log(e.preventDefault());
33747 e.preventDefault();
33752 if(!this.preventDefault){
33756 e.preventDefault();
33758 if (this.activeClass != '') {
33759 this.selectBrick();
33762 this.fireEvent('click', this, e);
33765 enter: function(e, el)
33767 e.preventDefault();
33769 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33773 if(this.bgimage.length && this.html.length){
33774 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33778 leave: function(e, el)
33780 e.preventDefault();
33782 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33786 if(this.bgimage.length && this.html.length){
33787 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33791 onTouchStart: function(e, el)
33793 // e.preventDefault();
33795 this.touchmoved = false;
33797 if(!this.isFitContainer){
33801 if(!this.bgimage.length || !this.html.length){
33805 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33807 this.timer = new Date().getTime();
33811 onTouchMove: function(e, el)
33813 this.touchmoved = true;
33816 onContextMenu : function(e,el)
33818 e.preventDefault();
33819 e.stopPropagation();
33823 onTouchEnd: function(e, el)
33825 // e.preventDefault();
33827 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33834 if(!this.bgimage.length || !this.html.length){
33836 if(this.href.length){
33837 window.location.href = this.href;
33843 if(!this.isFitContainer){
33847 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33849 window.location.href = this.href;
33852 //selection on single brick only
33853 selectBrick : function() {
33855 if (!this.parentId) {
33859 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33860 var index = m.selectedBrick.indexOf(this.id);
33863 m.selectedBrick.splice(index,1);
33864 this.el.removeClass(this.activeClass);
33868 for(var i = 0; i < m.selectedBrick.length; i++) {
33869 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33870 b.el.removeClass(b.activeClass);
33873 m.selectedBrick = [];
33875 m.selectedBrick.push(this.id);
33876 this.el.addClass(this.activeClass);
33880 isSelected : function(){
33881 return this.el.hasClass(this.activeClass);
33886 Roo.apply(Roo.bootstrap.MasonryBrick, {
33889 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33891 * register a Masonry Brick
33892 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33895 register : function(brick)
33897 //this.groups[brick.id] = brick;
33898 this.groups.add(brick.id, brick);
33901 * fetch a masonry brick based on the masonry brick ID
33902 * @param {string} the masonry brick to add
33903 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33906 get: function(brick_id)
33908 // if (typeof(this.groups[brick_id]) == 'undefined') {
33911 // return this.groups[brick_id] ;
33913 if(this.groups.key(brick_id)) {
33914 return this.groups.key(brick_id);
33932 * @class Roo.bootstrap.Brick
33933 * @extends Roo.bootstrap.Component
33934 * Bootstrap Brick class
33937 * Create a new Brick
33938 * @param {Object} config The config object
33941 Roo.bootstrap.Brick = function(config){
33942 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33948 * When a Brick is click
33949 * @param {Roo.bootstrap.Brick} this
33950 * @param {Roo.EventObject} e
33956 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33959 * @cfg {String} title
33963 * @cfg {String} html
33967 * @cfg {String} bgimage
33971 * @cfg {String} cls
33975 * @cfg {String} href
33979 * @cfg {String} video
33983 * @cfg {Boolean} square
33987 getAutoCreate : function()
33989 var cls = 'roo-brick';
33991 if(this.href.length){
33992 cls += ' roo-brick-link';
33995 if(this.bgimage.length){
33996 cls += ' roo-brick-image';
33999 if(!this.html.length && !this.bgimage.length){
34000 cls += ' roo-brick-center-title';
34003 if(!this.html.length && this.bgimage.length){
34004 cls += ' roo-brick-bottom-title';
34008 cls += ' ' + this.cls;
34012 tag: (this.href.length) ? 'a' : 'div',
34017 cls: 'roo-brick-paragraph',
34023 if(this.href.length){
34024 cfg.href = this.href;
34027 var cn = cfg.cn[0].cn;
34029 if(this.title.length){
34032 cls: 'roo-brick-title',
34037 if(this.html.length){
34040 cls: 'roo-brick-text',
34047 if(this.bgimage.length){
34050 cls: 'roo-brick-image-view',
34058 initEvents: function()
34060 if(this.title.length || this.html.length){
34061 this.el.on('mouseenter' ,this.enter, this);
34062 this.el.on('mouseleave', this.leave, this);
34065 Roo.EventManager.onWindowResize(this.resize, this);
34067 if(this.bgimage.length){
34068 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
34069 this.imageEl.on('load', this.onImageLoad, this);
34076 onImageLoad : function()
34081 resize : function()
34083 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
34085 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
34087 if(this.bgimage.length){
34088 var image = this.el.select('.roo-brick-image-view', true).first();
34090 image.setWidth(paragraph.getWidth());
34093 image.setHeight(paragraph.getWidth());
34096 this.el.setHeight(image.getHeight());
34097 paragraph.setHeight(image.getHeight());
34103 enter: function(e, el)
34105 e.preventDefault();
34107 if(this.bgimage.length){
34108 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
34109 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
34113 leave: function(e, el)
34115 e.preventDefault();
34117 if(this.bgimage.length){
34118 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
34119 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
34134 * @class Roo.bootstrap.NumberField
34135 * @extends Roo.bootstrap.Input
34136 * Bootstrap NumberField class
34142 * Create a new NumberField
34143 * @param {Object} config The config object
34146 Roo.bootstrap.NumberField = function(config){
34147 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
34150 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
34153 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
34155 allowDecimals : true,
34157 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
34159 decimalSeparator : ".",
34161 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
34163 decimalPrecision : 2,
34165 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
34167 allowNegative : true,
34170 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
34174 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
34176 minValue : Number.NEGATIVE_INFINITY,
34178 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
34180 maxValue : Number.MAX_VALUE,
34182 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
34184 minText : "The minimum value for this field is {0}",
34186 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
34188 maxText : "The maximum value for this field is {0}",
34190 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
34191 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
34193 nanText : "{0} is not a valid number",
34195 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
34197 thousandsDelimiter : false,
34199 * @cfg {String} valueAlign alignment of value
34201 valueAlign : "left",
34203 getAutoCreate : function()
34205 var hiddenInput = {
34209 cls: 'hidden-number-input'
34213 hiddenInput.name = this.name;
34218 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
34220 this.name = hiddenInput.name;
34222 if(cfg.cn.length > 0) {
34223 cfg.cn.push(hiddenInput);
34230 initEvents : function()
34232 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
34234 var allowed = "0123456789";
34236 if(this.allowDecimals){
34237 allowed += this.decimalSeparator;
34240 if(this.allowNegative){
34244 if(this.thousandsDelimiter) {
34248 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
34250 var keyPress = function(e){
34252 var k = e.getKey();
34254 var c = e.getCharCode();
34257 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
34258 allowed.indexOf(String.fromCharCode(c)) === -1
34264 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
34268 if(allowed.indexOf(String.fromCharCode(c)) === -1){
34273 this.el.on("keypress", keyPress, this);
34276 validateValue : function(value)
34279 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
34283 var num = this.parseValue(value);
34286 this.markInvalid(String.format(this.nanText, value));
34290 if(num < this.minValue){
34291 this.markInvalid(String.format(this.minText, this.minValue));
34295 if(num > this.maxValue){
34296 this.markInvalid(String.format(this.maxText, this.maxValue));
34303 getValue : function()
34305 var v = this.hiddenEl().getValue();
34307 return this.fixPrecision(this.parseValue(v));
34310 parseValue : function(value)
34312 if(this.thousandsDelimiter) {
34314 r = new RegExp(",", "g");
34315 value = value.replace(r, "");
34318 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34319 return isNaN(value) ? '' : value;
34322 fixPrecision : function(value)
34324 if(this.thousandsDelimiter) {
34326 r = new RegExp(",", "g");
34327 value = value.replace(r, "");
34330 var nan = isNaN(value);
34332 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34333 return nan ? '' : value;
34335 return parseFloat(value).toFixed(this.decimalPrecision);
34338 setValue : function(v)
34340 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34346 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34348 this.inputEl().dom.value = (v == '') ? '' :
34349 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34351 if(!this.allowZero && v === '0') {
34352 this.hiddenEl().dom.value = '';
34353 this.inputEl().dom.value = '';
34360 decimalPrecisionFcn : function(v)
34362 return Math.floor(v);
34365 beforeBlur : function()
34367 var v = this.parseValue(this.getRawValue());
34369 if(v || v === 0 || v === ''){
34374 hiddenEl : function()
34376 return this.el.select('input.hidden-number-input',true).first();
34388 * @class Roo.bootstrap.DocumentSlider
34389 * @extends Roo.bootstrap.Component
34390 * Bootstrap DocumentSlider class
34393 * Create a new DocumentViewer
34394 * @param {Object} config The config object
34397 Roo.bootstrap.DocumentSlider = function(config){
34398 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34405 * Fire after initEvent
34406 * @param {Roo.bootstrap.DocumentSlider} this
34411 * Fire after update
34412 * @param {Roo.bootstrap.DocumentSlider} this
34418 * @param {Roo.bootstrap.DocumentSlider} this
34424 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
34430 getAutoCreate : function()
34434 cls : 'roo-document-slider',
34438 cls : 'roo-document-slider-header',
34442 cls : 'roo-document-slider-header-title'
34448 cls : 'roo-document-slider-body',
34452 cls : 'roo-document-slider-prev',
34456 cls : 'fa fa-chevron-left'
34462 cls : 'roo-document-slider-thumb',
34466 cls : 'roo-document-slider-image'
34472 cls : 'roo-document-slider-next',
34476 cls : 'fa fa-chevron-right'
34488 initEvents : function()
34490 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34491 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34493 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34494 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34496 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34497 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34499 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34500 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34502 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34503 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34505 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34506 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34508 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34509 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34511 this.thumbEl.on('click', this.onClick, this);
34513 this.prevIndicator.on('click', this.prev, this);
34515 this.nextIndicator.on('click', this.next, this);
34519 initial : function()
34521 if(this.files.length){
34522 this.indicator = 1;
34526 this.fireEvent('initial', this);
34529 update : function()
34531 this.imageEl.attr('src', this.files[this.indicator - 1]);
34533 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34535 this.prevIndicator.show();
34537 if(this.indicator == 1){
34538 this.prevIndicator.hide();
34541 this.nextIndicator.show();
34543 if(this.indicator == this.files.length){
34544 this.nextIndicator.hide();
34547 this.thumbEl.scrollTo('top');
34549 this.fireEvent('update', this);
34552 onClick : function(e)
34554 e.preventDefault();
34556 this.fireEvent('click', this);
34561 e.preventDefault();
34563 this.indicator = Math.max(1, this.indicator - 1);
34570 e.preventDefault();
34572 this.indicator = Math.min(this.files.length, this.indicator + 1);
34586 * @class Roo.bootstrap.RadioSet
34587 * @extends Roo.bootstrap.Input
34588 * Bootstrap RadioSet class
34589 * @cfg {String} indicatorpos (left|right) default left
34590 * @cfg {Boolean} inline (true|false) inline the element (default true)
34591 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34593 * Create a new RadioSet
34594 * @param {Object} config The config object
34597 Roo.bootstrap.RadioSet = function(config){
34599 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34603 Roo.bootstrap.RadioSet.register(this);
34608 * Fires when the element is checked or unchecked.
34609 * @param {Roo.bootstrap.RadioSet} this This radio
34610 * @param {Roo.bootstrap.Radio} item The checked item
34615 * Fires when the element is click.
34616 * @param {Roo.bootstrap.RadioSet} this This radio set
34617 * @param {Roo.bootstrap.Radio} item The checked item
34618 * @param {Roo.EventObject} e The event object
34625 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
34633 indicatorpos : 'left',
34635 getAutoCreate : function()
34639 cls : 'roo-radio-set-label',
34643 html : this.fieldLabel
34647 if (Roo.bootstrap.version == 3) {
34650 if(this.indicatorpos == 'left'){
34653 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34654 tooltip : 'This field is required'
34659 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34660 tooltip : 'This field is required'
34666 cls : 'roo-radio-set-items'
34669 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34671 if (align === 'left' && this.fieldLabel.length) {
34674 cls : "roo-radio-set-right",
34680 if(this.labelWidth > 12){
34681 label.style = "width: " + this.labelWidth + 'px';
34684 if(this.labelWidth < 13 && this.labelmd == 0){
34685 this.labelmd = this.labelWidth;
34688 if(this.labellg > 0){
34689 label.cls += ' col-lg-' + this.labellg;
34690 items.cls += ' col-lg-' + (12 - this.labellg);
34693 if(this.labelmd > 0){
34694 label.cls += ' col-md-' + this.labelmd;
34695 items.cls += ' col-md-' + (12 - this.labelmd);
34698 if(this.labelsm > 0){
34699 label.cls += ' col-sm-' + this.labelsm;
34700 items.cls += ' col-sm-' + (12 - this.labelsm);
34703 if(this.labelxs > 0){
34704 label.cls += ' col-xs-' + this.labelxs;
34705 items.cls += ' col-xs-' + (12 - this.labelxs);
34711 cls : 'roo-radio-set',
34715 cls : 'roo-radio-set-input',
34718 value : this.value ? this.value : ''
34725 if(this.weight.length){
34726 cfg.cls += ' roo-radio-' + this.weight;
34730 cfg.cls += ' roo-radio-set-inline';
34734 ['xs','sm','md','lg'].map(function(size){
34735 if (settings[size]) {
34736 cfg.cls += ' col-' + size + '-' + settings[size];
34744 initEvents : function()
34746 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34747 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34749 if(!this.fieldLabel.length){
34750 this.labelEl.hide();
34753 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34754 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34756 this.indicator = this.indicatorEl();
34758 if(this.indicator){
34759 this.indicator.addClass('invisible');
34762 this.originalValue = this.getValue();
34766 inputEl: function ()
34768 return this.el.select('.roo-radio-set-input', true).first();
34771 getChildContainer : function()
34773 return this.itemsEl;
34776 register : function(item)
34778 this.radioes.push(item);
34782 validate : function()
34784 if(this.getVisibilityEl().hasClass('hidden')){
34790 Roo.each(this.radioes, function(i){
34799 if(this.allowBlank) {
34803 if(this.disabled || valid){
34808 this.markInvalid();
34813 markValid : function()
34815 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34816 this.indicatorEl().removeClass('visible');
34817 this.indicatorEl().addClass('invisible');
34821 if (Roo.bootstrap.version == 3) {
34822 this.el.removeClass([this.invalidClass, this.validClass]);
34823 this.el.addClass(this.validClass);
34825 this.el.removeClass(['is-invalid','is-valid']);
34826 this.el.addClass(['is-valid']);
34828 this.fireEvent('valid', this);
34831 markInvalid : function(msg)
34833 if(this.allowBlank || this.disabled){
34837 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34838 this.indicatorEl().removeClass('invisible');
34839 this.indicatorEl().addClass('visible');
34841 if (Roo.bootstrap.version == 3) {
34842 this.el.removeClass([this.invalidClass, this.validClass]);
34843 this.el.addClass(this.invalidClass);
34845 this.el.removeClass(['is-invalid','is-valid']);
34846 this.el.addClass(['is-invalid']);
34849 this.fireEvent('invalid', this, msg);
34853 setValue : function(v, suppressEvent)
34855 if(this.value === v){
34862 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34865 Roo.each(this.radioes, function(i){
34867 i.el.removeClass('checked');
34870 Roo.each(this.radioes, function(i){
34872 if(i.value === v || i.value.toString() === v.toString()){
34874 i.el.addClass('checked');
34876 if(suppressEvent !== true){
34877 this.fireEvent('check', this, i);
34888 clearInvalid : function(){
34890 if(!this.el || this.preventMark){
34894 this.el.removeClass([this.invalidClass]);
34896 this.fireEvent('valid', this);
34901 Roo.apply(Roo.bootstrap.RadioSet, {
34905 register : function(set)
34907 this.groups[set.name] = set;
34910 get: function(name)
34912 if (typeof(this.groups[name]) == 'undefined') {
34916 return this.groups[name] ;
34922 * Ext JS Library 1.1.1
34923 * Copyright(c) 2006-2007, Ext JS, LLC.
34925 * Originally Released Under LGPL - original licence link has changed is not relivant.
34928 * <script type="text/javascript">
34933 * @class Roo.bootstrap.SplitBar
34934 * @extends Roo.util.Observable
34935 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34939 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34940 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34941 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34942 split.minSize = 100;
34943 split.maxSize = 600;
34944 split.animate = true;
34945 split.on('moved', splitterMoved);
34948 * Create a new SplitBar
34949 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34950 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34951 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34952 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34953 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34954 position of the SplitBar).
34956 Roo.bootstrap.SplitBar = function(cfg){
34961 // dragElement : elm
34962 // resizingElement: el,
34964 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34965 // placement : Roo.bootstrap.SplitBar.LEFT ,
34966 // existingProxy ???
34969 this.el = Roo.get(cfg.dragElement, true);
34970 this.el.dom.unselectable = "on";
34972 this.resizingEl = Roo.get(cfg.resizingElement, true);
34976 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34977 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34980 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34983 * The minimum size of the resizing element. (Defaults to 0)
34989 * The maximum size of the resizing element. (Defaults to 2000)
34992 this.maxSize = 2000;
34995 * Whether to animate the transition to the new size
34998 this.animate = false;
35001 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
35004 this.useShim = false;
35009 if(!cfg.existingProxy){
35011 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
35013 this.proxy = Roo.get(cfg.existingProxy).dom;
35016 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
35019 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
35022 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
35025 this.dragSpecs = {};
35028 * @private The adapter to use to positon and resize elements
35030 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35031 this.adapter.init(this);
35033 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35035 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
35036 this.el.addClass("roo-splitbar-h");
35039 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
35040 this.el.addClass("roo-splitbar-v");
35046 * Fires when the splitter is moved (alias for {@link #event-moved})
35047 * @param {Roo.bootstrap.SplitBar} this
35048 * @param {Number} newSize the new width or height
35053 * Fires when the splitter is moved
35054 * @param {Roo.bootstrap.SplitBar} this
35055 * @param {Number} newSize the new width or height
35059 * @event beforeresize
35060 * Fires before the splitter is dragged
35061 * @param {Roo.bootstrap.SplitBar} this
35063 "beforeresize" : true,
35065 "beforeapply" : true
35068 Roo.util.Observable.call(this);
35071 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
35072 onStartProxyDrag : function(x, y){
35073 this.fireEvent("beforeresize", this);
35075 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
35077 o.enableDisplayMode("block");
35078 // all splitbars share the same overlay
35079 Roo.bootstrap.SplitBar.prototype.overlay = o;
35081 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
35082 this.overlay.show();
35083 Roo.get(this.proxy).setDisplayed("block");
35084 var size = this.adapter.getElementSize(this);
35085 this.activeMinSize = this.getMinimumSize();;
35086 this.activeMaxSize = this.getMaximumSize();;
35087 var c1 = size - this.activeMinSize;
35088 var c2 = Math.max(this.activeMaxSize - size, 0);
35089 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35090 this.dd.resetConstraints();
35091 this.dd.setXConstraint(
35092 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
35093 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
35095 this.dd.setYConstraint(0, 0);
35097 this.dd.resetConstraints();
35098 this.dd.setXConstraint(0, 0);
35099 this.dd.setYConstraint(
35100 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
35101 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
35104 this.dragSpecs.startSize = size;
35105 this.dragSpecs.startPoint = [x, y];
35106 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
35110 * @private Called after the drag operation by the DDProxy
35112 onEndProxyDrag : function(e){
35113 Roo.get(this.proxy).setDisplayed(false);
35114 var endPoint = Roo.lib.Event.getXY(e);
35116 this.overlay.hide();
35119 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35120 newSize = this.dragSpecs.startSize +
35121 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
35122 endPoint[0] - this.dragSpecs.startPoint[0] :
35123 this.dragSpecs.startPoint[0] - endPoint[0]
35126 newSize = this.dragSpecs.startSize +
35127 (this.placement == Roo.bootstrap.SplitBar.TOP ?
35128 endPoint[1] - this.dragSpecs.startPoint[1] :
35129 this.dragSpecs.startPoint[1] - endPoint[1]
35132 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
35133 if(newSize != this.dragSpecs.startSize){
35134 if(this.fireEvent('beforeapply', this, newSize) !== false){
35135 this.adapter.setElementSize(this, newSize);
35136 this.fireEvent("moved", this, newSize);
35137 this.fireEvent("resize", this, newSize);
35143 * Get the adapter this SplitBar uses
35144 * @return The adapter object
35146 getAdapter : function(){
35147 return this.adapter;
35151 * Set the adapter this SplitBar uses
35152 * @param {Object} adapter A SplitBar adapter object
35154 setAdapter : function(adapter){
35155 this.adapter = adapter;
35156 this.adapter.init(this);
35160 * Gets the minimum size for the resizing element
35161 * @return {Number} The minimum size
35163 getMinimumSize : function(){
35164 return this.minSize;
35168 * Sets the minimum size for the resizing element
35169 * @param {Number} minSize The minimum size
35171 setMinimumSize : function(minSize){
35172 this.minSize = minSize;
35176 * Gets the maximum size for the resizing element
35177 * @return {Number} The maximum size
35179 getMaximumSize : function(){
35180 return this.maxSize;
35184 * Sets the maximum size for the resizing element
35185 * @param {Number} maxSize The maximum size
35187 setMaximumSize : function(maxSize){
35188 this.maxSize = maxSize;
35192 * Sets the initialize size for the resizing element
35193 * @param {Number} size The initial size
35195 setCurrentSize : function(size){
35196 var oldAnimate = this.animate;
35197 this.animate = false;
35198 this.adapter.setElementSize(this, size);
35199 this.animate = oldAnimate;
35203 * Destroy this splitbar.
35204 * @param {Boolean} removeEl True to remove the element
35206 destroy : function(removeEl){
35208 this.shim.remove();
35211 this.proxy.parentNode.removeChild(this.proxy);
35219 * @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.
35221 Roo.bootstrap.SplitBar.createProxy = function(dir){
35222 var proxy = new Roo.Element(document.createElement("div"));
35223 proxy.unselectable();
35224 var cls = 'roo-splitbar-proxy';
35225 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
35226 document.body.appendChild(proxy.dom);
35231 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
35232 * Default Adapter. It assumes the splitter and resizing element are not positioned
35233 * elements and only gets/sets the width of the element. Generally used for table based layouts.
35235 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
35238 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
35239 // do nothing for now
35240 init : function(s){
35244 * Called before drag operations to get the current size of the resizing element.
35245 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35247 getElementSize : function(s){
35248 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35249 return s.resizingEl.getWidth();
35251 return s.resizingEl.getHeight();
35256 * Called after drag operations to set the size of the resizing element.
35257 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35258 * @param {Number} newSize The new size to set
35259 * @param {Function} onComplete A function to be invoked when resizing is complete
35261 setElementSize : function(s, newSize, onComplete){
35262 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35264 s.resizingEl.setWidth(newSize);
35266 onComplete(s, newSize);
35269 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
35274 s.resizingEl.setHeight(newSize);
35276 onComplete(s, newSize);
35279 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
35286 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
35287 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
35288 * Adapter that moves the splitter element to align with the resized sizing element.
35289 * Used with an absolute positioned SplitBar.
35290 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
35291 * document.body, make sure you assign an id to the body element.
35293 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
35294 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35295 this.container = Roo.get(container);
35298 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
35299 init : function(s){
35300 this.basic.init(s);
35303 getElementSize : function(s){
35304 return this.basic.getElementSize(s);
35307 setElementSize : function(s, newSize, onComplete){
35308 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
35311 moveSplitter : function(s){
35312 var yes = Roo.bootstrap.SplitBar;
35313 switch(s.placement){
35315 s.el.setX(s.resizingEl.getRight());
35318 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35321 s.el.setY(s.resizingEl.getBottom());
35324 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35331 * Orientation constant - Create a vertical SplitBar
35335 Roo.bootstrap.SplitBar.VERTICAL = 1;
35338 * Orientation constant - Create a horizontal SplitBar
35342 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35345 * Placement constant - The resizing element is to the left of the splitter element
35349 Roo.bootstrap.SplitBar.LEFT = 1;
35352 * Placement constant - The resizing element is to the right of the splitter element
35356 Roo.bootstrap.SplitBar.RIGHT = 2;
35359 * Placement constant - The resizing element is positioned above the splitter element
35363 Roo.bootstrap.SplitBar.TOP = 3;
35366 * Placement constant - The resizing element is positioned under splitter element
35370 Roo.bootstrap.SplitBar.BOTTOM = 4;
35371 Roo.namespace("Roo.bootstrap.layout");/*
35373 * Ext JS Library 1.1.1
35374 * Copyright(c) 2006-2007, Ext JS, LLC.
35376 * Originally Released Under LGPL - original licence link has changed is not relivant.
35379 * <script type="text/javascript">
35383 * @class Roo.bootstrap.layout.Manager
35384 * @extends Roo.bootstrap.Component
35385 * Base class for layout managers.
35387 Roo.bootstrap.layout.Manager = function(config)
35389 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35395 /** false to disable window resize monitoring @type Boolean */
35396 this.monitorWindowResize = true;
35401 * Fires when a layout is performed.
35402 * @param {Roo.LayoutManager} this
35406 * @event regionresized
35407 * Fires when the user resizes a region.
35408 * @param {Roo.LayoutRegion} region The resized region
35409 * @param {Number} newSize The new size (width for east/west, height for north/south)
35411 "regionresized" : true,
35413 * @event regioncollapsed
35414 * Fires when a region is collapsed.
35415 * @param {Roo.LayoutRegion} region The collapsed region
35417 "regioncollapsed" : true,
35419 * @event regionexpanded
35420 * Fires when a region is expanded.
35421 * @param {Roo.LayoutRegion} region The expanded region
35423 "regionexpanded" : true
35425 this.updating = false;
35428 this.el = Roo.get(config.el);
35434 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35439 monitorWindowResize : true,
35445 onRender : function(ct, position)
35448 this.el = Roo.get(ct);
35451 //this.fireEvent('render',this);
35455 initEvents: function()
35459 // ie scrollbar fix
35460 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35461 document.body.scroll = "no";
35462 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35463 this.el.position('relative');
35465 this.id = this.el.id;
35466 this.el.addClass("roo-layout-container");
35467 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35468 if(this.el.dom != document.body ) {
35469 this.el.on('resize', this.layout,this);
35470 this.el.on('show', this.layout,this);
35476 * Returns true if this layout is currently being updated
35477 * @return {Boolean}
35479 isUpdating : function(){
35480 return this.updating;
35484 * Suspend the LayoutManager from doing auto-layouts while
35485 * making multiple add or remove calls
35487 beginUpdate : function(){
35488 this.updating = true;
35492 * Restore auto-layouts and optionally disable the manager from performing a layout
35493 * @param {Boolean} noLayout true to disable a layout update
35495 endUpdate : function(noLayout){
35496 this.updating = false;
35502 layout: function(){
35506 onRegionResized : function(region, newSize){
35507 this.fireEvent("regionresized", region, newSize);
35511 onRegionCollapsed : function(region){
35512 this.fireEvent("regioncollapsed", region);
35515 onRegionExpanded : function(region){
35516 this.fireEvent("regionexpanded", region);
35520 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35521 * performs box-model adjustments.
35522 * @return {Object} The size as an object {width: (the width), height: (the height)}
35524 getViewSize : function()
35527 if(this.el.dom != document.body){
35528 size = this.el.getSize();
35530 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35532 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35533 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35538 * Returns the Element this layout is bound to.
35539 * @return {Roo.Element}
35541 getEl : function(){
35546 * Returns the specified region.
35547 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35548 * @return {Roo.LayoutRegion}
35550 getRegion : function(target){
35551 return this.regions[target.toLowerCase()];
35554 onWindowResize : function(){
35555 if(this.monitorWindowResize){
35562 * Ext JS Library 1.1.1
35563 * Copyright(c) 2006-2007, Ext JS, LLC.
35565 * Originally Released Under LGPL - original licence link has changed is not relivant.
35568 * <script type="text/javascript">
35571 * @class Roo.bootstrap.layout.Border
35572 * @extends Roo.bootstrap.layout.Manager
35573 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35574 * please see: examples/bootstrap/nested.html<br><br>
35576 <b>The container the layout is rendered into can be either the body element or any other element.
35577 If it is not the body element, the container needs to either be an absolute positioned element,
35578 or you will need to add "position:relative" to the css of the container. You will also need to specify
35579 the container size if it is not the body element.</b>
35582 * Create a new Border
35583 * @param {Object} config Configuration options
35585 Roo.bootstrap.layout.Border = function(config){
35586 config = config || {};
35587 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35591 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35592 if(config[region]){
35593 config[region].region = region;
35594 this.addRegion(config[region]);
35600 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
35602 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35604 parent : false, // this might point to a 'nest' or a ???
35607 * Creates and adds a new region if it doesn't already exist.
35608 * @param {String} target The target region key (north, south, east, west or center).
35609 * @param {Object} config The regions config object
35610 * @return {BorderLayoutRegion} The new region
35612 addRegion : function(config)
35614 if(!this.regions[config.region]){
35615 var r = this.factory(config);
35616 this.bindRegion(r);
35618 return this.regions[config.region];
35622 bindRegion : function(r){
35623 this.regions[r.config.region] = r;
35625 r.on("visibilitychange", this.layout, this);
35626 r.on("paneladded", this.layout, this);
35627 r.on("panelremoved", this.layout, this);
35628 r.on("invalidated", this.layout, this);
35629 r.on("resized", this.onRegionResized, this);
35630 r.on("collapsed", this.onRegionCollapsed, this);
35631 r.on("expanded", this.onRegionExpanded, this);
35635 * Performs a layout update.
35637 layout : function()
35639 if(this.updating) {
35643 // render all the rebions if they have not been done alreayd?
35644 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35645 if(this.regions[region] && !this.regions[region].bodyEl){
35646 this.regions[region].onRender(this.el)
35650 var size = this.getViewSize();
35651 var w = size.width;
35652 var h = size.height;
35657 //var x = 0, y = 0;
35659 var rs = this.regions;
35660 var north = rs["north"];
35661 var south = rs["south"];
35662 var west = rs["west"];
35663 var east = rs["east"];
35664 var center = rs["center"];
35665 //if(this.hideOnLayout){ // not supported anymore
35666 //c.el.setStyle("display", "none");
35668 if(north && north.isVisible()){
35669 var b = north.getBox();
35670 var m = north.getMargins();
35671 b.width = w - (m.left+m.right);
35674 centerY = b.height + b.y + m.bottom;
35675 centerH -= centerY;
35676 north.updateBox(this.safeBox(b));
35678 if(south && south.isVisible()){
35679 var b = south.getBox();
35680 var m = south.getMargins();
35681 b.width = w - (m.left+m.right);
35683 var totalHeight = (b.height + m.top + m.bottom);
35684 b.y = h - totalHeight + m.top;
35685 centerH -= totalHeight;
35686 south.updateBox(this.safeBox(b));
35688 if(west && west.isVisible()){
35689 var b = west.getBox();
35690 var m = west.getMargins();
35691 b.height = centerH - (m.top+m.bottom);
35693 b.y = centerY + m.top;
35694 var totalWidth = (b.width + m.left + m.right);
35695 centerX += totalWidth;
35696 centerW -= totalWidth;
35697 west.updateBox(this.safeBox(b));
35699 if(east && east.isVisible()){
35700 var b = east.getBox();
35701 var m = east.getMargins();
35702 b.height = centerH - (m.top+m.bottom);
35703 var totalWidth = (b.width + m.left + m.right);
35704 b.x = w - totalWidth + m.left;
35705 b.y = centerY + m.top;
35706 centerW -= totalWidth;
35707 east.updateBox(this.safeBox(b));
35710 var m = center.getMargins();
35712 x: centerX + m.left,
35713 y: centerY + m.top,
35714 width: centerW - (m.left+m.right),
35715 height: centerH - (m.top+m.bottom)
35717 //if(this.hideOnLayout){
35718 //center.el.setStyle("display", "block");
35720 center.updateBox(this.safeBox(centerBox));
35723 this.fireEvent("layout", this);
35727 safeBox : function(box){
35728 box.width = Math.max(0, box.width);
35729 box.height = Math.max(0, box.height);
35734 * Adds a ContentPanel (or subclass) to this layout.
35735 * @param {String} target The target region key (north, south, east, west or center).
35736 * @param {Roo.ContentPanel} panel The panel to add
35737 * @return {Roo.ContentPanel} The added panel
35739 add : function(target, panel){
35741 target = target.toLowerCase();
35742 return this.regions[target].add(panel);
35746 * Remove a ContentPanel (or subclass) to this layout.
35747 * @param {String} target The target region key (north, south, east, west or center).
35748 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35749 * @return {Roo.ContentPanel} The removed panel
35751 remove : function(target, panel){
35752 target = target.toLowerCase();
35753 return this.regions[target].remove(panel);
35757 * Searches all regions for a panel with the specified id
35758 * @param {String} panelId
35759 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35761 findPanel : function(panelId){
35762 var rs = this.regions;
35763 for(var target in rs){
35764 if(typeof rs[target] != "function"){
35765 var p = rs[target].getPanel(panelId);
35775 * Searches all regions for a panel with the specified id and activates (shows) it.
35776 * @param {String/ContentPanel} panelId The panels id or the panel itself
35777 * @return {Roo.ContentPanel} The shown panel or null
35779 showPanel : function(panelId) {
35780 var rs = this.regions;
35781 for(var target in rs){
35782 var r = rs[target];
35783 if(typeof r != "function"){
35784 if(r.hasPanel(panelId)){
35785 return r.showPanel(panelId);
35793 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35794 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35797 restoreState : function(provider){
35799 provider = Roo.state.Manager;
35801 var sm = new Roo.LayoutStateManager();
35802 sm.init(this, provider);
35808 * Adds a xtype elements to the layout.
35812 xtype : 'ContentPanel',
35819 xtype : 'NestedLayoutPanel',
35825 items : [ ... list of content panels or nested layout panels.. ]
35829 * @param {Object} cfg Xtype definition of item to add.
35831 addxtype : function(cfg)
35833 // basically accepts a pannel...
35834 // can accept a layout region..!?!?
35835 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35838 // theory? children can only be panels??
35840 //if (!cfg.xtype.match(/Panel$/)) {
35845 if (typeof(cfg.region) == 'undefined') {
35846 Roo.log("Failed to add Panel, region was not set");
35850 var region = cfg.region;
35856 xitems = cfg.items;
35861 if ( region == 'center') {
35862 Roo.log("Center: " + cfg.title);
35868 case 'Content': // ContentPanel (el, cfg)
35869 case 'Scroll': // ContentPanel (el, cfg)
35871 cfg.autoCreate = cfg.autoCreate || true;
35872 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35874 // var el = this.el.createChild();
35875 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35878 this.add(region, ret);
35882 case 'TreePanel': // our new panel!
35883 cfg.el = this.el.createChild();
35884 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35885 this.add(region, ret);
35890 // create a new Layout (which is a Border Layout...
35892 var clayout = cfg.layout;
35893 clayout.el = this.el.createChild();
35894 clayout.items = clayout.items || [];
35898 // replace this exitems with the clayout ones..
35899 xitems = clayout.items;
35901 // force background off if it's in center...
35902 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35903 cfg.background = false;
35905 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35908 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35909 //console.log('adding nested layout panel ' + cfg.toSource());
35910 this.add(region, ret);
35911 nb = {}; /// find first...
35916 // needs grid and region
35918 //var el = this.getRegion(region).el.createChild();
35920 *var el = this.el.createChild();
35921 // create the grid first...
35922 cfg.grid.container = el;
35923 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35926 if (region == 'center' && this.active ) {
35927 cfg.background = false;
35930 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35932 this.add(region, ret);
35934 if (cfg.background) {
35935 // render grid on panel activation (if panel background)
35936 ret.on('activate', function(gp) {
35937 if (!gp.grid.rendered) {
35938 // gp.grid.render(el);
35942 // cfg.grid.render(el);
35948 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35949 // it was the old xcomponent building that caused this before.
35950 // espeically if border is the top element in the tree.
35960 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35962 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35963 this.add(region, ret);
35967 throw "Can not add '" + cfg.xtype + "' to Border";
35973 this.beginUpdate();
35977 Roo.each(xitems, function(i) {
35978 region = nb && i.region ? i.region : false;
35980 var add = ret.addxtype(i);
35983 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35984 if (!i.background) {
35985 abn[region] = nb[region] ;
35992 // make the last non-background panel active..
35993 //if (nb) { Roo.log(abn); }
35996 for(var r in abn) {
35997 region = this.getRegion(r);
35999 // tried using nb[r], but it does not work..
36001 region.showPanel(abn[r]);
36012 factory : function(cfg)
36015 var validRegions = Roo.bootstrap.layout.Border.regions;
36017 var target = cfg.region;
36020 var r = Roo.bootstrap.layout;
36024 return new r.North(cfg);
36026 return new r.South(cfg);
36028 return new r.East(cfg);
36030 return new r.West(cfg);
36032 return new r.Center(cfg);
36034 throw 'Layout region "'+target+'" not supported.';
36041 * Ext JS Library 1.1.1
36042 * Copyright(c) 2006-2007, Ext JS, LLC.
36044 * Originally Released Under LGPL - original licence link has changed is not relivant.
36047 * <script type="text/javascript">
36051 * @class Roo.bootstrap.layout.Basic
36052 * @extends Roo.util.Observable
36053 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
36054 * and does not have a titlebar, tabs or any other features. All it does is size and position
36055 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
36056 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
36057 * @cfg {string} region the region that it inhabits..
36058 * @cfg {bool} skipConfig skip config?
36062 Roo.bootstrap.layout.Basic = function(config){
36064 this.mgr = config.mgr;
36066 this.position = config.region;
36068 var skipConfig = config.skipConfig;
36072 * @scope Roo.BasicLayoutRegion
36076 * @event beforeremove
36077 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
36078 * @param {Roo.LayoutRegion} this
36079 * @param {Roo.ContentPanel} panel The panel
36080 * @param {Object} e The cancel event object
36082 "beforeremove" : true,
36084 * @event invalidated
36085 * Fires when the layout for this region is changed.
36086 * @param {Roo.LayoutRegion} this
36088 "invalidated" : true,
36090 * @event visibilitychange
36091 * Fires when this region is shown or hidden
36092 * @param {Roo.LayoutRegion} this
36093 * @param {Boolean} visibility true or false
36095 "visibilitychange" : true,
36097 * @event paneladded
36098 * Fires when a panel is added.
36099 * @param {Roo.LayoutRegion} this
36100 * @param {Roo.ContentPanel} panel The panel
36102 "paneladded" : true,
36104 * @event panelremoved
36105 * Fires when a panel is removed.
36106 * @param {Roo.LayoutRegion} this
36107 * @param {Roo.ContentPanel} panel The panel
36109 "panelremoved" : true,
36111 * @event beforecollapse
36112 * Fires when this region before collapse.
36113 * @param {Roo.LayoutRegion} this
36115 "beforecollapse" : true,
36118 * Fires when this region is collapsed.
36119 * @param {Roo.LayoutRegion} this
36121 "collapsed" : true,
36124 * Fires when this region is expanded.
36125 * @param {Roo.LayoutRegion} this
36130 * Fires when this region is slid into view.
36131 * @param {Roo.LayoutRegion} this
36133 "slideshow" : true,
36136 * Fires when this region slides out of view.
36137 * @param {Roo.LayoutRegion} this
36139 "slidehide" : true,
36141 * @event panelactivated
36142 * Fires when a panel is activated.
36143 * @param {Roo.LayoutRegion} this
36144 * @param {Roo.ContentPanel} panel The activated panel
36146 "panelactivated" : true,
36149 * Fires when the user resizes this region.
36150 * @param {Roo.LayoutRegion} this
36151 * @param {Number} newSize The new size (width for east/west, height for north/south)
36155 /** A collection of panels in this region. @type Roo.util.MixedCollection */
36156 this.panels = new Roo.util.MixedCollection();
36157 this.panels.getKey = this.getPanelId.createDelegate(this);
36159 this.activePanel = null;
36160 // ensure listeners are added...
36162 if (config.listeners || config.events) {
36163 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
36164 listeners : config.listeners || {},
36165 events : config.events || {}
36169 if(skipConfig !== true){
36170 this.applyConfig(config);
36174 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
36176 getPanelId : function(p){
36180 applyConfig : function(config){
36181 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36182 this.config = config;
36187 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
36188 * the width, for horizontal (north, south) the height.
36189 * @param {Number} newSize The new width or height
36191 resizeTo : function(newSize){
36192 var el = this.el ? this.el :
36193 (this.activePanel ? this.activePanel.getEl() : null);
36195 switch(this.position){
36198 el.setWidth(newSize);
36199 this.fireEvent("resized", this, newSize);
36203 el.setHeight(newSize);
36204 this.fireEvent("resized", this, newSize);
36210 getBox : function(){
36211 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
36214 getMargins : function(){
36215 return this.margins;
36218 updateBox : function(box){
36220 var el = this.activePanel.getEl();
36221 el.dom.style.left = box.x + "px";
36222 el.dom.style.top = box.y + "px";
36223 this.activePanel.setSize(box.width, box.height);
36227 * Returns the container element for this region.
36228 * @return {Roo.Element}
36230 getEl : function(){
36231 return this.activePanel;
36235 * Returns true if this region is currently visible.
36236 * @return {Boolean}
36238 isVisible : function(){
36239 return this.activePanel ? true : false;
36242 setActivePanel : function(panel){
36243 panel = this.getPanel(panel);
36244 if(this.activePanel && this.activePanel != panel){
36245 this.activePanel.setActiveState(false);
36246 this.activePanel.getEl().setLeftTop(-10000,-10000);
36248 this.activePanel = panel;
36249 panel.setActiveState(true);
36251 panel.setSize(this.box.width, this.box.height);
36253 this.fireEvent("panelactivated", this, panel);
36254 this.fireEvent("invalidated");
36258 * Show the specified panel.
36259 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
36260 * @return {Roo.ContentPanel} The shown panel or null
36262 showPanel : function(panel){
36263 panel = this.getPanel(panel);
36265 this.setActivePanel(panel);
36271 * Get the active panel for this region.
36272 * @return {Roo.ContentPanel} The active panel or null
36274 getActivePanel : function(){
36275 return this.activePanel;
36279 * Add the passed ContentPanel(s)
36280 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36281 * @return {Roo.ContentPanel} The panel added (if only one was added)
36283 add : function(panel){
36284 if(arguments.length > 1){
36285 for(var i = 0, len = arguments.length; i < len; i++) {
36286 this.add(arguments[i]);
36290 if(this.hasPanel(panel)){
36291 this.showPanel(panel);
36294 var el = panel.getEl();
36295 if(el.dom.parentNode != this.mgr.el.dom){
36296 this.mgr.el.dom.appendChild(el.dom);
36298 if(panel.setRegion){
36299 panel.setRegion(this);
36301 this.panels.add(panel);
36302 el.setStyle("position", "absolute");
36303 if(!panel.background){
36304 this.setActivePanel(panel);
36305 if(this.config.initialSize && this.panels.getCount()==1){
36306 this.resizeTo(this.config.initialSize);
36309 this.fireEvent("paneladded", this, panel);
36314 * Returns true if the panel is in this region.
36315 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36316 * @return {Boolean}
36318 hasPanel : function(panel){
36319 if(typeof panel == "object"){ // must be panel obj
36320 panel = panel.getId();
36322 return this.getPanel(panel) ? true : false;
36326 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36327 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36328 * @param {Boolean} preservePanel Overrides the config preservePanel option
36329 * @return {Roo.ContentPanel} The panel that was removed
36331 remove : function(panel, preservePanel){
36332 panel = this.getPanel(panel);
36337 this.fireEvent("beforeremove", this, panel, e);
36338 if(e.cancel === true){
36341 var panelId = panel.getId();
36342 this.panels.removeKey(panelId);
36347 * Returns the panel specified or null if it's not in this region.
36348 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36349 * @return {Roo.ContentPanel}
36351 getPanel : function(id){
36352 if(typeof id == "object"){ // must be panel obj
36355 return this.panels.get(id);
36359 * Returns this regions position (north/south/east/west/center).
36362 getPosition: function(){
36363 return this.position;
36367 * Ext JS Library 1.1.1
36368 * Copyright(c) 2006-2007, Ext JS, LLC.
36370 * Originally Released Under LGPL - original licence link has changed is not relivant.
36373 * <script type="text/javascript">
36377 * @class Roo.bootstrap.layout.Region
36378 * @extends Roo.bootstrap.layout.Basic
36379 * This class represents a region in a layout manager.
36381 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36382 * @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})
36383 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
36384 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
36385 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
36386 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
36387 * @cfg {String} title The title for the region (overrides panel titles)
36388 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
36389 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36390 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
36391 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36392 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
36393 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36394 * the space available, similar to FireFox 1.5 tabs (defaults to false)
36395 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
36396 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
36397 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
36399 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
36400 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
36401 * @cfg {Boolean} disableTabTips True to disable tab tooltips
36402 * @cfg {Number} width For East/West panels
36403 * @cfg {Number} height For North/South panels
36404 * @cfg {Boolean} split To show the splitter
36405 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
36407 * @cfg {string} cls Extra CSS classes to add to region
36409 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
36410 * @cfg {string} region the region that it inhabits..
36413 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
36414 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
36416 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
36417 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
36418 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
36420 Roo.bootstrap.layout.Region = function(config)
36422 this.applyConfig(config);
36424 var mgr = config.mgr;
36425 var pos = config.region;
36426 config.skipConfig = true;
36427 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36430 this.onRender(mgr.el);
36433 this.visible = true;
36434 this.collapsed = false;
36435 this.unrendered_panels = [];
36438 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36440 position: '', // set by wrapper (eg. north/south etc..)
36441 unrendered_panels : null, // unrendered panels.
36443 tabPosition : false,
36445 mgr: false, // points to 'Border'
36448 createBody : function(){
36449 /** This region's body element
36450 * @type Roo.Element */
36451 this.bodyEl = this.el.createChild({
36453 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36457 onRender: function(ctr, pos)
36459 var dh = Roo.DomHelper;
36460 /** This region's container element
36461 * @type Roo.Element */
36462 this.el = dh.append(ctr.dom, {
36464 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36466 /** This region's title element
36467 * @type Roo.Element */
36469 this.titleEl = dh.append(this.el.dom, {
36471 unselectable: "on",
36472 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36474 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
36475 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36479 this.titleEl.enableDisplayMode();
36480 /** This region's title text element
36481 * @type HTMLElement */
36482 this.titleTextEl = this.titleEl.dom.firstChild;
36483 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36485 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36486 this.closeBtn.enableDisplayMode();
36487 this.closeBtn.on("click", this.closeClicked, this);
36488 this.closeBtn.hide();
36490 this.createBody(this.config);
36491 if(this.config.hideWhenEmpty){
36493 this.on("paneladded", this.validateVisibility, this);
36494 this.on("panelremoved", this.validateVisibility, this);
36496 if(this.autoScroll){
36497 this.bodyEl.setStyle("overflow", "auto");
36499 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36501 //if(c.titlebar !== false){
36502 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36503 this.titleEl.hide();
36505 this.titleEl.show();
36506 if(this.config.title){
36507 this.titleTextEl.innerHTML = this.config.title;
36511 if(this.config.collapsed){
36512 this.collapse(true);
36514 if(this.config.hidden){
36518 if (this.unrendered_panels && this.unrendered_panels.length) {
36519 for (var i =0;i< this.unrendered_panels.length; i++) {
36520 this.add(this.unrendered_panels[i]);
36522 this.unrendered_panels = null;
36528 applyConfig : function(c)
36531 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36532 var dh = Roo.DomHelper;
36533 if(c.titlebar !== false){
36534 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36535 this.collapseBtn.on("click", this.collapse, this);
36536 this.collapseBtn.enableDisplayMode();
36538 if(c.showPin === true || this.showPin){
36539 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36540 this.stickBtn.enableDisplayMode();
36541 this.stickBtn.on("click", this.expand, this);
36542 this.stickBtn.hide();
36547 /** This region's collapsed element
36548 * @type Roo.Element */
36551 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36552 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36555 if(c.floatable !== false){
36556 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36557 this.collapsedEl.on("click", this.collapseClick, this);
36560 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36561 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36562 id: "message", unselectable: "on", style:{"float":"left"}});
36563 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36565 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36566 this.expandBtn.on("click", this.expand, this);
36570 if(this.collapseBtn){
36571 this.collapseBtn.setVisible(c.collapsible == true);
36574 this.cmargins = c.cmargins || this.cmargins ||
36575 (this.position == "west" || this.position == "east" ?
36576 {top: 0, left: 2, right:2, bottom: 0} :
36577 {top: 2, left: 0, right:0, bottom: 2});
36579 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36582 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36584 this.autoScroll = c.autoScroll || false;
36589 this.duration = c.duration || .30;
36590 this.slideDuration = c.slideDuration || .45;
36595 * Returns true if this region is currently visible.
36596 * @return {Boolean}
36598 isVisible : function(){
36599 return this.visible;
36603 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36604 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
36606 //setCollapsedTitle : function(title){
36607 // title = title || " ";
36608 // if(this.collapsedTitleTextEl){
36609 // this.collapsedTitleTextEl.innerHTML = title;
36613 getBox : function(){
36615 // if(!this.collapsed){
36616 b = this.el.getBox(false, true);
36618 // b = this.collapsedEl.getBox(false, true);
36623 getMargins : function(){
36624 return this.margins;
36625 //return this.collapsed ? this.cmargins : this.margins;
36628 highlight : function(){
36629 this.el.addClass("x-layout-panel-dragover");
36632 unhighlight : function(){
36633 this.el.removeClass("x-layout-panel-dragover");
36636 updateBox : function(box)
36638 if (!this.bodyEl) {
36639 return; // not rendered yet..
36643 if(!this.collapsed){
36644 this.el.dom.style.left = box.x + "px";
36645 this.el.dom.style.top = box.y + "px";
36646 this.updateBody(box.width, box.height);
36648 this.collapsedEl.dom.style.left = box.x + "px";
36649 this.collapsedEl.dom.style.top = box.y + "px";
36650 this.collapsedEl.setSize(box.width, box.height);
36653 this.tabs.autoSizeTabs();
36657 updateBody : function(w, h)
36660 this.el.setWidth(w);
36661 w -= this.el.getBorderWidth("rl");
36662 if(this.config.adjustments){
36663 w += this.config.adjustments[0];
36666 if(h !== null && h > 0){
36667 this.el.setHeight(h);
36668 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36669 h -= this.el.getBorderWidth("tb");
36670 if(this.config.adjustments){
36671 h += this.config.adjustments[1];
36673 this.bodyEl.setHeight(h);
36675 h = this.tabs.syncHeight(h);
36678 if(this.panelSize){
36679 w = w !== null ? w : this.panelSize.width;
36680 h = h !== null ? h : this.panelSize.height;
36682 if(this.activePanel){
36683 var el = this.activePanel.getEl();
36684 w = w !== null ? w : el.getWidth();
36685 h = h !== null ? h : el.getHeight();
36686 this.panelSize = {width: w, height: h};
36687 this.activePanel.setSize(w, h);
36689 if(Roo.isIE && this.tabs){
36690 this.tabs.el.repaint();
36695 * Returns the container element for this region.
36696 * @return {Roo.Element}
36698 getEl : function(){
36703 * Hides this region.
36706 //if(!this.collapsed){
36707 this.el.dom.style.left = "-2000px";
36710 // this.collapsedEl.dom.style.left = "-2000px";
36711 // this.collapsedEl.hide();
36713 this.visible = false;
36714 this.fireEvent("visibilitychange", this, false);
36718 * Shows this region if it was previously hidden.
36721 //if(!this.collapsed){
36724 // this.collapsedEl.show();
36726 this.visible = true;
36727 this.fireEvent("visibilitychange", this, true);
36730 closeClicked : function(){
36731 if(this.activePanel){
36732 this.remove(this.activePanel);
36736 collapseClick : function(e){
36738 e.stopPropagation();
36741 e.stopPropagation();
36747 * Collapses this region.
36748 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36751 collapse : function(skipAnim, skipCheck = false){
36752 if(this.collapsed) {
36756 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36758 this.collapsed = true;
36760 this.split.el.hide();
36762 if(this.config.animate && skipAnim !== true){
36763 this.fireEvent("invalidated", this);
36764 this.animateCollapse();
36766 this.el.setLocation(-20000,-20000);
36768 this.collapsedEl.show();
36769 this.fireEvent("collapsed", this);
36770 this.fireEvent("invalidated", this);
36776 animateCollapse : function(){
36781 * Expands this region if it was previously collapsed.
36782 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36783 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36786 expand : function(e, skipAnim){
36788 e.stopPropagation();
36790 if(!this.collapsed || this.el.hasActiveFx()) {
36794 this.afterSlideIn();
36797 this.collapsed = false;
36798 if(this.config.animate && skipAnim !== true){
36799 this.animateExpand();
36803 this.split.el.show();
36805 this.collapsedEl.setLocation(-2000,-2000);
36806 this.collapsedEl.hide();
36807 this.fireEvent("invalidated", this);
36808 this.fireEvent("expanded", this);
36812 animateExpand : function(){
36816 initTabs : function()
36818 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36820 var ts = new Roo.bootstrap.panel.Tabs({
36821 el: this.bodyEl.dom,
36823 tabPosition: this.tabPosition ? this.tabPosition : 'top',
36824 disableTooltips: this.config.disableTabTips,
36825 toolbar : this.config.toolbar
36828 if(this.config.hideTabs){
36829 ts.stripWrap.setDisplayed(false);
36832 ts.resizeTabs = this.config.resizeTabs === true;
36833 ts.minTabWidth = this.config.minTabWidth || 40;
36834 ts.maxTabWidth = this.config.maxTabWidth || 250;
36835 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36836 ts.monitorResize = false;
36837 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36838 ts.bodyEl.addClass('roo-layout-tabs-body');
36839 this.panels.each(this.initPanelAsTab, this);
36842 initPanelAsTab : function(panel){
36843 var ti = this.tabs.addTab(
36847 this.config.closeOnTab && panel.isClosable(),
36850 if(panel.tabTip !== undefined){
36851 ti.setTooltip(panel.tabTip);
36853 ti.on("activate", function(){
36854 this.setActivePanel(panel);
36857 if(this.config.closeOnTab){
36858 ti.on("beforeclose", function(t, e){
36860 this.remove(panel);
36864 panel.tabItem = ti;
36869 updatePanelTitle : function(panel, title)
36871 if(this.activePanel == panel){
36872 this.updateTitle(title);
36875 var ti = this.tabs.getTab(panel.getEl().id);
36877 if(panel.tabTip !== undefined){
36878 ti.setTooltip(panel.tabTip);
36883 updateTitle : function(title){
36884 if(this.titleTextEl && !this.config.title){
36885 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36889 setActivePanel : function(panel)
36891 panel = this.getPanel(panel);
36892 if(this.activePanel && this.activePanel != panel){
36893 if(this.activePanel.setActiveState(false) === false){
36897 this.activePanel = panel;
36898 panel.setActiveState(true);
36899 if(this.panelSize){
36900 panel.setSize(this.panelSize.width, this.panelSize.height);
36903 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36905 this.updateTitle(panel.getTitle());
36907 this.fireEvent("invalidated", this);
36909 this.fireEvent("panelactivated", this, panel);
36913 * Shows the specified panel.
36914 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36915 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36917 showPanel : function(panel)
36919 panel = this.getPanel(panel);
36922 var tab = this.tabs.getTab(panel.getEl().id);
36923 if(tab.isHidden()){
36924 this.tabs.unhideTab(tab.id);
36928 this.setActivePanel(panel);
36935 * Get the active panel for this region.
36936 * @return {Roo.ContentPanel} The active panel or null
36938 getActivePanel : function(){
36939 return this.activePanel;
36942 validateVisibility : function(){
36943 if(this.panels.getCount() < 1){
36944 this.updateTitle(" ");
36945 this.closeBtn.hide();
36948 if(!this.isVisible()){
36955 * Adds the passed ContentPanel(s) to this region.
36956 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36957 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36959 add : function(panel)
36961 if(arguments.length > 1){
36962 for(var i = 0, len = arguments.length; i < len; i++) {
36963 this.add(arguments[i]);
36968 // if we have not been rendered yet, then we can not really do much of this..
36969 if (!this.bodyEl) {
36970 this.unrendered_panels.push(panel);
36977 if(this.hasPanel(panel)){
36978 this.showPanel(panel);
36981 panel.setRegion(this);
36982 this.panels.add(panel);
36983 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36984 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36985 // and hide them... ???
36986 this.bodyEl.dom.appendChild(panel.getEl().dom);
36987 if(panel.background !== true){
36988 this.setActivePanel(panel);
36990 this.fireEvent("paneladded", this, panel);
36997 this.initPanelAsTab(panel);
37001 if(panel.background !== true){
37002 this.tabs.activate(panel.getEl().id);
37004 this.fireEvent("paneladded", this, panel);
37009 * Hides the tab for the specified panel.
37010 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37012 hidePanel : function(panel){
37013 if(this.tabs && (panel = this.getPanel(panel))){
37014 this.tabs.hideTab(panel.getEl().id);
37019 * Unhides the tab for a previously hidden panel.
37020 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37022 unhidePanel : function(panel){
37023 if(this.tabs && (panel = this.getPanel(panel))){
37024 this.tabs.unhideTab(panel.getEl().id);
37028 clearPanels : function(){
37029 while(this.panels.getCount() > 0){
37030 this.remove(this.panels.first());
37035 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37036 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37037 * @param {Boolean} preservePanel Overrides the config preservePanel option
37038 * @return {Roo.ContentPanel} The panel that was removed
37040 remove : function(panel, preservePanel)
37042 panel = this.getPanel(panel);
37047 this.fireEvent("beforeremove", this, panel, e);
37048 if(e.cancel === true){
37051 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
37052 var panelId = panel.getId();
37053 this.panels.removeKey(panelId);
37055 document.body.appendChild(panel.getEl().dom);
37058 this.tabs.removeTab(panel.getEl().id);
37059 }else if (!preservePanel){
37060 this.bodyEl.dom.removeChild(panel.getEl().dom);
37062 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
37063 var p = this.panels.first();
37064 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
37065 tempEl.appendChild(p.getEl().dom);
37066 this.bodyEl.update("");
37067 this.bodyEl.dom.appendChild(p.getEl().dom);
37069 this.updateTitle(p.getTitle());
37071 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
37072 this.setActivePanel(p);
37074 panel.setRegion(null);
37075 if(this.activePanel == panel){
37076 this.activePanel = null;
37078 if(this.config.autoDestroy !== false && preservePanel !== true){
37079 try{panel.destroy();}catch(e){}
37081 this.fireEvent("panelremoved", this, panel);
37086 * Returns the TabPanel component used by this region
37087 * @return {Roo.TabPanel}
37089 getTabs : function(){
37093 createTool : function(parentEl, className){
37094 var btn = Roo.DomHelper.append(parentEl, {
37096 cls: "x-layout-tools-button",
37099 cls: "roo-layout-tools-button-inner " + className,
37103 btn.addClassOnOver("roo-layout-tools-button-over");
37108 * Ext JS Library 1.1.1
37109 * Copyright(c) 2006-2007, Ext JS, LLC.
37111 * Originally Released Under LGPL - original licence link has changed is not relivant.
37114 * <script type="text/javascript">
37120 * @class Roo.SplitLayoutRegion
37121 * @extends Roo.LayoutRegion
37122 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
37124 Roo.bootstrap.layout.Split = function(config){
37125 this.cursor = config.cursor;
37126 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
37129 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
37131 splitTip : "Drag to resize.",
37132 collapsibleSplitTip : "Drag to resize. Double click to hide.",
37133 useSplitTips : false,
37135 applyConfig : function(config){
37136 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
37139 onRender : function(ctr,pos) {
37141 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
37142 if(!this.config.split){
37147 var splitEl = Roo.DomHelper.append(ctr.dom, {
37149 id: this.el.id + "-split",
37150 cls: "roo-layout-split roo-layout-split-"+this.position,
37153 /** The SplitBar for this region
37154 * @type Roo.SplitBar */
37155 // does not exist yet...
37156 Roo.log([this.position, this.orientation]);
37158 this.split = new Roo.bootstrap.SplitBar({
37159 dragElement : splitEl,
37160 resizingElement: this.el,
37161 orientation : this.orientation
37164 this.split.on("moved", this.onSplitMove, this);
37165 this.split.useShim = this.config.useShim === true;
37166 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
37167 if(this.useSplitTips){
37168 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
37170 //if(config.collapsible){
37171 // this.split.el.on("dblclick", this.collapse, this);
37174 if(typeof this.config.minSize != "undefined"){
37175 this.split.minSize = this.config.minSize;
37177 if(typeof this.config.maxSize != "undefined"){
37178 this.split.maxSize = this.config.maxSize;
37180 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
37181 this.hideSplitter();
37186 getHMaxSize : function(){
37187 var cmax = this.config.maxSize || 10000;
37188 var center = this.mgr.getRegion("center");
37189 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
37192 getVMaxSize : function(){
37193 var cmax = this.config.maxSize || 10000;
37194 var center = this.mgr.getRegion("center");
37195 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
37198 onSplitMove : function(split, newSize){
37199 this.fireEvent("resized", this, newSize);
37203 * Returns the {@link Roo.SplitBar} for this region.
37204 * @return {Roo.SplitBar}
37206 getSplitBar : function(){
37211 this.hideSplitter();
37212 Roo.bootstrap.layout.Split.superclass.hide.call(this);
37215 hideSplitter : function(){
37217 this.split.el.setLocation(-2000,-2000);
37218 this.split.el.hide();
37224 this.split.el.show();
37226 Roo.bootstrap.layout.Split.superclass.show.call(this);
37229 beforeSlide: function(){
37230 if(Roo.isGecko){// firefox overflow auto bug workaround
37231 this.bodyEl.clip();
37233 this.tabs.bodyEl.clip();
37235 if(this.activePanel){
37236 this.activePanel.getEl().clip();
37238 if(this.activePanel.beforeSlide){
37239 this.activePanel.beforeSlide();
37245 afterSlide : function(){
37246 if(Roo.isGecko){// firefox overflow auto bug workaround
37247 this.bodyEl.unclip();
37249 this.tabs.bodyEl.unclip();
37251 if(this.activePanel){
37252 this.activePanel.getEl().unclip();
37253 if(this.activePanel.afterSlide){
37254 this.activePanel.afterSlide();
37260 initAutoHide : function(){
37261 if(this.autoHide !== false){
37262 if(!this.autoHideHd){
37263 var st = new Roo.util.DelayedTask(this.slideIn, this);
37264 this.autoHideHd = {
37265 "mouseout": function(e){
37266 if(!e.within(this.el, true)){
37270 "mouseover" : function(e){
37276 this.el.on(this.autoHideHd);
37280 clearAutoHide : function(){
37281 if(this.autoHide !== false){
37282 this.el.un("mouseout", this.autoHideHd.mouseout);
37283 this.el.un("mouseover", this.autoHideHd.mouseover);
37287 clearMonitor : function(){
37288 Roo.get(document).un("click", this.slideInIf, this);
37291 // these names are backwards but not changed for compat
37292 slideOut : function(){
37293 if(this.isSlid || this.el.hasActiveFx()){
37296 this.isSlid = true;
37297 if(this.collapseBtn){
37298 this.collapseBtn.hide();
37300 this.closeBtnState = this.closeBtn.getStyle('display');
37301 this.closeBtn.hide();
37303 this.stickBtn.show();
37306 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37307 this.beforeSlide();
37308 this.el.setStyle("z-index", 10001);
37309 this.el.slideIn(this.getSlideAnchor(), {
37310 callback: function(){
37312 this.initAutoHide();
37313 Roo.get(document).on("click", this.slideInIf, this);
37314 this.fireEvent("slideshow", this);
37321 afterSlideIn : function(){
37322 this.clearAutoHide();
37323 this.isSlid = false;
37324 this.clearMonitor();
37325 this.el.setStyle("z-index", "");
37326 if(this.collapseBtn){
37327 this.collapseBtn.show();
37329 this.closeBtn.setStyle('display', this.closeBtnState);
37331 this.stickBtn.hide();
37333 this.fireEvent("slidehide", this);
37336 slideIn : function(cb){
37337 if(!this.isSlid || this.el.hasActiveFx()){
37341 this.isSlid = false;
37342 this.beforeSlide();
37343 this.el.slideOut(this.getSlideAnchor(), {
37344 callback: function(){
37345 this.el.setLeftTop(-10000, -10000);
37347 this.afterSlideIn();
37355 slideInIf : function(e){
37356 if(!e.within(this.el)){
37361 animateCollapse : function(){
37362 this.beforeSlide();
37363 this.el.setStyle("z-index", 20000);
37364 var anchor = this.getSlideAnchor();
37365 this.el.slideOut(anchor, {
37366 callback : function(){
37367 this.el.setStyle("z-index", "");
37368 this.collapsedEl.slideIn(anchor, {duration:.3});
37370 this.el.setLocation(-10000,-10000);
37372 this.fireEvent("collapsed", this);
37379 animateExpand : function(){
37380 this.beforeSlide();
37381 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37382 this.el.setStyle("z-index", 20000);
37383 this.collapsedEl.hide({
37386 this.el.slideIn(this.getSlideAnchor(), {
37387 callback : function(){
37388 this.el.setStyle("z-index", "");
37391 this.split.el.show();
37393 this.fireEvent("invalidated", this);
37394 this.fireEvent("expanded", this);
37422 getAnchor : function(){
37423 return this.anchors[this.position];
37426 getCollapseAnchor : function(){
37427 return this.canchors[this.position];
37430 getSlideAnchor : function(){
37431 return this.sanchors[this.position];
37434 getAlignAdj : function(){
37435 var cm = this.cmargins;
37436 switch(this.position){
37452 getExpandAdj : function(){
37453 var c = this.collapsedEl, cm = this.cmargins;
37454 switch(this.position){
37456 return [-(cm.right+c.getWidth()+cm.left), 0];
37459 return [cm.right+c.getWidth()+cm.left, 0];
37462 return [0, -(cm.top+cm.bottom+c.getHeight())];
37465 return [0, cm.top+cm.bottom+c.getHeight()];
37471 * Ext JS Library 1.1.1
37472 * Copyright(c) 2006-2007, Ext JS, LLC.
37474 * Originally Released Under LGPL - original licence link has changed is not relivant.
37477 * <script type="text/javascript">
37480 * These classes are private internal classes
37482 Roo.bootstrap.layout.Center = function(config){
37483 config.region = "center";
37484 Roo.bootstrap.layout.Region.call(this, config);
37485 this.visible = true;
37486 this.minWidth = config.minWidth || 20;
37487 this.minHeight = config.minHeight || 20;
37490 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37492 // center panel can't be hidden
37496 // center panel can't be hidden
37499 getMinWidth: function(){
37500 return this.minWidth;
37503 getMinHeight: function(){
37504 return this.minHeight;
37518 Roo.bootstrap.layout.North = function(config)
37520 config.region = 'north';
37521 config.cursor = 'n-resize';
37523 Roo.bootstrap.layout.Split.call(this, config);
37527 this.split.placement = Roo.bootstrap.SplitBar.TOP;
37528 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37529 this.split.el.addClass("roo-layout-split-v");
37531 var size = config.initialSize || config.height;
37532 if(typeof size != "undefined"){
37533 this.el.setHeight(size);
37536 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37538 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37542 getBox : function(){
37543 if(this.collapsed){
37544 return this.collapsedEl.getBox();
37546 var box = this.el.getBox();
37548 box.height += this.split.el.getHeight();
37553 updateBox : function(box){
37554 if(this.split && !this.collapsed){
37555 box.height -= this.split.el.getHeight();
37556 this.split.el.setLeft(box.x);
37557 this.split.el.setTop(box.y+box.height);
37558 this.split.el.setWidth(box.width);
37560 if(this.collapsed){
37561 this.updateBody(box.width, null);
37563 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37571 Roo.bootstrap.layout.South = function(config){
37572 config.region = 'south';
37573 config.cursor = 's-resize';
37574 Roo.bootstrap.layout.Split.call(this, config);
37576 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37577 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37578 this.split.el.addClass("roo-layout-split-v");
37580 var size = config.initialSize || config.height;
37581 if(typeof size != "undefined"){
37582 this.el.setHeight(size);
37586 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37587 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37588 getBox : function(){
37589 if(this.collapsed){
37590 return this.collapsedEl.getBox();
37592 var box = this.el.getBox();
37594 var sh = this.split.el.getHeight();
37601 updateBox : function(box){
37602 if(this.split && !this.collapsed){
37603 var sh = this.split.el.getHeight();
37606 this.split.el.setLeft(box.x);
37607 this.split.el.setTop(box.y-sh);
37608 this.split.el.setWidth(box.width);
37610 if(this.collapsed){
37611 this.updateBody(box.width, null);
37613 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37617 Roo.bootstrap.layout.East = function(config){
37618 config.region = "east";
37619 config.cursor = "e-resize";
37620 Roo.bootstrap.layout.Split.call(this, config);
37622 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37623 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37624 this.split.el.addClass("roo-layout-split-h");
37626 var size = config.initialSize || config.width;
37627 if(typeof size != "undefined"){
37628 this.el.setWidth(size);
37631 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37632 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37633 getBox : function(){
37634 if(this.collapsed){
37635 return this.collapsedEl.getBox();
37637 var box = this.el.getBox();
37639 var sw = this.split.el.getWidth();
37646 updateBox : function(box){
37647 if(this.split && !this.collapsed){
37648 var sw = this.split.el.getWidth();
37650 this.split.el.setLeft(box.x);
37651 this.split.el.setTop(box.y);
37652 this.split.el.setHeight(box.height);
37655 if(this.collapsed){
37656 this.updateBody(null, box.height);
37658 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37662 Roo.bootstrap.layout.West = function(config){
37663 config.region = "west";
37664 config.cursor = "w-resize";
37666 Roo.bootstrap.layout.Split.call(this, config);
37668 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37669 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37670 this.split.el.addClass("roo-layout-split-h");
37674 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37675 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37677 onRender: function(ctr, pos)
37679 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37680 var size = this.config.initialSize || this.config.width;
37681 if(typeof size != "undefined"){
37682 this.el.setWidth(size);
37686 getBox : function(){
37687 if(this.collapsed){
37688 return this.collapsedEl.getBox();
37690 var box = this.el.getBox();
37692 box.width += this.split.el.getWidth();
37697 updateBox : function(box){
37698 if(this.split && !this.collapsed){
37699 var sw = this.split.el.getWidth();
37701 this.split.el.setLeft(box.x+box.width);
37702 this.split.el.setTop(box.y);
37703 this.split.el.setHeight(box.height);
37705 if(this.collapsed){
37706 this.updateBody(null, box.height);
37708 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37710 });Roo.namespace("Roo.bootstrap.panel");/*
37712 * Ext JS Library 1.1.1
37713 * Copyright(c) 2006-2007, Ext JS, LLC.
37715 * Originally Released Under LGPL - original licence link has changed is not relivant.
37718 * <script type="text/javascript">
37721 * @class Roo.ContentPanel
37722 * @extends Roo.util.Observable
37723 * A basic ContentPanel element.
37724 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37725 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37726 * @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
37727 * @cfg {Boolean} closable True if the panel can be closed/removed
37728 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37729 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37730 * @cfg {Toolbar} toolbar A toolbar for this panel
37731 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37732 * @cfg {String} title The title for this panel
37733 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37734 * @cfg {String} url Calls {@link #setUrl} with this value
37735 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37736 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37737 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37738 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37739 * @cfg {Boolean} badges render the badges
37742 * Create a new ContentPanel.
37743 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37744 * @param {String/Object} config A string to set only the title or a config object
37745 * @param {String} content (optional) Set the HTML content for this panel
37746 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37748 Roo.bootstrap.panel.Content = function( config){
37750 this.tpl = config.tpl || false;
37752 var el = config.el;
37753 var content = config.content;
37755 if(config.autoCreate){ // xtype is available if this is called from factory
37758 this.el = Roo.get(el);
37759 if(!this.el && config && config.autoCreate){
37760 if(typeof config.autoCreate == "object"){
37761 if(!config.autoCreate.id){
37762 config.autoCreate.id = config.id||el;
37764 this.el = Roo.DomHelper.append(document.body,
37765 config.autoCreate, true);
37767 var elcfg = { tag: "div",
37768 cls: "roo-layout-inactive-content",
37772 elcfg.html = config.html;
37776 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37779 this.closable = false;
37780 this.loaded = false;
37781 this.active = false;
37784 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37786 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37788 this.wrapEl = this.el; //this.el.wrap();
37790 if (config.toolbar.items) {
37791 ti = config.toolbar.items ;
37792 delete config.toolbar.items ;
37796 this.toolbar.render(this.wrapEl, 'before');
37797 for(var i =0;i < ti.length;i++) {
37798 // Roo.log(['add child', items[i]]);
37799 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37801 this.toolbar.items = nitems;
37802 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37803 delete config.toolbar;
37807 // xtype created footer. - not sure if will work as we normally have to render first..
37808 if (this.footer && !this.footer.el && this.footer.xtype) {
37809 if (!this.wrapEl) {
37810 this.wrapEl = this.el.wrap();
37813 this.footer.container = this.wrapEl.createChild();
37815 this.footer = Roo.factory(this.footer, Roo);
37820 if(typeof config == "string"){
37821 this.title = config;
37823 Roo.apply(this, config);
37827 this.resizeEl = Roo.get(this.resizeEl, true);
37829 this.resizeEl = this.el;
37831 // handle view.xtype
37839 * Fires when this panel is activated.
37840 * @param {Roo.ContentPanel} this
37844 * @event deactivate
37845 * Fires when this panel is activated.
37846 * @param {Roo.ContentPanel} this
37848 "deactivate" : true,
37852 * Fires when this panel is resized if fitToFrame is true.
37853 * @param {Roo.ContentPanel} this
37854 * @param {Number} width The width after any component adjustments
37855 * @param {Number} height The height after any component adjustments
37861 * Fires when this tab is created
37862 * @param {Roo.ContentPanel} this
37873 if(this.autoScroll){
37874 this.resizeEl.setStyle("overflow", "auto");
37876 // fix randome scrolling
37877 //this.el.on('scroll', function() {
37878 // Roo.log('fix random scolling');
37879 // this.scrollTo('top',0);
37882 content = content || this.content;
37884 this.setContent(content);
37886 if(config && config.url){
37887 this.setUrl(this.url, this.params, this.loadOnce);
37892 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37894 if (this.view && typeof(this.view.xtype) != 'undefined') {
37895 this.view.el = this.el.appendChild(document.createElement("div"));
37896 this.view = Roo.factory(this.view);
37897 this.view.render && this.view.render(false, '');
37901 this.fireEvent('render', this);
37904 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37908 setRegion : function(region){
37909 this.region = region;
37910 this.setActiveClass(region && !this.background);
37914 setActiveClass: function(state)
37917 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37918 this.el.setStyle('position','relative');
37920 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37921 this.el.setStyle('position', 'absolute');
37926 * Returns the toolbar for this Panel if one was configured.
37927 * @return {Roo.Toolbar}
37929 getToolbar : function(){
37930 return this.toolbar;
37933 setActiveState : function(active)
37935 this.active = active;
37936 this.setActiveClass(active);
37938 if(this.fireEvent("deactivate", this) === false){
37943 this.fireEvent("activate", this);
37947 * Updates this panel's element
37948 * @param {String} content The new content
37949 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37951 setContent : function(content, loadScripts){
37952 this.el.update(content, loadScripts);
37955 ignoreResize : function(w, h){
37956 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37959 this.lastSize = {width: w, height: h};
37964 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37965 * @return {Roo.UpdateManager} The UpdateManager
37967 getUpdateManager : function(){
37968 return this.el.getUpdateManager();
37971 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37972 * @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:
37975 url: "your-url.php",
37976 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37977 callback: yourFunction,
37978 scope: yourObject, //(optional scope)
37981 text: "Loading...",
37986 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37987 * 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.
37988 * @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}
37989 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37990 * @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.
37991 * @return {Roo.ContentPanel} this
37994 var um = this.el.getUpdateManager();
37995 um.update.apply(um, arguments);
38001 * 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.
38002 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
38003 * @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)
38004 * @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)
38005 * @return {Roo.UpdateManager} The UpdateManager
38007 setUrl : function(url, params, loadOnce){
38008 if(this.refreshDelegate){
38009 this.removeListener("activate", this.refreshDelegate);
38011 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38012 this.on("activate", this.refreshDelegate);
38013 return this.el.getUpdateManager();
38016 _handleRefresh : function(url, params, loadOnce){
38017 if(!loadOnce || !this.loaded){
38018 var updater = this.el.getUpdateManager();
38019 updater.update(url, params, this._setLoaded.createDelegate(this));
38023 _setLoaded : function(){
38024 this.loaded = true;
38028 * Returns this panel's id
38031 getId : function(){
38036 * Returns this panel's element - used by regiosn to add.
38037 * @return {Roo.Element}
38039 getEl : function(){
38040 return this.wrapEl || this.el;
38045 adjustForComponents : function(width, height)
38047 //Roo.log('adjustForComponents ');
38048 if(this.resizeEl != this.el){
38049 width -= this.el.getFrameWidth('lr');
38050 height -= this.el.getFrameWidth('tb');
38053 var te = this.toolbar.getEl();
38054 te.setWidth(width);
38055 height -= te.getHeight();
38058 var te = this.footer.getEl();
38059 te.setWidth(width);
38060 height -= te.getHeight();
38064 if(this.adjustments){
38065 width += this.adjustments[0];
38066 height += this.adjustments[1];
38068 return {"width": width, "height": height};
38071 setSize : function(width, height){
38072 if(this.fitToFrame && !this.ignoreResize(width, height)){
38073 if(this.fitContainer && this.resizeEl != this.el){
38074 this.el.setSize(width, height);
38076 var size = this.adjustForComponents(width, height);
38077 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
38078 this.fireEvent('resize', this, size.width, size.height);
38083 * Returns this panel's title
38086 getTitle : function(){
38088 if (typeof(this.title) != 'object') {
38093 for (var k in this.title) {
38094 if (!this.title.hasOwnProperty(k)) {
38098 if (k.indexOf('-') >= 0) {
38099 var s = k.split('-');
38100 for (var i = 0; i<s.length; i++) {
38101 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
38104 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
38111 * Set this panel's title
38112 * @param {String} title
38114 setTitle : function(title){
38115 this.title = title;
38117 this.region.updatePanelTitle(this, title);
38122 * Returns true is this panel was configured to be closable
38123 * @return {Boolean}
38125 isClosable : function(){
38126 return this.closable;
38129 beforeSlide : function(){
38131 this.resizeEl.clip();
38134 afterSlide : function(){
38136 this.resizeEl.unclip();
38140 * Force a content refresh from the URL specified in the {@link #setUrl} method.
38141 * Will fail silently if the {@link #setUrl} method has not been called.
38142 * This does not activate the panel, just updates its content.
38144 refresh : function(){
38145 if(this.refreshDelegate){
38146 this.loaded = false;
38147 this.refreshDelegate();
38152 * Destroys this panel
38154 destroy : function(){
38155 this.el.removeAllListeners();
38156 var tempEl = document.createElement("span");
38157 tempEl.appendChild(this.el.dom);
38158 tempEl.innerHTML = "";
38164 * form - if the content panel contains a form - this is a reference to it.
38165 * @type {Roo.form.Form}
38169 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
38170 * This contains a reference to it.
38176 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
38186 * @param {Object} cfg Xtype definition of item to add.
38190 getChildContainer: function () {
38191 return this.getEl();
38196 var ret = new Roo.factory(cfg);
38201 if (cfg.xtype.match(/^Form$/)) {
38204 //if (this.footer) {
38205 // el = this.footer.container.insertSibling(false, 'before');
38207 el = this.el.createChild();
38210 this.form = new Roo.form.Form(cfg);
38213 if ( this.form.allItems.length) {
38214 this.form.render(el.dom);
38218 // should only have one of theses..
38219 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
38220 // views.. should not be just added - used named prop 'view''
38222 cfg.el = this.el.appendChild(document.createElement("div"));
38225 var ret = new Roo.factory(cfg);
38227 ret.render && ret.render(false, ''); // render blank..
38237 * @class Roo.bootstrap.panel.Grid
38238 * @extends Roo.bootstrap.panel.Content
38240 * Create a new GridPanel.
38241 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
38242 * @param {Object} config A the config object
38248 Roo.bootstrap.panel.Grid = function(config)
38252 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
38253 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
38255 config.el = this.wrapper;
38256 //this.el = this.wrapper;
38258 if (config.container) {
38259 // ctor'ed from a Border/panel.grid
38262 this.wrapper.setStyle("overflow", "hidden");
38263 this.wrapper.addClass('roo-grid-container');
38268 if(config.toolbar){
38269 var tool_el = this.wrapper.createChild();
38270 this.toolbar = Roo.factory(config.toolbar);
38272 if (config.toolbar.items) {
38273 ti = config.toolbar.items ;
38274 delete config.toolbar.items ;
38278 this.toolbar.render(tool_el);
38279 for(var i =0;i < ti.length;i++) {
38280 // Roo.log(['add child', items[i]]);
38281 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38283 this.toolbar.items = nitems;
38285 delete config.toolbar;
38288 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
38289 config.grid.scrollBody = true;;
38290 config.grid.monitorWindowResize = false; // turn off autosizing
38291 config.grid.autoHeight = false;
38292 config.grid.autoWidth = false;
38294 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
38296 if (config.background) {
38297 // render grid on panel activation (if panel background)
38298 this.on('activate', function(gp) {
38299 if (!gp.grid.rendered) {
38300 gp.grid.render(this.wrapper);
38301 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
38306 this.grid.render(this.wrapper);
38307 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
38310 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
38311 // ??? needed ??? config.el = this.wrapper;
38316 // xtype created footer. - not sure if will work as we normally have to render first..
38317 if (this.footer && !this.footer.el && this.footer.xtype) {
38319 var ctr = this.grid.getView().getFooterPanel(true);
38320 this.footer.dataSource = this.grid.dataSource;
38321 this.footer = Roo.factory(this.footer, Roo);
38322 this.footer.render(ctr);
38332 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38333 getId : function(){
38334 return this.grid.id;
38338 * Returns the grid for this panel
38339 * @return {Roo.bootstrap.Table}
38341 getGrid : function(){
38345 setSize : function(width, height){
38346 if(!this.ignoreResize(width, height)){
38347 var grid = this.grid;
38348 var size = this.adjustForComponents(width, height);
38349 var gridel = grid.getGridEl();
38350 gridel.setSize(size.width, size.height);
38352 var thd = grid.getGridEl().select('thead',true).first();
38353 var tbd = grid.getGridEl().select('tbody', true).first();
38355 tbd.setSize(width, height - thd.getHeight());
38364 beforeSlide : function(){
38365 this.grid.getView().scroller.clip();
38368 afterSlide : function(){
38369 this.grid.getView().scroller.unclip();
38372 destroy : function(){
38373 this.grid.destroy();
38375 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
38380 * @class Roo.bootstrap.panel.Nest
38381 * @extends Roo.bootstrap.panel.Content
38383 * Create a new Panel, that can contain a layout.Border.
38386 * @param {Roo.BorderLayout} layout The layout for this panel
38387 * @param {String/Object} config A string to set only the title or a config object
38389 Roo.bootstrap.panel.Nest = function(config)
38391 // construct with only one argument..
38392 /* FIXME - implement nicer consturctors
38393 if (layout.layout) {
38395 layout = config.layout;
38396 delete config.layout;
38398 if (layout.xtype && !layout.getEl) {
38399 // then layout needs constructing..
38400 layout = Roo.factory(layout, Roo);
38404 config.el = config.layout.getEl();
38406 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38408 config.layout.monitorWindowResize = false; // turn off autosizing
38409 this.layout = config.layout;
38410 this.layout.getEl().addClass("roo-layout-nested-layout");
38411 this.layout.parent = this;
38418 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38420 setSize : function(width, height){
38421 if(!this.ignoreResize(width, height)){
38422 var size = this.adjustForComponents(width, height);
38423 var el = this.layout.getEl();
38424 if (size.height < 1) {
38425 el.setWidth(size.width);
38427 el.setSize(size.width, size.height);
38429 var touch = el.dom.offsetWidth;
38430 this.layout.layout();
38431 // ie requires a double layout on the first pass
38432 if(Roo.isIE && !this.initialized){
38433 this.initialized = true;
38434 this.layout.layout();
38439 // activate all subpanels if not currently active..
38441 setActiveState : function(active){
38442 this.active = active;
38443 this.setActiveClass(active);
38446 this.fireEvent("deactivate", this);
38450 this.fireEvent("activate", this);
38451 // not sure if this should happen before or after..
38452 if (!this.layout) {
38453 return; // should not happen..
38456 for (var r in this.layout.regions) {
38457 reg = this.layout.getRegion(r);
38458 if (reg.getActivePanel()) {
38459 //reg.showPanel(reg.getActivePanel()); // force it to activate..
38460 reg.setActivePanel(reg.getActivePanel());
38463 if (!reg.panels.length) {
38466 reg.showPanel(reg.getPanel(0));
38475 * Returns the nested BorderLayout for this panel
38476 * @return {Roo.BorderLayout}
38478 getLayout : function(){
38479 return this.layout;
38483 * Adds a xtype elements to the layout of the nested panel
38487 xtype : 'ContentPanel',
38494 xtype : 'NestedLayoutPanel',
38500 items : [ ... list of content panels or nested layout panels.. ]
38504 * @param {Object} cfg Xtype definition of item to add.
38506 addxtype : function(cfg) {
38507 return this.layout.addxtype(cfg);
38512 * Ext JS Library 1.1.1
38513 * Copyright(c) 2006-2007, Ext JS, LLC.
38515 * Originally Released Under LGPL - original licence link has changed is not relivant.
38518 * <script type="text/javascript">
38521 * @class Roo.TabPanel
38522 * @extends Roo.util.Observable
38523 * A lightweight tab container.
38527 // basic tabs 1, built from existing content
38528 var tabs = new Roo.TabPanel("tabs1");
38529 tabs.addTab("script", "View Script");
38530 tabs.addTab("markup", "View Markup");
38531 tabs.activate("script");
38533 // more advanced tabs, built from javascript
38534 var jtabs = new Roo.TabPanel("jtabs");
38535 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38537 // set up the UpdateManager
38538 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38539 var updater = tab2.getUpdateManager();
38540 updater.setDefaultUrl("ajax1.htm");
38541 tab2.on('activate', updater.refresh, updater, true);
38543 // Use setUrl for Ajax loading
38544 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38545 tab3.setUrl("ajax2.htm", null, true);
38548 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38551 jtabs.activate("jtabs-1");
38554 * Create a new TabPanel.
38555 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38556 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38558 Roo.bootstrap.panel.Tabs = function(config){
38560 * The container element for this TabPanel.
38561 * @type Roo.Element
38563 this.el = Roo.get(config.el);
38566 if(typeof config == "boolean"){
38567 this.tabPosition = config ? "bottom" : "top";
38569 Roo.apply(this, config);
38573 if(this.tabPosition == "bottom"){
38574 // if tabs are at the bottom = create the body first.
38575 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38576 this.el.addClass("roo-tabs-bottom");
38578 // next create the tabs holders
38580 if (this.tabPosition == "west"){
38582 var reg = this.region; // fake it..
38584 if (!reg.mgr.parent) {
38587 reg = reg.mgr.parent.region;
38589 Roo.log("got nest?");
38591 if (reg.mgr.getRegion('west')) {
38592 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38593 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38594 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38595 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38596 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38604 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38605 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38606 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38607 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38612 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38615 // finally - if tabs are at the top, then create the body last..
38616 if(this.tabPosition != "bottom"){
38617 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38618 * @type Roo.Element
38620 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38621 this.el.addClass("roo-tabs-top");
38625 this.bodyEl.setStyle("position", "relative");
38627 this.active = null;
38628 this.activateDelegate = this.activate.createDelegate(this);
38633 * Fires when the active tab changes
38634 * @param {Roo.TabPanel} this
38635 * @param {Roo.TabPanelItem} activePanel The new active tab
38639 * @event beforetabchange
38640 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38641 * @param {Roo.TabPanel} this
38642 * @param {Object} e Set cancel to true on this object to cancel the tab change
38643 * @param {Roo.TabPanelItem} tab The tab being changed to
38645 "beforetabchange" : true
38648 Roo.EventManager.onWindowResize(this.onResize, this);
38649 this.cpad = this.el.getPadding("lr");
38650 this.hiddenCount = 0;
38653 // toolbar on the tabbar support...
38654 if (this.toolbar) {
38655 alert("no toolbar support yet");
38656 this.toolbar = false;
38658 var tcfg = this.toolbar;
38659 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
38660 this.toolbar = new Roo.Toolbar(tcfg);
38661 if (Roo.isSafari) {
38662 var tbl = tcfg.container.child('table', true);
38663 tbl.setAttribute('width', '100%');
38671 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38674 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38676 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38678 tabPosition : "top",
38680 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38682 currentTabWidth : 0,
38684 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38688 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38692 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38694 preferredTabWidth : 175,
38696 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38698 resizeTabs : false,
38700 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38702 monitorResize : true,
38704 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
38706 toolbar : false, // set by caller..
38708 region : false, /// set by caller
38710 disableTooltips : true, // not used yet...
38713 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38714 * @param {String} id The id of the div to use <b>or create</b>
38715 * @param {String} text The text for the tab
38716 * @param {String} content (optional) Content to put in the TabPanelItem body
38717 * @param {Boolean} closable (optional) True to create a close icon on the tab
38718 * @return {Roo.TabPanelItem} The created TabPanelItem
38720 addTab : function(id, text, content, closable, tpl)
38722 var item = new Roo.bootstrap.panel.TabItem({
38726 closable : closable,
38729 this.addTabItem(item);
38731 item.setContent(content);
38737 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38738 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38739 * @return {Roo.TabPanelItem}
38741 getTab : function(id){
38742 return this.items[id];
38746 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38747 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38749 hideTab : function(id){
38750 var t = this.items[id];
38753 this.hiddenCount++;
38754 this.autoSizeTabs();
38759 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38760 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38762 unhideTab : function(id){
38763 var t = this.items[id];
38765 t.setHidden(false);
38766 this.hiddenCount--;
38767 this.autoSizeTabs();
38772 * Adds an existing {@link Roo.TabPanelItem}.
38773 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38775 addTabItem : function(item)
38777 this.items[item.id] = item;
38778 this.items.push(item);
38779 this.autoSizeTabs();
38780 // if(this.resizeTabs){
38781 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38782 // this.autoSizeTabs();
38784 // item.autoSize();
38789 * Removes a {@link Roo.TabPanelItem}.
38790 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38792 removeTab : function(id){
38793 var items = this.items;
38794 var tab = items[id];
38795 if(!tab) { return; }
38796 var index = items.indexOf(tab);
38797 if(this.active == tab && items.length > 1){
38798 var newTab = this.getNextAvailable(index);
38803 this.stripEl.dom.removeChild(tab.pnode.dom);
38804 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38805 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38807 items.splice(index, 1);
38808 delete this.items[tab.id];
38809 tab.fireEvent("close", tab);
38810 tab.purgeListeners();
38811 this.autoSizeTabs();
38814 getNextAvailable : function(start){
38815 var items = this.items;
38817 // look for a next tab that will slide over to
38818 // replace the one being removed
38819 while(index < items.length){
38820 var item = items[++index];
38821 if(item && !item.isHidden()){
38825 // if one isn't found select the previous tab (on the left)
38828 var item = items[--index];
38829 if(item && !item.isHidden()){
38837 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38838 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38840 disableTab : function(id){
38841 var tab = this.items[id];
38842 if(tab && this.active != tab){
38848 * Enables a {@link Roo.TabPanelItem} that is disabled.
38849 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38851 enableTab : function(id){
38852 var tab = this.items[id];
38857 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38858 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38859 * @return {Roo.TabPanelItem} The TabPanelItem.
38861 activate : function(id)
38863 //Roo.log('activite:' + id);
38865 var tab = this.items[id];
38869 if(tab == this.active || tab.disabled){
38873 this.fireEvent("beforetabchange", this, e, tab);
38874 if(e.cancel !== true && !tab.disabled){
38876 this.active.hide();
38878 this.active = this.items[id];
38879 this.active.show();
38880 this.fireEvent("tabchange", this, this.active);
38886 * Gets the active {@link Roo.TabPanelItem}.
38887 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38889 getActiveTab : function(){
38890 return this.active;
38894 * Updates the tab body element to fit the height of the container element
38895 * for overflow scrolling
38896 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38898 syncHeight : function(targetHeight){
38899 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38900 var bm = this.bodyEl.getMargins();
38901 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38902 this.bodyEl.setHeight(newHeight);
38906 onResize : function(){
38907 if(this.monitorResize){
38908 this.autoSizeTabs();
38913 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38915 beginUpdate : function(){
38916 this.updating = true;
38920 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38922 endUpdate : function(){
38923 this.updating = false;
38924 this.autoSizeTabs();
38928 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38930 autoSizeTabs : function()
38932 var count = this.items.length;
38933 var vcount = count - this.hiddenCount;
38936 this.stripEl.hide();
38938 this.stripEl.show();
38941 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38946 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38947 var availWidth = Math.floor(w / vcount);
38948 var b = this.stripBody;
38949 if(b.getWidth() > w){
38950 var tabs = this.items;
38951 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38952 if(availWidth < this.minTabWidth){
38953 /*if(!this.sleft){ // incomplete scrolling code
38954 this.createScrollButtons();
38957 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38960 if(this.currentTabWidth < this.preferredTabWidth){
38961 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38967 * Returns the number of tabs in this TabPanel.
38970 getCount : function(){
38971 return this.items.length;
38975 * Resizes all the tabs to the passed width
38976 * @param {Number} The new width
38978 setTabWidth : function(width){
38979 this.currentTabWidth = width;
38980 for(var i = 0, len = this.items.length; i < len; i++) {
38981 if(!this.items[i].isHidden()) {
38982 this.items[i].setWidth(width);
38988 * Destroys this TabPanel
38989 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38991 destroy : function(removeEl){
38992 Roo.EventManager.removeResizeListener(this.onResize, this);
38993 for(var i = 0, len = this.items.length; i < len; i++){
38994 this.items[i].purgeListeners();
38996 if(removeEl === true){
38997 this.el.update("");
39002 createStrip : function(container)
39004 var strip = document.createElement("nav");
39005 strip.className = Roo.bootstrap.version == 4 ?
39006 "navbar-light bg-light" :
39007 "navbar navbar-default"; //"x-tabs-wrap";
39008 container.appendChild(strip);
39012 createStripList : function(strip)
39014 // div wrapper for retard IE
39015 // returns the "tr" element.
39016 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
39017 //'<div class="x-tabs-strip-wrap">'+
39018 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
39019 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
39020 return strip.firstChild; //.firstChild.firstChild.firstChild;
39022 createBody : function(container)
39024 var body = document.createElement("div");
39025 Roo.id(body, "tab-body");
39026 //Roo.fly(body).addClass("x-tabs-body");
39027 Roo.fly(body).addClass("tab-content");
39028 container.appendChild(body);
39031 createItemBody :function(bodyEl, id){
39032 var body = Roo.getDom(id);
39034 body = document.createElement("div");
39037 //Roo.fly(body).addClass("x-tabs-item-body");
39038 Roo.fly(body).addClass("tab-pane");
39039 bodyEl.insertBefore(body, bodyEl.firstChild);
39043 createStripElements : function(stripEl, text, closable, tpl)
39045 var td = document.createElement("li"); // was td..
39046 td.className = 'nav-item';
39048 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
39051 stripEl.appendChild(td);
39053 td.className = "x-tabs-closable";
39054 if(!this.closeTpl){
39055 this.closeTpl = new Roo.Template(
39056 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39057 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
39058 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
39061 var el = this.closeTpl.overwrite(td, {"text": text});
39062 var close = el.getElementsByTagName("div")[0];
39063 var inner = el.getElementsByTagName("em")[0];
39064 return {"el": el, "close": close, "inner": inner};
39067 // not sure what this is..
39068 // if(!this.tabTpl){
39069 //this.tabTpl = new Roo.Template(
39070 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39071 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
39073 // this.tabTpl = new Roo.Template(
39074 // '<a href="#">' +
39075 // '<span unselectable="on"' +
39076 // (this.disableTooltips ? '' : ' title="{text}"') +
39077 // ' >{text}</span></a>'
39083 var template = tpl || this.tabTpl || false;
39086 template = new Roo.Template(
39087 Roo.bootstrap.version == 4 ?
39089 '<a class="nav-link" href="#" unselectable="on"' +
39090 (this.disableTooltips ? '' : ' title="{text}"') +
39093 '<a class="nav-link" href="#">' +
39094 '<span unselectable="on"' +
39095 (this.disableTooltips ? '' : ' title="{text}"') +
39096 ' >{text}</span></a>'
39101 switch (typeof(template)) {
39105 template = new Roo.Template(template);
39111 var el = template.overwrite(td, {"text": text});
39113 var inner = el.getElementsByTagName("span")[0];
39115 return {"el": el, "inner": inner};
39123 * @class Roo.TabPanelItem
39124 * @extends Roo.util.Observable
39125 * Represents an individual item (tab plus body) in a TabPanel.
39126 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
39127 * @param {String} id The id of this TabPanelItem
39128 * @param {String} text The text for the tab of this TabPanelItem
39129 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
39131 Roo.bootstrap.panel.TabItem = function(config){
39133 * The {@link Roo.TabPanel} this TabPanelItem belongs to
39134 * @type Roo.TabPanel
39136 this.tabPanel = config.panel;
39138 * The id for this TabPanelItem
39141 this.id = config.id;
39143 this.disabled = false;
39145 this.text = config.text;
39147 this.loaded = false;
39148 this.closable = config.closable;
39151 * The body element for this TabPanelItem.
39152 * @type Roo.Element
39154 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
39155 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
39156 this.bodyEl.setStyle("display", "block");
39157 this.bodyEl.setStyle("zoom", "1");
39158 //this.hideAction();
39160 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
39162 this.el = Roo.get(els.el);
39163 this.inner = Roo.get(els.inner, true);
39164 this.textEl = Roo.bootstrap.version == 4 ?
39165 this.el : Roo.get(this.el.dom.firstChild, true);
39167 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
39168 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
39171 // this.el.on("mousedown", this.onTabMouseDown, this);
39172 this.el.on("click", this.onTabClick, this);
39174 if(config.closable){
39175 var c = Roo.get(els.close, true);
39176 c.dom.title = this.closeText;
39177 c.addClassOnOver("close-over");
39178 c.on("click", this.closeClick, this);
39184 * Fires when this tab becomes the active tab.
39185 * @param {Roo.TabPanel} tabPanel The parent TabPanel
39186 * @param {Roo.TabPanelItem} this
39190 * @event beforeclose
39191 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
39192 * @param {Roo.TabPanelItem} this
39193 * @param {Object} e Set cancel to true on this object to cancel the close.
39195 "beforeclose": true,
39198 * Fires when this tab is closed.
39199 * @param {Roo.TabPanelItem} this
39203 * @event deactivate
39204 * Fires when this tab is no longer the active tab.
39205 * @param {Roo.TabPanel} tabPanel The parent TabPanel
39206 * @param {Roo.TabPanelItem} this
39208 "deactivate" : true
39210 this.hidden = false;
39212 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
39215 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
39217 purgeListeners : function(){
39218 Roo.util.Observable.prototype.purgeListeners.call(this);
39219 this.el.removeAllListeners();
39222 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
39225 this.status_node.addClass("active");
39228 this.tabPanel.stripWrap.repaint();
39230 this.fireEvent("activate", this.tabPanel, this);
39234 * Returns true if this tab is the active tab.
39235 * @return {Boolean}
39237 isActive : function(){
39238 return this.tabPanel.getActiveTab() == this;
39242 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
39245 this.status_node.removeClass("active");
39247 this.fireEvent("deactivate", this.tabPanel, this);
39250 hideAction : function(){
39251 this.bodyEl.hide();
39252 this.bodyEl.setStyle("position", "absolute");
39253 this.bodyEl.setLeft("-20000px");
39254 this.bodyEl.setTop("-20000px");
39257 showAction : function(){
39258 this.bodyEl.setStyle("position", "relative");
39259 this.bodyEl.setTop("");
39260 this.bodyEl.setLeft("");
39261 this.bodyEl.show();
39265 * Set the tooltip for the tab.
39266 * @param {String} tooltip The tab's tooltip
39268 setTooltip : function(text){
39269 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
39270 this.textEl.dom.qtip = text;
39271 this.textEl.dom.removeAttribute('title');
39273 this.textEl.dom.title = text;
39277 onTabClick : function(e){
39278 e.preventDefault();
39279 this.tabPanel.activate(this.id);
39282 onTabMouseDown : function(e){
39283 e.preventDefault();
39284 this.tabPanel.activate(this.id);
39287 getWidth : function(){
39288 return this.inner.getWidth();
39291 setWidth : function(width){
39292 var iwidth = width - this.linode.getPadding("lr");
39293 this.inner.setWidth(iwidth);
39294 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
39295 this.linode.setWidth(width);
39299 * Show or hide the tab
39300 * @param {Boolean} hidden True to hide or false to show.
39302 setHidden : function(hidden){
39303 this.hidden = hidden;
39304 this.linode.setStyle("display", hidden ? "none" : "");
39308 * Returns true if this tab is "hidden"
39309 * @return {Boolean}
39311 isHidden : function(){
39312 return this.hidden;
39316 * Returns the text for this tab
39319 getText : function(){
39323 autoSize : function(){
39324 //this.el.beginMeasure();
39325 this.textEl.setWidth(1);
39327 * #2804 [new] Tabs in Roojs
39328 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39330 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39331 //this.el.endMeasure();
39335 * Sets the text for the tab (Note: this also sets the tooltip text)
39336 * @param {String} text The tab's text and tooltip
39338 setText : function(text){
39340 this.textEl.update(text);
39341 this.setTooltip(text);
39342 //if(!this.tabPanel.resizeTabs){
39343 // this.autoSize();
39347 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39349 activate : function(){
39350 this.tabPanel.activate(this.id);
39354 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39356 disable : function(){
39357 if(this.tabPanel.active != this){
39358 this.disabled = true;
39359 this.status_node.addClass("disabled");
39364 * Enables this TabPanelItem if it was previously disabled.
39366 enable : function(){
39367 this.disabled = false;
39368 this.status_node.removeClass("disabled");
39372 * Sets the content for this TabPanelItem.
39373 * @param {String} content The content
39374 * @param {Boolean} loadScripts true to look for and load scripts
39376 setContent : function(content, loadScripts){
39377 this.bodyEl.update(content, loadScripts);
39381 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39382 * @return {Roo.UpdateManager} The UpdateManager
39384 getUpdateManager : function(){
39385 return this.bodyEl.getUpdateManager();
39389 * Set a URL to be used to load the content for this TabPanelItem.
39390 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39391 * @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)
39392 * @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)
39393 * @return {Roo.UpdateManager} The UpdateManager
39395 setUrl : function(url, params, loadOnce){
39396 if(this.refreshDelegate){
39397 this.un('activate', this.refreshDelegate);
39399 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39400 this.on("activate", this.refreshDelegate);
39401 return this.bodyEl.getUpdateManager();
39405 _handleRefresh : function(url, params, loadOnce){
39406 if(!loadOnce || !this.loaded){
39407 var updater = this.bodyEl.getUpdateManager();
39408 updater.update(url, params, this._setLoaded.createDelegate(this));
39413 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
39414 * Will fail silently if the setUrl method has not been called.
39415 * This does not activate the panel, just updates its content.
39417 refresh : function(){
39418 if(this.refreshDelegate){
39419 this.loaded = false;
39420 this.refreshDelegate();
39425 _setLoaded : function(){
39426 this.loaded = true;
39430 closeClick : function(e){
39433 this.fireEvent("beforeclose", this, o);
39434 if(o.cancel !== true){
39435 this.tabPanel.removeTab(this.id);
39439 * The text displayed in the tooltip for the close icon.
39442 closeText : "Close this tab"
39445 * This script refer to:
39446 * Title: International Telephone Input
39447 * Author: Jack O'Connor
39448 * Code version: v12.1.12
39449 * Availability: https://github.com/jackocnr/intl-tel-input.git
39452 Roo.bootstrap.PhoneInputData = function() {
39455 "Afghanistan (افغانستان)",
39460 "Albania (Shqipëri)",
39465 "Algeria (الجزائر)",
39490 "Antigua and Barbuda",
39500 "Armenia (Հայաստան)",
39516 "Austria (Österreich)",
39521 "Azerbaijan (Azərbaycan)",
39531 "Bahrain (البحرين)",
39536 "Bangladesh (বাংলাদেশ)",
39546 "Belarus (Беларусь)",
39551 "Belgium (België)",
39581 "Bosnia and Herzegovina (Босна и Херцеговина)",
39596 "British Indian Ocean Territory",
39601 "British Virgin Islands",
39611 "Bulgaria (България)",
39621 "Burundi (Uburundi)",
39626 "Cambodia (កម្ពុជា)",
39631 "Cameroon (Cameroun)",
39640 ["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"]
39643 "Cape Verde (Kabu Verdi)",
39648 "Caribbean Netherlands",
39659 "Central African Republic (République centrafricaine)",
39679 "Christmas Island",
39685 "Cocos (Keeling) Islands",
39696 "Comoros (جزر القمر)",
39701 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39706 "Congo (Republic) (Congo-Brazzaville)",
39726 "Croatia (Hrvatska)",
39747 "Czech Republic (Česká republika)",
39752 "Denmark (Danmark)",
39767 "Dominican Republic (República Dominicana)",
39771 ["809", "829", "849"]
39789 "Equatorial Guinea (Guinea Ecuatorial)",
39809 "Falkland Islands (Islas Malvinas)",
39814 "Faroe Islands (Føroyar)",
39835 "French Guiana (Guyane française)",
39840 "French Polynesia (Polynésie française)",
39855 "Georgia (საქართველო)",
39860 "Germany (Deutschland)",
39880 "Greenland (Kalaallit Nunaat)",
39917 "Guinea-Bissau (Guiné Bissau)",
39942 "Hungary (Magyarország)",
39947 "Iceland (Ísland)",
39967 "Iraq (العراق)",
39983 "Israel (ישראל)",
40010 "Jordan (الأردن)",
40015 "Kazakhstan (Казахстан)",
40036 "Kuwait (الكويت)",
40041 "Kyrgyzstan (Кыргызстан)",
40051 "Latvia (Latvija)",
40056 "Lebanon (لبنان)",
40071 "Libya (ليبيا)",
40081 "Lithuania (Lietuva)",
40096 "Macedonia (FYROM) (Македонија)",
40101 "Madagascar (Madagasikara)",
40131 "Marshall Islands",
40141 "Mauritania (موريتانيا)",
40146 "Mauritius (Moris)",
40167 "Moldova (Republica Moldova)",
40177 "Mongolia (Монгол)",
40182 "Montenegro (Crna Gora)",
40192 "Morocco (المغرب)",
40198 "Mozambique (Moçambique)",
40203 "Myanmar (Burma) (မြန်မာ)",
40208 "Namibia (Namibië)",
40223 "Netherlands (Nederland)",
40228 "New Caledonia (Nouvelle-Calédonie)",
40263 "North Korea (조선 민주주의 인민 공화국)",
40268 "Northern Mariana Islands",
40284 "Pakistan (پاکستان)",
40294 "Palestine (فلسطين)",
40304 "Papua New Guinea",
40346 "Réunion (La Réunion)",
40352 "Romania (România)",
40368 "Saint Barthélemy",
40379 "Saint Kitts and Nevis",
40389 "Saint Martin (Saint-Martin (partie française))",
40395 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40400 "Saint Vincent and the Grenadines",
40415 "São Tomé and Príncipe (São Tomé e Príncipe)",
40420 "Saudi Arabia (المملكة العربية السعودية)",
40425 "Senegal (Sénégal)",
40455 "Slovakia (Slovensko)",
40460 "Slovenia (Slovenija)",
40470 "Somalia (Soomaaliya)",
40480 "South Korea (대한민국)",
40485 "South Sudan (جنوب السودان)",
40495 "Sri Lanka (ශ්රී ලංකාව)",
40500 "Sudan (السودان)",
40510 "Svalbard and Jan Mayen",
40521 "Sweden (Sverige)",
40526 "Switzerland (Schweiz)",
40531 "Syria (سوريا)",
40576 "Trinidad and Tobago",
40581 "Tunisia (تونس)",
40586 "Turkey (Türkiye)",
40596 "Turks and Caicos Islands",
40606 "U.S. Virgin Islands",
40616 "Ukraine (Україна)",
40621 "United Arab Emirates (الإمارات العربية المتحدة)",
40643 "Uzbekistan (Oʻzbekiston)",
40653 "Vatican City (Città del Vaticano)",
40664 "Vietnam (Việt Nam)",
40669 "Wallis and Futuna (Wallis-et-Futuna)",
40674 "Western Sahara (الصحراء الغربية)",
40680 "Yemen (اليمن)",
40704 * This script refer to:
40705 * Title: International Telephone Input
40706 * Author: Jack O'Connor
40707 * Code version: v12.1.12
40708 * Availability: https://github.com/jackocnr/intl-tel-input.git
40712 * @class Roo.bootstrap.PhoneInput
40713 * @extends Roo.bootstrap.TriggerField
40714 * An input with International dial-code selection
40716 * @cfg {String} defaultDialCode default '+852'
40717 * @cfg {Array} preferedCountries default []
40720 * Create a new PhoneInput.
40721 * @param {Object} config Configuration options
40724 Roo.bootstrap.PhoneInput = function(config) {
40725 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40728 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40730 listWidth: undefined,
40732 selectedClass: 'active',
40734 invalidClass : "has-warning",
40736 validClass: 'has-success',
40738 allowed: '0123456789',
40743 * @cfg {String} defaultDialCode The default dial code when initializing the input
40745 defaultDialCode: '+852',
40748 * @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
40750 preferedCountries: false,
40752 getAutoCreate : function()
40754 var data = Roo.bootstrap.PhoneInputData();
40755 var align = this.labelAlign || this.parentLabelAlign();
40758 this.allCountries = [];
40759 this.dialCodeMapping = [];
40761 for (var i = 0; i < data.length; i++) {
40763 this.allCountries[i] = {
40767 priority: c[3] || 0,
40768 areaCodes: c[4] || null
40770 this.dialCodeMapping[c[2]] = {
40773 priority: c[3] || 0,
40774 areaCodes: c[4] || null
40786 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40787 maxlength: this.max_length,
40788 cls : 'form-control tel-input',
40789 autocomplete: 'new-password'
40792 var hiddenInput = {
40795 cls: 'hidden-tel-input'
40799 hiddenInput.name = this.name;
40802 if (this.disabled) {
40803 input.disabled = true;
40806 var flag_container = {
40823 cls: this.hasFeedback ? 'has-feedback' : '',
40829 cls: 'dial-code-holder',
40836 cls: 'roo-select2-container input-group',
40843 if (this.fieldLabel.length) {
40846 tooltip: 'This field is required'
40852 cls: 'control-label',
40858 html: this.fieldLabel
40861 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40867 if(this.indicatorpos == 'right') {
40868 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40875 if(align == 'left') {
40883 if(this.labelWidth > 12){
40884 label.style = "width: " + this.labelWidth + 'px';
40886 if(this.labelWidth < 13 && this.labelmd == 0){
40887 this.labelmd = this.labelWidth;
40889 if(this.labellg > 0){
40890 label.cls += ' col-lg-' + this.labellg;
40891 input.cls += ' col-lg-' + (12 - this.labellg);
40893 if(this.labelmd > 0){
40894 label.cls += ' col-md-' + this.labelmd;
40895 container.cls += ' col-md-' + (12 - this.labelmd);
40897 if(this.labelsm > 0){
40898 label.cls += ' col-sm-' + this.labelsm;
40899 container.cls += ' col-sm-' + (12 - this.labelsm);
40901 if(this.labelxs > 0){
40902 label.cls += ' col-xs-' + this.labelxs;
40903 container.cls += ' col-xs-' + (12 - this.labelxs);
40913 var settings = this;
40915 ['xs','sm','md','lg'].map(function(size){
40916 if (settings[size]) {
40917 cfg.cls += ' col-' + size + '-' + settings[size];
40921 this.store = new Roo.data.Store({
40922 proxy : new Roo.data.MemoryProxy({}),
40923 reader : new Roo.data.JsonReader({
40934 'name' : 'dialCode',
40938 'name' : 'priority',
40942 'name' : 'areaCodes',
40949 if(!this.preferedCountries) {
40950 this.preferedCountries = [
40957 var p = this.preferedCountries.reverse();
40960 for (var i = 0; i < p.length; i++) {
40961 for (var j = 0; j < this.allCountries.length; j++) {
40962 if(this.allCountries[j].iso2 == p[i]) {
40963 var t = this.allCountries[j];
40964 this.allCountries.splice(j,1);
40965 this.allCountries.unshift(t);
40971 this.store.proxy.data = {
40973 data: this.allCountries
40979 initEvents : function()
40982 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40984 this.indicator = this.indicatorEl();
40985 this.flag = this.flagEl();
40986 this.dialCodeHolder = this.dialCodeHolderEl();
40988 this.trigger = this.el.select('div.flag-box',true).first();
40989 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40994 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40995 _this.list.setWidth(lw);
40998 this.list.on('mouseover', this.onViewOver, this);
40999 this.list.on('mousemove', this.onViewMove, this);
41000 this.inputEl().on("keyup", this.onKeyUp, this);
41001 this.inputEl().on("keypress", this.onKeyPress, this);
41003 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
41005 this.view = new Roo.View(this.list, this.tpl, {
41006 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41009 this.view.on('click', this.onViewClick, this);
41010 this.setValue(this.defaultDialCode);
41013 onTriggerClick : function(e)
41015 Roo.log('trigger click');
41020 if(this.isExpanded()){
41022 this.hasFocus = false;
41024 this.store.load({});
41025 this.hasFocus = true;
41030 isExpanded : function()
41032 return this.list.isVisible();
41035 collapse : function()
41037 if(!this.isExpanded()){
41041 Roo.get(document).un('mousedown', this.collapseIf, this);
41042 Roo.get(document).un('mousewheel', this.collapseIf, this);
41043 this.fireEvent('collapse', this);
41047 expand : function()
41051 if(this.isExpanded() || !this.hasFocus){
41055 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
41056 this.list.setWidth(lw);
41059 this.restrictHeight();
41061 Roo.get(document).on('mousedown', this.collapseIf, this);
41062 Roo.get(document).on('mousewheel', this.collapseIf, this);
41064 this.fireEvent('expand', this);
41067 restrictHeight : function()
41069 this.list.alignTo(this.inputEl(), this.listAlign);
41070 this.list.alignTo(this.inputEl(), this.listAlign);
41073 onViewOver : function(e, t)
41075 if(this.inKeyMode){
41078 var item = this.view.findItemFromChild(t);
41081 var index = this.view.indexOf(item);
41082 this.select(index, false);
41087 onViewClick : function(view, doFocus, el, e)
41089 var index = this.view.getSelectedIndexes()[0];
41091 var r = this.store.getAt(index);
41094 this.onSelect(r, index);
41096 if(doFocus !== false && !this.blockFocus){
41097 this.inputEl().focus();
41101 onViewMove : function(e, t)
41103 this.inKeyMode = false;
41106 select : function(index, scrollIntoView)
41108 this.selectedIndex = index;
41109 this.view.select(index);
41110 if(scrollIntoView !== false){
41111 var el = this.view.getNode(index);
41113 this.list.scrollChildIntoView(el, false);
41118 createList : function()
41120 this.list = Roo.get(document.body).createChild({
41122 cls: 'typeahead typeahead-long dropdown-menu tel-list',
41123 style: 'display:none'
41126 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
41129 collapseIf : function(e)
41131 var in_combo = e.within(this.el);
41132 var in_list = e.within(this.list);
41133 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
41135 if (in_combo || in_list || is_list) {
41141 onSelect : function(record, index)
41143 if(this.fireEvent('beforeselect', this, record, index) !== false){
41145 this.setFlagClass(record.data.iso2);
41146 this.setDialCode(record.data.dialCode);
41147 this.hasFocus = false;
41149 this.fireEvent('select', this, record, index);
41153 flagEl : function()
41155 var flag = this.el.select('div.flag',true).first();
41162 dialCodeHolderEl : function()
41164 var d = this.el.select('input.dial-code-holder',true).first();
41171 setDialCode : function(v)
41173 this.dialCodeHolder.dom.value = '+'+v;
41176 setFlagClass : function(n)
41178 this.flag.dom.className = 'flag '+n;
41181 getValue : function()
41183 var v = this.inputEl().getValue();
41184 if(this.dialCodeHolder) {
41185 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
41190 setValue : function(v)
41192 var d = this.getDialCode(v);
41194 //invalid dial code
41195 if(v.length == 0 || !d || d.length == 0) {
41197 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41198 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41204 this.setFlagClass(this.dialCodeMapping[d].iso2);
41205 this.setDialCode(d);
41206 this.inputEl().dom.value = v.replace('+'+d,'');
41207 this.hiddenEl().dom.value = this.getValue();
41212 getDialCode : function(v)
41216 if (v.length == 0) {
41217 return this.dialCodeHolder.dom.value;
41221 if (v.charAt(0) != "+") {
41224 var numericChars = "";
41225 for (var i = 1; i < v.length; i++) {
41226 var c = v.charAt(i);
41229 if (this.dialCodeMapping[numericChars]) {
41230 dialCode = v.substr(1, i);
41232 if (numericChars.length == 4) {
41242 this.setValue(this.defaultDialCode);
41246 hiddenEl : function()
41248 return this.el.select('input.hidden-tel-input',true).first();
41251 // after setting val
41252 onKeyUp : function(e){
41253 this.setValue(this.getValue());
41256 onKeyPress : function(e){
41257 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
41264 * @class Roo.bootstrap.MoneyField
41265 * @extends Roo.bootstrap.ComboBox
41266 * Bootstrap MoneyField class
41269 * Create a new MoneyField.
41270 * @param {Object} config Configuration options
41273 Roo.bootstrap.MoneyField = function(config) {
41275 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
41279 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
41282 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41284 allowDecimals : true,
41286 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41288 decimalSeparator : ".",
41290 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41292 decimalPrecision : 0,
41294 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41296 allowNegative : true,
41298 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41302 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41304 minValue : Number.NEGATIVE_INFINITY,
41306 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41308 maxValue : Number.MAX_VALUE,
41310 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41312 minText : "The minimum value for this field is {0}",
41314 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41316 maxText : "The maximum value for this field is {0}",
41318 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
41319 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41321 nanText : "{0} is not a valid number",
41323 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41327 * @cfg {String} defaults currency of the MoneyField
41328 * value should be in lkey
41330 defaultCurrency : false,
41332 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41334 thousandsDelimiter : false,
41336 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41347 getAutoCreate : function()
41349 var align = this.labelAlign || this.parentLabelAlign();
41361 cls : 'form-control roo-money-amount-input',
41362 autocomplete: 'new-password'
41365 var hiddenInput = {
41369 cls: 'hidden-number-input'
41372 if(this.max_length) {
41373 input.maxlength = this.max_length;
41377 hiddenInput.name = this.name;
41380 if (this.disabled) {
41381 input.disabled = true;
41384 var clg = 12 - this.inputlg;
41385 var cmd = 12 - this.inputmd;
41386 var csm = 12 - this.inputsm;
41387 var cxs = 12 - this.inputxs;
41391 cls : 'row roo-money-field',
41395 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41399 cls: 'roo-select2-container input-group',
41403 cls : 'form-control roo-money-currency-input',
41404 autocomplete: 'new-password',
41406 name : this.currencyName
41410 cls : 'input-group-addon',
41424 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41428 cls: this.hasFeedback ? 'has-feedback' : '',
41439 if (this.fieldLabel.length) {
41442 tooltip: 'This field is required'
41448 cls: 'control-label',
41454 html: this.fieldLabel
41457 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41463 if(this.indicatorpos == 'right') {
41464 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41471 if(align == 'left') {
41479 if(this.labelWidth > 12){
41480 label.style = "width: " + this.labelWidth + 'px';
41482 if(this.labelWidth < 13 && this.labelmd == 0){
41483 this.labelmd = this.labelWidth;
41485 if(this.labellg > 0){
41486 label.cls += ' col-lg-' + this.labellg;
41487 input.cls += ' col-lg-' + (12 - this.labellg);
41489 if(this.labelmd > 0){
41490 label.cls += ' col-md-' + this.labelmd;
41491 container.cls += ' col-md-' + (12 - this.labelmd);
41493 if(this.labelsm > 0){
41494 label.cls += ' col-sm-' + this.labelsm;
41495 container.cls += ' col-sm-' + (12 - this.labelsm);
41497 if(this.labelxs > 0){
41498 label.cls += ' col-xs-' + this.labelxs;
41499 container.cls += ' col-xs-' + (12 - this.labelxs);
41510 var settings = this;
41512 ['xs','sm','md','lg'].map(function(size){
41513 if (settings[size]) {
41514 cfg.cls += ' col-' + size + '-' + settings[size];
41521 initEvents : function()
41523 this.indicator = this.indicatorEl();
41525 this.initCurrencyEvent();
41527 this.initNumberEvent();
41530 initCurrencyEvent : function()
41533 throw "can not find store for combo";
41536 this.store = Roo.factory(this.store, Roo.data);
41537 this.store.parent = this;
41541 this.triggerEl = this.el.select('.input-group-addon', true).first();
41543 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41548 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41549 _this.list.setWidth(lw);
41552 this.list.on('mouseover', this.onViewOver, this);
41553 this.list.on('mousemove', this.onViewMove, this);
41554 this.list.on('scroll', this.onViewScroll, this);
41557 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41560 this.view = new Roo.View(this.list, this.tpl, {
41561 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41564 this.view.on('click', this.onViewClick, this);
41566 this.store.on('beforeload', this.onBeforeLoad, this);
41567 this.store.on('load', this.onLoad, this);
41568 this.store.on('loadexception', this.onLoadException, this);
41570 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41571 "up" : function(e){
41572 this.inKeyMode = true;
41576 "down" : function(e){
41577 if(!this.isExpanded()){
41578 this.onTriggerClick();
41580 this.inKeyMode = true;
41585 "enter" : function(e){
41588 if(this.fireEvent("specialkey", this, e)){
41589 this.onViewClick(false);
41595 "esc" : function(e){
41599 "tab" : function(e){
41602 if(this.fireEvent("specialkey", this, e)){
41603 this.onViewClick(false);
41611 doRelay : function(foo, bar, hname){
41612 if(hname == 'down' || this.scope.isExpanded()){
41613 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41621 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41625 initNumberEvent : function(e)
41627 this.inputEl().on("keydown" , this.fireKey, this);
41628 this.inputEl().on("focus", this.onFocus, this);
41629 this.inputEl().on("blur", this.onBlur, this);
41631 this.inputEl().relayEvent('keyup', this);
41633 if(this.indicator){
41634 this.indicator.addClass('invisible');
41637 this.originalValue = this.getValue();
41639 if(this.validationEvent == 'keyup'){
41640 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41641 this.inputEl().on('keyup', this.filterValidation, this);
41643 else if(this.validationEvent !== false){
41644 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41647 if(this.selectOnFocus){
41648 this.on("focus", this.preFocus, this);
41651 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41652 this.inputEl().on("keypress", this.filterKeys, this);
41654 this.inputEl().relayEvent('keypress', this);
41657 var allowed = "0123456789";
41659 if(this.allowDecimals){
41660 allowed += this.decimalSeparator;
41663 if(this.allowNegative){
41667 if(this.thousandsDelimiter) {
41671 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41673 var keyPress = function(e){
41675 var k = e.getKey();
41677 var c = e.getCharCode();
41680 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41681 allowed.indexOf(String.fromCharCode(c)) === -1
41687 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41691 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41696 this.inputEl().on("keypress", keyPress, this);
41700 onTriggerClick : function(e)
41707 this.loadNext = false;
41709 if(this.isExpanded()){
41714 this.hasFocus = true;
41716 if(this.triggerAction == 'all') {
41717 this.doQuery(this.allQuery, true);
41721 this.doQuery(this.getRawValue());
41724 getCurrency : function()
41726 var v = this.currencyEl().getValue();
41731 restrictHeight : function()
41733 this.list.alignTo(this.currencyEl(), this.listAlign);
41734 this.list.alignTo(this.currencyEl(), this.listAlign);
41737 onViewClick : function(view, doFocus, el, e)
41739 var index = this.view.getSelectedIndexes()[0];
41741 var r = this.store.getAt(index);
41744 this.onSelect(r, index);
41748 onSelect : function(record, index){
41750 if(this.fireEvent('beforeselect', this, record, index) !== false){
41752 this.setFromCurrencyData(index > -1 ? record.data : false);
41756 this.fireEvent('select', this, record, index);
41760 setFromCurrencyData : function(o)
41764 this.lastCurrency = o;
41766 if (this.currencyField) {
41767 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41769 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41772 this.lastSelectionText = currency;
41774 //setting default currency
41775 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41776 this.setCurrency(this.defaultCurrency);
41780 this.setCurrency(currency);
41783 setFromData : function(o)
41787 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41789 this.setFromCurrencyData(c);
41794 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41796 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41799 this.setValue(value);
41803 setCurrency : function(v)
41805 this.currencyValue = v;
41808 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41813 setValue : function(v)
41815 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41821 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41823 this.inputEl().dom.value = (v == '') ? '' :
41824 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41826 if(!this.allowZero && v === '0') {
41827 this.hiddenEl().dom.value = '';
41828 this.inputEl().dom.value = '';
41835 getRawValue : function()
41837 var v = this.inputEl().getValue();
41842 getValue : function()
41844 return this.fixPrecision(this.parseValue(this.getRawValue()));
41847 parseValue : function(value)
41849 if(this.thousandsDelimiter) {
41851 r = new RegExp(",", "g");
41852 value = value.replace(r, "");
41855 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41856 return isNaN(value) ? '' : value;
41860 fixPrecision : function(value)
41862 if(this.thousandsDelimiter) {
41864 r = new RegExp(",", "g");
41865 value = value.replace(r, "");
41868 var nan = isNaN(value);
41870 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41871 return nan ? '' : value;
41873 return parseFloat(value).toFixed(this.decimalPrecision);
41876 decimalPrecisionFcn : function(v)
41878 return Math.floor(v);
41881 validateValue : function(value)
41883 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41887 var num = this.parseValue(value);
41890 this.markInvalid(String.format(this.nanText, value));
41894 if(num < this.minValue){
41895 this.markInvalid(String.format(this.minText, this.minValue));
41899 if(num > this.maxValue){
41900 this.markInvalid(String.format(this.maxText, this.maxValue));
41907 validate : function()
41909 if(this.disabled || this.allowBlank){
41914 var currency = this.getCurrency();
41916 if(this.validateValue(this.getRawValue()) && currency.length){
41921 this.markInvalid();
41925 getName: function()
41930 beforeBlur : function()
41936 var v = this.parseValue(this.getRawValue());
41943 onBlur : function()
41947 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41948 //this.el.removeClass(this.focusClass);
41951 this.hasFocus = false;
41953 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41957 var v = this.getValue();
41959 if(String(v) !== String(this.startValue)){
41960 this.fireEvent('change', this, v, this.startValue);
41963 this.fireEvent("blur", this);
41966 inputEl : function()
41968 return this.el.select('.roo-money-amount-input', true).first();
41971 currencyEl : function()
41973 return this.el.select('.roo-money-currency-input', true).first();
41976 hiddenEl : function()
41978 return this.el.select('input.hidden-number-input',true).first();
41982 * @class Roo.bootstrap.BezierSignature
41983 * @extends Roo.bootstrap.Component
41984 * Bootstrap BezierSignature class
41985 * This script refer to:
41986 * Title: Signature Pad
41988 * Availability: https://github.com/szimek/signature_pad
41991 * Create a new BezierSignature
41992 * @param {Object} config The config object
41995 Roo.bootstrap.BezierSignature = function(config){
41996 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
42002 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
42009 mouse_btn_down: true,
42012 * @cfg {int} canvas height
42014 canvas_height: '200px',
42017 * @cfg {float|function} Radius of a single dot.
42022 * @cfg {float} Minimum width of a line. Defaults to 0.5.
42027 * @cfg {float} Maximum width of a line. Defaults to 2.5.
42032 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
42037 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
42042 * @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.
42044 bg_color: 'rgba(0, 0, 0, 0)',
42047 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
42049 dot_color: 'black',
42052 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
42054 velocity_filter_weight: 0.7,
42057 * @cfg {function} Callback when stroke begin.
42062 * @cfg {function} Callback when stroke end.
42066 getAutoCreate : function()
42068 var cls = 'roo-signature column';
42071 cls += ' ' + this.cls;
42081 for(var i = 0; i < col_sizes.length; i++) {
42082 if(this[col_sizes[i]]) {
42083 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
42093 cls: 'roo-signature-body',
42097 cls: 'roo-signature-body-canvas',
42098 height: this.canvas_height,
42099 width: this.canvas_width
42106 style: 'display: none'
42114 initEvents: function()
42116 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
42118 var canvas = this.canvasEl();
42120 // mouse && touch event swapping...
42121 canvas.dom.style.touchAction = 'none';
42122 canvas.dom.style.msTouchAction = 'none';
42124 this.mouse_btn_down = false;
42125 canvas.on('mousedown', this._handleMouseDown, this);
42126 canvas.on('mousemove', this._handleMouseMove, this);
42127 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
42129 if (window.PointerEvent) {
42130 canvas.on('pointerdown', this._handleMouseDown, this);
42131 canvas.on('pointermove', this._handleMouseMove, this);
42132 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
42135 if ('ontouchstart' in window) {
42136 canvas.on('touchstart', this._handleTouchStart, this);
42137 canvas.on('touchmove', this._handleTouchMove, this);
42138 canvas.on('touchend', this._handleTouchEnd, this);
42141 Roo.EventManager.onWindowResize(this.resize, this, true);
42143 // file input event
42144 this.fileEl().on('change', this.uploadImage, this);
42151 resize: function(){
42153 var canvas = this.canvasEl().dom;
42154 var ctx = this.canvasElCtx();
42155 var img_data = false;
42157 if(canvas.width > 0) {
42158 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
42160 // setting canvas width will clean img data
42163 var style = window.getComputedStyle ?
42164 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
42166 var padding_left = parseInt(style.paddingLeft) || 0;
42167 var padding_right = parseInt(style.paddingRight) || 0;
42169 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
42172 ctx.putImageData(img_data, 0, 0);
42176 _handleMouseDown: function(e)
42178 if (e.browserEvent.which === 1) {
42179 this.mouse_btn_down = true;
42180 this.strokeBegin(e);
42184 _handleMouseMove: function (e)
42186 if (this.mouse_btn_down) {
42187 this.strokeMoveUpdate(e);
42191 _handleMouseUp: function (e)
42193 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
42194 this.mouse_btn_down = false;
42199 _handleTouchStart: function (e) {
42201 e.preventDefault();
42202 if (e.browserEvent.targetTouches.length === 1) {
42203 // var touch = e.browserEvent.changedTouches[0];
42204 // this.strokeBegin(touch);
42206 this.strokeBegin(e); // assume e catching the correct xy...
42210 _handleTouchMove: function (e) {
42211 e.preventDefault();
42212 // var touch = event.targetTouches[0];
42213 // _this._strokeMoveUpdate(touch);
42214 this.strokeMoveUpdate(e);
42217 _handleTouchEnd: function (e) {
42218 var wasCanvasTouched = e.target === this.canvasEl().dom;
42219 if (wasCanvasTouched) {
42220 e.preventDefault();
42221 // var touch = event.changedTouches[0];
42222 // _this._strokeEnd(touch);
42227 reset: function () {
42228 this._lastPoints = [];
42229 this._lastVelocity = 0;
42230 this._lastWidth = (this.min_width + this.max_width) / 2;
42231 this.canvasElCtx().fillStyle = this.dot_color;
42234 strokeMoveUpdate: function(e)
42236 this.strokeUpdate(e);
42238 if (this.throttle) {
42239 this.throttleStroke(this.strokeUpdate, this.throttle);
42242 this.strokeUpdate(e);
42246 strokeBegin: function(e)
42248 var newPointGroup = {
42249 color: this.dot_color,
42253 if (typeof this.onBegin === 'function') {
42257 this.curve_data.push(newPointGroup);
42259 this.strokeUpdate(e);
42262 strokeUpdate: function(e)
42264 var rect = this.canvasEl().dom.getBoundingClientRect();
42265 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
42266 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
42267 var lastPoints = lastPointGroup.points;
42268 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
42269 var isLastPointTooClose = lastPoint
42270 ? point.distanceTo(lastPoint) <= this.min_distance
42272 var color = lastPointGroup.color;
42273 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
42274 var curve = this.addPoint(point);
42276 this.drawDot({color: color, point: point});
42279 this.drawCurve({color: color, curve: curve});
42289 strokeEnd: function(e)
42291 this.strokeUpdate(e);
42292 if (typeof this.onEnd === 'function') {
42297 addPoint: function (point) {
42298 var _lastPoints = this._lastPoints;
42299 _lastPoints.push(point);
42300 if (_lastPoints.length > 2) {
42301 if (_lastPoints.length === 3) {
42302 _lastPoints.unshift(_lastPoints[0]);
42304 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
42305 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
42306 _lastPoints.shift();
42312 calculateCurveWidths: function (startPoint, endPoint) {
42313 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42314 (1 - this.velocity_filter_weight) * this._lastVelocity;
42316 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42319 start: this._lastWidth
42322 this._lastVelocity = velocity;
42323 this._lastWidth = newWidth;
42327 drawDot: function (_a) {
42328 var color = _a.color, point = _a.point;
42329 var ctx = this.canvasElCtx();
42330 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42332 this.drawCurveSegment(point.x, point.y, width);
42334 ctx.fillStyle = color;
42338 drawCurve: function (_a) {
42339 var color = _a.color, curve = _a.curve;
42340 var ctx = this.canvasElCtx();
42341 var widthDelta = curve.endWidth - curve.startWidth;
42342 var drawSteps = Math.floor(curve.length()) * 2;
42344 ctx.fillStyle = color;
42345 for (var i = 0; i < drawSteps; i += 1) {
42346 var t = i / drawSteps;
42352 var x = uuu * curve.startPoint.x;
42353 x += 3 * uu * t * curve.control1.x;
42354 x += 3 * u * tt * curve.control2.x;
42355 x += ttt * curve.endPoint.x;
42356 var y = uuu * curve.startPoint.y;
42357 y += 3 * uu * t * curve.control1.y;
42358 y += 3 * u * tt * curve.control2.y;
42359 y += ttt * curve.endPoint.y;
42360 var width = curve.startWidth + ttt * widthDelta;
42361 this.drawCurveSegment(x, y, width);
42367 drawCurveSegment: function (x, y, width) {
42368 var ctx = this.canvasElCtx();
42370 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42371 this.is_empty = false;
42376 var ctx = this.canvasElCtx();
42377 var canvas = this.canvasEl().dom;
42378 ctx.fillStyle = this.bg_color;
42379 ctx.clearRect(0, 0, canvas.width, canvas.height);
42380 ctx.fillRect(0, 0, canvas.width, canvas.height);
42381 this.curve_data = [];
42383 this.is_empty = true;
42388 return this.el.select('input',true).first();
42391 canvasEl: function()
42393 return this.el.select('canvas',true).first();
42396 canvasElCtx: function()
42398 return this.el.select('canvas',true).first().dom.getContext('2d');
42401 getImage: function(type)
42403 if(this.is_empty) {
42408 return this.canvasEl().dom.toDataURL('image/'+type, 1);
42411 drawFromImage: function(img_src)
42413 var img = new Image();
42415 img.onload = function(){
42416 this.canvasElCtx().drawImage(img, 0, 0);
42421 this.is_empty = false;
42424 selectImage: function()
42426 this.fileEl().dom.click();
42429 uploadImage: function(e)
42431 var reader = new FileReader();
42433 reader.onload = function(e){
42434 var img = new Image();
42435 img.onload = function(){
42437 this.canvasElCtx().drawImage(img, 0, 0);
42439 img.src = e.target.result;
42442 reader.readAsDataURL(e.target.files[0]);
42445 // Bezier Point Constructor
42446 Point: (function () {
42447 function Point(x, y, time) {
42450 this.time = time || Date.now();
42452 Point.prototype.distanceTo = function (start) {
42453 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42455 Point.prototype.equals = function (other) {
42456 return this.x === other.x && this.y === other.y && this.time === other.time;
42458 Point.prototype.velocityFrom = function (start) {
42459 return this.time !== start.time
42460 ? this.distanceTo(start) / (this.time - start.time)
42467 // Bezier Constructor
42468 Bezier: (function () {
42469 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42470 this.startPoint = startPoint;
42471 this.control2 = control2;
42472 this.control1 = control1;
42473 this.endPoint = endPoint;
42474 this.startWidth = startWidth;
42475 this.endWidth = endWidth;
42477 Bezier.fromPoints = function (points, widths, scope) {
42478 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42479 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42480 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42482 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42483 var dx1 = s1.x - s2.x;
42484 var dy1 = s1.y - s2.y;
42485 var dx2 = s2.x - s3.x;
42486 var dy2 = s2.y - s3.y;
42487 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42488 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42489 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42490 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42491 var dxm = m1.x - m2.x;
42492 var dym = m1.y - m2.y;
42493 var k = l2 / (l1 + l2);
42494 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42495 var tx = s2.x - cm.x;
42496 var ty = s2.y - cm.y;
42498 c1: new scope.Point(m1.x + tx, m1.y + ty),
42499 c2: new scope.Point(m2.x + tx, m2.y + ty)
42502 Bezier.prototype.length = function () {
42507 for (var i = 0; i <= steps; i += 1) {
42509 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42510 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42512 var xdiff = cx - px;
42513 var ydiff = cy - py;
42514 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42521 Bezier.prototype.point = function (t, start, c1, c2, end) {
42522 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42523 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42524 + (3.0 * c2 * (1.0 - t) * t * t)
42525 + (end * t * t * t);
42530 throttleStroke: function(fn, wait) {
42531 if (wait === void 0) { wait = 250; }
42533 var timeout = null;
42537 var later = function () {
42538 previous = Date.now();
42540 result = fn.apply(storedContext, storedArgs);
42542 storedContext = null;
42546 return function wrapper() {
42548 for (var _i = 0; _i < arguments.length; _i++) {
42549 args[_i] = arguments[_i];
42551 var now = Date.now();
42552 var remaining = wait - (now - previous);
42553 storedContext = this;
42555 if (remaining <= 0 || remaining > wait) {
42557 clearTimeout(timeout);
42561 result = fn.apply(storedContext, storedArgs);
42563 storedContext = null;
42567 else if (!timeout) {
42568 timeout = window.setTimeout(later, remaining);