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|Object} 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?
1683 cls : 'card-header d-none'
1686 if (this.header_image.length) {
1689 cls : 'card-img-top',
1690 src: this.header_image // escape?
1701 if (this.title.length) {
1705 src: this.title // escape?
1709 if (this.subtitle.length) {
1713 src: this.subtitle // escape?
1719 cls : 'roo-card-body-ctr'
1722 if (this.html.length) {
1728 // fixme ? handle objects?
1729 if (this.footer.length) {
1732 cls : 'card-footer',
1733 html: this.footer // escape?
1742 getCardHeader : function()
1744 var ret = this.el.select('.card-header',true).first();
1745 if (ret.hasClass('d-none')) {
1746 ret.removeClass('d-none');
1752 getChildContainer : function()
1758 return this.el.select('.roo-card-body-ctr',true).first();
1761 initEvents: function()
1764 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
1765 containerScroll: true,
1766 ddGroup: this.drag_group || 'default_card_drag_group'
1768 this.dragZone.getDragData = this.getDragData.createDelegate(this);
1774 getDragData : function(e) {
1775 var target = this.getEl();
1777 //this.handleSelection(e);
1782 nodes: this.getEl(),
1787 dragData.ddel = target.dom ; // the div element
1788 Roo.log(target.getWidth( ));
1789 dragData.ddel.style.width = target.getWidth() + 'px';
1807 * @class Roo.bootstrap.Img
1808 * @extends Roo.bootstrap.Component
1809 * Bootstrap Img class
1810 * @cfg {Boolean} imgResponsive false | true
1811 * @cfg {String} border rounded | circle | thumbnail
1812 * @cfg {String} src image source
1813 * @cfg {String} alt image alternative text
1814 * @cfg {String} href a tag href
1815 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1816 * @cfg {String} xsUrl xs image source
1817 * @cfg {String} smUrl sm image source
1818 * @cfg {String} mdUrl md image source
1819 * @cfg {String} lgUrl lg image source
1822 * Create a new Input
1823 * @param {Object} config The config object
1826 Roo.bootstrap.Img = function(config){
1827 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1833 * The img click event for the img.
1834 * @param {Roo.EventObject} e
1840 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1842 imgResponsive: true,
1852 getAutoCreate : function()
1854 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1855 return this.createSingleImg();
1860 cls: 'roo-image-responsive-group',
1865 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1867 if(!_this[size + 'Url']){
1873 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1874 html: _this.html || cfg.html,
1875 src: _this[size + 'Url']
1878 img.cls += ' roo-image-responsive-' + size;
1880 var s = ['xs', 'sm', 'md', 'lg'];
1882 s.splice(s.indexOf(size), 1);
1884 Roo.each(s, function(ss){
1885 img.cls += ' hidden-' + ss;
1888 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1889 cfg.cls += ' img-' + _this.border;
1893 cfg.alt = _this.alt;
1906 a.target = _this.target;
1910 cfg.cn.push((_this.href) ? a : img);
1917 createSingleImg : function()
1921 cls: (this.imgResponsive) ? 'img-responsive' : '',
1923 src : 'about:blank' // just incase src get's set to undefined?!?
1926 cfg.html = this.html || cfg.html;
1928 cfg.src = this.src || cfg.src;
1930 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1931 cfg.cls += ' img-' + this.border;
1948 a.target = this.target;
1953 return (this.href) ? a : cfg;
1956 initEvents: function()
1959 this.el.on('click', this.onClick, this);
1964 onClick : function(e)
1966 Roo.log('img onclick');
1967 this.fireEvent('click', this, e);
1970 * Sets the url of the image - used to update it
1971 * @param {String} url the url of the image
1974 setSrc : function(url)
1978 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1979 this.el.dom.src = url;
1983 this.el.select('img', true).first().dom.src = url;
1999 * @class Roo.bootstrap.Link
2000 * @extends Roo.bootstrap.Component
2001 * Bootstrap Link Class
2002 * @cfg {String} alt image alternative text
2003 * @cfg {String} href a tag href
2004 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2005 * @cfg {String} html the content of the link.
2006 * @cfg {String} anchor name for the anchor link
2007 * @cfg {String} fa - favicon
2009 * @cfg {Boolean} preventDefault (true | false) default false
2013 * Create a new Input
2014 * @param {Object} config The config object
2017 Roo.bootstrap.Link = function(config){
2018 Roo.bootstrap.Link.superclass.constructor.call(this, config);
2024 * The img click event for the img.
2025 * @param {Roo.EventObject} e
2031 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
2035 preventDefault: false,
2041 getAutoCreate : function()
2043 var html = this.html || '';
2045 if (this.fa !== false) {
2046 html = '<i class="fa fa-' + this.fa + '"></i>';
2051 // anchor's do not require html/href...
2052 if (this.anchor === false) {
2054 cfg.href = this.href || '#';
2056 cfg.name = this.anchor;
2057 if (this.html !== false || this.fa !== false) {
2060 if (this.href !== false) {
2061 cfg.href = this.href;
2065 if(this.alt !== false){
2070 if(this.target !== false) {
2071 cfg.target = this.target;
2077 initEvents: function() {
2079 if(!this.href || this.preventDefault){
2080 this.el.on('click', this.onClick, this);
2084 onClick : function(e)
2086 if(this.preventDefault){
2089 //Roo.log('img onclick');
2090 this.fireEvent('click', this, e);
2103 * @class Roo.bootstrap.Header
2104 * @extends Roo.bootstrap.Component
2105 * Bootstrap Header class
2106 * @cfg {String} html content of header
2107 * @cfg {Number} level (1|2|3|4|5|6) default 1
2110 * Create a new Header
2111 * @param {Object} config The config object
2115 Roo.bootstrap.Header = function(config){
2116 Roo.bootstrap.Header.superclass.constructor.call(this, config);
2119 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
2127 getAutoCreate : function(){
2132 tag: 'h' + (1 *this.level),
2133 html: this.html || ''
2145 * Ext JS Library 1.1.1
2146 * Copyright(c) 2006-2007, Ext JS, LLC.
2148 * Originally Released Under LGPL - original licence link has changed is not relivant.
2151 * <script type="text/javascript">
2155 * @class Roo.bootstrap.MenuMgr
2156 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
2159 Roo.bootstrap.MenuMgr = function(){
2160 var menus, active, groups = {}, attached = false, lastShow = new Date();
2162 // private - called when first menu is created
2165 active = new Roo.util.MixedCollection();
2166 Roo.get(document).addKeyListener(27, function(){
2167 if(active.length > 0){
2175 if(active && active.length > 0){
2176 var c = active.clone();
2186 if(active.length < 1){
2187 Roo.get(document).un("mouseup", onMouseDown);
2195 var last = active.last();
2196 lastShow = new Date();
2199 Roo.get(document).on("mouseup", onMouseDown);
2204 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
2205 m.parentMenu.activeChild = m;
2206 }else if(last && last.isVisible()){
2207 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
2212 function onBeforeHide(m){
2214 m.activeChild.hide();
2216 if(m.autoHideTimer){
2217 clearTimeout(m.autoHideTimer);
2218 delete m.autoHideTimer;
2223 function onBeforeShow(m){
2224 var pm = m.parentMenu;
2225 if(!pm && !m.allowOtherMenus){
2227 }else if(pm && pm.activeChild && active != m){
2228 pm.activeChild.hide();
2232 // private this should really trigger on mouseup..
2233 function onMouseDown(e){
2234 Roo.log("on Mouse Up");
2236 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
2237 Roo.log("MenuManager hideAll");
2246 function onBeforeCheck(mi, state){
2248 var g = groups[mi.group];
2249 for(var i = 0, l = g.length; i < l; i++){
2251 g[i].setChecked(false);
2260 * Hides all menus that are currently visible
2262 hideAll : function(){
2267 register : function(menu){
2271 menus[menu.id] = menu;
2272 menu.on("beforehide", onBeforeHide);
2273 menu.on("hide", onHide);
2274 menu.on("beforeshow", onBeforeShow);
2275 menu.on("show", onShow);
2277 if(g && menu.events["checkchange"]){
2281 groups[g].push(menu);
2282 menu.on("checkchange", onCheck);
2287 * Returns a {@link Roo.menu.Menu} object
2288 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
2289 * be used to generate and return a new Menu instance.
2291 get : function(menu){
2292 if(typeof menu == "string"){ // menu id
2294 }else if(menu.events){ // menu instance
2297 /*else if(typeof menu.length == 'number'){ // array of menu items?
2298 return new Roo.bootstrap.Menu({items:menu});
2299 }else{ // otherwise, must be a config
2300 return new Roo.bootstrap.Menu(menu);
2307 unregister : function(menu){
2308 delete menus[menu.id];
2309 menu.un("beforehide", onBeforeHide);
2310 menu.un("hide", onHide);
2311 menu.un("beforeshow", onBeforeShow);
2312 menu.un("show", onShow);
2314 if(g && menu.events["checkchange"]){
2315 groups[g].remove(menu);
2316 menu.un("checkchange", onCheck);
2321 registerCheckable : function(menuItem){
2322 var g = menuItem.group;
2327 groups[g].push(menuItem);
2328 menuItem.on("beforecheckchange", onBeforeCheck);
2333 unregisterCheckable : function(menuItem){
2334 var g = menuItem.group;
2336 groups[g].remove(menuItem);
2337 menuItem.un("beforecheckchange", onBeforeCheck);
2349 * @class Roo.bootstrap.Menu
2350 * @extends Roo.bootstrap.Component
2351 * Bootstrap Menu class - container for MenuItems
2352 * @cfg {String} type (dropdown|treeview|submenu) type of menu
2353 * @cfg {bool} hidden if the menu should be hidden when rendered.
2354 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
2355 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
2359 * @param {Object} config The config object
2363 Roo.bootstrap.Menu = function(config){
2364 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2365 if (this.registerMenu && this.type != 'treeview') {
2366 Roo.bootstrap.MenuMgr.register(this);
2373 * Fires before this menu is displayed (return false to block)
2374 * @param {Roo.menu.Menu} this
2379 * Fires before this menu is hidden (return false to block)
2380 * @param {Roo.menu.Menu} this
2385 * Fires after this menu is displayed
2386 * @param {Roo.menu.Menu} this
2391 * Fires after this menu is hidden
2392 * @param {Roo.menu.Menu} this
2397 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2398 * @param {Roo.menu.Menu} this
2399 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2400 * @param {Roo.EventObject} e
2405 * Fires when the mouse is hovering over this menu
2406 * @param {Roo.menu.Menu} this
2407 * @param {Roo.EventObject} e
2408 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2413 * Fires when the mouse exits this menu
2414 * @param {Roo.menu.Menu} this
2415 * @param {Roo.EventObject} e
2416 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2421 * Fires when a menu item contained in this menu is clicked
2422 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2423 * @param {Roo.EventObject} e
2427 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2430 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2434 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2437 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2439 registerMenu : true,
2441 menuItems :false, // stores the menu items..
2451 getChildContainer : function() {
2455 getAutoCreate : function(){
2457 //if (['right'].indexOf(this.align)!==-1) {
2458 // cfg.cn[1].cls += ' pull-right'
2464 cls : 'dropdown-menu' ,
2465 style : 'z-index:1000'
2469 if (this.type === 'submenu') {
2470 cfg.cls = 'submenu active';
2472 if (this.type === 'treeview') {
2473 cfg.cls = 'treeview-menu';
2478 initEvents : function() {
2480 // Roo.log("ADD event");
2481 // Roo.log(this.triggerEl.dom);
2483 this.triggerEl.on('click', this.onTriggerClick, this);
2485 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2488 if (this.triggerEl.hasClass('nav-item')) {
2489 // dropdown toggle on the 'a' in BS4?
2490 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2492 this.triggerEl.addClass('dropdown-toggle');
2495 this.el.on('touchstart' , this.onTouch, this);
2497 this.el.on('click' , this.onClick, this);
2499 this.el.on("mouseover", this.onMouseOver, this);
2500 this.el.on("mouseout", this.onMouseOut, this);
2504 findTargetItem : function(e)
2506 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2510 //Roo.log(t); Roo.log(t.id);
2512 //Roo.log(this.menuitems);
2513 return this.menuitems.get(t.id);
2515 //return this.items.get(t.menuItemId);
2521 onTouch : function(e)
2523 Roo.log("menu.onTouch");
2524 //e.stopEvent(); this make the user popdown broken
2528 onClick : function(e)
2530 Roo.log("menu.onClick");
2532 var t = this.findTargetItem(e);
2533 if(!t || t.isContainer){
2538 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2539 if(t == this.activeItem && t.shouldDeactivate(e)){
2540 this.activeItem.deactivate();
2541 delete this.activeItem;
2545 this.setActiveItem(t, true);
2553 Roo.log('pass click event');
2557 this.fireEvent("click", this, t, e);
2561 if(!t.href.length || t.href == '#'){
2562 (function() { _this.hide(); }).defer(100);
2567 onMouseOver : function(e){
2568 var t = this.findTargetItem(e);
2571 // if(t.canActivate && !t.disabled){
2572 // this.setActiveItem(t, true);
2576 this.fireEvent("mouseover", this, e, t);
2578 isVisible : function(){
2579 return !this.hidden;
2581 onMouseOut : function(e){
2582 var t = this.findTargetItem(e);
2585 // if(t == this.activeItem && t.shouldDeactivate(e)){
2586 // this.activeItem.deactivate();
2587 // delete this.activeItem;
2590 this.fireEvent("mouseout", this, e, t);
2595 * Displays this menu relative to another element
2596 * @param {String/HTMLElement/Roo.Element} element The element to align to
2597 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2598 * the element (defaults to this.defaultAlign)
2599 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2601 show : function(el, pos, parentMenu)
2603 if (false === this.fireEvent("beforeshow", this)) {
2604 Roo.log("show canceled");
2607 this.parentMenu = parentMenu;
2612 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2615 * Displays this menu at a specific xy position
2616 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2617 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2619 showAt : function(xy, parentMenu, /* private: */_e){
2620 this.parentMenu = parentMenu;
2625 this.fireEvent("beforeshow", this);
2626 //xy = this.el.adjustForConstraints(xy);
2630 this.hideMenuItems();
2631 this.hidden = false;
2632 this.triggerEl.addClass('open');
2633 this.el.addClass('show');
2635 // reassign x when hitting right
2636 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2637 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2640 // reassign y when hitting bottom
2641 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2642 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2645 // but the list may align on trigger left or trigger top... should it be a properity?
2647 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2652 this.fireEvent("show", this);
2658 this.doFocus.defer(50, this);
2662 doFocus : function(){
2664 this.focusEl.focus();
2669 * Hides this menu and optionally all parent menus
2670 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2672 hide : function(deep)
2674 if (false === this.fireEvent("beforehide", this)) {
2675 Roo.log("hide canceled");
2678 this.hideMenuItems();
2679 if(this.el && this.isVisible()){
2681 if(this.activeItem){
2682 this.activeItem.deactivate();
2683 this.activeItem = null;
2685 this.triggerEl.removeClass('open');;
2686 this.el.removeClass('show');
2688 this.fireEvent("hide", this);
2690 if(deep === true && this.parentMenu){
2691 this.parentMenu.hide(true);
2695 onTriggerClick : function(e)
2697 Roo.log('trigger click');
2699 var target = e.getTarget();
2701 Roo.log(target.nodeName.toLowerCase());
2703 if(target.nodeName.toLowerCase() === 'i'){
2709 onTriggerPress : function(e)
2711 Roo.log('trigger press');
2712 //Roo.log(e.getTarget());
2713 // Roo.log(this.triggerEl.dom);
2715 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2716 var pel = Roo.get(e.getTarget());
2717 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2718 Roo.log('is treeview or dropdown?');
2722 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2726 if (this.isVisible()) {
2731 this.show(this.triggerEl, '?', false);
2734 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2741 hideMenuItems : function()
2743 Roo.log("hide Menu Items");
2748 this.el.select('.open',true).each(function(aa) {
2750 aa.removeClass('open');
2754 addxtypeChild : function (tree, cntr) {
2755 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2757 this.menuitems.add(comp);
2769 this.getEl().dom.innerHTML = '';
2770 this.menuitems.clear();
2784 * @class Roo.bootstrap.MenuItem
2785 * @extends Roo.bootstrap.Component
2786 * Bootstrap MenuItem class
2787 * @cfg {String} html the menu label
2788 * @cfg {String} href the link
2789 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2790 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2791 * @cfg {Boolean} active used on sidebars to highlight active itesm
2792 * @cfg {String} fa favicon to show on left of menu item.
2793 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2797 * Create a new MenuItem
2798 * @param {Object} config The config object
2802 Roo.bootstrap.MenuItem = function(config){
2803 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2808 * The raw click event for the entire grid.
2809 * @param {Roo.bootstrap.MenuItem} this
2810 * @param {Roo.EventObject} e
2816 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2820 preventDefault: false,
2821 isContainer : false,
2825 getAutoCreate : function(){
2827 if(this.isContainer){
2830 cls: 'dropdown-menu-item '
2840 cls : 'dropdown-item',
2845 if (this.fa !== false) {
2848 cls : 'fa fa-' + this.fa
2857 cls: 'dropdown-menu-item',
2860 if (this.parent().type == 'treeview') {
2861 cfg.cls = 'treeview-menu';
2864 cfg.cls += ' active';
2869 anc.href = this.href || cfg.cn[0].href ;
2870 ctag.html = this.html || cfg.cn[0].html ;
2874 initEvents: function()
2876 if (this.parent().type == 'treeview') {
2877 this.el.select('a').on('click', this.onClick, this);
2881 this.menu.parentType = this.xtype;
2882 this.menu.triggerEl = this.el;
2883 this.menu = this.addxtype(Roo.apply({}, this.menu));
2887 onClick : function(e)
2889 Roo.log('item on click ');
2891 if(this.preventDefault){
2894 //this.parent().hideMenuItems();
2896 this.fireEvent('click', this, e);
2915 * @class Roo.bootstrap.MenuSeparator
2916 * @extends Roo.bootstrap.Component
2917 * Bootstrap MenuSeparator class
2920 * Create a new MenuItem
2921 * @param {Object} config The config object
2925 Roo.bootstrap.MenuSeparator = function(config){
2926 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2929 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2931 getAutoCreate : function(){
2950 * @class Roo.bootstrap.Modal
2951 * @extends Roo.bootstrap.Component
2952 * Bootstrap Modal class
2953 * @cfg {String} title Title of dialog
2954 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2955 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2956 * @cfg {Boolean} specificTitle default false
2957 * @cfg {Array} buttons Array of buttons or standard button set..
2958 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2959 * @cfg {Boolean} animate default true
2960 * @cfg {Boolean} allow_close default true
2961 * @cfg {Boolean} fitwindow default false
2962 * @cfg {Number} width fixed width - usefull for chrome extension only really.
2963 * @cfg {Number} height fixed height - usefull for chrome extension only really.
2964 * @cfg {String} size (sm|lg) default empty
2965 * @cfg {Number} max_width set the max width of modal
2969 * Create a new Modal Dialog
2970 * @param {Object} config The config object
2973 Roo.bootstrap.Modal = function(config){
2974 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2979 * The raw btnclick event for the button
2980 * @param {Roo.EventObject} e
2985 * Fire when dialog resize
2986 * @param {Roo.bootstrap.Modal} this
2987 * @param {Roo.EventObject} e
2991 this.buttons = this.buttons || [];
2994 this.tmpl = Roo.factory(this.tmpl);
2999 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
3001 title : 'test dialog',
3011 specificTitle: false,
3013 buttonPosition: 'right',
3036 onRender : function(ct, position)
3038 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3041 var cfg = Roo.apply({}, this.getAutoCreate());
3044 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3046 //if (!cfg.name.length) {
3050 cfg.cls += ' ' + this.cls;
3053 cfg.style = this.style;
3055 this.el = Roo.get(document.body).createChild(cfg, position);
3057 //var type = this.el.dom.type;
3060 if(this.tabIndex !== undefined){
3061 this.el.dom.setAttribute('tabIndex', this.tabIndex);
3064 this.dialogEl = this.el.select('.modal-dialog',true).first();
3065 this.bodyEl = this.el.select('.modal-body',true).first();
3066 this.closeEl = this.el.select('.modal-header .close', true).first();
3067 this.headerEl = this.el.select('.modal-header',true).first();
3068 this.titleEl = this.el.select('.modal-title',true).first();
3069 this.footerEl = this.el.select('.modal-footer',true).first();
3071 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3073 //this.el.addClass("x-dlg-modal");
3075 if (this.buttons.length) {
3076 Roo.each(this.buttons, function(bb) {
3077 var b = Roo.apply({}, bb);
3078 b.xns = b.xns || Roo.bootstrap;
3079 b.xtype = b.xtype || 'Button';
3080 if (typeof(b.listeners) == 'undefined') {
3081 b.listeners = { click : this.onButtonClick.createDelegate(this) };
3084 var btn = Roo.factory(b);
3086 btn.render(this.getButtonContainer());
3090 // render the children.
3093 if(typeof(this.items) != 'undefined'){
3094 var items = this.items;
3097 for(var i =0;i < items.length;i++) {
3098 nitems.push(this.addxtype(Roo.apply({}, items[i])));
3102 this.items = nitems;
3104 // where are these used - they used to be body/close/footer
3108 //this.el.addClass([this.fieldClass, this.cls]);
3112 getAutoCreate : function()
3114 // we will default to modal-body-overflow - might need to remove or make optional later.
3116 cls : 'modal-body enable-modal-body-overflow ',
3117 html : this.html || ''
3122 cls : 'modal-title',
3126 if(this.specificTitle){
3132 if (this.allow_close && Roo.bootstrap.version == 3) {
3142 if (this.allow_close && Roo.bootstrap.version == 4) {
3152 if(this.size.length){
3153 size = 'modal-' + this.size;
3156 var footer = Roo.bootstrap.version == 3 ?
3158 cls : 'modal-footer',
3162 cls: 'btn-' + this.buttonPosition
3167 { // BS4 uses mr-auto on left buttons....
3168 cls : 'modal-footer'
3179 cls: "modal-dialog " + size,
3182 cls : "modal-content",
3185 cls : 'modal-header',
3200 modal.cls += ' fade';
3206 getChildContainer : function() {
3211 getButtonContainer : function() {
3213 return Roo.bootstrap.version == 4 ?
3214 this.el.select('.modal-footer',true).first()
3215 : this.el.select('.modal-footer div',true).first();
3218 initEvents : function()
3220 if (this.allow_close) {
3221 this.closeEl.on('click', this.hide, this);
3223 Roo.EventManager.onWindowResize(this.resize, this, true);
3231 this.maskEl.setSize(
3232 Roo.lib.Dom.getViewWidth(true),
3233 Roo.lib.Dom.getViewHeight(true)
3236 if (this.fitwindow) {
3240 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
3241 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
3246 if(this.max_width !== 0) {
3248 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
3251 this.setSize(w, this.height);
3255 if(this.max_height) {
3256 this.setSize(w,Math.min(
3258 Roo.lib.Dom.getViewportHeight(true) - 60
3264 if(!this.fit_content) {
3265 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
3269 this.setSize(w, Math.min(
3271 this.headerEl.getHeight() +
3272 this.footerEl.getHeight() +
3273 this.getChildHeight(this.bodyEl.dom.childNodes),
3274 Roo.lib.Dom.getViewportHeight(true) - 60)
3280 setSize : function(w,h)
3291 if (!this.rendered) {
3295 //this.el.setStyle('display', 'block');
3296 this.el.removeClass('hideing');
3297 this.el.dom.style.display='block';
3299 Roo.get(document.body).addClass('modal-open');
3301 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
3304 this.el.addClass('show');
3305 this.el.addClass('in');
3308 this.el.addClass('show');
3309 this.el.addClass('in');
3312 // not sure how we can show data in here..
3314 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3317 Roo.get(document.body).addClass("x-body-masked");
3319 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3320 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3321 this.maskEl.dom.style.display = 'block';
3322 this.maskEl.addClass('show');
3327 this.fireEvent('show', this);
3329 // set zindex here - otherwise it appears to be ignored...
3330 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3333 this.items.forEach( function(e) {
3334 e.layout ? e.layout() : false;
3342 if(this.fireEvent("beforehide", this) !== false){
3344 this.maskEl.removeClass('show');
3346 this.maskEl.dom.style.display = '';
3347 Roo.get(document.body).removeClass("x-body-masked");
3348 this.el.removeClass('in');
3349 this.el.select('.modal-dialog', true).first().setStyle('transform','');
3351 if(this.animate){ // why
3352 this.el.addClass('hideing');
3353 this.el.removeClass('show');
3355 if (!this.el.hasClass('hideing')) {
3356 return; // it's been shown again...
3359 this.el.dom.style.display='';
3361 Roo.get(document.body).removeClass('modal-open');
3362 this.el.removeClass('hideing');
3366 this.el.removeClass('show');
3367 this.el.dom.style.display='';
3368 Roo.get(document.body).removeClass('modal-open');
3371 this.fireEvent('hide', this);
3374 isVisible : function()
3377 return this.el.hasClass('show') && !this.el.hasClass('hideing');
3381 addButton : function(str, cb)
3385 var b = Roo.apply({}, { html : str } );
3386 b.xns = b.xns || Roo.bootstrap;
3387 b.xtype = b.xtype || 'Button';
3388 if (typeof(b.listeners) == 'undefined') {
3389 b.listeners = { click : cb.createDelegate(this) };
3392 var btn = Roo.factory(b);
3394 btn.render(this.getButtonContainer());
3400 setDefaultButton : function(btn)
3402 //this.el.select('.modal-footer').()
3405 resizeTo: function(w,h)
3407 this.dialogEl.setWidth(w);
3409 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
3411 this.bodyEl.setHeight(h - diff);
3413 this.fireEvent('resize', this);
3416 setContentSize : function(w, h)
3420 onButtonClick: function(btn,e)
3423 this.fireEvent('btnclick', btn.name, e);
3426 * Set the title of the Dialog
3427 * @param {String} str new Title
3429 setTitle: function(str) {
3430 this.titleEl.dom.innerHTML = str;
3433 * Set the body of the Dialog
3434 * @param {String} str new Title
3436 setBody: function(str) {
3437 this.bodyEl.dom.innerHTML = str;
3440 * Set the body of the Dialog using the template
3441 * @param {Obj} data - apply this data to the template and replace the body contents.
3443 applyBody: function(obj)
3446 Roo.log("Error - using apply Body without a template");
3449 this.tmpl.overwrite(this.bodyEl, obj);
3452 getChildHeight : function(child_nodes)
3456 child_nodes.length == 0
3461 var child_height = 0;
3463 for(var i = 0; i < child_nodes.length; i++) {
3466 * for modal with tabs...
3467 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3469 var layout_childs = child_nodes[i].childNodes;
3471 for(var j = 0; j < layout_childs.length; j++) {
3473 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3475 var layout_body_childs = layout_childs[j].childNodes;
3477 for(var k = 0; k < layout_body_childs.length; k++) {
3479 if(layout_body_childs[k].classList.contains('navbar')) {
3480 child_height += layout_body_childs[k].offsetHeight;
3484 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3486 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3488 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3490 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3491 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3506 child_height += child_nodes[i].offsetHeight;
3507 // Roo.log(child_nodes[i].offsetHeight);
3510 return child_height;
3516 Roo.apply(Roo.bootstrap.Modal, {
3518 * Button config that displays a single OK button
3527 * Button config that displays Yes and No buttons
3543 * Button config that displays OK and Cancel buttons
3558 * Button config that displays Yes, No and Cancel buttons
3582 * messagebox - can be used as a replace
3586 * @class Roo.MessageBox
3587 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3591 Roo.Msg.alert('Status', 'Changes saved successfully.');
3593 // Prompt for user data:
3594 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3596 // process text value...
3600 // Show a dialog using config options:
3602 title:'Save Changes?',
3603 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3604 buttons: Roo.Msg.YESNOCANCEL,
3611 Roo.bootstrap.MessageBox = function(){
3612 var dlg, opt, mask, waitTimer;
3613 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3614 var buttons, activeTextEl, bwidth;
3618 var handleButton = function(button){
3620 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3624 var handleHide = function(){
3626 dlg.el.removeClass(opt.cls);
3629 // Roo.TaskMgr.stop(waitTimer);
3630 // waitTimer = null;
3635 var updateButtons = function(b){
3638 buttons["ok"].hide();
3639 buttons["cancel"].hide();
3640 buttons["yes"].hide();
3641 buttons["no"].hide();
3642 dlg.footerEl.hide();
3646 dlg.footerEl.show();
3647 for(var k in buttons){
3648 if(typeof buttons[k] != "function"){
3651 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3652 width += buttons[k].el.getWidth()+15;
3662 var handleEsc = function(d, k, e){
3663 if(opt && opt.closable !== false){
3673 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3674 * @return {Roo.BasicDialog} The BasicDialog element
3676 getDialog : function(){
3678 dlg = new Roo.bootstrap.Modal( {
3681 //constraintoviewport:false,
3683 //collapsible : false,
3688 //buttonAlign:"center",
3689 closeClick : function(){
3690 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3693 handleButton("cancel");
3698 dlg.on("hide", handleHide);
3700 //dlg.addKeyListener(27, handleEsc);
3702 this.buttons = buttons;
3703 var bt = this.buttonText;
3704 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3705 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3706 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3707 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3709 bodyEl = dlg.bodyEl.createChild({
3711 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3712 '<textarea class="roo-mb-textarea"></textarea>' +
3713 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3715 msgEl = bodyEl.dom.firstChild;
3716 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3717 textboxEl.enableDisplayMode();
3718 textboxEl.addKeyListener([10,13], function(){
3719 if(dlg.isVisible() && opt && opt.buttons){
3722 }else if(opt.buttons.yes){
3723 handleButton("yes");
3727 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3728 textareaEl.enableDisplayMode();
3729 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3730 progressEl.enableDisplayMode();
3732 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3733 var pf = progressEl.dom.firstChild;
3735 pp = Roo.get(pf.firstChild);
3736 pp.setHeight(pf.offsetHeight);
3744 * Updates the message box body text
3745 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3746 * the XHTML-compliant non-breaking space character '&#160;')
3747 * @return {Roo.MessageBox} This message box
3749 updateText : function(text)
3751 if(!dlg.isVisible() && !opt.width){
3752 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3753 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3755 msgEl.innerHTML = text || ' ';
3757 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3758 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3760 Math.min(opt.width || cw , this.maxWidth),
3761 Math.max(opt.minWidth || this.minWidth, bwidth)
3764 activeTextEl.setWidth(w);
3766 if(dlg.isVisible()){
3767 dlg.fixedcenter = false;
3769 // to big, make it scroll. = But as usual stupid IE does not support
3772 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3773 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3774 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3776 bodyEl.dom.style.height = '';
3777 bodyEl.dom.style.overflowY = '';
3780 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3782 bodyEl.dom.style.overflowX = '';
3785 dlg.setContentSize(w, bodyEl.getHeight());
3786 if(dlg.isVisible()){
3787 dlg.fixedcenter = true;
3793 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3794 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3795 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3796 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3797 * @return {Roo.MessageBox} This message box
3799 updateProgress : function(value, text){
3801 this.updateText(text);
3804 if (pp) { // weird bug on my firefox - for some reason this is not defined
3805 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3806 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3812 * Returns true if the message box is currently displayed
3813 * @return {Boolean} True if the message box is visible, else false
3815 isVisible : function(){
3816 return dlg && dlg.isVisible();
3820 * Hides the message box if it is displayed
3823 if(this.isVisible()){
3829 * Displays a new message box, or reinitializes an existing message box, based on the config options
3830 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3831 * The following config object properties are supported:
3833 Property Type Description
3834 ---------- --------------- ------------------------------------------------------------------------------------
3835 animEl String/Element An id or Element from which the message box should animate as it opens and
3836 closes (defaults to undefined)
3837 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3838 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3839 closable Boolean False to hide the top-right close button (defaults to true). Note that
3840 progress and wait dialogs will ignore this property and always hide the
3841 close button as they can only be closed programmatically.
3842 cls String A custom CSS class to apply to the message box element
3843 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3844 displayed (defaults to 75)
3845 fn Function A callback function to execute after closing the dialog. The arguments to the
3846 function will be btn (the name of the button that was clicked, if applicable,
3847 e.g. "ok"), and text (the value of the active text field, if applicable).
3848 Progress and wait dialogs will ignore this option since they do not respond to
3849 user actions and can only be closed programmatically, so any required function
3850 should be called by the same code after it closes the dialog.
3851 icon String A CSS class that provides a background image to be used as an icon for
3852 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3853 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3854 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3855 modal Boolean False to allow user interaction with the page while the message box is
3856 displayed (defaults to true)
3857 msg String A string that will replace the existing message box body text (defaults
3858 to the XHTML-compliant non-breaking space character ' ')
3859 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3860 progress Boolean True to display a progress bar (defaults to false)
3861 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3862 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3863 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3864 title String The title text
3865 value String The string value to set into the active textbox element if displayed
3866 wait Boolean True to display a progress bar (defaults to false)
3867 width Number The width of the dialog in pixels
3874 msg: 'Please enter your address:',
3876 buttons: Roo.MessageBox.OKCANCEL,
3879 animEl: 'addAddressBtn'
3882 * @param {Object} config Configuration options
3883 * @return {Roo.MessageBox} This message box
3885 show : function(options)
3888 // this causes nightmares if you show one dialog after another
3889 // especially on callbacks..
3891 if(this.isVisible()){
3894 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3895 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3896 Roo.log("New Dialog Message:" + options.msg )
3897 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3898 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3901 var d = this.getDialog();
3903 d.setTitle(opt.title || " ");
3904 d.closeEl.setDisplayed(opt.closable !== false);
3905 activeTextEl = textboxEl;
3906 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3911 textareaEl.setHeight(typeof opt.multiline == "number" ?
3912 opt.multiline : this.defaultTextHeight);
3913 activeTextEl = textareaEl;
3922 progressEl.setDisplayed(opt.progress === true);
3924 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3926 this.updateProgress(0);
3927 activeTextEl.dom.value = opt.value || "";
3929 dlg.setDefaultButton(activeTextEl);
3931 var bs = opt.buttons;
3935 }else if(bs && bs.yes){
3936 db = buttons["yes"];
3938 dlg.setDefaultButton(db);
3940 bwidth = updateButtons(opt.buttons);
3941 this.updateText(opt.msg);
3943 d.el.addClass(opt.cls);
3945 d.proxyDrag = opt.proxyDrag === true;
3946 d.modal = opt.modal !== false;
3947 d.mask = opt.modal !== false ? mask : false;
3949 // force it to the end of the z-index stack so it gets a cursor in FF
3950 document.body.appendChild(dlg.el.dom);
3951 d.animateTarget = null;
3952 d.show(options.animEl);
3958 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3959 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3960 * and closing the message box when the process is complete.
3961 * @param {String} title The title bar text
3962 * @param {String} msg The message box body text
3963 * @return {Roo.MessageBox} This message box
3965 progress : function(title, msg){
3972 minWidth: this.minProgressWidth,
3979 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3980 * If a callback function is passed it will be called after the user clicks the button, and the
3981 * id of the button that was clicked will be passed as the only parameter to the callback
3982 * (could also be the top-right close button).
3983 * @param {String} title The title bar text
3984 * @param {String} msg The message box body text
3985 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3986 * @param {Object} scope (optional) The scope of the callback function
3987 * @return {Roo.MessageBox} This message box
3989 alert : function(title, msg, fn, scope)
4004 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
4005 * interaction while waiting for a long-running process to complete that does not have defined intervals.
4006 * You are responsible for closing the message box when the process is complete.
4007 * @param {String} msg The message box body text
4008 * @param {String} title (optional) The title bar text
4009 * @return {Roo.MessageBox} This message box
4011 wait : function(msg, title){
4022 waitTimer = Roo.TaskMgr.start({
4024 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
4032 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
4033 * If a callback function is passed it will be called after the user clicks either button, and the id of the
4034 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
4035 * @param {String} title The title bar text
4036 * @param {String} msg The message box body text
4037 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4038 * @param {Object} scope (optional) The scope of the callback function
4039 * @return {Roo.MessageBox} This message box
4041 confirm : function(title, msg, fn, scope){
4045 buttons: this.YESNO,
4054 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
4055 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
4056 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
4057 * (could also be the top-right close button) and the text that was entered will be passed as the two
4058 * parameters to the callback.
4059 * @param {String} title The title bar text
4060 * @param {String} msg The message box body text
4061 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4062 * @param {Object} scope (optional) The scope of the callback function
4063 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
4064 * property, or the height in pixels to create the textbox (defaults to false / single-line)
4065 * @return {Roo.MessageBox} This message box
4067 prompt : function(title, msg, fn, scope, multiline){
4071 buttons: this.OKCANCEL,
4076 multiline: multiline,
4083 * Button config that displays a single OK button
4088 * Button config that displays Yes and No buttons
4091 YESNO : {yes:true, no:true},
4093 * Button config that displays OK and Cancel buttons
4096 OKCANCEL : {ok:true, cancel:true},
4098 * Button config that displays Yes, No and Cancel buttons
4101 YESNOCANCEL : {yes:true, no:true, cancel:true},
4104 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
4107 defaultTextHeight : 75,
4109 * The maximum width in pixels of the message box (defaults to 600)
4114 * The minimum width in pixels of the message box (defaults to 100)
4119 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
4120 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
4123 minProgressWidth : 250,
4125 * An object containing the default button text strings that can be overriden for localized language support.
4126 * Supported properties are: ok, cancel, yes and no.
4127 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
4140 * Shorthand for {@link Roo.MessageBox}
4142 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
4143 Roo.Msg = Roo.Msg || Roo.MessageBox;
4152 * @class Roo.bootstrap.Navbar
4153 * @extends Roo.bootstrap.Component
4154 * Bootstrap Navbar class
4157 * Create a new Navbar
4158 * @param {Object} config The config object
4162 Roo.bootstrap.Navbar = function(config){
4163 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
4167 * @event beforetoggle
4168 * Fire before toggle the menu
4169 * @param {Roo.EventObject} e
4171 "beforetoggle" : true
4175 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
4184 getAutoCreate : function(){
4187 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
4191 initEvents :function ()
4193 //Roo.log(this.el.select('.navbar-toggle',true));
4194 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
4201 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
4203 var size = this.el.getSize();
4204 this.maskEl.setSize(size.width, size.height);
4205 this.maskEl.enableDisplayMode("block");
4214 getChildContainer : function()
4216 if (this.el && this.el.select('.collapse').getCount()) {
4217 return this.el.select('.collapse',true).first();
4232 onToggle : function()
4235 if(this.fireEvent('beforetoggle', this) === false){
4238 var ce = this.el.select('.navbar-collapse',true).first();
4240 if (!ce.hasClass('show')) {
4250 * Expand the navbar pulldown
4252 expand : function ()
4255 var ce = this.el.select('.navbar-collapse',true).first();
4256 if (ce.hasClass('collapsing')) {
4259 ce.dom.style.height = '';
4261 ce.addClass('in'); // old...
4262 ce.removeClass('collapse');
4263 ce.addClass('show');
4264 var h = ce.getHeight();
4266 ce.removeClass('show');
4267 // at this point we should be able to see it..
4268 ce.addClass('collapsing');
4270 ce.setHeight(0); // resize it ...
4271 ce.on('transitionend', function() {
4272 //Roo.log('done transition');
4273 ce.removeClass('collapsing');
4274 ce.addClass('show');
4275 ce.removeClass('collapse');
4277 ce.dom.style.height = '';
4278 }, this, { single: true} );
4280 ce.dom.scrollTop = 0;
4283 * Collapse the navbar pulldown
4285 collapse : function()
4287 var ce = this.el.select('.navbar-collapse',true).first();
4289 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
4290 // it's collapsed or collapsing..
4293 ce.removeClass('in'); // old...
4294 ce.setHeight(ce.getHeight());
4295 ce.removeClass('show');
4296 ce.addClass('collapsing');
4298 ce.on('transitionend', function() {
4299 ce.dom.style.height = '';
4300 ce.removeClass('collapsing');
4301 ce.addClass('collapse');
4302 }, this, { single: true} );
4322 * @class Roo.bootstrap.NavSimplebar
4323 * @extends Roo.bootstrap.Navbar
4324 * Bootstrap Sidebar class
4326 * @cfg {Boolean} inverse is inverted color
4328 * @cfg {String} type (nav | pills | tabs)
4329 * @cfg {Boolean} arrangement stacked | justified
4330 * @cfg {String} align (left | right) alignment
4332 * @cfg {Boolean} main (true|false) main nav bar? default false
4333 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4335 * @cfg {String} tag (header|footer|nav|div) default is nav
4337 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4341 * Create a new Sidebar
4342 * @param {Object} config The config object
4346 Roo.bootstrap.NavSimplebar = function(config){
4347 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4350 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
4366 getAutoCreate : function(){
4370 tag : this.tag || 'div',
4371 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
4373 if (['light','white'].indexOf(this.weight) > -1) {
4374 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4376 cfg.cls += ' bg-' + this.weight;
4379 cfg.cls += ' navbar-inverse';
4383 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4385 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4394 cls: 'nav nav-' + this.xtype,
4400 this.type = this.type || 'nav';
4401 if (['tabs','pills'].indexOf(this.type) != -1) {
4402 cfg.cn[0].cls += ' nav-' + this.type
4406 if (this.type!=='nav') {
4407 Roo.log('nav type must be nav/tabs/pills')
4409 cfg.cn[0].cls += ' navbar-nav'
4415 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4416 cfg.cn[0].cls += ' nav-' + this.arrangement;
4420 if (this.align === 'right') {
4421 cfg.cn[0].cls += ' navbar-right';
4446 * navbar-expand-md fixed-top
4450 * @class Roo.bootstrap.NavHeaderbar
4451 * @extends Roo.bootstrap.NavSimplebar
4452 * Bootstrap Sidebar class
4454 * @cfg {String} brand what is brand
4455 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4456 * @cfg {String} brand_href href of the brand
4457 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4458 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4459 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4460 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4463 * Create a new Sidebar
4464 * @param {Object} config The config object
4468 Roo.bootstrap.NavHeaderbar = function(config){
4469 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4473 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4480 desktopCenter : false,
4483 getAutoCreate : function(){
4486 tag: this.nav || 'nav',
4487 cls: 'navbar navbar-expand-md',
4493 if (this.desktopCenter) {
4494 cn.push({cls : 'container', cn : []});
4502 cls: 'navbar-toggle navbar-toggler',
4503 'data-toggle': 'collapse',
4508 html: 'Toggle navigation'
4512 cls: 'icon-bar navbar-toggler-icon'
4525 cn.push( Roo.bootstrap.version == 4 ? btn : {
4527 cls: 'navbar-header',
4536 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
4540 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4542 if (['light','white'].indexOf(this.weight) > -1) {
4543 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4545 cfg.cls += ' bg-' + this.weight;
4548 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4549 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4551 // tag can override this..
4553 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4556 if (this.brand !== '') {
4557 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4558 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4560 href: this.brand_href ? this.brand_href : '#',
4561 cls: 'navbar-brand',
4569 cfg.cls += ' main-nav';
4577 getHeaderChildContainer : function()
4579 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4580 return this.el.select('.navbar-header',true).first();
4583 return this.getChildContainer();
4586 getChildContainer : function()
4589 return this.el.select('.roo-navbar-collapse',true).first();
4594 initEvents : function()
4596 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4598 if (this.autohide) {
4603 Roo.get(document).on('scroll',function(e) {
4604 var ns = Roo.get(document).getScroll().top;
4605 var os = prevScroll;
4609 ft.removeClass('slideDown');
4610 ft.addClass('slideUp');
4613 ft.removeClass('slideUp');
4614 ft.addClass('slideDown');
4635 * @class Roo.bootstrap.NavSidebar
4636 * @extends Roo.bootstrap.Navbar
4637 * Bootstrap Sidebar class
4640 * Create a new Sidebar
4641 * @param {Object} config The config object
4645 Roo.bootstrap.NavSidebar = function(config){
4646 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4649 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4651 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4653 getAutoCreate : function(){
4658 cls: 'sidebar sidebar-nav'
4680 * @class Roo.bootstrap.NavGroup
4681 * @extends Roo.bootstrap.Component
4682 * Bootstrap NavGroup class
4683 * @cfg {String} align (left|right)
4684 * @cfg {Boolean} inverse
4685 * @cfg {String} type (nav|pills|tab) default nav
4686 * @cfg {String} navId - reference Id for navbar.
4690 * Create a new nav group
4691 * @param {Object} config The config object
4694 Roo.bootstrap.NavGroup = function(config){
4695 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4698 Roo.bootstrap.NavGroup.register(this);
4702 * Fires when the active item changes
4703 * @param {Roo.bootstrap.NavGroup} this
4704 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4705 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4712 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4723 getAutoCreate : function()
4725 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4731 if (Roo.bootstrap.version == 4) {
4732 if (['tabs','pills'].indexOf(this.type) != -1) {
4733 cfg.cls += ' nav-' + this.type;
4735 // trying to remove so header bar can right align top?
4736 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
4737 // do not use on header bar...
4738 cfg.cls += ' navbar-nav';
4743 if (['tabs','pills'].indexOf(this.type) != -1) {
4744 cfg.cls += ' nav-' + this.type
4746 if (this.type !== 'nav') {
4747 Roo.log('nav type must be nav/tabs/pills')
4749 cfg.cls += ' navbar-nav'
4753 if (this.parent() && this.parent().sidebar) {
4756 cls: 'dashboard-menu sidebar-menu'
4762 if (this.form === true) {
4765 cls: 'navbar-form form-inline'
4767 //nav navbar-right ml-md-auto
4768 if (this.align === 'right') {
4769 cfg.cls += ' navbar-right ml-md-auto';
4771 cfg.cls += ' navbar-left';
4775 if (this.align === 'right') {
4776 cfg.cls += ' navbar-right ml-md-auto';
4778 cfg.cls += ' mr-auto';
4782 cfg.cls += ' navbar-inverse';
4790 * sets the active Navigation item
4791 * @param {Roo.bootstrap.NavItem} the new current navitem
4793 setActiveItem : function(item)
4796 Roo.each(this.navItems, function(v){
4801 v.setActive(false, true);
4808 item.setActive(true, true);
4809 this.fireEvent('changed', this, item, prev);
4814 * gets the active Navigation item
4815 * @return {Roo.bootstrap.NavItem} the current navitem
4817 getActive : function()
4821 Roo.each(this.navItems, function(v){
4832 indexOfNav : function()
4836 Roo.each(this.navItems, function(v,i){
4847 * adds a Navigation item
4848 * @param {Roo.bootstrap.NavItem} the navitem to add
4850 addItem : function(cfg)
4852 if (this.form && Roo.bootstrap.version == 4) {
4855 var cn = new Roo.bootstrap.NavItem(cfg);
4857 cn.parentId = this.id;
4858 cn.onRender(this.el, null);
4862 * register a Navigation item
4863 * @param {Roo.bootstrap.NavItem} the navitem to add
4865 register : function(item)
4867 this.navItems.push( item);
4868 item.navId = this.navId;
4873 * clear all the Navigation item
4876 clearAll : function()
4879 this.el.dom.innerHTML = '';
4882 getNavItem: function(tabId)
4885 Roo.each(this.navItems, function(e) {
4886 if (e.tabId == tabId) {
4896 setActiveNext : function()
4898 var i = this.indexOfNav(this.getActive());
4899 if (i > this.navItems.length) {
4902 this.setActiveItem(this.navItems[i+1]);
4904 setActivePrev : function()
4906 var i = this.indexOfNav(this.getActive());
4910 this.setActiveItem(this.navItems[i-1]);
4912 clearWasActive : function(except) {
4913 Roo.each(this.navItems, function(e) {
4914 if (e.tabId != except.tabId && e.was_active) {
4915 e.was_active = false;
4922 getWasActive : function ()
4925 Roo.each(this.navItems, function(e) {
4940 Roo.apply(Roo.bootstrap.NavGroup, {
4944 * register a Navigation Group
4945 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4947 register : function(navgrp)
4949 this.groups[navgrp.navId] = navgrp;
4953 * fetch a Navigation Group based on the navigation ID
4954 * @param {string} the navgroup to add
4955 * @returns {Roo.bootstrap.NavGroup} the navgroup
4957 get: function(navId) {
4958 if (typeof(this.groups[navId]) == 'undefined') {
4960 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4962 return this.groups[navId] ;
4977 * @class Roo.bootstrap.NavItem
4978 * @extends Roo.bootstrap.Component
4979 * Bootstrap Navbar.NavItem class
4980 * @cfg {String} href link to
4981 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4983 * @cfg {String} html content of button
4984 * @cfg {String} badge text inside badge
4985 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4986 * @cfg {String} glyphicon DEPRICATED - use fa
4987 * @cfg {String} icon DEPRICATED - use fa
4988 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4989 * @cfg {Boolean} active Is item active
4990 * @cfg {Boolean} disabled Is item disabled
4992 * @cfg {Boolean} preventDefault (true | false) default false
4993 * @cfg {String} tabId the tab that this item activates.
4994 * @cfg {String} tagtype (a|span) render as a href or span?
4995 * @cfg {Boolean} animateRef (true|false) link to element default false
4998 * Create a new Navbar Item
4999 * @param {Object} config The config object
5001 Roo.bootstrap.NavItem = function(config){
5002 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5007 * The raw click event for the entire grid.
5008 * @param {Roo.EventObject} e
5013 * Fires when the active item active state changes
5014 * @param {Roo.bootstrap.NavItem} this
5015 * @param {boolean} state the new state
5021 * Fires when scroll to element
5022 * @param {Roo.bootstrap.NavItem} this
5023 * @param {Object} options
5024 * @param {Roo.EventObject} e
5032 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
5041 preventDefault : false,
5049 button_outline : false,
5053 getAutoCreate : function(){
5061 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
5063 if (this.disabled) {
5064 cfg.cls += ' disabled';
5068 if (this.button_weight.length) {
5069 cfg.tag = this.href ? 'a' : 'button';
5070 cfg.html = this.html || '';
5071 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
5073 cfg.href = this.href;
5076 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
5079 // menu .. should add dropdown-menu class - so no need for carat..
5081 if (this.badge !== '') {
5083 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5088 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
5092 href : this.href || "#",
5093 html: this.html || ''
5096 if (this.tagtype == 'a') {
5097 cfg.cn[0].cls = 'nav-link';
5100 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
5103 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
5105 if(this.glyphicon) {
5106 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
5111 cfg.cn[0].html += " <span class='caret'></span>";
5115 if (this.badge !== '') {
5117 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5125 onRender : function(ct, position)
5127 // Roo.log("Call onRender: " + this.xtype);
5128 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
5132 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
5133 this.navLink = this.el.select('.nav-link',true).first();
5138 initEvents: function()
5140 if (typeof (this.menu) != 'undefined') {
5141 this.menu.parentType = this.xtype;
5142 this.menu.triggerEl = this.el;
5143 this.menu = this.addxtype(Roo.apply({}, this.menu));
5146 this.el.select('a',true).on('click', this.onClick, this);
5148 if(this.tagtype == 'span'){
5149 this.el.select('span',true).on('click', this.onClick, this);
5152 // at this point parent should be available..
5153 this.parent().register(this);
5156 onClick : function(e)
5158 if (e.getTarget('.dropdown-menu-item')) {
5159 // did you click on a menu itemm.... - then don't trigger onclick..
5164 this.preventDefault ||
5167 Roo.log("NavItem - prevent Default?");
5171 if (this.disabled) {
5175 var tg = Roo.bootstrap.TabGroup.get(this.navId);
5176 if (tg && tg.transition) {
5177 Roo.log("waiting for the transitionend");
5183 //Roo.log("fire event clicked");
5184 if(this.fireEvent('click', this, e) === false){
5188 if(this.tagtype == 'span'){
5192 //Roo.log(this.href);
5193 var ael = this.el.select('a',true).first();
5196 if(ael && this.animateRef && this.href.indexOf('#') > -1){
5197 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
5198 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
5199 return; // ignore... - it's a 'hash' to another page.
5201 Roo.log("NavItem - prevent Default?");
5203 this.scrollToElement(e);
5207 var p = this.parent();
5209 if (['tabs','pills'].indexOf(p.type)!==-1) {
5210 if (typeof(p.setActiveItem) !== 'undefined') {
5211 p.setActiveItem(this);
5215 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
5216 if (p.parentType == 'NavHeaderbar' && !this.menu) {
5217 // remove the collapsed menu expand...
5218 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
5222 isActive: function () {
5225 setActive : function(state, fire, is_was_active)
5227 if (this.active && !state && this.navId) {
5228 this.was_active = true;
5229 var nv = Roo.bootstrap.NavGroup.get(this.navId);
5231 nv.clearWasActive(this);
5235 this.active = state;
5238 this.el.removeClass('active');
5239 this.navLink ? this.navLink.removeClass('active') : false;
5240 } else if (!this.el.hasClass('active')) {
5242 this.el.addClass('active');
5243 if (Roo.bootstrap.version == 4 && this.navLink ) {
5244 this.navLink.addClass('active');
5249 this.fireEvent('changed', this, state);
5252 // show a panel if it's registered and related..
5254 if (!this.navId || !this.tabId || !state || is_was_active) {
5258 var tg = Roo.bootstrap.TabGroup.get(this.navId);
5262 var pan = tg.getPanelByName(this.tabId);
5266 // if we can not flip to new panel - go back to old nav highlight..
5267 if (false == tg.showPanel(pan)) {
5268 var nv = Roo.bootstrap.NavGroup.get(this.navId);
5270 var onav = nv.getWasActive();
5272 onav.setActive(true, false, true);
5281 // this should not be here...
5282 setDisabled : function(state)
5284 this.disabled = state;
5286 this.el.removeClass('disabled');
5287 } else if (!this.el.hasClass('disabled')) {
5288 this.el.addClass('disabled');
5294 * Fetch the element to display the tooltip on.
5295 * @return {Roo.Element} defaults to this.el
5297 tooltipEl : function()
5299 return this.el.select('' + this.tagtype + '', true).first();
5302 scrollToElement : function(e)
5304 var c = document.body;
5307 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
5309 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
5310 c = document.documentElement;
5313 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5319 var o = target.calcOffsetsTo(c);
5326 this.fireEvent('scrollto', this, options, e);
5328 Roo.get(c).scrollTo('top', options.value, true);
5341 * <span> icon </span>
5342 * <span> text </span>
5343 * <span>badge </span>
5347 * @class Roo.bootstrap.NavSidebarItem
5348 * @extends Roo.bootstrap.NavItem
5349 * Bootstrap Navbar.NavSidebarItem class
5350 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5351 * {Boolean} open is the menu open
5352 * {Boolean} buttonView use button as the tigger el rather that a (default false)
5353 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5354 * {String} buttonSize (sm|md|lg)the extra classes for the button
5355 * {Boolean} showArrow show arrow next to the text (default true)
5357 * Create a new Navbar Button
5358 * @param {Object} config The config object
5360 Roo.bootstrap.NavSidebarItem = function(config){
5361 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5366 * The raw click event for the entire grid.
5367 * @param {Roo.EventObject} e
5372 * Fires when the active item active state changes
5373 * @param {Roo.bootstrap.NavSidebarItem} this
5374 * @param {boolean} state the new state
5382 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
5384 badgeWeight : 'default',
5390 buttonWeight : 'default',
5396 getAutoCreate : function(){
5401 href : this.href || '#',
5407 if(this.buttonView){
5410 href : this.href || '#',
5411 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5424 cfg.cls += ' active';
5427 if (this.disabled) {
5428 cfg.cls += ' disabled';
5431 cfg.cls += ' open x-open';
5434 if (this.glyphicon || this.icon) {
5435 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
5436 a.cn.push({ tag : 'i', cls : c }) ;
5439 if(!this.buttonView){
5442 html : this.html || ''
5449 if (this.badge !== '') {
5450 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
5456 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5459 a.cls += ' dropdown-toggle treeview' ;
5465 initEvents : function()
5467 if (typeof (this.menu) != 'undefined') {
5468 this.menu.parentType = this.xtype;
5469 this.menu.triggerEl = this.el;
5470 this.menu = this.addxtype(Roo.apply({}, this.menu));
5473 this.el.on('click', this.onClick, this);
5475 if(this.badge !== ''){
5476 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5481 onClick : function(e)
5488 if(this.preventDefault){
5492 this.fireEvent('click', this, e);
5495 disable : function()
5497 this.setDisabled(true);
5502 this.setDisabled(false);
5505 setDisabled : function(state)
5507 if(this.disabled == state){
5511 this.disabled = state;
5514 this.el.addClass('disabled');
5518 this.el.removeClass('disabled');
5523 setActive : function(state)
5525 if(this.active == state){
5529 this.active = state;
5532 this.el.addClass('active');
5536 this.el.removeClass('active');
5541 isActive: function ()
5546 setBadge : function(str)
5552 this.badgeEl.dom.innerHTML = str;
5569 * @class Roo.bootstrap.Row
5570 * @extends Roo.bootstrap.Component
5571 * Bootstrap Row class (contains columns...)
5575 * @param {Object} config The config object
5578 Roo.bootstrap.Row = function(config){
5579 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5582 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5584 getAutoCreate : function(){
5603 * @class Roo.bootstrap.Element
5604 * @extends Roo.bootstrap.Component
5605 * Bootstrap Element class
5606 * @cfg {String} html contents of the element
5607 * @cfg {String} tag tag of the element
5608 * @cfg {String} cls class of the element
5609 * @cfg {Boolean} preventDefault (true|false) default false
5610 * @cfg {Boolean} clickable (true|false) default false
5613 * Create a new Element
5614 * @param {Object} config The config object
5617 Roo.bootstrap.Element = function(config){
5618 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5624 * When a element is chick
5625 * @param {Roo.bootstrap.Element} this
5626 * @param {Roo.EventObject} e
5632 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5637 preventDefault: false,
5640 getAutoCreate : function(){
5644 // cls: this.cls, double assign in parent class Component.js :: onRender
5651 initEvents: function()
5653 Roo.bootstrap.Element.superclass.initEvents.call(this);
5656 this.el.on('click', this.onClick, this);
5661 onClick : function(e)
5663 if(this.preventDefault){
5667 this.fireEvent('click', this, e);
5670 getValue : function()
5672 return this.el.dom.innerHTML;
5675 setValue : function(value)
5677 this.el.dom.innerHTML = value;
5692 * @class Roo.bootstrap.Pagination
5693 * @extends Roo.bootstrap.Component
5694 * Bootstrap Pagination class
5695 * @cfg {String} size xs | sm | md | lg
5696 * @cfg {Boolean} inverse false | true
5699 * Create a new Pagination
5700 * @param {Object} config The config object
5703 Roo.bootstrap.Pagination = function(config){
5704 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5707 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5713 getAutoCreate : function(){
5719 cfg.cls += ' inverse';
5725 cfg.cls += " " + this.cls;
5743 * @class Roo.bootstrap.PaginationItem
5744 * @extends Roo.bootstrap.Component
5745 * Bootstrap PaginationItem class
5746 * @cfg {String} html text
5747 * @cfg {String} href the link
5748 * @cfg {Boolean} preventDefault (true | false) default true
5749 * @cfg {Boolean} active (true | false) default false
5750 * @cfg {Boolean} disabled default false
5754 * Create a new PaginationItem
5755 * @param {Object} config The config object
5759 Roo.bootstrap.PaginationItem = function(config){
5760 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5765 * The raw click event for the entire grid.
5766 * @param {Roo.EventObject} e
5772 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5776 preventDefault: true,
5781 getAutoCreate : function(){
5787 href : this.href ? this.href : '#',
5788 html : this.html ? this.html : ''
5798 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5802 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5808 initEvents: function() {
5810 this.el.on('click', this.onClick, this);
5813 onClick : function(e)
5815 Roo.log('PaginationItem on click ');
5816 if(this.preventDefault){
5824 this.fireEvent('click', this, e);
5840 * @class Roo.bootstrap.Slider
5841 * @extends Roo.bootstrap.Component
5842 * Bootstrap Slider class
5845 * Create a new Slider
5846 * @param {Object} config The config object
5849 Roo.bootstrap.Slider = function(config){
5850 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5853 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5855 getAutoCreate : function(){
5859 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5863 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5875 * Ext JS Library 1.1.1
5876 * Copyright(c) 2006-2007, Ext JS, LLC.
5878 * Originally Released Under LGPL - original licence link has changed is not relivant.
5881 * <script type="text/javascript">
5886 * @class Roo.grid.ColumnModel
5887 * @extends Roo.util.Observable
5888 * This is the default implementation of a ColumnModel used by the Grid. It defines
5889 * the columns in the grid.
5892 var colModel = new Roo.grid.ColumnModel([
5893 {header: "Ticker", width: 60, sortable: true, locked: true},
5894 {header: "Company Name", width: 150, sortable: true},
5895 {header: "Market Cap.", width: 100, sortable: true},
5896 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5897 {header: "Employees", width: 100, sortable: true, resizable: false}
5902 * The config options listed for this class are options which may appear in each
5903 * individual column definition.
5904 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5906 * @param {Object} config An Array of column config objects. See this class's
5907 * config objects for details.
5909 Roo.grid.ColumnModel = function(config){
5911 * The config passed into the constructor
5913 this.config = config;
5916 // if no id, create one
5917 // if the column does not have a dataIndex mapping,
5918 // map it to the order it is in the config
5919 for(var i = 0, len = config.length; i < len; i++){
5921 if(typeof c.dataIndex == "undefined"){
5924 if(typeof c.renderer == "string"){
5925 c.renderer = Roo.util.Format[c.renderer];
5927 if(typeof c.id == "undefined"){
5930 if(c.editor && c.editor.xtype){
5931 c.editor = Roo.factory(c.editor, Roo.grid);
5933 if(c.editor && c.editor.isFormField){
5934 c.editor = new Roo.grid.GridEditor(c.editor);
5936 this.lookup[c.id] = c;
5940 * The width of columns which have no width specified (defaults to 100)
5943 this.defaultWidth = 100;
5946 * Default sortable of columns which have no sortable specified (defaults to false)
5949 this.defaultSortable = false;
5953 * @event widthchange
5954 * Fires when the width of a column changes.
5955 * @param {ColumnModel} this
5956 * @param {Number} columnIndex The column index
5957 * @param {Number} newWidth The new width
5959 "widthchange": true,
5961 * @event headerchange
5962 * Fires when the text of a header changes.
5963 * @param {ColumnModel} this
5964 * @param {Number} columnIndex The column index
5965 * @param {Number} newText The new header text
5967 "headerchange": true,
5969 * @event hiddenchange
5970 * Fires when a column is hidden or "unhidden".
5971 * @param {ColumnModel} this
5972 * @param {Number} columnIndex The column index
5973 * @param {Boolean} hidden true if hidden, false otherwise
5975 "hiddenchange": true,
5977 * @event columnmoved
5978 * Fires when a column is moved.
5979 * @param {ColumnModel} this
5980 * @param {Number} oldIndex
5981 * @param {Number} newIndex
5983 "columnmoved" : true,
5985 * @event columlockchange
5986 * Fires when a column's locked state is changed
5987 * @param {ColumnModel} this
5988 * @param {Number} colIndex
5989 * @param {Boolean} locked true if locked
5991 "columnlockchange" : true
5993 Roo.grid.ColumnModel.superclass.constructor.call(this);
5995 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5997 * @cfg {String} header The header text to display in the Grid view.
6000 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6001 * {@link Roo.data.Record} definition from which to draw the column's value. If not
6002 * specified, the column's index is used as an index into the Record's data Array.
6005 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6006 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6009 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6010 * Defaults to the value of the {@link #defaultSortable} property.
6011 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6014 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
6017 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
6020 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6023 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6026 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6027 * given the cell's data value. See {@link #setRenderer}. If not specified, the
6028 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6029 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6032 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
6035 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
6038 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
6041 * @cfg {String} cursor (Optional)
6044 * @cfg {String} tooltip (Optional)
6047 * @cfg {Number} xs (Optional)
6050 * @cfg {Number} sm (Optional)
6053 * @cfg {Number} md (Optional)
6056 * @cfg {Number} lg (Optional)
6059 * Returns the id of the column at the specified index.
6060 * @param {Number} index The column index
6061 * @return {String} the id
6063 getColumnId : function(index){
6064 return this.config[index].id;
6068 * Returns the column for a specified id.
6069 * @param {String} id The column id
6070 * @return {Object} the column
6072 getColumnById : function(id){
6073 return this.lookup[id];
6078 * Returns the column for a specified dataIndex.
6079 * @param {String} dataIndex The column dataIndex
6080 * @return {Object|Boolean} the column or false if not found
6082 getColumnByDataIndex: function(dataIndex){
6083 var index = this.findColumnIndex(dataIndex);
6084 return index > -1 ? this.config[index] : false;
6088 * Returns the index for a specified column id.
6089 * @param {String} id The column id
6090 * @return {Number} the index, or -1 if not found
6092 getIndexById : function(id){
6093 for(var i = 0, len = this.config.length; i < len; i++){
6094 if(this.config[i].id == id){
6102 * Returns the index for a specified column dataIndex.
6103 * @param {String} dataIndex The column dataIndex
6104 * @return {Number} the index, or -1 if not found
6107 findColumnIndex : function(dataIndex){
6108 for(var i = 0, len = this.config.length; i < len; i++){
6109 if(this.config[i].dataIndex == dataIndex){
6117 moveColumn : function(oldIndex, newIndex){
6118 var c = this.config[oldIndex];
6119 this.config.splice(oldIndex, 1);
6120 this.config.splice(newIndex, 0, c);
6121 this.dataMap = null;
6122 this.fireEvent("columnmoved", this, oldIndex, newIndex);
6125 isLocked : function(colIndex){
6126 return this.config[colIndex].locked === true;
6129 setLocked : function(colIndex, value, suppressEvent){
6130 if(this.isLocked(colIndex) == value){
6133 this.config[colIndex].locked = value;
6135 this.fireEvent("columnlockchange", this, colIndex, value);
6139 getTotalLockedWidth : function(){
6141 for(var i = 0; i < this.config.length; i++){
6142 if(this.isLocked(i) && !this.isHidden(i)){
6143 this.totalWidth += this.getColumnWidth(i);
6149 getLockedCount : function(){
6150 for(var i = 0, len = this.config.length; i < len; i++){
6151 if(!this.isLocked(i)){
6156 return this.config.length;
6160 * Returns the number of columns.
6163 getColumnCount : function(visibleOnly){
6164 if(visibleOnly === true){
6166 for(var i = 0, len = this.config.length; i < len; i++){
6167 if(!this.isHidden(i)){
6173 return this.config.length;
6177 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
6178 * @param {Function} fn
6179 * @param {Object} scope (optional)
6180 * @return {Array} result
6182 getColumnsBy : function(fn, scope){
6184 for(var i = 0, len = this.config.length; i < len; i++){
6185 var c = this.config[i];
6186 if(fn.call(scope||this, c, i) === true){
6194 * Returns true if the specified column is sortable.
6195 * @param {Number} col The column index
6198 isSortable : function(col){
6199 if(typeof this.config[col].sortable == "undefined"){
6200 return this.defaultSortable;
6202 return this.config[col].sortable;
6206 * Returns the rendering (formatting) function defined for the column.
6207 * @param {Number} col The column index.
6208 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
6210 getRenderer : function(col){
6211 if(!this.config[col].renderer){
6212 return Roo.grid.ColumnModel.defaultRenderer;
6214 return this.config[col].renderer;
6218 * Sets the rendering (formatting) function for a column.
6219 * @param {Number} col The column index
6220 * @param {Function} fn The function to use to process the cell's raw data
6221 * to return HTML markup for the grid view. The render function is called with
6222 * the following parameters:<ul>
6223 * <li>Data value.</li>
6224 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
6225 * <li>css A CSS style string to apply to the table cell.</li>
6226 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
6227 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
6228 * <li>Row index</li>
6229 * <li>Column index</li>
6230 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
6232 setRenderer : function(col, fn){
6233 this.config[col].renderer = fn;
6237 * Returns the width for the specified column.
6238 * @param {Number} col The column index
6241 getColumnWidth : function(col){
6242 return this.config[col].width * 1 || this.defaultWidth;
6246 * Sets the width for a column.
6247 * @param {Number} col The column index
6248 * @param {Number} width The new width
6250 setColumnWidth : function(col, width, suppressEvent){
6251 this.config[col].width = width;
6252 this.totalWidth = null;
6254 this.fireEvent("widthchange", this, col, width);
6259 * Returns the total width of all columns.
6260 * @param {Boolean} includeHidden True to include hidden column widths
6263 getTotalWidth : function(includeHidden){
6264 if(!this.totalWidth){
6265 this.totalWidth = 0;
6266 for(var i = 0, len = this.config.length; i < len; i++){
6267 if(includeHidden || !this.isHidden(i)){
6268 this.totalWidth += this.getColumnWidth(i);
6272 return this.totalWidth;
6276 * Returns the header for the specified column.
6277 * @param {Number} col The column index
6280 getColumnHeader : function(col){
6281 return this.config[col].header;
6285 * Sets the header for a column.
6286 * @param {Number} col The column index
6287 * @param {String} header The new header
6289 setColumnHeader : function(col, header){
6290 this.config[col].header = header;
6291 this.fireEvent("headerchange", this, col, header);
6295 * Returns the tooltip for the specified column.
6296 * @param {Number} col The column index
6299 getColumnTooltip : function(col){
6300 return this.config[col].tooltip;
6303 * Sets the tooltip for a column.
6304 * @param {Number} col The column index
6305 * @param {String} tooltip The new tooltip
6307 setColumnTooltip : function(col, tooltip){
6308 this.config[col].tooltip = tooltip;
6312 * Returns the dataIndex for the specified column.
6313 * @param {Number} col The column index
6316 getDataIndex : function(col){
6317 return this.config[col].dataIndex;
6321 * Sets the dataIndex for a column.
6322 * @param {Number} col The column index
6323 * @param {Number} dataIndex The new dataIndex
6325 setDataIndex : function(col, dataIndex){
6326 this.config[col].dataIndex = dataIndex;
6332 * Returns true if the cell is editable.
6333 * @param {Number} colIndex The column index
6334 * @param {Number} rowIndex The row index - this is nto actually used..?
6337 isCellEditable : function(colIndex, rowIndex){
6338 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6342 * Returns the editor defined for the cell/column.
6343 * return false or null to disable editing.
6344 * @param {Number} colIndex The column index
6345 * @param {Number} rowIndex The row index
6348 getCellEditor : function(colIndex, rowIndex){
6349 return this.config[colIndex].editor;
6353 * Sets if a column is editable.
6354 * @param {Number} col The column index
6355 * @param {Boolean} editable True if the column is editable
6357 setEditable : function(col, editable){
6358 this.config[col].editable = editable;
6363 * Returns true if the column is hidden.
6364 * @param {Number} colIndex The column index
6367 isHidden : function(colIndex){
6368 return this.config[colIndex].hidden;
6373 * Returns true if the column width cannot be changed
6375 isFixed : function(colIndex){
6376 return this.config[colIndex].fixed;
6380 * Returns true if the column can be resized
6383 isResizable : function(colIndex){
6384 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6387 * Sets if a column is hidden.
6388 * @param {Number} colIndex The column index
6389 * @param {Boolean} hidden True if the column is hidden
6391 setHidden : function(colIndex, hidden){
6392 this.config[colIndex].hidden = hidden;
6393 this.totalWidth = null;
6394 this.fireEvent("hiddenchange", this, colIndex, hidden);
6398 * Sets the editor for a column.
6399 * @param {Number} col The column index
6400 * @param {Object} editor The editor object
6402 setEditor : function(col, editor){
6403 this.config[col].editor = editor;
6407 Roo.grid.ColumnModel.defaultRenderer = function(value)
6409 if(typeof value == "object") {
6412 if(typeof value == "string" && value.length < 1){
6416 return String.format("{0}", value);
6419 // Alias for backwards compatibility
6420 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6423 * Ext JS Library 1.1.1
6424 * Copyright(c) 2006-2007, Ext JS, LLC.
6426 * Originally Released Under LGPL - original licence link has changed is not relivant.
6429 * <script type="text/javascript">
6433 * @class Roo.LoadMask
6434 * A simple utility class for generically masking elements while loading data. If the element being masked has
6435 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6436 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
6437 * element's UpdateManager load indicator and will be destroyed after the initial load.
6439 * Create a new LoadMask
6440 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6441 * @param {Object} config The config object
6443 Roo.LoadMask = function(el, config){
6444 this.el = Roo.get(el);
6445 Roo.apply(this, config);
6447 this.store.on('beforeload', this.onBeforeLoad, this);
6448 this.store.on('load', this.onLoad, this);
6449 this.store.on('loadexception', this.onLoadException, this);
6450 this.removeMask = false;
6452 var um = this.el.getUpdateManager();
6453 um.showLoadIndicator = false; // disable the default indicator
6454 um.on('beforeupdate', this.onBeforeLoad, this);
6455 um.on('update', this.onLoad, this);
6456 um.on('failure', this.onLoad, this);
6457 this.removeMask = true;
6461 Roo.LoadMask.prototype = {
6463 * @cfg {Boolean} removeMask
6464 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6465 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
6469 * The text to display in a centered loading message box (defaults to 'Loading...')
6473 * @cfg {String} msgCls
6474 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6476 msgCls : 'x-mask-loading',
6479 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6485 * Disables the mask to prevent it from being displayed
6487 disable : function(){
6488 this.disabled = true;
6492 * Enables the mask so that it can be displayed
6494 enable : function(){
6495 this.disabled = false;
6498 onLoadException : function()
6502 if (typeof(arguments[3]) != 'undefined') {
6503 Roo.MessageBox.alert("Error loading",arguments[3]);
6507 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6508 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6515 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6520 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6524 onBeforeLoad : function(){
6526 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6531 destroy : function(){
6533 this.store.un('beforeload', this.onBeforeLoad, this);
6534 this.store.un('load', this.onLoad, this);
6535 this.store.un('loadexception', this.onLoadException, this);
6537 var um = this.el.getUpdateManager();
6538 um.un('beforeupdate', this.onBeforeLoad, this);
6539 um.un('update', this.onLoad, this);
6540 um.un('failure', this.onLoad, this);
6551 * @class Roo.bootstrap.Table
6552 * @extends Roo.bootstrap.Component
6553 * Bootstrap Table class
6554 * @cfg {String} cls table class
6555 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6556 * @cfg {String} bgcolor Specifies the background color for a table
6557 * @cfg {Number} border Specifies whether the table cells should have borders or not
6558 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6559 * @cfg {Number} cellspacing Specifies the space between cells
6560 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6561 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6562 * @cfg {String} sortable Specifies that the table should be sortable
6563 * @cfg {String} summary Specifies a summary of the content of a table
6564 * @cfg {Number} width Specifies the width of a table
6565 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6567 * @cfg {boolean} striped Should the rows be alternative striped
6568 * @cfg {boolean} bordered Add borders to the table
6569 * @cfg {boolean} hover Add hover highlighting
6570 * @cfg {boolean} condensed Format condensed
6571 * @cfg {boolean} responsive Format condensed
6572 * @cfg {Boolean} loadMask (true|false) default false
6573 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6574 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6575 * @cfg {Boolean} rowSelection (true|false) default false
6576 * @cfg {Boolean} cellSelection (true|false) default false
6577 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6578 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6579 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6580 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6584 * Create a new Table
6585 * @param {Object} config The config object
6588 Roo.bootstrap.Table = function(config){
6589 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6594 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6595 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6596 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6597 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6599 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6601 this.sm.grid = this;
6602 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6603 this.sm = this.selModel;
6604 this.sm.xmodule = this.xmodule || false;
6607 if (this.cm && typeof(this.cm.config) == 'undefined') {
6608 this.colModel = new Roo.grid.ColumnModel(this.cm);
6609 this.cm = this.colModel;
6610 this.cm.xmodule = this.xmodule || false;
6613 this.store= Roo.factory(this.store, Roo.data);
6614 this.ds = this.store;
6615 this.ds.xmodule = this.xmodule || false;
6618 if (this.footer && this.store) {
6619 this.footer.dataSource = this.ds;
6620 this.footer = Roo.factory(this.footer);
6627 * Fires when a cell is clicked
6628 * @param {Roo.bootstrap.Table} this
6629 * @param {Roo.Element} el
6630 * @param {Number} rowIndex
6631 * @param {Number} columnIndex
6632 * @param {Roo.EventObject} e
6636 * @event celldblclick
6637 * Fires when a cell is double clicked
6638 * @param {Roo.bootstrap.Table} this
6639 * @param {Roo.Element} el
6640 * @param {Number} rowIndex
6641 * @param {Number} columnIndex
6642 * @param {Roo.EventObject} e
6644 "celldblclick" : true,
6647 * Fires when a row is clicked
6648 * @param {Roo.bootstrap.Table} this
6649 * @param {Roo.Element} el
6650 * @param {Number} rowIndex
6651 * @param {Roo.EventObject} e
6655 * @event rowdblclick
6656 * Fires when a row is double clicked
6657 * @param {Roo.bootstrap.Table} this
6658 * @param {Roo.Element} el
6659 * @param {Number} rowIndex
6660 * @param {Roo.EventObject} e
6662 "rowdblclick" : true,
6665 * Fires when a mouseover occur
6666 * @param {Roo.bootstrap.Table} this
6667 * @param {Roo.Element} el
6668 * @param {Number} rowIndex
6669 * @param {Number} columnIndex
6670 * @param {Roo.EventObject} e
6675 * Fires when a mouseout occur
6676 * @param {Roo.bootstrap.Table} this
6677 * @param {Roo.Element} el
6678 * @param {Number} rowIndex
6679 * @param {Number} columnIndex
6680 * @param {Roo.EventObject} e
6685 * Fires when a row is rendered, so you can change add a style to it.
6686 * @param {Roo.bootstrap.Table} this
6687 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6691 * @event rowsrendered
6692 * Fires when all the rows have been rendered
6693 * @param {Roo.bootstrap.Table} this
6695 'rowsrendered' : true,
6697 * @event contextmenu
6698 * The raw contextmenu event for the entire grid.
6699 * @param {Roo.EventObject} e
6701 "contextmenu" : true,
6703 * @event rowcontextmenu
6704 * Fires when a row is right clicked
6705 * @param {Roo.bootstrap.Table} this
6706 * @param {Number} rowIndex
6707 * @param {Roo.EventObject} e
6709 "rowcontextmenu" : true,
6711 * @event cellcontextmenu
6712 * Fires when a cell is right clicked
6713 * @param {Roo.bootstrap.Table} this
6714 * @param {Number} rowIndex
6715 * @param {Number} cellIndex
6716 * @param {Roo.EventObject} e
6718 "cellcontextmenu" : true,
6720 * @event headercontextmenu
6721 * Fires when a header is right clicked
6722 * @param {Roo.bootstrap.Table} this
6723 * @param {Number} columnIndex
6724 * @param {Roo.EventObject} e
6726 "headercontextmenu" : true
6730 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6756 rowSelection : false,
6757 cellSelection : false,
6760 // Roo.Element - the tbody
6762 // Roo.Element - thead element
6765 container: false, // used by gridpanel...
6771 auto_hide_footer : false,
6773 getAutoCreate : function()
6775 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6782 if (this.scrollBody) {
6783 cfg.cls += ' table-body-fixed';
6786 cfg.cls += ' table-striped';
6790 cfg.cls += ' table-hover';
6792 if (this.bordered) {
6793 cfg.cls += ' table-bordered';
6795 if (this.condensed) {
6796 cfg.cls += ' table-condensed';
6798 if (this.responsive) {
6799 cfg.cls += ' table-responsive';
6803 cfg.cls+= ' ' +this.cls;
6806 // this lot should be simplifed...
6819 ].forEach(function(k) {
6827 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6830 if(this.store || this.cm){
6831 if(this.headerShow){
6832 cfg.cn.push(this.renderHeader());
6835 cfg.cn.push(this.renderBody());
6837 if(this.footerShow){
6838 cfg.cn.push(this.renderFooter());
6840 // where does this come from?
6841 //cfg.cls+= ' TableGrid';
6844 return { cn : [ cfg ] };
6847 initEvents : function()
6849 if(!this.store || !this.cm){
6852 if (this.selModel) {
6853 this.selModel.initEvents();
6857 //Roo.log('initEvents with ds!!!!');
6859 this.mainBody = this.el.select('tbody', true).first();
6860 this.mainHead = this.el.select('thead', true).first();
6861 this.mainFoot = this.el.select('tfoot', true).first();
6867 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6868 e.on('click', _this.sort, _this);
6871 this.mainBody.on("click", this.onClick, this);
6872 this.mainBody.on("dblclick", this.onDblClick, this);
6874 // why is this done????? = it breaks dialogs??
6875 //this.parent().el.setStyle('position', 'relative');
6879 this.footer.parentId = this.id;
6880 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6883 this.el.select('tfoot tr td').first().addClass('hide');
6888 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6891 this.store.on('load', this.onLoad, this);
6892 this.store.on('beforeload', this.onBeforeLoad, this);
6893 this.store.on('update', this.onUpdate, this);
6894 this.store.on('add', this.onAdd, this);
6895 this.store.on("clear", this.clear, this);
6897 this.el.on("contextmenu", this.onContextMenu, this);
6899 this.mainBody.on('scroll', this.onBodyScroll, this);
6901 this.cm.on("headerchange", this.onHeaderChange, this);
6903 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6907 onContextMenu : function(e, t)
6909 this.processEvent("contextmenu", e);
6912 processEvent : function(name, e)
6914 if (name != 'touchstart' ) {
6915 this.fireEvent(name, e);
6918 var t = e.getTarget();
6920 var cell = Roo.get(t);
6926 if(cell.findParent('tfoot', false, true)){
6930 if(cell.findParent('thead', false, true)){
6932 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6933 cell = Roo.get(t).findParent('th', false, true);
6935 Roo.log("failed to find th in thead?");
6936 Roo.log(e.getTarget());
6941 var cellIndex = cell.dom.cellIndex;
6943 var ename = name == 'touchstart' ? 'click' : name;
6944 this.fireEvent("header" + ename, this, cellIndex, e);
6949 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6950 cell = Roo.get(t).findParent('td', false, true);
6952 Roo.log("failed to find th in tbody?");
6953 Roo.log(e.getTarget());
6958 var row = cell.findParent('tr', false, true);
6959 var cellIndex = cell.dom.cellIndex;
6960 var rowIndex = row.dom.rowIndex - 1;
6964 this.fireEvent("row" + name, this, rowIndex, e);
6968 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6974 onMouseover : function(e, el)
6976 var cell = Roo.get(el);
6982 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6983 cell = cell.findParent('td', false, true);
6986 var row = cell.findParent('tr', false, true);
6987 var cellIndex = cell.dom.cellIndex;
6988 var rowIndex = row.dom.rowIndex - 1; // start from 0
6990 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6994 onMouseout : function(e, el)
6996 var cell = Roo.get(el);
7002 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7003 cell = cell.findParent('td', false, true);
7006 var row = cell.findParent('tr', false, true);
7007 var cellIndex = cell.dom.cellIndex;
7008 var rowIndex = row.dom.rowIndex - 1; // start from 0
7010 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7014 onClick : function(e, el)
7016 var cell = Roo.get(el);
7018 if(!cell || (!this.cellSelection && !this.rowSelection)){
7022 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7023 cell = cell.findParent('td', false, true);
7026 if(!cell || typeof(cell) == 'undefined'){
7030 var row = cell.findParent('tr', false, true);
7032 if(!row || typeof(row) == 'undefined'){
7036 var cellIndex = cell.dom.cellIndex;
7037 var rowIndex = this.getRowIndex(row);
7039 // why??? - should these not be based on SelectionModel?
7040 if(this.cellSelection){
7041 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7044 if(this.rowSelection){
7045 this.fireEvent('rowclick', this, row, rowIndex, e);
7051 onDblClick : function(e,el)
7053 var cell = Roo.get(el);
7055 if(!cell || (!this.cellSelection && !this.rowSelection)){
7059 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7060 cell = cell.findParent('td', false, true);
7063 if(!cell || typeof(cell) == 'undefined'){
7067 var row = cell.findParent('tr', false, true);
7069 if(!row || typeof(row) == 'undefined'){
7073 var cellIndex = cell.dom.cellIndex;
7074 var rowIndex = this.getRowIndex(row);
7076 if(this.cellSelection){
7077 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7080 if(this.rowSelection){
7081 this.fireEvent('rowdblclick', this, row, rowIndex, e);
7085 sort : function(e,el)
7087 var col = Roo.get(el);
7089 if(!col.hasClass('sortable')){
7093 var sort = col.attr('sort');
7096 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7100 this.store.sortInfo = {field : sort, direction : dir};
7103 Roo.log("calling footer first");
7104 this.footer.onClick('first');
7107 this.store.load({ params : { start : 0 } });
7111 renderHeader : function()
7119 this.totalWidth = 0;
7121 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7123 var config = cm.config[i];
7127 cls : 'x-hcol-' + i,
7129 html: cm.getColumnHeader(i)
7134 if(typeof(config.sortable) != 'undefined' && config.sortable){
7136 c.html = '<i class="glyphicon"></i>' + c.html;
7139 // could use BS4 hidden-..-down
7141 if(typeof(config.lgHeader) != 'undefined'){
7142 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
7145 if(typeof(config.mdHeader) != 'undefined'){
7146 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
7149 if(typeof(config.smHeader) != 'undefined'){
7150 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
7153 if(typeof(config.xsHeader) != 'undefined'){
7154 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
7161 if(typeof(config.tooltip) != 'undefined'){
7162 c.tooltip = config.tooltip;
7165 if(typeof(config.colspan) != 'undefined'){
7166 c.colspan = config.colspan;
7169 if(typeof(config.hidden) != 'undefined' && config.hidden){
7170 c.style += ' display:none;';
7173 if(typeof(config.dataIndex) != 'undefined'){
7174 c.sort = config.dataIndex;
7179 if(typeof(config.align) != 'undefined' && config.align.length){
7180 c.style += ' text-align:' + config.align + ';';
7183 if(typeof(config.width) != 'undefined'){
7184 c.style += ' width:' + config.width + 'px;';
7185 this.totalWidth += config.width;
7187 this.totalWidth += 100; // assume minimum of 100 per column?
7190 if(typeof(config.cls) != 'undefined'){
7191 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
7194 ['xs','sm','md','lg'].map(function(size){
7196 if(typeof(config[size]) == 'undefined'){
7200 if (!config[size]) { // 0 = hidden
7201 // BS 4 '0' is treated as hide that column and below.
7202 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
7206 c.cls += ' col-' + size + '-' + config[size] + (
7207 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7219 renderBody : function()
7229 colspan : this.cm.getColumnCount()
7239 renderFooter : function()
7249 colspan : this.cm.getColumnCount()
7263 // Roo.log('ds onload');
7268 var ds = this.store;
7270 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7271 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
7272 if (_this.store.sortInfo) {
7274 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
7275 e.select('i', true).addClass(['glyphicon-arrow-up']);
7278 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
7279 e.select('i', true).addClass(['glyphicon-arrow-down']);
7284 var tbody = this.mainBody;
7286 if(ds.getCount() > 0){
7287 ds.data.each(function(d,rowIndex){
7288 var row = this.renderRow(cm, ds, rowIndex);
7290 tbody.createChild(row);
7294 if(row.cellObjects.length){
7295 Roo.each(row.cellObjects, function(r){
7296 _this.renderCellObject(r);
7303 var tfoot = this.el.select('tfoot', true).first();
7305 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7307 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7309 var total = this.ds.getTotalCount();
7311 if(this.footer.pageSize < total){
7312 this.mainFoot.show();
7316 Roo.each(this.el.select('tbody td', true).elements, function(e){
7317 e.on('mouseover', _this.onMouseover, _this);
7320 Roo.each(this.el.select('tbody td', true).elements, function(e){
7321 e.on('mouseout', _this.onMouseout, _this);
7323 this.fireEvent('rowsrendered', this);
7329 onUpdate : function(ds,record)
7331 this.refreshRow(record);
7335 onRemove : function(ds, record, index, isUpdate){
7336 if(isUpdate !== true){
7337 this.fireEvent("beforerowremoved", this, index, record);
7339 var bt = this.mainBody.dom;
7341 var rows = this.el.select('tbody > tr', true).elements;
7343 if(typeof(rows[index]) != 'undefined'){
7344 bt.removeChild(rows[index].dom);
7347 // if(bt.rows[index]){
7348 // bt.removeChild(bt.rows[index]);
7351 if(isUpdate !== true){
7352 //this.stripeRows(index);
7353 //this.syncRowHeights(index, index);
7355 this.fireEvent("rowremoved", this, index, record);
7359 onAdd : function(ds, records, rowIndex)
7361 //Roo.log('on Add called');
7362 // - note this does not handle multiple adding very well..
7363 var bt = this.mainBody.dom;
7364 for (var i =0 ; i < records.length;i++) {
7365 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7366 //Roo.log(records[i]);
7367 //Roo.log(this.store.getAt(rowIndex+i));
7368 this.insertRow(this.store, rowIndex + i, false);
7375 refreshRow : function(record){
7376 var ds = this.store, index;
7377 if(typeof record == 'number'){
7379 record = ds.getAt(index);
7381 index = ds.indexOf(record);
7383 this.insertRow(ds, index, true);
7385 this.onRemove(ds, record, index+1, true);
7387 //this.syncRowHeights(index, index);
7389 this.fireEvent("rowupdated", this, index, record);
7392 insertRow : function(dm, rowIndex, isUpdate){
7395 this.fireEvent("beforerowsinserted", this, rowIndex);
7397 //var s = this.getScrollState();
7398 var row = this.renderRow(this.cm, this.store, rowIndex);
7399 // insert before rowIndex..
7400 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7404 if(row.cellObjects.length){
7405 Roo.each(row.cellObjects, function(r){
7406 _this.renderCellObject(r);
7411 this.fireEvent("rowsinserted", this, rowIndex);
7412 //this.syncRowHeights(firstRow, lastRow);
7413 //this.stripeRows(firstRow);
7420 getRowDom : function(rowIndex)
7422 var rows = this.el.select('tbody > tr', true).elements;
7424 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7427 // returns the object tree for a tr..
7430 renderRow : function(cm, ds, rowIndex)
7432 var d = ds.getAt(rowIndex);
7436 cls : 'x-row-' + rowIndex,
7440 var cellObjects = [];
7442 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7443 var config = cm.config[i];
7445 var renderer = cm.getRenderer(i);
7449 if(typeof(renderer) !== 'undefined'){
7450 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7452 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7453 // and are rendered into the cells after the row is rendered - using the id for the element.
7455 if(typeof(value) === 'object'){
7465 rowIndex : rowIndex,
7470 this.fireEvent('rowclass', this, rowcfg);
7474 cls : rowcfg.rowClass + ' x-col-' + i,
7476 html: (typeof(value) === 'object') ? '' : value
7483 if(typeof(config.colspan) != 'undefined'){
7484 td.colspan = config.colspan;
7487 if(typeof(config.hidden) != 'undefined' && config.hidden){
7488 td.style += ' display:none;';
7491 if(typeof(config.align) != 'undefined' && config.align.length){
7492 td.style += ' text-align:' + config.align + ';';
7494 if(typeof(config.valign) != 'undefined' && config.valign.length){
7495 td.style += ' vertical-align:' + config.valign + ';';
7498 if(typeof(config.width) != 'undefined'){
7499 td.style += ' width:' + config.width + 'px;';
7502 if(typeof(config.cursor) != 'undefined'){
7503 td.style += ' cursor:' + config.cursor + ';';
7506 if(typeof(config.cls) != 'undefined'){
7507 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7510 ['xs','sm','md','lg'].map(function(size){
7512 if(typeof(config[size]) == 'undefined'){
7518 if (!config[size]) { // 0 = hidden
7519 // BS 4 '0' is treated as hide that column and below.
7520 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7524 td.cls += ' col-' + size + '-' + config[size] + (
7525 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7535 row.cellObjects = cellObjects;
7543 onBeforeLoad : function()
7552 this.el.select('tbody', true).first().dom.innerHTML = '';
7555 * Show or hide a row.
7556 * @param {Number} rowIndex to show or hide
7557 * @param {Boolean} state hide
7559 setRowVisibility : function(rowIndex, state)
7561 var bt = this.mainBody.dom;
7563 var rows = this.el.select('tbody > tr', true).elements;
7565 if(typeof(rows[rowIndex]) == 'undefined'){
7568 rows[rowIndex].dom.style.display = state ? '' : 'none';
7572 getSelectionModel : function(){
7574 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7576 return this.selModel;
7579 * Render the Roo.bootstrap object from renderder
7581 renderCellObject : function(r)
7585 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7587 var t = r.cfg.render(r.container);
7590 Roo.each(r.cfg.cn, function(c){
7592 container: t.getChildContainer(),
7595 _this.renderCellObject(child);
7600 getRowIndex : function(row)
7604 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7615 * Returns the grid's underlying element = used by panel.Grid
7616 * @return {Element} The element
7618 getGridEl : function(){
7622 * Forces a resize - used by panel.Grid
7623 * @return {Element} The element
7625 autoSize : function()
7627 //var ctr = Roo.get(this.container.dom.parentElement);
7628 var ctr = Roo.get(this.el.dom);
7630 var thd = this.getGridEl().select('thead',true).first();
7631 var tbd = this.getGridEl().select('tbody', true).first();
7632 var tfd = this.getGridEl().select('tfoot', true).first();
7634 var cw = ctr.getWidth();
7638 tbd.setWidth(ctr.getWidth());
7639 // if the body has a max height - and then scrolls - we should perhaps set up the height here
7640 // this needs fixing for various usage - currently only hydra job advers I think..
7642 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7644 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7647 cw = Math.max(cw, this.totalWidth);
7648 this.getGridEl().select('tr',true).setWidth(cw);
7649 // resize 'expandable coloumn?
7651 return; // we doe not have a view in this design..
7654 onBodyScroll: function()
7656 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7658 this.mainHead.setStyle({
7659 'position' : 'relative',
7660 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7666 var scrollHeight = this.mainBody.dom.scrollHeight;
7668 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7670 var height = this.mainBody.getHeight();
7672 if(scrollHeight - height == scrollTop) {
7674 var total = this.ds.getTotalCount();
7676 if(this.footer.cursor + this.footer.pageSize < total){
7678 this.footer.ds.load({
7680 start : this.footer.cursor + this.footer.pageSize,
7681 limit : this.footer.pageSize
7691 onHeaderChange : function()
7693 var header = this.renderHeader();
7694 var table = this.el.select('table', true).first();
7696 this.mainHead.remove();
7697 this.mainHead = table.createChild(header, this.mainBody, false);
7700 onHiddenChange : function(colModel, colIndex, hidden)
7702 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7703 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7705 this.CSS.updateRule(thSelector, "display", "");
7706 this.CSS.updateRule(tdSelector, "display", "");
7709 this.CSS.updateRule(thSelector, "display", "none");
7710 this.CSS.updateRule(tdSelector, "display", "none");
7713 this.onHeaderChange();
7717 setColumnWidth: function(col_index, width)
7719 // width = "md-2 xs-2..."
7720 if(!this.colModel.config[col_index]) {
7724 var w = width.split(" ");
7726 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7728 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7731 for(var j = 0; j < w.length; j++) {
7737 var size_cls = w[j].split("-");
7739 if(!Number.isInteger(size_cls[1] * 1)) {
7743 if(!this.colModel.config[col_index][size_cls[0]]) {
7747 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7751 h_row[0].classList.replace(
7752 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7753 "col-"+size_cls[0]+"-"+size_cls[1]
7756 for(var i = 0; i < rows.length; i++) {
7758 var size_cls = w[j].split("-");
7760 if(!Number.isInteger(size_cls[1] * 1)) {
7764 if(!this.colModel.config[col_index][size_cls[0]]) {
7768 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7772 rows[i].classList.replace(
7773 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7774 "col-"+size_cls[0]+"-"+size_cls[1]
7778 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7793 * @class Roo.bootstrap.TableCell
7794 * @extends Roo.bootstrap.Component
7795 * Bootstrap TableCell class
7796 * @cfg {String} html cell contain text
7797 * @cfg {String} cls cell class
7798 * @cfg {String} tag cell tag (td|th) default td
7799 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7800 * @cfg {String} align Aligns the content in a cell
7801 * @cfg {String} axis Categorizes cells
7802 * @cfg {String} bgcolor Specifies the background color of a cell
7803 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7804 * @cfg {Number} colspan Specifies the number of columns a cell should span
7805 * @cfg {String} headers Specifies one or more header cells a cell is related to
7806 * @cfg {Number} height Sets the height of a cell
7807 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7808 * @cfg {Number} rowspan Sets the number of rows a cell should span
7809 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7810 * @cfg {String} valign Vertical aligns the content in a cell
7811 * @cfg {Number} width Specifies the width of a cell
7814 * Create a new TableCell
7815 * @param {Object} config The config object
7818 Roo.bootstrap.TableCell = function(config){
7819 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7822 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7842 getAutoCreate : function(){
7843 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7863 cfg.align=this.align
7869 cfg.bgcolor=this.bgcolor
7872 cfg.charoff=this.charoff
7875 cfg.colspan=this.colspan
7878 cfg.headers=this.headers
7881 cfg.height=this.height
7884 cfg.nowrap=this.nowrap
7887 cfg.rowspan=this.rowspan
7890 cfg.scope=this.scope
7893 cfg.valign=this.valign
7896 cfg.width=this.width
7915 * @class Roo.bootstrap.TableRow
7916 * @extends Roo.bootstrap.Component
7917 * Bootstrap TableRow class
7918 * @cfg {String} cls row class
7919 * @cfg {String} align Aligns the content in a table row
7920 * @cfg {String} bgcolor Specifies a background color for a table row
7921 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7922 * @cfg {String} valign Vertical aligns the content in a table row
7925 * Create a new TableRow
7926 * @param {Object} config The config object
7929 Roo.bootstrap.TableRow = function(config){
7930 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7933 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7941 getAutoCreate : function(){
7942 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7952 cfg.align = this.align;
7955 cfg.bgcolor = this.bgcolor;
7958 cfg.charoff = this.charoff;
7961 cfg.valign = this.valign;
7979 * @class Roo.bootstrap.TableBody
7980 * @extends Roo.bootstrap.Component
7981 * Bootstrap TableBody class
7982 * @cfg {String} cls element class
7983 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7984 * @cfg {String} align Aligns the content inside the element
7985 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7986 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7989 * Create a new TableBody
7990 * @param {Object} config The config object
7993 Roo.bootstrap.TableBody = function(config){
7994 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7997 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
8005 getAutoCreate : function(){
8006 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8020 cfg.align = this.align;
8023 cfg.charoff = this.charoff;
8026 cfg.valign = this.valign;
8033 // initEvents : function()
8040 // this.store = Roo.factory(this.store, Roo.data);
8041 // this.store.on('load', this.onLoad, this);
8043 // this.store.load();
8047 // onLoad: function ()
8049 // this.fireEvent('load', this);
8059 * Ext JS Library 1.1.1
8060 * Copyright(c) 2006-2007, Ext JS, LLC.
8062 * Originally Released Under LGPL - original licence link has changed is not relivant.
8065 * <script type="text/javascript">
8068 // as we use this in bootstrap.
8069 Roo.namespace('Roo.form');
8071 * @class Roo.form.Action
8072 * Internal Class used to handle form actions
8074 * @param {Roo.form.BasicForm} el The form element or its id
8075 * @param {Object} config Configuration options
8080 // define the action interface
8081 Roo.form.Action = function(form, options){
8083 this.options = options || {};
8086 * Client Validation Failed
8089 Roo.form.Action.CLIENT_INVALID = 'client';
8091 * Server Validation Failed
8094 Roo.form.Action.SERVER_INVALID = 'server';
8096 * Connect to Server Failed
8099 Roo.form.Action.CONNECT_FAILURE = 'connect';
8101 * Reading Data from Server Failed
8104 Roo.form.Action.LOAD_FAILURE = 'load';
8106 Roo.form.Action.prototype = {
8108 failureType : undefined,
8109 response : undefined,
8113 run : function(options){
8118 success : function(response){
8123 handleResponse : function(response){
8127 // default connection failure
8128 failure : function(response){
8130 this.response = response;
8131 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8132 this.form.afterAction(this, false);
8135 processResponse : function(response){
8136 this.response = response;
8137 if(!response.responseText){
8140 this.result = this.handleResponse(response);
8144 // utility functions used internally
8145 getUrl : function(appendParams){
8146 var url = this.options.url || this.form.url || this.form.el.dom.action;
8148 var p = this.getParams();
8150 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
8156 getMethod : function(){
8157 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
8160 getParams : function(){
8161 var bp = this.form.baseParams;
8162 var p = this.options.params;
8164 if(typeof p == "object"){
8165 p = Roo.urlEncode(Roo.applyIf(p, bp));
8166 }else if(typeof p == 'string' && bp){
8167 p += '&' + Roo.urlEncode(bp);
8170 p = Roo.urlEncode(bp);
8175 createCallback : function(){
8177 success: this.success,
8178 failure: this.failure,
8180 timeout: (this.form.timeout*1000),
8181 upload: this.form.fileUpload ? this.success : undefined
8186 Roo.form.Action.Submit = function(form, options){
8187 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
8190 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
8193 haveProgress : false,
8194 uploadComplete : false,
8196 // uploadProgress indicator.
8197 uploadProgress : function()
8199 if (!this.form.progressUrl) {
8203 if (!this.haveProgress) {
8204 Roo.MessageBox.progress("Uploading", "Uploading");
8206 if (this.uploadComplete) {
8207 Roo.MessageBox.hide();
8211 this.haveProgress = true;
8213 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
8215 var c = new Roo.data.Connection();
8217 url : this.form.progressUrl,
8222 success : function(req){
8223 //console.log(data);
8227 rdata = Roo.decode(req.responseText)
8229 Roo.log("Invalid data from server..");
8233 if (!rdata || !rdata.success) {
8235 Roo.MessageBox.alert(Roo.encode(rdata));
8238 var data = rdata.data;
8240 if (this.uploadComplete) {
8241 Roo.MessageBox.hide();
8246 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
8247 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
8250 this.uploadProgress.defer(2000,this);
8253 failure: function(data) {
8254 Roo.log('progress url failed ');
8265 // run get Values on the form, so it syncs any secondary forms.
8266 this.form.getValues();
8268 var o = this.options;
8269 var method = this.getMethod();
8270 var isPost = method == 'POST';
8271 if(o.clientValidation === false || this.form.isValid()){
8273 if (this.form.progressUrl) {
8274 this.form.findField('UPLOAD_IDENTIFIER').setValue(
8275 (new Date() * 1) + '' + Math.random());
8280 Roo.Ajax.request(Roo.apply(this.createCallback(), {
8281 form:this.form.el.dom,
8282 url:this.getUrl(!isPost),
8284 params:isPost ? this.getParams() : null,
8285 isUpload: this.form.fileUpload,
8286 formData : this.form.formData
8289 this.uploadProgress();
8291 }else if (o.clientValidation !== false){ // client validation failed
8292 this.failureType = Roo.form.Action.CLIENT_INVALID;
8293 this.form.afterAction(this, false);
8297 success : function(response)
8299 this.uploadComplete= true;
8300 if (this.haveProgress) {
8301 Roo.MessageBox.hide();
8305 var result = this.processResponse(response);
8306 if(result === true || result.success){
8307 this.form.afterAction(this, true);
8311 this.form.markInvalid(result.errors);
8312 this.failureType = Roo.form.Action.SERVER_INVALID;
8314 this.form.afterAction(this, false);
8316 failure : function(response)
8318 this.uploadComplete= true;
8319 if (this.haveProgress) {
8320 Roo.MessageBox.hide();
8323 this.response = response;
8324 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8325 this.form.afterAction(this, false);
8328 handleResponse : function(response){
8329 if(this.form.errorReader){
8330 var rs = this.form.errorReader.read(response);
8333 for(var i = 0, len = rs.records.length; i < len; i++) {
8334 var r = rs.records[i];
8338 if(errors.length < 1){
8342 success : rs.success,
8348 ret = Roo.decode(response.responseText);
8352 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8362 Roo.form.Action.Load = function(form, options){
8363 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8364 this.reader = this.form.reader;
8367 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8372 Roo.Ajax.request(Roo.apply(
8373 this.createCallback(), {
8374 method:this.getMethod(),
8375 url:this.getUrl(false),
8376 params:this.getParams()
8380 success : function(response){
8382 var result = this.processResponse(response);
8383 if(result === true || !result.success || !result.data){
8384 this.failureType = Roo.form.Action.LOAD_FAILURE;
8385 this.form.afterAction(this, false);
8388 this.form.clearInvalid();
8389 this.form.setValues(result.data);
8390 this.form.afterAction(this, true);
8393 handleResponse : function(response){
8394 if(this.form.reader){
8395 var rs = this.form.reader.read(response);
8396 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8398 success : rs.success,
8402 return Roo.decode(response.responseText);
8406 Roo.form.Action.ACTION_TYPES = {
8407 'load' : Roo.form.Action.Load,
8408 'submit' : Roo.form.Action.Submit
8417 * @class Roo.bootstrap.Form
8418 * @extends Roo.bootstrap.Component
8419 * Bootstrap Form class
8420 * @cfg {String} method GET | POST (default POST)
8421 * @cfg {String} labelAlign top | left (default top)
8422 * @cfg {String} align left | right - for navbars
8423 * @cfg {Boolean} loadMask load mask when submit (default true)
8428 * @param {Object} config The config object
8432 Roo.bootstrap.Form = function(config){
8434 Roo.bootstrap.Form.superclass.constructor.call(this, config);
8436 Roo.bootstrap.Form.popover.apply();
8440 * @event clientvalidation
8441 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8442 * @param {Form} this
8443 * @param {Boolean} valid true if the form has passed client-side validation
8445 clientvalidation: true,
8447 * @event beforeaction
8448 * Fires before any action is performed. Return false to cancel the action.
8449 * @param {Form} this
8450 * @param {Action} action The action to be performed
8454 * @event actionfailed
8455 * Fires when an action fails.
8456 * @param {Form} this
8457 * @param {Action} action The action that failed
8459 actionfailed : true,
8461 * @event actioncomplete
8462 * Fires when an action is completed.
8463 * @param {Form} this
8464 * @param {Action} action The action that completed
8466 actioncomplete : true
8470 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
8473 * @cfg {String} method
8474 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8479 * The URL to use for form actions if one isn't supplied in the action options.
8482 * @cfg {Boolean} fileUpload
8483 * Set to true if this form is a file upload.
8487 * @cfg {Object} baseParams
8488 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8492 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8496 * @cfg {Sting} align (left|right) for navbar forms
8501 activeAction : null,
8504 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8505 * element by passing it or its id or mask the form itself by passing in true.
8508 waitMsgTarget : false,
8513 * @cfg {Boolean} errorMask (true|false) default false
8518 * @cfg {Number} maskOffset Default 100
8523 * @cfg {Boolean} maskBody
8527 getAutoCreate : function(){
8531 method : this.method || 'POST',
8532 id : this.id || Roo.id(),
8535 if (this.parent().xtype.match(/^Nav/)) {
8536 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8540 if (this.labelAlign == 'left' ) {
8541 cfg.cls += ' form-horizontal';
8547 initEvents : function()
8549 this.el.on('submit', this.onSubmit, this);
8550 // this was added as random key presses on the form where triggering form submit.
8551 this.el.on('keypress', function(e) {
8552 if (e.getCharCode() != 13) {
8555 // we might need to allow it for textareas.. and some other items.
8556 // check e.getTarget().
8558 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8562 Roo.log("keypress blocked");
8570 onSubmit : function(e){
8575 * Returns true if client-side validation on the form is successful.
8578 isValid : function(){
8579 var items = this.getItems();
8583 items.each(function(f){
8589 Roo.log('invalid field: ' + f.name);
8593 if(!target && f.el.isVisible(true)){
8599 if(this.errorMask && !valid){
8600 Roo.bootstrap.Form.popover.mask(this, target);
8607 * Returns true if any fields in this form have changed since their original load.
8610 isDirty : function(){
8612 var items = this.getItems();
8613 items.each(function(f){
8623 * Performs a predefined action (submit or load) or custom actions you define on this form.
8624 * @param {String} actionName The name of the action type
8625 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8626 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8627 * accept other config options):
8629 Property Type Description
8630 ---------------- --------------- ----------------------------------------------------------------------------------
8631 url String The url for the action (defaults to the form's url)
8632 method String The form method to use (defaults to the form's method, or POST if not defined)
8633 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8634 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8635 validate the form on the client (defaults to false)
8637 * @return {BasicForm} this
8639 doAction : function(action, options){
8640 if(typeof action == 'string'){
8641 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8643 if(this.fireEvent('beforeaction', this, action) !== false){
8644 this.beforeAction(action);
8645 action.run.defer(100, action);
8651 beforeAction : function(action){
8652 var o = action.options;
8657 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8659 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8662 // not really supported yet.. ??
8664 //if(this.waitMsgTarget === true){
8665 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8666 //}else if(this.waitMsgTarget){
8667 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8668 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8670 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8676 afterAction : function(action, success){
8677 this.activeAction = null;
8678 var o = action.options;
8683 Roo.get(document.body).unmask();
8689 //if(this.waitMsgTarget === true){
8690 // this.el.unmask();
8691 //}else if(this.waitMsgTarget){
8692 // this.waitMsgTarget.unmask();
8694 // Roo.MessageBox.updateProgress(1);
8695 // Roo.MessageBox.hide();
8702 Roo.callback(o.success, o.scope, [this, action]);
8703 this.fireEvent('actioncomplete', this, action);
8707 // failure condition..
8708 // we have a scenario where updates need confirming.
8709 // eg. if a locking scenario exists..
8710 // we look for { errors : { needs_confirm : true }} in the response.
8712 (typeof(action.result) != 'undefined') &&
8713 (typeof(action.result.errors) != 'undefined') &&
8714 (typeof(action.result.errors.needs_confirm) != 'undefined')
8717 Roo.log("not supported yet");
8720 Roo.MessageBox.confirm(
8721 "Change requires confirmation",
8722 action.result.errorMsg,
8727 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8737 Roo.callback(o.failure, o.scope, [this, action]);
8738 // show an error message if no failed handler is set..
8739 if (!this.hasListener('actionfailed')) {
8740 Roo.log("need to add dialog support");
8742 Roo.MessageBox.alert("Error",
8743 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8744 action.result.errorMsg :
8745 "Saving Failed, please check your entries or try again"
8750 this.fireEvent('actionfailed', this, action);
8755 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8756 * @param {String} id The value to search for
8759 findField : function(id){
8760 var items = this.getItems();
8761 var field = items.get(id);
8763 items.each(function(f){
8764 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8771 return field || null;
8774 * Mark fields in this form invalid in bulk.
8775 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8776 * @return {BasicForm} this
8778 markInvalid : function(errors){
8779 if(errors instanceof Array){
8780 for(var i = 0, len = errors.length; i < len; i++){
8781 var fieldError = errors[i];
8782 var f = this.findField(fieldError.id);
8784 f.markInvalid(fieldError.msg);
8790 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8791 field.markInvalid(errors[id]);
8795 //Roo.each(this.childForms || [], function (f) {
8796 // f.markInvalid(errors);
8803 * Set values for fields in this form in bulk.
8804 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8805 * @return {BasicForm} this
8807 setValues : function(values){
8808 if(values instanceof Array){ // array of objects
8809 for(var i = 0, len = values.length; i < len; i++){
8811 var f = this.findField(v.id);
8813 f.setValue(v.value);
8814 if(this.trackResetOnLoad){
8815 f.originalValue = f.getValue();
8819 }else{ // object hash
8822 if(typeof values[id] != 'function' && (field = this.findField(id))){
8824 if (field.setFromData &&
8826 field.displayField &&
8827 // combos' with local stores can
8828 // be queried via setValue()
8829 // to set their value..
8830 (field.store && !field.store.isLocal)
8834 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8835 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8836 field.setFromData(sd);
8838 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8840 field.setFromData(values);
8843 field.setValue(values[id]);
8847 if(this.trackResetOnLoad){
8848 field.originalValue = field.getValue();
8854 //Roo.each(this.childForms || [], function (f) {
8855 // f.setValues(values);
8862 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8863 * they are returned as an array.
8864 * @param {Boolean} asString
8867 getValues : function(asString){
8868 //if (this.childForms) {
8869 // copy values from the child forms
8870 // Roo.each(this.childForms, function (f) {
8871 // this.setValues(f.getValues());
8877 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8878 if(asString === true){
8881 return Roo.urlDecode(fs);
8885 * Returns the fields in this form as an object with key/value pairs.
8886 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8889 getFieldValues : function(with_hidden)
8891 var items = this.getItems();
8893 items.each(function(f){
8899 var v = f.getValue();
8901 if (f.inputType =='radio') {
8902 if (typeof(ret[f.getName()]) == 'undefined') {
8903 ret[f.getName()] = ''; // empty..
8906 if (!f.el.dom.checked) {
8914 if(f.xtype == 'MoneyField'){
8915 ret[f.currencyName] = f.getCurrency();
8918 // not sure if this supported any more..
8919 if ((typeof(v) == 'object') && f.getRawValue) {
8920 v = f.getRawValue() ; // dates..
8922 // combo boxes where name != hiddenName...
8923 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8924 ret[f.name] = f.getRawValue();
8926 ret[f.getName()] = v;
8933 * Clears all invalid messages in this form.
8934 * @return {BasicForm} this
8936 clearInvalid : function(){
8937 var items = this.getItems();
8939 items.each(function(f){
8948 * @return {BasicForm} this
8951 var items = this.getItems();
8952 items.each(function(f){
8956 Roo.each(this.childForms || [], function (f) {
8964 getItems : function()
8966 var r=new Roo.util.MixedCollection(false, function(o){
8967 return o.id || (o.id = Roo.id());
8969 var iter = function(el) {
8976 Roo.each(el.items,function(e) {
8985 hideFields : function(items)
8987 Roo.each(items, function(i){
8989 var f = this.findField(i);
9000 showFields : function(items)
9002 Roo.each(items, function(i){
9004 var f = this.findField(i);
9017 Roo.apply(Roo.bootstrap.Form, {
9044 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9045 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9046 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9047 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9050 this.maskEl.top.enableDisplayMode("block");
9051 this.maskEl.left.enableDisplayMode("block");
9052 this.maskEl.bottom.enableDisplayMode("block");
9053 this.maskEl.right.enableDisplayMode("block");
9055 this.toolTip = new Roo.bootstrap.Tooltip({
9056 cls : 'roo-form-error-popover',
9058 'left' : ['r-l', [-2,0], 'right'],
9059 'right' : ['l-r', [2,0], 'left'],
9060 'bottom' : ['tl-bl', [0,2], 'top'],
9061 'top' : [ 'bl-tl', [0,-2], 'bottom']
9065 this.toolTip.render(Roo.get(document.body));
9067 this.toolTip.el.enableDisplayMode("block");
9069 Roo.get(document.body).on('click', function(){
9073 Roo.get(document.body).on('touchstart', function(){
9077 this.isApplied = true
9080 mask : function(form, target)
9084 this.target = target;
9086 if(!this.form.errorMask || !target.el){
9090 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9092 Roo.log(scrollable);
9094 var ot = this.target.el.calcOffsetsTo(scrollable);
9096 var scrollTo = ot[1] - this.form.maskOffset;
9098 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9100 scrollable.scrollTo('top', scrollTo);
9102 var box = this.target.el.getBox();
9104 var zIndex = Roo.bootstrap.Modal.zIndex++;
9107 this.maskEl.top.setStyle('position', 'absolute');
9108 this.maskEl.top.setStyle('z-index', zIndex);
9109 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9110 this.maskEl.top.setLeft(0);
9111 this.maskEl.top.setTop(0);
9112 this.maskEl.top.show();
9114 this.maskEl.left.setStyle('position', 'absolute');
9115 this.maskEl.left.setStyle('z-index', zIndex);
9116 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9117 this.maskEl.left.setLeft(0);
9118 this.maskEl.left.setTop(box.y - this.padding);
9119 this.maskEl.left.show();
9121 this.maskEl.bottom.setStyle('position', 'absolute');
9122 this.maskEl.bottom.setStyle('z-index', zIndex);
9123 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9124 this.maskEl.bottom.setLeft(0);
9125 this.maskEl.bottom.setTop(box.bottom + this.padding);
9126 this.maskEl.bottom.show();
9128 this.maskEl.right.setStyle('position', 'absolute');
9129 this.maskEl.right.setStyle('z-index', zIndex);
9130 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9131 this.maskEl.right.setLeft(box.right + this.padding);
9132 this.maskEl.right.setTop(box.y - this.padding);
9133 this.maskEl.right.show();
9135 this.toolTip.bindEl = this.target.el;
9137 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9139 var tip = this.target.blankText;
9141 if(this.target.getValue() !== '' ) {
9143 if (this.target.invalidText.length) {
9144 tip = this.target.invalidText;
9145 } else if (this.target.regexText.length){
9146 tip = this.target.regexText;
9150 this.toolTip.show(tip);
9152 this.intervalID = window.setInterval(function() {
9153 Roo.bootstrap.Form.popover.unmask();
9156 window.onwheel = function(){ return false;};
9158 (function(){ this.isMasked = true; }).defer(500, this);
9164 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
9168 this.maskEl.top.setStyle('position', 'absolute');
9169 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
9170 this.maskEl.top.hide();
9172 this.maskEl.left.setStyle('position', 'absolute');
9173 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
9174 this.maskEl.left.hide();
9176 this.maskEl.bottom.setStyle('position', 'absolute');
9177 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
9178 this.maskEl.bottom.hide();
9180 this.maskEl.right.setStyle('position', 'absolute');
9181 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
9182 this.maskEl.right.hide();
9184 this.toolTip.hide();
9186 this.toolTip.el.hide();
9188 window.onwheel = function(){ return true;};
9190 if(this.intervalID){
9191 window.clearInterval(this.intervalID);
9192 this.intervalID = false;
9195 this.isMasked = false;
9205 * Ext JS Library 1.1.1
9206 * Copyright(c) 2006-2007, Ext JS, LLC.
9208 * Originally Released Under LGPL - original licence link has changed is not relivant.
9211 * <script type="text/javascript">
9214 * @class Roo.form.VTypes
9215 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
9218 Roo.form.VTypes = function(){
9219 // closure these in so they are only created once.
9220 var alpha = /^[a-zA-Z_]+$/;
9221 var alphanum = /^[a-zA-Z0-9_]+$/;
9222 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
9223 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
9225 // All these messages and functions are configurable
9228 * The function used to validate email addresses
9229 * @param {String} value The email address
9231 'email' : function(v){
9232 return email.test(v);
9235 * The error text to display when the email validation function returns false
9238 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
9240 * The keystroke filter mask to be applied on email input
9243 'emailMask' : /[a-z0-9_\.\-@]/i,
9246 * The function used to validate URLs
9247 * @param {String} value The URL
9249 'url' : function(v){
9253 * The error text to display when the url validation function returns false
9256 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
9259 * The function used to validate alpha values
9260 * @param {String} value The value
9262 'alpha' : function(v){
9263 return alpha.test(v);
9266 * The error text to display when the alpha validation function returns false
9269 'alphaText' : 'This field should only contain letters and _',
9271 * The keystroke filter mask to be applied on alpha input
9274 'alphaMask' : /[a-z_]/i,
9277 * The function used to validate alphanumeric values
9278 * @param {String} value The value
9280 'alphanum' : function(v){
9281 return alphanum.test(v);
9284 * The error text to display when the alphanumeric validation function returns false
9287 'alphanumText' : 'This field should only contain letters, numbers and _',
9289 * The keystroke filter mask to be applied on alphanumeric input
9292 'alphanumMask' : /[a-z0-9_]/i
9302 * @class Roo.bootstrap.Input
9303 * @extends Roo.bootstrap.Component
9304 * Bootstrap Input class
9305 * @cfg {Boolean} disabled is it disabled
9306 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9307 * @cfg {String} name name of the input
9308 * @cfg {string} fieldLabel - the label associated
9309 * @cfg {string} placeholder - placeholder to put in text.
9310 * @cfg {string} before - input group add on before
9311 * @cfg {string} after - input group add on after
9312 * @cfg {string} size - (lg|sm) or leave empty..
9313 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9314 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9315 * @cfg {Number} md colspan out of 12 for computer-sized screens
9316 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9317 * @cfg {string} value default value of the input
9318 * @cfg {Number} labelWidth set the width of label
9319 * @cfg {Number} labellg set the width of label (1-12)
9320 * @cfg {Number} labelmd set the width of label (1-12)
9321 * @cfg {Number} labelsm set the width of label (1-12)
9322 * @cfg {Number} labelxs set the width of label (1-12)
9323 * @cfg {String} labelAlign (top|left)
9324 * @cfg {Boolean} readOnly Specifies that the field should be read-only
9325 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9326 * @cfg {String} indicatorpos (left|right) default left
9327 * @cfg {String} capture (user|camera) use for file input only. (default empty)
9328 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9330 * @cfg {String} align (left|center|right) Default left
9331 * @cfg {Boolean} forceFeedback (true|false) Default false
9334 * Create a new Input
9335 * @param {Object} config The config object
9338 Roo.bootstrap.Input = function(config){
9340 Roo.bootstrap.Input.superclass.constructor.call(this, config);
9345 * Fires when this field receives input focus.
9346 * @param {Roo.form.Field} this
9351 * Fires when this field loses input focus.
9352 * @param {Roo.form.Field} this
9357 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
9358 * {@link Roo.EventObject#getKey} to determine which key was pressed.
9359 * @param {Roo.form.Field} this
9360 * @param {Roo.EventObject} e The event object
9365 * Fires just before the field blurs if the field value has changed.
9366 * @param {Roo.form.Field} this
9367 * @param {Mixed} newValue The new value
9368 * @param {Mixed} oldValue The original value
9373 * Fires after the field has been marked as invalid.
9374 * @param {Roo.form.Field} this
9375 * @param {String} msg The validation message
9380 * Fires after the field has been validated with no errors.
9381 * @param {Roo.form.Field} this
9386 * Fires after the key up
9387 * @param {Roo.form.Field} this
9388 * @param {Roo.EventObject} e The event Object
9394 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
9396 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9397 automatic validation (defaults to "keyup").
9399 validationEvent : "keyup",
9401 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9403 validateOnBlur : true,
9405 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9407 validationDelay : 250,
9409 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9411 focusClass : "x-form-focus", // not needed???
9415 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9417 invalidClass : "has-warning",
9420 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9422 validClass : "has-success",
9425 * @cfg {Boolean} hasFeedback (true|false) default true
9430 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9432 invalidFeedbackClass : "glyphicon-warning-sign",
9435 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9437 validFeedbackClass : "glyphicon-ok",
9440 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9442 selectOnFocus : false,
9445 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9449 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9454 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9456 disableKeyFilter : false,
9459 * @cfg {Boolean} disabled True to disable the field (defaults to false).
9463 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9467 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9469 blankText : "Please complete this mandatory field",
9472 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9476 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9478 maxLength : Number.MAX_VALUE,
9480 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9482 minLengthText : "The minimum length for this field is {0}",
9484 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9486 maxLengthText : "The maximum length for this field is {0}",
9490 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9491 * If available, this function will be called only after the basic validators all return true, and will be passed the
9492 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9496 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9497 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9498 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9502 * @cfg {String} regexText -- Depricated - use Invalid Text
9507 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9513 autocomplete: false,
9532 formatedValue : false,
9533 forceFeedback : false,
9535 indicatorpos : 'left',
9545 parentLabelAlign : function()
9548 while (parent.parent()) {
9549 parent = parent.parent();
9550 if (typeof(parent.labelAlign) !='undefined') {
9551 return parent.labelAlign;
9558 getAutoCreate : function()
9560 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9566 if(this.inputType != 'hidden'){
9567 cfg.cls = 'form-group' //input-group
9573 type : this.inputType,
9575 cls : 'form-control',
9576 placeholder : this.placeholder || '',
9577 autocomplete : this.autocomplete || 'new-password'
9580 if(this.capture.length){
9581 input.capture = this.capture;
9584 if(this.accept.length){
9585 input.accept = this.accept + "/*";
9589 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9592 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9593 input.maxLength = this.maxLength;
9596 if (this.disabled) {
9597 input.disabled=true;
9600 if (this.readOnly) {
9601 input.readonly=true;
9605 input.name = this.name;
9609 input.cls += ' input-' + this.size;
9613 ['xs','sm','md','lg'].map(function(size){
9614 if (settings[size]) {
9615 cfg.cls += ' col-' + size + '-' + settings[size];
9619 var inputblock = input;
9623 cls: 'glyphicon form-control-feedback'
9626 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9629 cls : 'has-feedback',
9637 if (this.before || this.after) {
9640 cls : 'input-group',
9644 if (this.before && typeof(this.before) == 'string') {
9646 inputblock.cn.push({
9648 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9652 if (this.before && typeof(this.before) == 'object') {
9653 this.before = Roo.factory(this.before);
9655 inputblock.cn.push({
9657 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9658 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9662 inputblock.cn.push(input);
9664 if (this.after && typeof(this.after) == 'string') {
9665 inputblock.cn.push({
9667 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9671 if (this.after && typeof(this.after) == 'object') {
9672 this.after = Roo.factory(this.after);
9674 inputblock.cn.push({
9676 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9677 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9681 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9682 inputblock.cls += ' has-feedback';
9683 inputblock.cn.push(feedback);
9688 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9689 tooltip : 'This field is required'
9691 if (Roo.bootstrap.version == 4) {
9694 style : 'display-none'
9697 if (align ==='left' && this.fieldLabel.length) {
9699 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
9706 cls : 'control-label col-form-label',
9707 html : this.fieldLabel
9718 var labelCfg = cfg.cn[1];
9719 var contentCfg = cfg.cn[2];
9721 if(this.indicatorpos == 'right'){
9726 cls : 'control-label col-form-label',
9730 html : this.fieldLabel
9744 labelCfg = cfg.cn[0];
9745 contentCfg = cfg.cn[1];
9749 if(this.labelWidth > 12){
9750 labelCfg.style = "width: " + this.labelWidth + 'px';
9753 if(this.labelWidth < 13 && this.labelmd == 0){
9754 this.labelmd = this.labelWidth;
9757 if(this.labellg > 0){
9758 labelCfg.cls += ' col-lg-' + this.labellg;
9759 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9762 if(this.labelmd > 0){
9763 labelCfg.cls += ' col-md-' + this.labelmd;
9764 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9767 if(this.labelsm > 0){
9768 labelCfg.cls += ' col-sm-' + this.labelsm;
9769 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9772 if(this.labelxs > 0){
9773 labelCfg.cls += ' col-xs-' + this.labelxs;
9774 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9778 } else if ( this.fieldLabel.length) {
9783 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9784 tooltip : 'This field is required'
9788 //cls : 'input-group-addon',
9789 html : this.fieldLabel
9797 if(this.indicatorpos == 'right'){
9802 //cls : 'input-group-addon',
9803 html : this.fieldLabel
9808 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9809 tooltip : 'This field is required'
9829 if (this.parentType === 'Navbar' && this.parent().bar) {
9830 cfg.cls += ' navbar-form';
9833 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9834 // on BS4 we do this only if not form
9835 cfg.cls += ' navbar-form';
9843 * return the real input element.
9845 inputEl: function ()
9847 return this.el.select('input.form-control',true).first();
9850 tooltipEl : function()
9852 return this.inputEl();
9855 indicatorEl : function()
9857 if (Roo.bootstrap.version == 4) {
9858 return false; // not enabled in v4 yet.
9861 var indicator = this.el.select('i.roo-required-indicator',true).first();
9871 setDisabled : function(v)
9873 var i = this.inputEl().dom;
9875 i.removeAttribute('disabled');
9879 i.setAttribute('disabled','true');
9881 initEvents : function()
9884 this.inputEl().on("keydown" , this.fireKey, this);
9885 this.inputEl().on("focus", this.onFocus, this);
9886 this.inputEl().on("blur", this.onBlur, this);
9888 this.inputEl().relayEvent('keyup', this);
9890 this.indicator = this.indicatorEl();
9893 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9896 // reference to original value for reset
9897 this.originalValue = this.getValue();
9898 //Roo.form.TextField.superclass.initEvents.call(this);
9899 if(this.validationEvent == 'keyup'){
9900 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9901 this.inputEl().on('keyup', this.filterValidation, this);
9903 else if(this.validationEvent !== false){
9904 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9907 if(this.selectOnFocus){
9908 this.on("focus", this.preFocus, this);
9911 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9912 this.inputEl().on("keypress", this.filterKeys, this);
9914 this.inputEl().relayEvent('keypress', this);
9917 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9918 this.el.on("click", this.autoSize, this);
9921 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9922 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9925 if (typeof(this.before) == 'object') {
9926 this.before.render(this.el.select('.roo-input-before',true).first());
9928 if (typeof(this.after) == 'object') {
9929 this.after.render(this.el.select('.roo-input-after',true).first());
9932 this.inputEl().on('change', this.onChange, this);
9935 filterValidation : function(e){
9936 if(!e.isNavKeyPress()){
9937 this.validationTask.delay(this.validationDelay);
9941 * Validates the field value
9942 * @return {Boolean} True if the value is valid, else false
9944 validate : function(){
9945 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9946 if(this.disabled || this.validateValue(this.getRawValue())){
9957 * Validates a value according to the field's validation rules and marks the field as invalid
9958 * if the validation fails
9959 * @param {Mixed} value The value to validate
9960 * @return {Boolean} True if the value is valid, else false
9962 validateValue : function(value)
9964 if(this.getVisibilityEl().hasClass('hidden')){
9968 if(value.length < 1) { // if it's blank
9969 if(this.allowBlank){
9975 if(value.length < this.minLength){
9978 if(value.length > this.maxLength){
9982 var vt = Roo.form.VTypes;
9983 if(!vt[this.vtype](value, this)){
9987 if(typeof this.validator == "function"){
9988 var msg = this.validator(value);
9992 if (typeof(msg) == 'string') {
9993 this.invalidText = msg;
9997 if(this.regex && !this.regex.test(value)){
10005 fireKey : function(e){
10006 //Roo.log('field ' + e.getKey());
10007 if(e.isNavKeyPress()){
10008 this.fireEvent("specialkey", this, e);
10011 focus : function (selectText){
10013 this.inputEl().focus();
10014 if(selectText === true){
10015 this.inputEl().dom.select();
10021 onFocus : function(){
10022 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10023 // this.el.addClass(this.focusClass);
10025 if(!this.hasFocus){
10026 this.hasFocus = true;
10027 this.startValue = this.getValue();
10028 this.fireEvent("focus", this);
10032 beforeBlur : Roo.emptyFn,
10036 onBlur : function(){
10038 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10039 //this.el.removeClass(this.focusClass);
10041 this.hasFocus = false;
10042 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10045 var v = this.getValue();
10046 if(String(v) !== String(this.startValue)){
10047 this.fireEvent('change', this, v, this.startValue);
10049 this.fireEvent("blur", this);
10052 onChange : function(e)
10054 var v = this.getValue();
10055 if(String(v) !== String(this.startValue)){
10056 this.fireEvent('change', this, v, this.startValue);
10062 * Resets the current field value to the originally loaded value and clears any validation messages
10064 reset : function(){
10065 this.setValue(this.originalValue);
10069 * Returns the name of the field
10070 * @return {Mixed} name The name field
10072 getName: function(){
10076 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
10077 * @return {Mixed} value The field value
10079 getValue : function(){
10081 var v = this.inputEl().getValue();
10086 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
10087 * @return {Mixed} value The field value
10089 getRawValue : function(){
10090 var v = this.inputEl().getValue();
10096 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
10097 * @param {Mixed} value The value to set
10099 setRawValue : function(v){
10100 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10103 selectText : function(start, end){
10104 var v = this.getRawValue();
10106 start = start === undefined ? 0 : start;
10107 end = end === undefined ? v.length : end;
10108 var d = this.inputEl().dom;
10109 if(d.setSelectionRange){
10110 d.setSelectionRange(start, end);
10111 }else if(d.createTextRange){
10112 var range = d.createTextRange();
10113 range.moveStart("character", start);
10114 range.moveEnd("character", v.length-end);
10121 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
10122 * @param {Mixed} value The value to set
10124 setValue : function(v){
10127 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10133 processValue : function(value){
10134 if(this.stripCharsRe){
10135 var newValue = value.replace(this.stripCharsRe, '');
10136 if(newValue !== value){
10137 this.setRawValue(newValue);
10144 preFocus : function(){
10146 if(this.selectOnFocus){
10147 this.inputEl().dom.select();
10150 filterKeys : function(e){
10151 var k = e.getKey();
10152 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
10155 var c = e.getCharCode(), cc = String.fromCharCode(c);
10156 if(Roo.isIE && (e.isSpecialKey() || !cc)){
10159 if(!this.maskRe.test(cc)){
10164 * Clear any invalid styles/messages for this field
10166 clearInvalid : function(){
10168 if(!this.el || this.preventMark){ // not rendered
10173 this.el.removeClass([this.invalidClass, 'is-invalid']);
10175 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10177 var feedback = this.el.select('.form-control-feedback', true).first();
10180 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10185 if(this.indicator){
10186 this.indicator.removeClass('visible');
10187 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10190 this.fireEvent('valid', this);
10194 * Mark this field as valid
10196 markValid : function()
10198 if(!this.el || this.preventMark){ // not rendered...
10202 this.el.removeClass([this.invalidClass, this.validClass]);
10203 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10205 var feedback = this.el.select('.form-control-feedback', true).first();
10208 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10211 if(this.indicator){
10212 this.indicator.removeClass('visible');
10213 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10220 if(this.allowBlank && !this.getRawValue().length){
10223 if (Roo.bootstrap.version == 3) {
10224 this.el.addClass(this.validClass);
10226 this.inputEl().addClass('is-valid');
10229 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10231 var feedback = this.el.select('.form-control-feedback', true).first();
10234 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10235 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10240 this.fireEvent('valid', this);
10244 * Mark this field as invalid
10245 * @param {String} msg The validation message
10247 markInvalid : function(msg)
10249 if(!this.el || this.preventMark){ // not rendered
10253 this.el.removeClass([this.invalidClass, this.validClass]);
10254 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10256 var feedback = this.el.select('.form-control-feedback', true).first();
10259 this.el.select('.form-control-feedback', true).first().removeClass(
10260 [this.invalidFeedbackClass, this.validFeedbackClass]);
10267 if(this.allowBlank && !this.getRawValue().length){
10271 if(this.indicator){
10272 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10273 this.indicator.addClass('visible');
10275 if (Roo.bootstrap.version == 3) {
10276 this.el.addClass(this.invalidClass);
10278 this.inputEl().addClass('is-invalid');
10283 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10285 var feedback = this.el.select('.form-control-feedback', true).first();
10288 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10290 if(this.getValue().length || this.forceFeedback){
10291 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10298 this.fireEvent('invalid', this, msg);
10301 SafariOnKeyDown : function(event)
10303 // this is a workaround for a password hang bug on chrome/ webkit.
10304 if (this.inputEl().dom.type != 'password') {
10308 var isSelectAll = false;
10310 if(this.inputEl().dom.selectionEnd > 0){
10311 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10313 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10314 event.preventDefault();
10319 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10321 event.preventDefault();
10322 // this is very hacky as keydown always get's upper case.
10324 var cc = String.fromCharCode(event.getCharCode());
10325 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
10329 adjustWidth : function(tag, w){
10330 tag = tag.toLowerCase();
10331 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10332 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10333 if(tag == 'input'){
10336 if(tag == 'textarea'){
10339 }else if(Roo.isOpera){
10340 if(tag == 'input'){
10343 if(tag == 'textarea'){
10351 setFieldLabel : function(v)
10353 if(!this.rendered){
10357 if(this.indicatorEl()){
10358 var ar = this.el.select('label > span',true);
10360 if (ar.elements.length) {
10361 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10362 this.fieldLabel = v;
10366 var br = this.el.select('label',true);
10368 if(br.elements.length) {
10369 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10370 this.fieldLabel = v;
10374 Roo.log('Cannot Found any of label > span || label in input');
10378 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10379 this.fieldLabel = v;
10394 * @class Roo.bootstrap.TextArea
10395 * @extends Roo.bootstrap.Input
10396 * Bootstrap TextArea class
10397 * @cfg {Number} cols Specifies the visible width of a text area
10398 * @cfg {Number} rows Specifies the visible number of lines in a text area
10399 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10400 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10401 * @cfg {string} html text
10404 * Create a new TextArea
10405 * @param {Object} config The config object
10408 Roo.bootstrap.TextArea = function(config){
10409 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10413 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
10423 getAutoCreate : function(){
10425 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10431 if(this.inputType != 'hidden'){
10432 cfg.cls = 'form-group' //input-group
10440 value : this.value || '',
10441 html: this.html || '',
10442 cls : 'form-control',
10443 placeholder : this.placeholder || ''
10447 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10448 input.maxLength = this.maxLength;
10452 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10456 input.cols = this.cols;
10459 if (this.readOnly) {
10460 input.readonly = true;
10464 input.name = this.name;
10468 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10472 ['xs','sm','md','lg'].map(function(size){
10473 if (settings[size]) {
10474 cfg.cls += ' col-' + size + '-' + settings[size];
10478 var inputblock = input;
10480 if(this.hasFeedback && !this.allowBlank){
10484 cls: 'glyphicon form-control-feedback'
10488 cls : 'has-feedback',
10497 if (this.before || this.after) {
10500 cls : 'input-group',
10504 inputblock.cn.push({
10506 cls : 'input-group-addon',
10511 inputblock.cn.push(input);
10513 if(this.hasFeedback && !this.allowBlank){
10514 inputblock.cls += ' has-feedback';
10515 inputblock.cn.push(feedback);
10519 inputblock.cn.push({
10521 cls : 'input-group-addon',
10528 if (align ==='left' && this.fieldLabel.length) {
10533 cls : 'control-label',
10534 html : this.fieldLabel
10545 if(this.labelWidth > 12){
10546 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10549 if(this.labelWidth < 13 && this.labelmd == 0){
10550 this.labelmd = this.labelWidth;
10553 if(this.labellg > 0){
10554 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10555 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10558 if(this.labelmd > 0){
10559 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10560 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10563 if(this.labelsm > 0){
10564 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10565 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10568 if(this.labelxs > 0){
10569 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10570 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10573 } else if ( this.fieldLabel.length) {
10578 //cls : 'input-group-addon',
10579 html : this.fieldLabel
10597 if (this.disabled) {
10598 input.disabled=true;
10605 * return the real textarea element.
10607 inputEl: function ()
10609 return this.el.select('textarea.form-control',true).first();
10613 * Clear any invalid styles/messages for this field
10615 clearInvalid : function()
10618 if(!this.el || this.preventMark){ // not rendered
10622 var label = this.el.select('label', true).first();
10623 var icon = this.el.select('i.fa-star', true).first();
10628 this.el.removeClass( this.validClass);
10629 this.inputEl().removeClass('is-invalid');
10631 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10633 var feedback = this.el.select('.form-control-feedback', true).first();
10636 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10641 this.fireEvent('valid', this);
10645 * Mark this field as valid
10647 markValid : function()
10649 if(!this.el || this.preventMark){ // not rendered
10653 this.el.removeClass([this.invalidClass, this.validClass]);
10654 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10656 var feedback = this.el.select('.form-control-feedback', true).first();
10659 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10662 if(this.disabled || this.allowBlank){
10666 var label = this.el.select('label', true).first();
10667 var icon = this.el.select('i.fa-star', true).first();
10672 if (Roo.bootstrap.version == 3) {
10673 this.el.addClass(this.validClass);
10675 this.inputEl().addClass('is-valid');
10679 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10681 var feedback = this.el.select('.form-control-feedback', true).first();
10684 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10685 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10690 this.fireEvent('valid', this);
10694 * Mark this field as invalid
10695 * @param {String} msg The validation message
10697 markInvalid : function(msg)
10699 if(!this.el || this.preventMark){ // not rendered
10703 this.el.removeClass([this.invalidClass, this.validClass]);
10704 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10706 var feedback = this.el.select('.form-control-feedback', true).first();
10709 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10712 if(this.disabled || this.allowBlank){
10716 var label = this.el.select('label', true).first();
10717 var icon = this.el.select('i.fa-star', true).first();
10719 if(!this.getValue().length && label && !icon){
10720 this.el.createChild({
10722 cls : 'text-danger fa fa-lg fa-star',
10723 tooltip : 'This field is required',
10724 style : 'margin-right:5px;'
10728 if (Roo.bootstrap.version == 3) {
10729 this.el.addClass(this.invalidClass);
10731 this.inputEl().addClass('is-invalid');
10734 // fixme ... this may be depricated need to test..
10735 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10737 var feedback = this.el.select('.form-control-feedback', true).first();
10740 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10742 if(this.getValue().length || this.forceFeedback){
10743 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10750 this.fireEvent('invalid', this, msg);
10758 * trigger field - base class for combo..
10763 * @class Roo.bootstrap.TriggerField
10764 * @extends Roo.bootstrap.Input
10765 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10766 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10767 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10768 * for which you can provide a custom implementation. For example:
10770 var trigger = new Roo.bootstrap.TriggerField();
10771 trigger.onTriggerClick = myTriggerFn;
10772 trigger.applyTo('my-field');
10775 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10776 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10777 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10778 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10779 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10782 * Create a new TriggerField.
10783 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10784 * to the base TextField)
10786 Roo.bootstrap.TriggerField = function(config){
10787 this.mimicing = false;
10788 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10791 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10793 * @cfg {String} triggerClass A CSS class to apply to the trigger
10796 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10801 * @cfg {Boolean} removable (true|false) special filter default false
10805 /** @cfg {Boolean} grow @hide */
10806 /** @cfg {Number} growMin @hide */
10807 /** @cfg {Number} growMax @hide */
10813 autoSize: Roo.emptyFn,
10817 deferHeight : true,
10820 actionMode : 'wrap',
10825 getAutoCreate : function(){
10827 var align = this.labelAlign || this.parentLabelAlign();
10832 cls: 'form-group' //input-group
10839 type : this.inputType,
10840 cls : 'form-control',
10841 autocomplete: 'new-password',
10842 placeholder : this.placeholder || ''
10846 input.name = this.name;
10849 input.cls += ' input-' + this.size;
10852 if (this.disabled) {
10853 input.disabled=true;
10856 var inputblock = input;
10858 if(this.hasFeedback && !this.allowBlank){
10862 cls: 'glyphicon form-control-feedback'
10865 if(this.removable && !this.editable && !this.tickable){
10867 cls : 'has-feedback',
10873 cls : 'roo-combo-removable-btn close'
10880 cls : 'has-feedback',
10889 if(this.removable && !this.editable && !this.tickable){
10891 cls : 'roo-removable',
10897 cls : 'roo-combo-removable-btn close'
10904 if (this.before || this.after) {
10907 cls : 'input-group',
10911 inputblock.cn.push({
10913 cls : 'input-group-addon input-group-prepend input-group-text',
10918 inputblock.cn.push(input);
10920 if(this.hasFeedback && !this.allowBlank){
10921 inputblock.cls += ' has-feedback';
10922 inputblock.cn.push(feedback);
10926 inputblock.cn.push({
10928 cls : 'input-group-addon input-group-append input-group-text',
10937 var ibwrap = inputblock;
10942 cls: 'roo-select2-choices',
10946 cls: 'roo-select2-search-field',
10958 cls: 'roo-select2-container input-group',
10963 cls: 'form-hidden-field'
10969 if(!this.multiple && this.showToggleBtn){
10975 if (this.caret != false) {
10978 cls: 'fa fa-' + this.caret
10985 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10987 Roo.bootstrap.version == 3 ? caret : '',
10990 cls: 'combobox-clear',
11004 combobox.cls += ' roo-select2-container-multi';
11008 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11009 tooltip : 'This field is required'
11011 if (Roo.bootstrap.version == 4) {
11014 style : 'display:none'
11019 if (align ==='left' && this.fieldLabel.length) {
11021 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11028 cls : 'control-label',
11029 html : this.fieldLabel
11041 var labelCfg = cfg.cn[1];
11042 var contentCfg = cfg.cn[2];
11044 if(this.indicatorpos == 'right'){
11049 cls : 'control-label',
11053 html : this.fieldLabel
11067 labelCfg = cfg.cn[0];
11068 contentCfg = cfg.cn[1];
11071 if(this.labelWidth > 12){
11072 labelCfg.style = "width: " + this.labelWidth + 'px';
11075 if(this.labelWidth < 13 && this.labelmd == 0){
11076 this.labelmd = this.labelWidth;
11079 if(this.labellg > 0){
11080 labelCfg.cls += ' col-lg-' + this.labellg;
11081 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11084 if(this.labelmd > 0){
11085 labelCfg.cls += ' col-md-' + this.labelmd;
11086 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11089 if(this.labelsm > 0){
11090 labelCfg.cls += ' col-sm-' + this.labelsm;
11091 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11094 if(this.labelxs > 0){
11095 labelCfg.cls += ' col-xs-' + this.labelxs;
11096 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11099 } else if ( this.fieldLabel.length) {
11100 // Roo.log(" label");
11105 //cls : 'input-group-addon',
11106 html : this.fieldLabel
11114 if(this.indicatorpos == 'right'){
11122 html : this.fieldLabel
11136 // Roo.log(" no label && no align");
11143 ['xs','sm','md','lg'].map(function(size){
11144 if (settings[size]) {
11145 cfg.cls += ' col-' + size + '-' + settings[size];
11156 onResize : function(w, h){
11157 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
11158 // if(typeof w == 'number'){
11159 // var x = w - this.trigger.getWidth();
11160 // this.inputEl().setWidth(this.adjustWidth('input', x));
11161 // this.trigger.setStyle('left', x+'px');
11166 adjustSize : Roo.BoxComponent.prototype.adjustSize,
11169 getResizeEl : function(){
11170 return this.inputEl();
11174 getPositionEl : function(){
11175 return this.inputEl();
11179 alignErrorIcon : function(){
11180 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
11184 initEvents : function(){
11188 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
11189 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
11190 if(!this.multiple && this.showToggleBtn){
11191 this.trigger = this.el.select('span.dropdown-toggle',true).first();
11192 if(this.hideTrigger){
11193 this.trigger.setDisplayed(false);
11195 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
11199 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
11202 if(this.removable && !this.editable && !this.tickable){
11203 var close = this.closeTriggerEl();
11206 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
11207 close.on('click', this.removeBtnClick, this, close);
11211 //this.trigger.addClassOnOver('x-form-trigger-over');
11212 //this.trigger.addClassOnClick('x-form-trigger-click');
11215 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
11219 closeTriggerEl : function()
11221 var close = this.el.select('.roo-combo-removable-btn', true).first();
11222 return close ? close : false;
11225 removeBtnClick : function(e, h, el)
11227 e.preventDefault();
11229 if(this.fireEvent("remove", this) !== false){
11231 this.fireEvent("afterremove", this)
11235 createList : function()
11237 this.list = Roo.get(document.body).createChild({
11238 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
11239 cls: 'typeahead typeahead-long dropdown-menu',
11240 style: 'display:none'
11243 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
11248 initTrigger : function(){
11253 onDestroy : function(){
11255 this.trigger.removeAllListeners();
11256 // this.trigger.remove();
11259 // this.wrap.remove();
11261 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
11265 onFocus : function(){
11266 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
11268 if(!this.mimicing){
11269 this.wrap.addClass('x-trigger-wrap-focus');
11270 this.mimicing = true;
11271 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
11272 if(this.monitorTab){
11273 this.el.on("keydown", this.checkTab, this);
11280 checkTab : function(e){
11281 if(e.getKey() == e.TAB){
11282 this.triggerBlur();
11287 onBlur : function(){
11292 mimicBlur : function(e, t){
11294 if(!this.wrap.contains(t) && this.validateBlur()){
11295 this.triggerBlur();
11301 triggerBlur : function(){
11302 this.mimicing = false;
11303 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
11304 if(this.monitorTab){
11305 this.el.un("keydown", this.checkTab, this);
11307 //this.wrap.removeClass('x-trigger-wrap-focus');
11308 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11312 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11313 validateBlur : function(e, t){
11318 onDisable : function(){
11319 this.inputEl().dom.disabled = true;
11320 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11322 // this.wrap.addClass('x-item-disabled');
11327 onEnable : function(){
11328 this.inputEl().dom.disabled = false;
11329 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11331 // this.el.removeClass('x-item-disabled');
11336 onShow : function(){
11337 var ae = this.getActionEl();
11340 ae.dom.style.display = '';
11341 ae.dom.style.visibility = 'visible';
11347 onHide : function(){
11348 var ae = this.getActionEl();
11349 ae.dom.style.display = 'none';
11353 * The function that should handle the trigger's click event. This method does nothing by default until overridden
11354 * by an implementing function.
11356 * @param {EventObject} e
11358 onTriggerClick : Roo.emptyFn
11362 * Ext JS Library 1.1.1
11363 * Copyright(c) 2006-2007, Ext JS, LLC.
11365 * Originally Released Under LGPL - original licence link has changed is not relivant.
11368 * <script type="text/javascript">
11373 * @class Roo.data.SortTypes
11375 * Defines the default sorting (casting?) comparison functions used when sorting data.
11377 Roo.data.SortTypes = {
11379 * Default sort that does nothing
11380 * @param {Mixed} s The value being converted
11381 * @return {Mixed} The comparison value
11383 none : function(s){
11388 * The regular expression used to strip tags
11392 stripTagsRE : /<\/?[^>]+>/gi,
11395 * Strips all HTML tags to sort on text only
11396 * @param {Mixed} s The value being converted
11397 * @return {String} The comparison value
11399 asText : function(s){
11400 return String(s).replace(this.stripTagsRE, "");
11404 * Strips all HTML tags to sort on text only - Case insensitive
11405 * @param {Mixed} s The value being converted
11406 * @return {String} The comparison value
11408 asUCText : function(s){
11409 return String(s).toUpperCase().replace(this.stripTagsRE, "");
11413 * Case insensitive string
11414 * @param {Mixed} s The value being converted
11415 * @return {String} The comparison value
11417 asUCString : function(s) {
11418 return String(s).toUpperCase();
11423 * @param {Mixed} s The value being converted
11424 * @return {Number} The comparison value
11426 asDate : function(s) {
11430 if(s instanceof Date){
11431 return s.getTime();
11433 return Date.parse(String(s));
11438 * @param {Mixed} s The value being converted
11439 * @return {Float} The comparison value
11441 asFloat : function(s) {
11442 var val = parseFloat(String(s).replace(/,/g, ""));
11451 * @param {Mixed} s The value being converted
11452 * @return {Number} The comparison value
11454 asInt : function(s) {
11455 var val = parseInt(String(s).replace(/,/g, ""));
11463 * Ext JS Library 1.1.1
11464 * Copyright(c) 2006-2007, Ext JS, LLC.
11466 * Originally Released Under LGPL - original licence link has changed is not relivant.
11469 * <script type="text/javascript">
11473 * @class Roo.data.Record
11474 * Instances of this class encapsulate both record <em>definition</em> information, and record
11475 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11476 * to access Records cached in an {@link Roo.data.Store} object.<br>
11478 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11479 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11482 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11484 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11485 * {@link #create}. The parameters are the same.
11486 * @param {Array} data An associative Array of data values keyed by the field name.
11487 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11488 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11489 * not specified an integer id is generated.
11491 Roo.data.Record = function(data, id){
11492 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11497 * Generate a constructor for a specific record layout.
11498 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11499 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11500 * Each field definition object may contain the following properties: <ul>
11501 * <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,
11502 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11503 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11504 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11505 * is being used, then this is a string containing the javascript expression to reference the data relative to
11506 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11507 * to the data item relative to the record element. If the mapping expression is the same as the field name,
11508 * this may be omitted.</p></li>
11509 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11510 * <ul><li>auto (Default, implies no conversion)</li>
11515 * <li>date</li></ul></p></li>
11516 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11517 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11518 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11519 * by the Reader into an object that will be stored in the Record. It is passed the
11520 * following parameters:<ul>
11521 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11523 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11525 * <br>usage:<br><pre><code>
11526 var TopicRecord = Roo.data.Record.create(
11527 {name: 'title', mapping: 'topic_title'},
11528 {name: 'author', mapping: 'username'},
11529 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11530 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11531 {name: 'lastPoster', mapping: 'user2'},
11532 {name: 'excerpt', mapping: 'post_text'}
11535 var myNewRecord = new TopicRecord({
11536 title: 'Do my job please',
11539 lastPost: new Date(),
11540 lastPoster: 'Animal',
11541 excerpt: 'No way dude!'
11543 myStore.add(myNewRecord);
11548 Roo.data.Record.create = function(o){
11549 var f = function(){
11550 f.superclass.constructor.apply(this, arguments);
11552 Roo.extend(f, Roo.data.Record);
11553 var p = f.prototype;
11554 p.fields = new Roo.util.MixedCollection(false, function(field){
11557 for(var i = 0, len = o.length; i < len; i++){
11558 p.fields.add(new Roo.data.Field(o[i]));
11560 f.getField = function(name){
11561 return p.fields.get(name);
11566 Roo.data.Record.AUTO_ID = 1000;
11567 Roo.data.Record.EDIT = 'edit';
11568 Roo.data.Record.REJECT = 'reject';
11569 Roo.data.Record.COMMIT = 'commit';
11571 Roo.data.Record.prototype = {
11573 * Readonly flag - true if this record has been modified.
11582 join : function(store){
11583 this.store = store;
11587 * Set the named field to the specified value.
11588 * @param {String} name The name of the field to set.
11589 * @param {Object} value The value to set the field to.
11591 set : function(name, value){
11592 if(this.data[name] == value){
11596 if(!this.modified){
11597 this.modified = {};
11599 if(typeof this.modified[name] == 'undefined'){
11600 this.modified[name] = this.data[name];
11602 this.data[name] = value;
11603 if(!this.editing && this.store){
11604 this.store.afterEdit(this);
11609 * Get the value of the named field.
11610 * @param {String} name The name of the field to get the value of.
11611 * @return {Object} The value of the field.
11613 get : function(name){
11614 return this.data[name];
11618 beginEdit : function(){
11619 this.editing = true;
11620 this.modified = {};
11624 cancelEdit : function(){
11625 this.editing = false;
11626 delete this.modified;
11630 endEdit : function(){
11631 this.editing = false;
11632 if(this.dirty && this.store){
11633 this.store.afterEdit(this);
11638 * Usually called by the {@link Roo.data.Store} which owns the Record.
11639 * Rejects all changes made to the Record since either creation, or the last commit operation.
11640 * Modified fields are reverted to their original values.
11642 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11643 * of reject operations.
11645 reject : function(){
11646 var m = this.modified;
11648 if(typeof m[n] != "function"){
11649 this.data[n] = m[n];
11652 this.dirty = false;
11653 delete this.modified;
11654 this.editing = false;
11656 this.store.afterReject(this);
11661 * Usually called by the {@link Roo.data.Store} which owns the Record.
11662 * Commits all changes made to the Record since either creation, or the last commit operation.
11664 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11665 * of commit operations.
11667 commit : function(){
11668 this.dirty = false;
11669 delete this.modified;
11670 this.editing = false;
11672 this.store.afterCommit(this);
11677 hasError : function(){
11678 return this.error != null;
11682 clearError : function(){
11687 * Creates a copy of this record.
11688 * @param {String} id (optional) A new record id if you don't want to use this record's id
11691 copy : function(newId) {
11692 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11696 * Ext JS Library 1.1.1
11697 * Copyright(c) 2006-2007, Ext JS, LLC.
11699 * Originally Released Under LGPL - original licence link has changed is not relivant.
11702 * <script type="text/javascript">
11708 * @class Roo.data.Store
11709 * @extends Roo.util.Observable
11710 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11711 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11713 * 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
11714 * has no knowledge of the format of the data returned by the Proxy.<br>
11716 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11717 * instances from the data object. These records are cached and made available through accessor functions.
11719 * Creates a new Store.
11720 * @param {Object} config A config object containing the objects needed for the Store to access data,
11721 * and read the data into Records.
11723 Roo.data.Store = function(config){
11724 this.data = new Roo.util.MixedCollection(false);
11725 this.data.getKey = function(o){
11728 this.baseParams = {};
11730 this.paramNames = {
11735 "multisort" : "_multisort"
11738 if(config && config.data){
11739 this.inlineData = config.data;
11740 delete config.data;
11743 Roo.apply(this, config);
11745 if(this.reader){ // reader passed
11746 this.reader = Roo.factory(this.reader, Roo.data);
11747 this.reader.xmodule = this.xmodule || false;
11748 if(!this.recordType){
11749 this.recordType = this.reader.recordType;
11751 if(this.reader.onMetaChange){
11752 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11756 if(this.recordType){
11757 this.fields = this.recordType.prototype.fields;
11759 this.modified = [];
11763 * @event datachanged
11764 * Fires when the data cache has changed, and a widget which is using this Store
11765 * as a Record cache should refresh its view.
11766 * @param {Store} this
11768 datachanged : true,
11770 * @event metachange
11771 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11772 * @param {Store} this
11773 * @param {Object} meta The JSON metadata
11778 * Fires when Records have been added to the Store
11779 * @param {Store} this
11780 * @param {Roo.data.Record[]} records The array of Records added
11781 * @param {Number} index The index at which the record(s) were added
11786 * Fires when a Record has been removed from the Store
11787 * @param {Store} this
11788 * @param {Roo.data.Record} record The Record that was removed
11789 * @param {Number} index The index at which the record was removed
11794 * Fires when a Record has been updated
11795 * @param {Store} this
11796 * @param {Roo.data.Record} record The Record that was updated
11797 * @param {String} operation The update operation being performed. Value may be one of:
11799 Roo.data.Record.EDIT
11800 Roo.data.Record.REJECT
11801 Roo.data.Record.COMMIT
11807 * Fires when the data cache has been cleared.
11808 * @param {Store} this
11812 * @event beforeload
11813 * Fires before a request is made for a new data object. If the beforeload handler returns false
11814 * the load action will be canceled.
11815 * @param {Store} this
11816 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11820 * @event beforeloadadd
11821 * Fires after a new set of Records has been loaded.
11822 * @param {Store} this
11823 * @param {Roo.data.Record[]} records The Records that were loaded
11824 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11826 beforeloadadd : true,
11829 * Fires after a new set of Records has been loaded, before they are added to the store.
11830 * @param {Store} this
11831 * @param {Roo.data.Record[]} records The Records that were loaded
11832 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11833 * @params {Object} return from reader
11837 * @event loadexception
11838 * Fires if an exception occurs in the Proxy during loading.
11839 * Called with the signature of the Proxy's "loadexception" event.
11840 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11843 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11844 * @param {Object} load options
11845 * @param {Object} jsonData from your request (normally this contains the Exception)
11847 loadexception : true
11851 this.proxy = Roo.factory(this.proxy, Roo.data);
11852 this.proxy.xmodule = this.xmodule || false;
11853 this.relayEvents(this.proxy, ["loadexception"]);
11855 this.sortToggle = {};
11856 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11858 Roo.data.Store.superclass.constructor.call(this);
11860 if(this.inlineData){
11861 this.loadData(this.inlineData);
11862 delete this.inlineData;
11866 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11868 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11869 * without a remote query - used by combo/forms at present.
11873 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11876 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11879 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11880 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11883 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11884 * on any HTTP request
11887 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11890 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11894 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11895 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11897 remoteSort : false,
11900 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11901 * loaded or when a record is removed. (defaults to false).
11903 pruneModifiedRecords : false,
11906 lastOptions : null,
11909 * Add Records to the Store and fires the add event.
11910 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11912 add : function(records){
11913 records = [].concat(records);
11914 for(var i = 0, len = records.length; i < len; i++){
11915 records[i].join(this);
11917 var index = this.data.length;
11918 this.data.addAll(records);
11919 this.fireEvent("add", this, records, index);
11923 * Remove a Record from the Store and fires the remove event.
11924 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11926 remove : function(record){
11927 var index = this.data.indexOf(record);
11928 this.data.removeAt(index);
11930 if(this.pruneModifiedRecords){
11931 this.modified.remove(record);
11933 this.fireEvent("remove", this, record, index);
11937 * Remove all Records from the Store and fires the clear event.
11939 removeAll : function(){
11941 if(this.pruneModifiedRecords){
11942 this.modified = [];
11944 this.fireEvent("clear", this);
11948 * Inserts Records to the Store at the given index and fires the add event.
11949 * @param {Number} index The start index at which to insert the passed Records.
11950 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11952 insert : function(index, records){
11953 records = [].concat(records);
11954 for(var i = 0, len = records.length; i < len; i++){
11955 this.data.insert(index, records[i]);
11956 records[i].join(this);
11958 this.fireEvent("add", this, records, index);
11962 * Get the index within the cache of the passed Record.
11963 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11964 * @return {Number} The index of the passed Record. Returns -1 if not found.
11966 indexOf : function(record){
11967 return this.data.indexOf(record);
11971 * Get the index within the cache of the Record with the passed id.
11972 * @param {String} id The id of the Record to find.
11973 * @return {Number} The index of the Record. Returns -1 if not found.
11975 indexOfId : function(id){
11976 return this.data.indexOfKey(id);
11980 * Get the Record with the specified id.
11981 * @param {String} id The id of the Record to find.
11982 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11984 getById : function(id){
11985 return this.data.key(id);
11989 * Get the Record at the specified index.
11990 * @param {Number} index The index of the Record to find.
11991 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11993 getAt : function(index){
11994 return this.data.itemAt(index);
11998 * Returns a range of Records between specified indices.
11999 * @param {Number} startIndex (optional) The starting index (defaults to 0)
12000 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
12001 * @return {Roo.data.Record[]} An array of Records
12003 getRange : function(start, end){
12004 return this.data.getRange(start, end);
12008 storeOptions : function(o){
12009 o = Roo.apply({}, o);
12012 this.lastOptions = o;
12016 * Loads the Record cache from the configured Proxy using the configured Reader.
12018 * If using remote paging, then the first load call must specify the <em>start</em>
12019 * and <em>limit</em> properties in the options.params property to establish the initial
12020 * position within the dataset, and the number of Records to cache on each read from the Proxy.
12022 * <strong>It is important to note that for remote data sources, loading is asynchronous,
12023 * and this call will return before the new data has been loaded. Perform any post-processing
12024 * in a callback function, or in a "load" event handler.</strong>
12026 * @param {Object} options An object containing properties which control loading options:<ul>
12027 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
12028 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
12029 * passed the following arguments:<ul>
12030 * <li>r : Roo.data.Record[]</li>
12031 * <li>options: Options object from the load call</li>
12032 * <li>success: Boolean success indicator</li></ul></li>
12033 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
12034 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
12037 load : function(options){
12038 options = options || {};
12039 if(this.fireEvent("beforeload", this, options) !== false){
12040 this.storeOptions(options);
12041 var p = Roo.apply(options.params || {}, this.baseParams);
12042 // if meta was not loaded from remote source.. try requesting it.
12043 if (!this.reader.metaFromRemote) {
12044 p._requestMeta = 1;
12046 if(this.sortInfo && this.remoteSort){
12047 var pn = this.paramNames;
12048 p[pn["sort"]] = this.sortInfo.field;
12049 p[pn["dir"]] = this.sortInfo.direction;
12051 if (this.multiSort) {
12052 var pn = this.paramNames;
12053 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
12056 this.proxy.load(p, this.reader, this.loadRecords, this, options);
12061 * Reloads the Record cache from the configured Proxy using the configured Reader and
12062 * the options from the last load operation performed.
12063 * @param {Object} options (optional) An object containing properties which may override the options
12064 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
12065 * the most recently used options are reused).
12067 reload : function(options){
12068 this.load(Roo.applyIf(options||{}, this.lastOptions));
12072 // Called as a callback by the Reader during a load operation.
12073 loadRecords : function(o, options, success){
12074 if(!o || success === false){
12075 if(success !== false){
12076 this.fireEvent("load", this, [], options, o);
12078 if(options.callback){
12079 options.callback.call(options.scope || this, [], options, false);
12083 // if data returned failure - throw an exception.
12084 if (o.success === false) {
12085 // show a message if no listener is registered.
12086 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
12087 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
12089 // loadmask wil be hooked into this..
12090 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
12093 var r = o.records, t = o.totalRecords || r.length;
12095 this.fireEvent("beforeloadadd", this, r, options, o);
12097 if(!options || options.add !== true){
12098 if(this.pruneModifiedRecords){
12099 this.modified = [];
12101 for(var i = 0, len = r.length; i < len; i++){
12105 this.data = this.snapshot;
12106 delete this.snapshot;
12109 this.data.addAll(r);
12110 this.totalLength = t;
12112 this.fireEvent("datachanged", this);
12114 this.totalLength = Math.max(t, this.data.length+r.length);
12118 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
12120 var e = new Roo.data.Record({});
12122 e.set(this.parent.displayField, this.parent.emptyTitle);
12123 e.set(this.parent.valueField, '');
12128 this.fireEvent("load", this, r, options, o);
12129 if(options.callback){
12130 options.callback.call(options.scope || this, r, options, true);
12136 * Loads data from a passed data block. A Reader which understands the format of the data
12137 * must have been configured in the constructor.
12138 * @param {Object} data The data block from which to read the Records. The format of the data expected
12139 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
12140 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
12142 loadData : function(o, append){
12143 var r = this.reader.readRecords(o);
12144 this.loadRecords(r, {add: append}, true);
12148 * using 'cn' the nested child reader read the child array into it's child stores.
12149 * @param {Object} rec The record with a 'children array
12151 loadDataFromChildren : function(rec)
12153 this.loadData(this.reader.toLoadData(rec));
12158 * Gets the number of cached records.
12160 * <em>If using paging, this may not be the total size of the dataset. If the data object
12161 * used by the Reader contains the dataset size, then the getTotalCount() function returns
12162 * the data set size</em>
12164 getCount : function(){
12165 return this.data.length || 0;
12169 * Gets the total number of records in the dataset as returned by the server.
12171 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
12172 * the dataset size</em>
12174 getTotalCount : function(){
12175 return this.totalLength || 0;
12179 * Returns the sort state of the Store as an object with two properties:
12181 field {String} The name of the field by which the Records are sorted
12182 direction {String} The sort order, "ASC" or "DESC"
12185 getSortState : function(){
12186 return this.sortInfo;
12190 applySort : function(){
12191 if(this.sortInfo && !this.remoteSort){
12192 var s = this.sortInfo, f = s.field;
12193 var st = this.fields.get(f).sortType;
12194 var fn = function(r1, r2){
12195 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
12196 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12198 this.data.sort(s.direction, fn);
12199 if(this.snapshot && this.snapshot != this.data){
12200 this.snapshot.sort(s.direction, fn);
12206 * Sets the default sort column and order to be used by the next load operation.
12207 * @param {String} fieldName The name of the field to sort by.
12208 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12210 setDefaultSort : function(field, dir){
12211 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
12215 * Sort the Records.
12216 * If remote sorting is used, the sort is performed on the server, and the cache is
12217 * reloaded. If local sorting is used, the cache is sorted internally.
12218 * @param {String} fieldName The name of the field to sort by.
12219 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12221 sort : function(fieldName, dir){
12222 var f = this.fields.get(fieldName);
12224 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
12226 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
12227 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
12232 this.sortToggle[f.name] = dir;
12233 this.sortInfo = {field: f.name, direction: dir};
12234 if(!this.remoteSort){
12236 this.fireEvent("datachanged", this);
12238 this.load(this.lastOptions);
12243 * Calls the specified function for each of the Records in the cache.
12244 * @param {Function} fn The function to call. The Record is passed as the first parameter.
12245 * Returning <em>false</em> aborts and exits the iteration.
12246 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
12248 each : function(fn, scope){
12249 this.data.each(fn, scope);
12253 * Gets all records modified since the last commit. Modified records are persisted across load operations
12254 * (e.g., during paging).
12255 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
12257 getModifiedRecords : function(){
12258 return this.modified;
12262 createFilterFn : function(property, value, anyMatch){
12263 if(!value.exec){ // not a regex
12264 value = String(value);
12265 if(value.length == 0){
12268 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
12270 return function(r){
12271 return value.test(r.data[property]);
12276 * Sums the value of <i>property</i> for each record between start and end and returns the result.
12277 * @param {String} property A field on your records
12278 * @param {Number} start The record index to start at (defaults to 0)
12279 * @param {Number} end The last record index to include (defaults to length - 1)
12280 * @return {Number} The sum
12282 sum : function(property, start, end){
12283 var rs = this.data.items, v = 0;
12284 start = start || 0;
12285 end = (end || end === 0) ? end : rs.length-1;
12287 for(var i = start; i <= end; i++){
12288 v += (rs[i].data[property] || 0);
12294 * Filter the records by a specified property.
12295 * @param {String} field A field on your records
12296 * @param {String/RegExp} value Either a string that the field
12297 * should start with or a RegExp to test against the field
12298 * @param {Boolean} anyMatch True to match any part not just the beginning
12300 filter : function(property, value, anyMatch){
12301 var fn = this.createFilterFn(property, value, anyMatch);
12302 return fn ? this.filterBy(fn) : this.clearFilter();
12306 * Filter by a function. The specified function will be called with each
12307 * record in this data source. If the function returns true the record is included,
12308 * otherwise it is filtered.
12309 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12310 * @param {Object} scope (optional) The scope of the function (defaults to this)
12312 filterBy : function(fn, scope){
12313 this.snapshot = this.snapshot || this.data;
12314 this.data = this.queryBy(fn, scope||this);
12315 this.fireEvent("datachanged", this);
12319 * Query the records by a specified property.
12320 * @param {String} field A field on your records
12321 * @param {String/RegExp} value Either a string that the field
12322 * should start with or a RegExp to test against the field
12323 * @param {Boolean} anyMatch True to match any part not just the beginning
12324 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12326 query : function(property, value, anyMatch){
12327 var fn = this.createFilterFn(property, value, anyMatch);
12328 return fn ? this.queryBy(fn) : this.data.clone();
12332 * Query by a function. The specified function will be called with each
12333 * record in this data source. If the function returns true the record is included
12335 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12336 * @param {Object} scope (optional) The scope of the function (defaults to this)
12337 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12339 queryBy : function(fn, scope){
12340 var data = this.snapshot || this.data;
12341 return data.filterBy(fn, scope||this);
12345 * Collects unique values for a particular dataIndex from this store.
12346 * @param {String} dataIndex The property to collect
12347 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12348 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12349 * @return {Array} An array of the unique values
12351 collect : function(dataIndex, allowNull, bypassFilter){
12352 var d = (bypassFilter === true && this.snapshot) ?
12353 this.snapshot.items : this.data.items;
12354 var v, sv, r = [], l = {};
12355 for(var i = 0, len = d.length; i < len; i++){
12356 v = d[i].data[dataIndex];
12358 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12367 * Revert to a view of the Record cache with no filtering applied.
12368 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12370 clearFilter : function(suppressEvent){
12371 if(this.snapshot && this.snapshot != this.data){
12372 this.data = this.snapshot;
12373 delete this.snapshot;
12374 if(suppressEvent !== true){
12375 this.fireEvent("datachanged", this);
12381 afterEdit : function(record){
12382 if(this.modified.indexOf(record) == -1){
12383 this.modified.push(record);
12385 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12389 afterReject : function(record){
12390 this.modified.remove(record);
12391 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12395 afterCommit : function(record){
12396 this.modified.remove(record);
12397 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12401 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12402 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12404 commitChanges : function(){
12405 var m = this.modified.slice(0);
12406 this.modified = [];
12407 for(var i = 0, len = m.length; i < len; i++){
12413 * Cancel outstanding changes on all changed records.
12415 rejectChanges : function(){
12416 var m = this.modified.slice(0);
12417 this.modified = [];
12418 for(var i = 0, len = m.length; i < len; i++){
12423 onMetaChange : function(meta, rtype, o){
12424 this.recordType = rtype;
12425 this.fields = rtype.prototype.fields;
12426 delete this.snapshot;
12427 this.sortInfo = meta.sortInfo || this.sortInfo;
12428 this.modified = [];
12429 this.fireEvent('metachange', this, this.reader.meta);
12432 moveIndex : function(data, type)
12434 var index = this.indexOf(data);
12436 var newIndex = index + type;
12440 this.insert(newIndex, data);
12445 * Ext JS Library 1.1.1
12446 * Copyright(c) 2006-2007, Ext JS, LLC.
12448 * Originally Released Under LGPL - original licence link has changed is not relivant.
12451 * <script type="text/javascript">
12455 * @class Roo.data.SimpleStore
12456 * @extends Roo.data.Store
12457 * Small helper class to make creating Stores from Array data easier.
12458 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12459 * @cfg {Array} fields An array of field definition objects, or field name strings.
12460 * @cfg {Object} an existing reader (eg. copied from another store)
12461 * @cfg {Array} data The multi-dimensional array of data
12463 * @param {Object} config
12465 Roo.data.SimpleStore = function(config)
12467 Roo.data.SimpleStore.superclass.constructor.call(this, {
12469 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
12472 Roo.data.Record.create(config.fields)
12474 proxy : new Roo.data.MemoryProxy(config.data)
12478 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12480 * Ext JS Library 1.1.1
12481 * Copyright(c) 2006-2007, Ext JS, LLC.
12483 * Originally Released Under LGPL - original licence link has changed is not relivant.
12486 * <script type="text/javascript">
12491 * @extends Roo.data.Store
12492 * @class Roo.data.JsonStore
12493 * Small helper class to make creating Stores for JSON data easier. <br/>
12495 var store = new Roo.data.JsonStore({
12496 url: 'get-images.php',
12498 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12501 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12502 * JsonReader and HttpProxy (unless inline data is provided).</b>
12503 * @cfg {Array} fields An array of field definition objects, or field name strings.
12505 * @param {Object} config
12507 Roo.data.JsonStore = function(c){
12508 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12509 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12510 reader: new Roo.data.JsonReader(c, c.fields)
12513 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12515 * Ext JS Library 1.1.1
12516 * Copyright(c) 2006-2007, Ext JS, LLC.
12518 * Originally Released Under LGPL - original licence link has changed is not relivant.
12521 * <script type="text/javascript">
12525 Roo.data.Field = function(config){
12526 if(typeof config == "string"){
12527 config = {name: config};
12529 Roo.apply(this, config);
12532 this.type = "auto";
12535 var st = Roo.data.SortTypes;
12536 // named sortTypes are supported, here we look them up
12537 if(typeof this.sortType == "string"){
12538 this.sortType = st[this.sortType];
12541 // set default sortType for strings and dates
12542 if(!this.sortType){
12545 this.sortType = st.asUCString;
12548 this.sortType = st.asDate;
12551 this.sortType = st.none;
12556 var stripRe = /[\$,%]/g;
12558 // prebuilt conversion function for this field, instead of
12559 // switching every time we're reading a value
12561 var cv, dateFormat = this.dateFormat;
12566 cv = function(v){ return v; };
12569 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12573 return v !== undefined && v !== null && v !== '' ?
12574 parseInt(String(v).replace(stripRe, ""), 10) : '';
12579 return v !== undefined && v !== null && v !== '' ?
12580 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12585 cv = function(v){ return v === true || v === "true" || v == 1; };
12592 if(v instanceof Date){
12596 if(dateFormat == "timestamp"){
12597 return new Date(v*1000);
12599 return Date.parseDate(v, dateFormat);
12601 var parsed = Date.parse(v);
12602 return parsed ? new Date(parsed) : null;
12611 Roo.data.Field.prototype = {
12619 * Ext JS Library 1.1.1
12620 * Copyright(c) 2006-2007, Ext JS, LLC.
12622 * Originally Released Under LGPL - original licence link has changed is not relivant.
12625 * <script type="text/javascript">
12628 // Base class for reading structured data from a data source. This class is intended to be
12629 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12632 * @class Roo.data.DataReader
12633 * Base class for reading structured data from a data source. This class is intended to be
12634 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12637 Roo.data.DataReader = function(meta, recordType){
12641 this.recordType = recordType instanceof Array ?
12642 Roo.data.Record.create(recordType) : recordType;
12645 Roo.data.DataReader.prototype = {
12648 readerType : 'Data',
12650 * Create an empty record
12651 * @param {Object} data (optional) - overlay some values
12652 * @return {Roo.data.Record} record created.
12654 newRow : function(d) {
12656 this.recordType.prototype.fields.each(function(c) {
12658 case 'int' : da[c.name] = 0; break;
12659 case 'date' : da[c.name] = new Date(); break;
12660 case 'float' : da[c.name] = 0.0; break;
12661 case 'boolean' : da[c.name] = false; break;
12662 default : da[c.name] = ""; break;
12666 return new this.recordType(Roo.apply(da, d));
12672 * Ext JS Library 1.1.1
12673 * Copyright(c) 2006-2007, Ext JS, LLC.
12675 * Originally Released Under LGPL - original licence link has changed is not relivant.
12678 * <script type="text/javascript">
12682 * @class Roo.data.DataProxy
12683 * @extends Roo.data.Observable
12684 * This class is an abstract base class for implementations which provide retrieval of
12685 * unformatted data objects.<br>
12687 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12688 * (of the appropriate type which knows how to parse the data object) to provide a block of
12689 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12691 * Custom implementations must implement the load method as described in
12692 * {@link Roo.data.HttpProxy#load}.
12694 Roo.data.DataProxy = function(){
12697 * @event beforeload
12698 * Fires before a network request is made to retrieve a data object.
12699 * @param {Object} This DataProxy object.
12700 * @param {Object} params The params parameter to the load function.
12705 * Fires before the load method's callback is called.
12706 * @param {Object} This DataProxy object.
12707 * @param {Object} o The data object.
12708 * @param {Object} arg The callback argument object passed to the load function.
12712 * @event loadexception
12713 * Fires if an Exception occurs during data retrieval.
12714 * @param {Object} This DataProxy object.
12715 * @param {Object} o The data object.
12716 * @param {Object} arg The callback argument object passed to the load function.
12717 * @param {Object} e The Exception.
12719 loadexception : true
12721 Roo.data.DataProxy.superclass.constructor.call(this);
12724 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12727 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12731 * Ext JS Library 1.1.1
12732 * Copyright(c) 2006-2007, Ext JS, LLC.
12734 * Originally Released Under LGPL - original licence link has changed is not relivant.
12737 * <script type="text/javascript">
12740 * @class Roo.data.MemoryProxy
12741 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12742 * to the Reader when its load method is called.
12744 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12746 Roo.data.MemoryProxy = function(data){
12750 Roo.data.MemoryProxy.superclass.constructor.call(this);
12754 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12757 * Load data from the requested source (in this case an in-memory
12758 * data object passed to the constructor), read the data object into
12759 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12760 * process that block using the passed callback.
12761 * @param {Object} params This parameter is not used by the MemoryProxy class.
12762 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12763 * object into a block of Roo.data.Records.
12764 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12765 * The function must be passed <ul>
12766 * <li>The Record block object</li>
12767 * <li>The "arg" argument from the load function</li>
12768 * <li>A boolean success indicator</li>
12770 * @param {Object} scope The scope in which to call the callback
12771 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12773 load : function(params, reader, callback, scope, arg){
12774 params = params || {};
12777 result = reader.readRecords(params.data ? params.data :this.data);
12779 this.fireEvent("loadexception", this, arg, null, e);
12780 callback.call(scope, null, arg, false);
12783 callback.call(scope, result, arg, true);
12787 update : function(params, records){
12792 * Ext JS Library 1.1.1
12793 * Copyright(c) 2006-2007, Ext JS, LLC.
12795 * Originally Released Under LGPL - original licence link has changed is not relivant.
12798 * <script type="text/javascript">
12801 * @class Roo.data.HttpProxy
12802 * @extends Roo.data.DataProxy
12803 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12804 * configured to reference a certain URL.<br><br>
12806 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12807 * from which the running page was served.<br><br>
12809 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12811 * Be aware that to enable the browser to parse an XML document, the server must set
12812 * the Content-Type header in the HTTP response to "text/xml".
12814 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12815 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12816 * will be used to make the request.
12818 Roo.data.HttpProxy = function(conn){
12819 Roo.data.HttpProxy.superclass.constructor.call(this);
12820 // is conn a conn config or a real conn?
12822 this.useAjax = !conn || !conn.events;
12826 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12827 // thse are take from connection...
12830 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12833 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12834 * extra parameters to each request made by this object. (defaults to undefined)
12837 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12838 * to each request made by this object. (defaults to undefined)
12841 * @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)
12844 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12847 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12853 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12857 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12858 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12859 * a finer-grained basis than the DataProxy events.
12861 getConnection : function(){
12862 return this.useAjax ? Roo.Ajax : this.conn;
12866 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12867 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12868 * process that block using the passed callback.
12869 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12870 * for the request to the remote server.
12871 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12872 * object into a block of Roo.data.Records.
12873 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12874 * The function must be passed <ul>
12875 * <li>The Record block object</li>
12876 * <li>The "arg" argument from the load function</li>
12877 * <li>A boolean success indicator</li>
12879 * @param {Object} scope The scope in which to call the callback
12880 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12882 load : function(params, reader, callback, scope, arg){
12883 if(this.fireEvent("beforeload", this, params) !== false){
12885 params : params || {},
12887 callback : callback,
12892 callback : this.loadResponse,
12896 Roo.applyIf(o, this.conn);
12897 if(this.activeRequest){
12898 Roo.Ajax.abort(this.activeRequest);
12900 this.activeRequest = Roo.Ajax.request(o);
12902 this.conn.request(o);
12905 callback.call(scope||this, null, arg, false);
12910 loadResponse : function(o, success, response){
12911 delete this.activeRequest;
12913 this.fireEvent("loadexception", this, o, response);
12914 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12919 result = o.reader.read(response);
12921 this.fireEvent("loadexception", this, o, response, e);
12922 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12926 this.fireEvent("load", this, o, o.request.arg);
12927 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12931 update : function(dataSet){
12936 updateResponse : function(dataSet){
12941 * Ext JS Library 1.1.1
12942 * Copyright(c) 2006-2007, Ext JS, LLC.
12944 * Originally Released Under LGPL - original licence link has changed is not relivant.
12947 * <script type="text/javascript">
12951 * @class Roo.data.ScriptTagProxy
12952 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12953 * other than the originating domain of the running page.<br><br>
12955 * <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
12956 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12958 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12959 * source code that is used as the source inside a <script> tag.<br><br>
12961 * In order for the browser to process the returned data, the server must wrap the data object
12962 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12963 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12964 * depending on whether the callback name was passed:
12967 boolean scriptTag = false;
12968 String cb = request.getParameter("callback");
12971 response.setContentType("text/javascript");
12973 response.setContentType("application/x-json");
12975 Writer out = response.getWriter();
12977 out.write(cb + "(");
12979 out.print(dataBlock.toJsonString());
12986 * @param {Object} config A configuration object.
12988 Roo.data.ScriptTagProxy = function(config){
12989 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12990 Roo.apply(this, config);
12991 this.head = document.getElementsByTagName("head")[0];
12994 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12996 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12998 * @cfg {String} url The URL from which to request the data object.
13001 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
13005 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
13006 * the server the name of the callback function set up by the load call to process the returned data object.
13007 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
13008 * javascript output which calls this named function passing the data object as its only parameter.
13010 callbackParam : "callback",
13012 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
13013 * name to the request.
13018 * Load data from the configured URL, read the data object into
13019 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13020 * process that block using the passed callback.
13021 * @param {Object} params An object containing properties which are to be used as HTTP parameters
13022 * for the request to the remote server.
13023 * @param {Roo.data.DataReader} reader The Reader object which converts the data
13024 * object into a block of Roo.data.Records.
13025 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
13026 * The function must be passed <ul>
13027 * <li>The Record block object</li>
13028 * <li>The "arg" argument from the load function</li>
13029 * <li>A boolean success indicator</li>
13031 * @param {Object} scope The scope in which to call the callback
13032 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13034 load : function(params, reader, callback, scope, arg){
13035 if(this.fireEvent("beforeload", this, params) !== false){
13037 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
13039 var url = this.url;
13040 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
13042 url += "&_dc=" + (new Date().getTime());
13044 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
13047 cb : "stcCallback"+transId,
13048 scriptId : "stcScript"+transId,
13052 callback : callback,
13058 window[trans.cb] = function(o){
13059 conn.handleResponse(o, trans);
13062 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
13064 if(this.autoAbort !== false){
13068 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
13070 var script = document.createElement("script");
13071 script.setAttribute("src", url);
13072 script.setAttribute("type", "text/javascript");
13073 script.setAttribute("id", trans.scriptId);
13074 this.head.appendChild(script);
13076 this.trans = trans;
13078 callback.call(scope||this, null, arg, false);
13083 isLoading : function(){
13084 return this.trans ? true : false;
13088 * Abort the current server request.
13090 abort : function(){
13091 if(this.isLoading()){
13092 this.destroyTrans(this.trans);
13097 destroyTrans : function(trans, isLoaded){
13098 this.head.removeChild(document.getElementById(trans.scriptId));
13099 clearTimeout(trans.timeoutId);
13101 window[trans.cb] = undefined;
13103 delete window[trans.cb];
13106 // if hasn't been loaded, wait for load to remove it to prevent script error
13107 window[trans.cb] = function(){
13108 window[trans.cb] = undefined;
13110 delete window[trans.cb];
13117 handleResponse : function(o, trans){
13118 this.trans = false;
13119 this.destroyTrans(trans, true);
13122 result = trans.reader.readRecords(o);
13124 this.fireEvent("loadexception", this, o, trans.arg, e);
13125 trans.callback.call(trans.scope||window, null, trans.arg, false);
13128 this.fireEvent("load", this, o, trans.arg);
13129 trans.callback.call(trans.scope||window, result, trans.arg, true);
13133 handleFailure : function(trans){
13134 this.trans = false;
13135 this.destroyTrans(trans, false);
13136 this.fireEvent("loadexception", this, null, trans.arg);
13137 trans.callback.call(trans.scope||window, null, trans.arg, false);
13141 * Ext JS Library 1.1.1
13142 * Copyright(c) 2006-2007, Ext JS, LLC.
13144 * Originally Released Under LGPL - original licence link has changed is not relivant.
13147 * <script type="text/javascript">
13151 * @class Roo.data.JsonReader
13152 * @extends Roo.data.DataReader
13153 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
13154 * based on mappings in a provided Roo.data.Record constructor.
13156 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
13157 * in the reply previously.
13162 var RecordDef = Roo.data.Record.create([
13163 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
13164 {name: 'occupation'} // This field will use "occupation" as the mapping.
13166 var myReader = new Roo.data.JsonReader({
13167 totalProperty: "results", // The property which contains the total dataset size (optional)
13168 root: "rows", // The property which contains an Array of row objects
13169 id: "id" // The property within each row object that provides an ID for the record (optional)
13173 * This would consume a JSON file like this:
13175 { 'results': 2, 'rows': [
13176 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
13177 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
13180 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
13181 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
13182 * paged from the remote server.
13183 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
13184 * @cfg {String} root name of the property which contains the Array of row objects.
13185 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13186 * @cfg {Array} fields Array of field definition objects
13188 * Create a new JsonReader
13189 * @param {Object} meta Metadata configuration options
13190 * @param {Object} recordType Either an Array of field definition objects,
13191 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
13193 Roo.data.JsonReader = function(meta, recordType){
13196 // set some defaults:
13197 Roo.applyIf(meta, {
13198 totalProperty: 'total',
13199 successProperty : 'success',
13204 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13206 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
13208 readerType : 'Json',
13211 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
13212 * Used by Store query builder to append _requestMeta to params.
13215 metaFromRemote : false,
13217 * This method is only used by a DataProxy which has retrieved data from a remote server.
13218 * @param {Object} response The XHR object which contains the JSON data in its responseText.
13219 * @return {Object} data A data block which is used by an Roo.data.Store object as
13220 * a cache of Roo.data.Records.
13222 read : function(response){
13223 var json = response.responseText;
13225 var o = /* eval:var:o */ eval("("+json+")");
13227 throw {message: "JsonReader.read: Json object not found"};
13233 this.metaFromRemote = true;
13234 this.meta = o.metaData;
13235 this.recordType = Roo.data.Record.create(o.metaData.fields);
13236 this.onMetaChange(this.meta, this.recordType, o);
13238 return this.readRecords(o);
13241 // private function a store will implement
13242 onMetaChange : function(meta, recordType, o){
13249 simpleAccess: function(obj, subsc) {
13256 getJsonAccessor: function(){
13258 return function(expr) {
13260 return(re.test(expr))
13261 ? new Function("obj", "return obj." + expr)
13266 return Roo.emptyFn;
13271 * Create a data block containing Roo.data.Records from an XML document.
13272 * @param {Object} o An object which contains an Array of row objects in the property specified
13273 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
13274 * which contains the total size of the dataset.
13275 * @return {Object} data A data block which is used by an Roo.data.Store object as
13276 * a cache of Roo.data.Records.
13278 readRecords : function(o){
13280 * After any data loads, the raw JSON data is available for further custom processing.
13284 var s = this.meta, Record = this.recordType,
13285 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
13287 // Generate extraction functions for the totalProperty, the root, the id, and for each field
13289 if(s.totalProperty) {
13290 this.getTotal = this.getJsonAccessor(s.totalProperty);
13292 if(s.successProperty) {
13293 this.getSuccess = this.getJsonAccessor(s.successProperty);
13295 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
13297 var g = this.getJsonAccessor(s.id);
13298 this.getId = function(rec) {
13300 return (r === undefined || r === "") ? null : r;
13303 this.getId = function(){return null;};
13306 for(var jj = 0; jj < fl; jj++){
13308 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
13309 this.ef[jj] = this.getJsonAccessor(map);
13313 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
13314 if(s.totalProperty){
13315 var vt = parseInt(this.getTotal(o), 10);
13320 if(s.successProperty){
13321 var vs = this.getSuccess(o);
13322 if(vs === false || vs === 'false'){
13327 for(var i = 0; i < c; i++){
13330 var id = this.getId(n);
13331 for(var j = 0; j < fl; j++){
13333 var v = this.ef[j](n);
13335 Roo.log('missing convert for ' + f.name);
13339 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13341 var record = new Record(values, id);
13343 records[i] = record;
13349 totalRecords : totalRecords
13352 // used when loading children.. @see loadDataFromChildren
13353 toLoadData: function(rec)
13355 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13356 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13357 return { data : data, total : data.length };
13362 * Ext JS Library 1.1.1
13363 * Copyright(c) 2006-2007, Ext JS, LLC.
13365 * Originally Released Under LGPL - original licence link has changed is not relivant.
13368 * <script type="text/javascript">
13372 * @class Roo.data.ArrayReader
13373 * @extends Roo.data.DataReader
13374 * Data reader class to create an Array of Roo.data.Record objects from an Array.
13375 * Each element of that Array represents a row of data fields. The
13376 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13377 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13381 var RecordDef = Roo.data.Record.create([
13382 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
13383 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
13385 var myReader = new Roo.data.ArrayReader({
13386 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
13390 * This would consume an Array like this:
13392 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13396 * Create a new JsonReader
13397 * @param {Object} meta Metadata configuration options.
13398 * @param {Object|Array} recordType Either an Array of field definition objects
13400 * @cfg {Array} fields Array of field definition objects
13401 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13402 * as specified to {@link Roo.data.Record#create},
13403 * or an {@link Roo.data.Record} object
13406 * created using {@link Roo.data.Record#create}.
13408 Roo.data.ArrayReader = function(meta, recordType)
13410 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13413 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13416 * Create a data block containing Roo.data.Records from an XML document.
13417 * @param {Object} o An Array of row objects which represents the dataset.
13418 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13419 * a cache of Roo.data.Records.
13421 readRecords : function(o)
13423 var sid = this.meta ? this.meta.id : null;
13424 var recordType = this.recordType, fields = recordType.prototype.fields;
13427 for(var i = 0; i < root.length; i++){
13430 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13431 for(var j = 0, jlen = fields.length; j < jlen; j++){
13432 var f = fields.items[j];
13433 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13434 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13436 values[f.name] = v;
13438 var record = new recordType(values, id);
13440 records[records.length] = record;
13444 totalRecords : records.length
13447 // used when loading children.. @see loadDataFromChildren
13448 toLoadData: function(rec)
13450 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13451 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13462 * @class Roo.bootstrap.ComboBox
13463 * @extends Roo.bootstrap.TriggerField
13464 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13465 * @cfg {Boolean} append (true|false) default false
13466 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13467 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13468 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13469 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13470 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13471 * @cfg {Boolean} animate default true
13472 * @cfg {Boolean} emptyResultText only for touch device
13473 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13474 * @cfg {String} emptyTitle default ''
13476 * Create a new ComboBox.
13477 * @param {Object} config Configuration options
13479 Roo.bootstrap.ComboBox = function(config){
13480 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13484 * Fires when the dropdown list is expanded
13485 * @param {Roo.bootstrap.ComboBox} combo This combo box
13490 * Fires when the dropdown list is collapsed
13491 * @param {Roo.bootstrap.ComboBox} combo This combo box
13495 * @event beforeselect
13496 * Fires before a list item is selected. Return false to cancel the selection.
13497 * @param {Roo.bootstrap.ComboBox} combo This combo box
13498 * @param {Roo.data.Record} record The data record returned from the underlying store
13499 * @param {Number} index The index of the selected item in the dropdown list
13501 'beforeselect' : true,
13504 * Fires when a list item is selected
13505 * @param {Roo.bootstrap.ComboBox} combo This combo box
13506 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13507 * @param {Number} index The index of the selected item in the dropdown list
13511 * @event beforequery
13512 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13513 * The event object passed has these properties:
13514 * @param {Roo.bootstrap.ComboBox} combo This combo box
13515 * @param {String} query The query
13516 * @param {Boolean} forceAll true to force "all" query
13517 * @param {Boolean} cancel true to cancel the query
13518 * @param {Object} e The query event object
13520 'beforequery': true,
13523 * Fires when the 'add' icon is pressed (add a listener to enable add button)
13524 * @param {Roo.bootstrap.ComboBox} combo This combo box
13529 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13530 * @param {Roo.bootstrap.ComboBox} combo This combo box
13531 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13536 * Fires when the remove value from the combobox array
13537 * @param {Roo.bootstrap.ComboBox} combo This combo box
13541 * @event afterremove
13542 * Fires when the remove value from the combobox array
13543 * @param {Roo.bootstrap.ComboBox} combo This combo box
13545 'afterremove' : true,
13547 * @event specialfilter
13548 * Fires when specialfilter
13549 * @param {Roo.bootstrap.ComboBox} combo This combo box
13551 'specialfilter' : true,
13554 * Fires when tick the element
13555 * @param {Roo.bootstrap.ComboBox} combo This combo box
13559 * @event touchviewdisplay
13560 * Fires when touch view require special display (default is using displayField)
13561 * @param {Roo.bootstrap.ComboBox} combo This combo box
13562 * @param {Object} cfg set html .
13564 'touchviewdisplay' : true
13569 this.tickItems = [];
13571 this.selectedIndex = -1;
13572 if(this.mode == 'local'){
13573 if(config.queryDelay === undefined){
13574 this.queryDelay = 10;
13576 if(config.minChars === undefined){
13582 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13585 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13586 * rendering into an Roo.Editor, defaults to false)
13589 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13590 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13593 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13596 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13597 * the dropdown list (defaults to undefined, with no header element)
13601 * @cfg {String/Roo.Template} tpl The template to use to render the output
13605 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13607 listWidth: undefined,
13609 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13610 * mode = 'remote' or 'text' if mode = 'local')
13612 displayField: undefined,
13615 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13616 * mode = 'remote' or 'value' if mode = 'local').
13617 * Note: use of a valueField requires the user make a selection
13618 * in order for a value to be mapped.
13620 valueField: undefined,
13622 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13627 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13628 * field's data value (defaults to the underlying DOM element's name)
13630 hiddenName: undefined,
13632 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13636 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13638 selectedClass: 'active',
13641 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13645 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13646 * anchor positions (defaults to 'tl-bl')
13648 listAlign: 'tl-bl?',
13650 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13654 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13655 * query specified by the allQuery config option (defaults to 'query')
13657 triggerAction: 'query',
13659 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13660 * (defaults to 4, does not apply if editable = false)
13664 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13665 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13669 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13670 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13674 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13675 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13679 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13680 * when editable = true (defaults to false)
13682 selectOnFocus:false,
13684 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13686 queryParam: 'query',
13688 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13689 * when mode = 'remote' (defaults to 'Loading...')
13691 loadingText: 'Loading...',
13693 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13697 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13701 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13702 * traditional select (defaults to true)
13706 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13710 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13714 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13715 * listWidth has a higher value)
13719 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13720 * allow the user to set arbitrary text into the field (defaults to false)
13722 forceSelection:false,
13724 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13725 * if typeAhead = true (defaults to 250)
13727 typeAheadDelay : 250,
13729 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13730 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13732 valueNotFoundText : undefined,
13734 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13736 blockFocus : false,
13739 * @cfg {Boolean} disableClear Disable showing of clear button.
13741 disableClear : false,
13743 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13745 alwaysQuery : false,
13748 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13753 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13755 invalidClass : "has-warning",
13758 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13760 validClass : "has-success",
13763 * @cfg {Boolean} specialFilter (true|false) special filter default false
13765 specialFilter : false,
13768 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13770 mobileTouchView : true,
13773 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13775 useNativeIOS : false,
13778 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13780 mobile_restrict_height : false,
13782 ios_options : false,
13794 btnPosition : 'right',
13795 triggerList : true,
13796 showToggleBtn : true,
13798 emptyResultText: 'Empty',
13799 triggerText : 'Select',
13802 // element that contains real text value.. (when hidden is used..)
13804 getAutoCreate : function()
13809 * Render classic select for iso
13812 if(Roo.isIOS && this.useNativeIOS){
13813 cfg = this.getAutoCreateNativeIOS();
13821 if(Roo.isTouch && this.mobileTouchView){
13822 cfg = this.getAutoCreateTouchView();
13829 if(!this.tickable){
13830 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13835 * ComboBox with tickable selections
13838 var align = this.labelAlign || this.parentLabelAlign();
13841 cls : 'form-group roo-combobox-tickable' //input-group
13844 var btn_text_select = '';
13845 var btn_text_done = '';
13846 var btn_text_cancel = '';
13848 if (this.btn_text_show) {
13849 btn_text_select = 'Select';
13850 btn_text_done = 'Done';
13851 btn_text_cancel = 'Cancel';
13856 cls : 'tickable-buttons',
13861 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13862 //html : this.triggerText
13863 html: btn_text_select
13869 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13871 html: btn_text_done
13877 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13879 html: btn_text_cancel
13885 buttons.cn.unshift({
13887 cls: 'roo-select2-search-field-input'
13893 Roo.each(buttons.cn, function(c){
13895 c.cls += ' btn-' + _this.size;
13898 if (_this.disabled) {
13905 style : 'display: contents',
13910 cls: 'form-hidden-field'
13914 cls: 'roo-select2-choices',
13918 cls: 'roo-select2-search-field',
13929 cls: 'roo-select2-container input-group roo-select2-container-multi',
13935 // cls: 'typeahead typeahead-long dropdown-menu',
13936 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13941 if(this.hasFeedback && !this.allowBlank){
13945 cls: 'glyphicon form-control-feedback'
13948 combobox.cn.push(feedback);
13953 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13954 tooltip : 'This field is required'
13956 if (Roo.bootstrap.version == 4) {
13959 style : 'display:none'
13962 if (align ==='left' && this.fieldLabel.length) {
13964 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13971 cls : 'control-label col-form-label',
13972 html : this.fieldLabel
13984 var labelCfg = cfg.cn[1];
13985 var contentCfg = cfg.cn[2];
13988 if(this.indicatorpos == 'right'){
13994 cls : 'control-label col-form-label',
13998 html : this.fieldLabel
14014 labelCfg = cfg.cn[0];
14015 contentCfg = cfg.cn[1];
14019 if(this.labelWidth > 12){
14020 labelCfg.style = "width: " + this.labelWidth + 'px';
14023 if(this.labelWidth < 13 && this.labelmd == 0){
14024 this.labelmd = this.labelWidth;
14027 if(this.labellg > 0){
14028 labelCfg.cls += ' col-lg-' + this.labellg;
14029 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14032 if(this.labelmd > 0){
14033 labelCfg.cls += ' col-md-' + this.labelmd;
14034 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14037 if(this.labelsm > 0){
14038 labelCfg.cls += ' col-sm-' + this.labelsm;
14039 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14042 if(this.labelxs > 0){
14043 labelCfg.cls += ' col-xs-' + this.labelxs;
14044 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14048 } else if ( this.fieldLabel.length) {
14049 // Roo.log(" label");
14054 //cls : 'input-group-addon',
14055 html : this.fieldLabel
14060 if(this.indicatorpos == 'right'){
14064 //cls : 'input-group-addon',
14065 html : this.fieldLabel
14075 // Roo.log(" no label && no align");
14082 ['xs','sm','md','lg'].map(function(size){
14083 if (settings[size]) {
14084 cfg.cls += ' col-' + size + '-' + settings[size];
14092 _initEventsCalled : false,
14095 initEvents: function()
14097 if (this._initEventsCalled) { // as we call render... prevent looping...
14100 this._initEventsCalled = true;
14103 throw "can not find store for combo";
14106 this.indicator = this.indicatorEl();
14108 this.store = Roo.factory(this.store, Roo.data);
14109 this.store.parent = this;
14111 // if we are building from html. then this element is so complex, that we can not really
14112 // use the rendered HTML.
14113 // so we have to trash and replace the previous code.
14114 if (Roo.XComponent.build_from_html) {
14115 // remove this element....
14116 var e = this.el.dom, k=0;
14117 while (e ) { e = e.previousSibling; ++k;}
14122 this.rendered = false;
14124 this.render(this.parent().getChildContainer(true), k);
14127 if(Roo.isIOS && this.useNativeIOS){
14128 this.initIOSView();
14136 if(Roo.isTouch && this.mobileTouchView){
14137 this.initTouchView();
14142 this.initTickableEvents();
14146 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
14148 if(this.hiddenName){
14150 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14152 this.hiddenField.dom.value =
14153 this.hiddenValue !== undefined ? this.hiddenValue :
14154 this.value !== undefined ? this.value : '';
14156 // prevent input submission
14157 this.el.dom.removeAttribute('name');
14158 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14163 // this.el.dom.setAttribute('autocomplete', 'off');
14166 var cls = 'x-combo-list';
14168 //this.list = new Roo.Layer({
14169 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
14175 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14176 _this.list.setWidth(lw);
14179 this.list.on('mouseover', this.onViewOver, this);
14180 this.list.on('mousemove', this.onViewMove, this);
14181 this.list.on('scroll', this.onViewScroll, this);
14184 this.list.swallowEvent('mousewheel');
14185 this.assetHeight = 0;
14188 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
14189 this.assetHeight += this.header.getHeight();
14192 this.innerList = this.list.createChild({cls:cls+'-inner'});
14193 this.innerList.on('mouseover', this.onViewOver, this);
14194 this.innerList.on('mousemove', this.onViewMove, this);
14195 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14197 if(this.allowBlank && !this.pageSize && !this.disableClear){
14198 this.footer = this.list.createChild({cls:cls+'-ft'});
14199 this.pageTb = new Roo.Toolbar(this.footer);
14203 this.footer = this.list.createChild({cls:cls+'-ft'});
14204 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
14205 {pageSize: this.pageSize});
14209 if (this.pageTb && this.allowBlank && !this.disableClear) {
14211 this.pageTb.add(new Roo.Toolbar.Fill(), {
14212 cls: 'x-btn-icon x-btn-clear',
14214 handler: function()
14217 _this.clearValue();
14218 _this.onSelect(false, -1);
14223 this.assetHeight += this.footer.getHeight();
14228 this.tpl = Roo.bootstrap.version == 4 ?
14229 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
14230 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
14233 this.view = new Roo.View(this.list, this.tpl, {
14234 singleSelect:true, store: this.store, selectedClass: this.selectedClass
14236 //this.view.wrapEl.setDisplayed(false);
14237 this.view.on('click', this.onViewClick, this);
14240 this.store.on('beforeload', this.onBeforeLoad, this);
14241 this.store.on('load', this.onLoad, this);
14242 this.store.on('loadexception', this.onLoadException, this);
14244 if(this.resizable){
14245 this.resizer = new Roo.Resizable(this.list, {
14246 pinned:true, handles:'se'
14248 this.resizer.on('resize', function(r, w, h){
14249 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
14250 this.listWidth = w;
14251 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
14252 this.restrictHeight();
14254 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
14257 if(!this.editable){
14258 this.editable = true;
14259 this.setEditable(false);
14264 if (typeof(this.events.add.listeners) != 'undefined') {
14266 this.addicon = this.wrap.createChild(
14267 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
14269 this.addicon.on('click', function(e) {
14270 this.fireEvent('add', this);
14273 if (typeof(this.events.edit.listeners) != 'undefined') {
14275 this.editicon = this.wrap.createChild(
14276 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
14277 if (this.addicon) {
14278 this.editicon.setStyle('margin-left', '40px');
14280 this.editicon.on('click', function(e) {
14282 // we fire even if inothing is selected..
14283 this.fireEvent('edit', this, this.lastData );
14289 this.keyNav = new Roo.KeyNav(this.inputEl(), {
14290 "up" : function(e){
14291 this.inKeyMode = true;
14295 "down" : function(e){
14296 if(!this.isExpanded()){
14297 this.onTriggerClick();
14299 this.inKeyMode = true;
14304 "enter" : function(e){
14305 // this.onViewClick();
14309 if(this.fireEvent("specialkey", this, e)){
14310 this.onViewClick(false);
14316 "esc" : function(e){
14320 "tab" : function(e){
14323 if(this.fireEvent("specialkey", this, e)){
14324 this.onViewClick(false);
14332 doRelay : function(foo, bar, hname){
14333 if(hname == 'down' || this.scope.isExpanded()){
14334 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14343 this.queryDelay = Math.max(this.queryDelay || 10,
14344 this.mode == 'local' ? 10 : 250);
14347 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14349 if(this.typeAhead){
14350 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14352 if(this.editable !== false){
14353 this.inputEl().on("keyup", this.onKeyUp, this);
14355 if(this.forceSelection){
14356 this.inputEl().on('blur', this.doForce, this);
14360 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14361 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14365 initTickableEvents: function()
14369 if(this.hiddenName){
14371 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14373 this.hiddenField.dom.value =
14374 this.hiddenValue !== undefined ? this.hiddenValue :
14375 this.value !== undefined ? this.value : '';
14377 // prevent input submission
14378 this.el.dom.removeAttribute('name');
14379 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14384 // this.list = this.el.select('ul.dropdown-menu',true).first();
14386 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14387 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14388 if(this.triggerList){
14389 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14392 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14393 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14395 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14396 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14398 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14399 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14401 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14402 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14403 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14406 this.cancelBtn.hide();
14411 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14412 _this.list.setWidth(lw);
14415 this.list.on('mouseover', this.onViewOver, this);
14416 this.list.on('mousemove', this.onViewMove, this);
14418 this.list.on('scroll', this.onViewScroll, this);
14421 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
14422 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14425 this.view = new Roo.View(this.list, this.tpl, {
14430 selectedClass: this.selectedClass
14433 //this.view.wrapEl.setDisplayed(false);
14434 this.view.on('click', this.onViewClick, this);
14438 this.store.on('beforeload', this.onBeforeLoad, this);
14439 this.store.on('load', this.onLoad, this);
14440 this.store.on('loadexception', this.onLoadException, this);
14443 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14444 "up" : function(e){
14445 this.inKeyMode = true;
14449 "down" : function(e){
14450 this.inKeyMode = true;
14454 "enter" : function(e){
14455 if(this.fireEvent("specialkey", this, e)){
14456 this.onViewClick(false);
14462 "esc" : function(e){
14463 this.onTickableFooterButtonClick(e, false, false);
14466 "tab" : function(e){
14467 this.fireEvent("specialkey", this, e);
14469 this.onTickableFooterButtonClick(e, false, false);
14476 doRelay : function(e, fn, key){
14477 if(this.scope.isExpanded()){
14478 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14487 this.queryDelay = Math.max(this.queryDelay || 10,
14488 this.mode == 'local' ? 10 : 250);
14491 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14493 if(this.typeAhead){
14494 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14497 if(this.editable !== false){
14498 this.tickableInputEl().on("keyup", this.onKeyUp, this);
14501 this.indicator = this.indicatorEl();
14503 if(this.indicator){
14504 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14505 this.indicator.hide();
14510 onDestroy : function(){
14512 this.view.setStore(null);
14513 this.view.el.removeAllListeners();
14514 this.view.el.remove();
14515 this.view.purgeListeners();
14518 this.list.dom.innerHTML = '';
14522 this.store.un('beforeload', this.onBeforeLoad, this);
14523 this.store.un('load', this.onLoad, this);
14524 this.store.un('loadexception', this.onLoadException, this);
14526 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14530 fireKey : function(e){
14531 if(e.isNavKeyPress() && !this.list.isVisible()){
14532 this.fireEvent("specialkey", this, e);
14537 onResize: function(w, h){
14538 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14540 // if(typeof w != 'number'){
14541 // // we do not handle it!?!?
14544 // var tw = this.trigger.getWidth();
14545 // // tw += this.addicon ? this.addicon.getWidth() : 0;
14546 // // tw += this.editicon ? this.editicon.getWidth() : 0;
14548 // this.inputEl().setWidth( this.adjustWidth('input', x));
14550 // //this.trigger.setStyle('left', x+'px');
14552 // if(this.list && this.listWidth === undefined){
14553 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14554 // this.list.setWidth(lw);
14555 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14563 * Allow or prevent the user from directly editing the field text. If false is passed,
14564 * the user will only be able to select from the items defined in the dropdown list. This method
14565 * is the runtime equivalent of setting the 'editable' config option at config time.
14566 * @param {Boolean} value True to allow the user to directly edit the field text
14568 setEditable : function(value){
14569 if(value == this.editable){
14572 this.editable = value;
14574 this.inputEl().dom.setAttribute('readOnly', true);
14575 this.inputEl().on('mousedown', this.onTriggerClick, this);
14576 this.inputEl().addClass('x-combo-noedit');
14578 this.inputEl().dom.setAttribute('readOnly', false);
14579 this.inputEl().un('mousedown', this.onTriggerClick, this);
14580 this.inputEl().removeClass('x-combo-noedit');
14586 onBeforeLoad : function(combo,opts){
14587 if(!this.hasFocus){
14591 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14593 this.restrictHeight();
14594 this.selectedIndex = -1;
14598 onLoad : function(){
14600 this.hasQuery = false;
14602 if(!this.hasFocus){
14606 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14607 this.loading.hide();
14610 if(this.store.getCount() > 0){
14613 this.restrictHeight();
14614 if(this.lastQuery == this.allQuery){
14615 if(this.editable && !this.tickable){
14616 this.inputEl().dom.select();
14620 !this.selectByValue(this.value, true) &&
14623 !this.store.lastOptions ||
14624 typeof(this.store.lastOptions.add) == 'undefined' ||
14625 this.store.lastOptions.add != true
14628 this.select(0, true);
14631 if(this.autoFocus){
14634 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14635 this.taTask.delay(this.typeAheadDelay);
14639 this.onEmptyResults();
14645 onLoadException : function()
14647 this.hasQuery = false;
14649 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14650 this.loading.hide();
14653 if(this.tickable && this.editable){
14658 // only causes errors at present
14659 //Roo.log(this.store.reader.jsonData);
14660 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14662 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14668 onTypeAhead : function(){
14669 if(this.store.getCount() > 0){
14670 var r = this.store.getAt(0);
14671 var newValue = r.data[this.displayField];
14672 var len = newValue.length;
14673 var selStart = this.getRawValue().length;
14675 if(selStart != len){
14676 this.setRawValue(newValue);
14677 this.selectText(selStart, newValue.length);
14683 onSelect : function(record, index){
14685 if(this.fireEvent('beforeselect', this, record, index) !== false){
14687 this.setFromData(index > -1 ? record.data : false);
14690 this.fireEvent('select', this, record, index);
14695 * Returns the currently selected field value or empty string if no value is set.
14696 * @return {String} value The selected value
14698 getValue : function()
14700 if(Roo.isIOS && this.useNativeIOS){
14701 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14705 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14708 if(this.valueField){
14709 return typeof this.value != 'undefined' ? this.value : '';
14711 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14715 getRawValue : function()
14717 if(Roo.isIOS && this.useNativeIOS){
14718 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14721 var v = this.inputEl().getValue();
14727 * Clears any text/value currently set in the field
14729 clearValue : function(){
14731 if(this.hiddenField){
14732 this.hiddenField.dom.value = '';
14735 this.setRawValue('');
14736 this.lastSelectionText = '';
14737 this.lastData = false;
14739 var close = this.closeTriggerEl();
14750 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14751 * will be displayed in the field. If the value does not match the data value of an existing item,
14752 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14753 * Otherwise the field will be blank (although the value will still be set).
14754 * @param {String} value The value to match
14756 setValue : function(v)
14758 if(Roo.isIOS && this.useNativeIOS){
14759 this.setIOSValue(v);
14769 if(this.valueField){
14770 var r = this.findRecord(this.valueField, v);
14772 text = r.data[this.displayField];
14773 }else if(this.valueNotFoundText !== undefined){
14774 text = this.valueNotFoundText;
14777 this.lastSelectionText = text;
14778 if(this.hiddenField){
14779 this.hiddenField.dom.value = v;
14781 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14784 var close = this.closeTriggerEl();
14787 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14793 * @property {Object} the last set data for the element
14798 * Sets the value of the field based on a object which is related to the record format for the store.
14799 * @param {Object} value the value to set as. or false on reset?
14801 setFromData : function(o){
14808 var dv = ''; // display value
14809 var vv = ''; // value value..
14811 if (this.displayField) {
14812 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14814 // this is an error condition!!!
14815 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14818 if(this.valueField){
14819 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14822 var close = this.closeTriggerEl();
14825 if(dv.length || vv * 1 > 0){
14827 this.blockFocus=true;
14833 if(this.hiddenField){
14834 this.hiddenField.dom.value = vv;
14836 this.lastSelectionText = dv;
14837 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14841 // no hidden field.. - we store the value in 'value', but still display
14842 // display field!!!!
14843 this.lastSelectionText = dv;
14844 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14851 reset : function(){
14852 // overridden so that last data is reset..
14859 this.setValue(this.originalValue);
14860 //this.clearInvalid();
14861 this.lastData = false;
14863 this.view.clearSelections();
14869 findRecord : function(prop, value){
14871 if(this.store.getCount() > 0){
14872 this.store.each(function(r){
14873 if(r.data[prop] == value){
14883 getName: function()
14885 // returns hidden if it's set..
14886 if (!this.rendered) {return ''};
14887 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14891 onViewMove : function(e, t){
14892 this.inKeyMode = false;
14896 onViewOver : function(e, t){
14897 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14900 var item = this.view.findItemFromChild(t);
14903 var index = this.view.indexOf(item);
14904 this.select(index, false);
14909 onViewClick : function(view, doFocus, el, e)
14911 var index = this.view.getSelectedIndexes()[0];
14913 var r = this.store.getAt(index);
14917 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14924 Roo.each(this.tickItems, function(v,k){
14926 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14928 _this.tickItems.splice(k, 1);
14930 if(typeof(e) == 'undefined' && view == false){
14931 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14943 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14944 this.tickItems.push(r.data);
14947 if(typeof(e) == 'undefined' && view == false){
14948 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14955 this.onSelect(r, index);
14957 if(doFocus !== false && !this.blockFocus){
14958 this.inputEl().focus();
14963 restrictHeight : function(){
14964 //this.innerList.dom.style.height = '';
14965 //var inner = this.innerList.dom;
14966 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14967 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14968 //this.list.beginUpdate();
14969 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14970 this.list.alignTo(this.inputEl(), this.listAlign);
14971 this.list.alignTo(this.inputEl(), this.listAlign);
14972 //this.list.endUpdate();
14976 onEmptyResults : function(){
14978 if(this.tickable && this.editable){
14979 this.hasFocus = false;
14980 this.restrictHeight();
14988 * Returns true if the dropdown list is expanded, else false.
14990 isExpanded : function(){
14991 return this.list.isVisible();
14995 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14996 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14997 * @param {String} value The data value of the item to select
14998 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14999 * selected item if it is not currently in view (defaults to true)
15000 * @return {Boolean} True if the value matched an item in the list, else false
15002 selectByValue : function(v, scrollIntoView){
15003 if(v !== undefined && v !== null){
15004 var r = this.findRecord(this.valueField || this.displayField, v);
15006 this.select(this.store.indexOf(r), scrollIntoView);
15014 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
15015 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15016 * @param {Number} index The zero-based index of the list item to select
15017 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15018 * selected item if it is not currently in view (defaults to true)
15020 select : function(index, scrollIntoView){
15021 this.selectedIndex = index;
15022 this.view.select(index);
15023 if(scrollIntoView !== false){
15024 var el = this.view.getNode(index);
15026 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
15029 this.list.scrollChildIntoView(el, false);
15035 selectNext : function(){
15036 var ct = this.store.getCount();
15038 if(this.selectedIndex == -1){
15040 }else if(this.selectedIndex < ct-1){
15041 this.select(this.selectedIndex+1);
15047 selectPrev : function(){
15048 var ct = this.store.getCount();
15050 if(this.selectedIndex == -1){
15052 }else if(this.selectedIndex != 0){
15053 this.select(this.selectedIndex-1);
15059 onKeyUp : function(e){
15060 if(this.editable !== false && !e.isSpecialKey()){
15061 this.lastKey = e.getKey();
15062 this.dqTask.delay(this.queryDelay);
15067 validateBlur : function(){
15068 return !this.list || !this.list.isVisible();
15072 initQuery : function(){
15074 var v = this.getRawValue();
15076 if(this.tickable && this.editable){
15077 v = this.tickableInputEl().getValue();
15084 doForce : function(){
15085 if(this.inputEl().dom.value.length > 0){
15086 this.inputEl().dom.value =
15087 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
15093 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
15094 * query allowing the query action to be canceled if needed.
15095 * @param {String} query The SQL query to execute
15096 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
15097 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
15098 * saved in the current store (defaults to false)
15100 doQuery : function(q, forceAll){
15102 if(q === undefined || q === null){
15107 forceAll: forceAll,
15111 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
15116 forceAll = qe.forceAll;
15117 if(forceAll === true || (q.length >= this.minChars)){
15119 this.hasQuery = true;
15121 if(this.lastQuery != q || this.alwaysQuery){
15122 this.lastQuery = q;
15123 if(this.mode == 'local'){
15124 this.selectedIndex = -1;
15126 this.store.clearFilter();
15129 if(this.specialFilter){
15130 this.fireEvent('specialfilter', this);
15135 this.store.filter(this.displayField, q);
15138 this.store.fireEvent("datachanged", this.store);
15145 this.store.baseParams[this.queryParam] = q;
15147 var options = {params : this.getParams(q)};
15150 options.add = true;
15151 options.params.start = this.page * this.pageSize;
15154 this.store.load(options);
15157 * this code will make the page width larger, at the beginning, the list not align correctly,
15158 * we should expand the list on onLoad
15159 * so command out it
15164 this.selectedIndex = -1;
15169 this.loadNext = false;
15173 getParams : function(q){
15175 //p[this.queryParam] = q;
15179 p.limit = this.pageSize;
15185 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
15187 collapse : function(){
15188 if(!this.isExpanded()){
15194 this.hasFocus = false;
15198 this.cancelBtn.hide();
15199 this.trigger.show();
15202 this.tickableInputEl().dom.value = '';
15203 this.tickableInputEl().blur();
15208 Roo.get(document).un('mousedown', this.collapseIf, this);
15209 Roo.get(document).un('mousewheel', this.collapseIf, this);
15210 if (!this.editable) {
15211 Roo.get(document).un('keydown', this.listKeyPress, this);
15213 this.fireEvent('collapse', this);
15219 collapseIf : function(e){
15220 var in_combo = e.within(this.el);
15221 var in_list = e.within(this.list);
15222 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
15224 if (in_combo || in_list || is_list) {
15225 //e.stopPropagation();
15230 this.onTickableFooterButtonClick(e, false, false);
15238 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
15240 expand : function(){
15242 if(this.isExpanded() || !this.hasFocus){
15246 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
15247 this.list.setWidth(lw);
15253 this.restrictHeight();
15257 this.tickItems = Roo.apply([], this.item);
15260 this.cancelBtn.show();
15261 this.trigger.hide();
15264 this.tickableInputEl().focus();
15269 Roo.get(document).on('mousedown', this.collapseIf, this);
15270 Roo.get(document).on('mousewheel', this.collapseIf, this);
15271 if (!this.editable) {
15272 Roo.get(document).on('keydown', this.listKeyPress, this);
15275 this.fireEvent('expand', this);
15279 // Implements the default empty TriggerField.onTriggerClick function
15280 onTriggerClick : function(e)
15282 Roo.log('trigger click');
15284 if(this.disabled || !this.triggerList){
15289 this.loadNext = false;
15291 if(this.isExpanded()){
15293 if (!this.blockFocus) {
15294 this.inputEl().focus();
15298 this.hasFocus = true;
15299 if(this.triggerAction == 'all') {
15300 this.doQuery(this.allQuery, true);
15302 this.doQuery(this.getRawValue());
15304 if (!this.blockFocus) {
15305 this.inputEl().focus();
15310 onTickableTriggerClick : function(e)
15317 this.loadNext = false;
15318 this.hasFocus = true;
15320 if(this.triggerAction == 'all') {
15321 this.doQuery(this.allQuery, true);
15323 this.doQuery(this.getRawValue());
15327 onSearchFieldClick : function(e)
15329 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15330 this.onTickableFooterButtonClick(e, false, false);
15334 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15339 this.loadNext = false;
15340 this.hasFocus = true;
15342 if(this.triggerAction == 'all') {
15343 this.doQuery(this.allQuery, true);
15345 this.doQuery(this.getRawValue());
15349 listKeyPress : function(e)
15351 //Roo.log('listkeypress');
15352 // scroll to first matching element based on key pres..
15353 if (e.isSpecialKey()) {
15356 var k = String.fromCharCode(e.getKey()).toUpperCase();
15359 var csel = this.view.getSelectedNodes();
15360 var cselitem = false;
15362 var ix = this.view.indexOf(csel[0]);
15363 cselitem = this.store.getAt(ix);
15364 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15370 this.store.each(function(v) {
15372 // start at existing selection.
15373 if (cselitem.id == v.id) {
15379 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15380 match = this.store.indexOf(v);
15386 if (match === false) {
15387 return true; // no more action?
15390 this.view.select(match);
15391 var sn = Roo.get(this.view.getSelectedNodes()[0]);
15392 sn.scrollIntoView(sn.dom.parentNode, false);
15395 onViewScroll : function(e, t){
15397 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){
15401 this.hasQuery = true;
15403 this.loading = this.list.select('.loading', true).first();
15405 if(this.loading === null){
15406 this.list.createChild({
15408 cls: 'loading roo-select2-more-results roo-select2-active',
15409 html: 'Loading more results...'
15412 this.loading = this.list.select('.loading', true).first();
15414 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15416 this.loading.hide();
15419 this.loading.show();
15424 this.loadNext = true;
15426 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15431 addItem : function(o)
15433 var dv = ''; // display value
15435 if (this.displayField) {
15436 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15438 // this is an error condition!!!
15439 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
15446 var choice = this.choices.createChild({
15448 cls: 'roo-select2-search-choice',
15457 cls: 'roo-select2-search-choice-close fa fa-times',
15462 }, this.searchField);
15464 var close = choice.select('a.roo-select2-search-choice-close', true).first();
15466 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15474 this.inputEl().dom.value = '';
15479 onRemoveItem : function(e, _self, o)
15481 e.preventDefault();
15483 this.lastItem = Roo.apply([], this.item);
15485 var index = this.item.indexOf(o.data) * 1;
15488 Roo.log('not this item?!');
15492 this.item.splice(index, 1);
15497 this.fireEvent('remove', this, e);
15503 syncValue : function()
15505 if(!this.item.length){
15512 Roo.each(this.item, function(i){
15513 if(_this.valueField){
15514 value.push(i[_this.valueField]);
15521 this.value = value.join(',');
15523 if(this.hiddenField){
15524 this.hiddenField.dom.value = this.value;
15527 this.store.fireEvent("datachanged", this.store);
15532 clearItem : function()
15534 if(!this.multiple){
15540 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15548 if(this.tickable && !Roo.isTouch){
15549 this.view.refresh();
15553 inputEl: function ()
15555 if(Roo.isIOS && this.useNativeIOS){
15556 return this.el.select('select.roo-ios-select', true).first();
15559 if(Roo.isTouch && this.mobileTouchView){
15560 return this.el.select('input.form-control',true).first();
15564 return this.searchField;
15567 return this.el.select('input.form-control',true).first();
15570 onTickableFooterButtonClick : function(e, btn, el)
15572 e.preventDefault();
15574 this.lastItem = Roo.apply([], this.item);
15576 if(btn && btn.name == 'cancel'){
15577 this.tickItems = Roo.apply([], this.item);
15586 Roo.each(this.tickItems, function(o){
15594 validate : function()
15596 if(this.getVisibilityEl().hasClass('hidden')){
15600 var v = this.getRawValue();
15603 v = this.getValue();
15606 if(this.disabled || this.allowBlank || v.length){
15611 this.markInvalid();
15615 tickableInputEl : function()
15617 if(!this.tickable || !this.editable){
15618 return this.inputEl();
15621 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15625 getAutoCreateTouchView : function()
15630 cls: 'form-group' //input-group
15636 type : this.inputType,
15637 cls : 'form-control x-combo-noedit',
15638 autocomplete: 'new-password',
15639 placeholder : this.placeholder || '',
15644 input.name = this.name;
15648 input.cls += ' input-' + this.size;
15651 if (this.disabled) {
15652 input.disabled = true;
15663 inputblock.cls += ' input-group';
15665 inputblock.cn.unshift({
15667 cls : 'input-group-addon input-group-prepend input-group-text',
15672 if(this.removable && !this.multiple){
15673 inputblock.cls += ' roo-removable';
15675 inputblock.cn.push({
15678 cls : 'roo-combo-removable-btn close'
15682 if(this.hasFeedback && !this.allowBlank){
15684 inputblock.cls += ' has-feedback';
15686 inputblock.cn.push({
15688 cls: 'glyphicon form-control-feedback'
15695 inputblock.cls += (this.before) ? '' : ' input-group';
15697 inputblock.cn.push({
15699 cls : 'input-group-addon input-group-append input-group-text',
15705 var ibwrap = inputblock;
15710 cls: 'roo-select2-choices',
15714 cls: 'roo-select2-search-field',
15727 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15732 cls: 'form-hidden-field'
15738 if(!this.multiple && this.showToggleBtn){
15744 if (this.caret != false) {
15747 cls: 'fa fa-' + this.caret
15754 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15756 Roo.bootstrap.version == 3 ? caret : '',
15759 cls: 'combobox-clear',
15773 combobox.cls += ' roo-select2-container-multi';
15776 var align = this.labelAlign || this.parentLabelAlign();
15778 if (align ==='left' && this.fieldLabel.length) {
15783 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15784 tooltip : 'This field is required'
15788 cls : 'control-label col-form-label',
15789 html : this.fieldLabel
15800 var labelCfg = cfg.cn[1];
15801 var contentCfg = cfg.cn[2];
15804 if(this.indicatorpos == 'right'){
15809 cls : 'control-label col-form-label',
15813 html : this.fieldLabel
15817 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15818 tooltip : 'This field is required'
15831 labelCfg = cfg.cn[0];
15832 contentCfg = cfg.cn[1];
15837 if(this.labelWidth > 12){
15838 labelCfg.style = "width: " + this.labelWidth + 'px';
15841 if(this.labelWidth < 13 && this.labelmd == 0){
15842 this.labelmd = this.labelWidth;
15845 if(this.labellg > 0){
15846 labelCfg.cls += ' col-lg-' + this.labellg;
15847 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15850 if(this.labelmd > 0){
15851 labelCfg.cls += ' col-md-' + this.labelmd;
15852 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15855 if(this.labelsm > 0){
15856 labelCfg.cls += ' col-sm-' + this.labelsm;
15857 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15860 if(this.labelxs > 0){
15861 labelCfg.cls += ' col-xs-' + this.labelxs;
15862 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15866 } else if ( this.fieldLabel.length) {
15870 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15871 tooltip : 'This field is required'
15875 cls : 'control-label',
15876 html : this.fieldLabel
15887 if(this.indicatorpos == 'right'){
15891 cls : 'control-label',
15892 html : this.fieldLabel,
15896 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15897 tooltip : 'This field is required'
15914 var settings = this;
15916 ['xs','sm','md','lg'].map(function(size){
15917 if (settings[size]) {
15918 cfg.cls += ' col-' + size + '-' + settings[size];
15925 initTouchView : function()
15927 this.renderTouchView();
15929 this.touchViewEl.on('scroll', function(){
15930 this.el.dom.scrollTop = 0;
15933 this.originalValue = this.getValue();
15935 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15937 this.inputEl().on("click", this.showTouchView, this);
15938 if (this.triggerEl) {
15939 this.triggerEl.on("click", this.showTouchView, this);
15943 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15944 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15946 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15948 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15949 this.store.on('load', this.onTouchViewLoad, this);
15950 this.store.on('loadexception', this.onTouchViewLoadException, this);
15952 if(this.hiddenName){
15954 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15956 this.hiddenField.dom.value =
15957 this.hiddenValue !== undefined ? this.hiddenValue :
15958 this.value !== undefined ? this.value : '';
15960 this.el.dom.removeAttribute('name');
15961 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15965 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15966 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15969 if(this.removable && !this.multiple){
15970 var close = this.closeTriggerEl();
15972 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15973 close.on('click', this.removeBtnClick, this, close);
15977 * fix the bug in Safari iOS8
15979 this.inputEl().on("focus", function(e){
15980 document.activeElement.blur();
15983 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15990 renderTouchView : function()
15992 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15993 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15995 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15996 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15998 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15999 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16000 this.touchViewBodyEl.setStyle('overflow', 'auto');
16002 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
16003 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16005 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
16006 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16010 showTouchView : function()
16016 this.touchViewHeaderEl.hide();
16018 if(this.modalTitle.length){
16019 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
16020 this.touchViewHeaderEl.show();
16023 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
16024 this.touchViewEl.show();
16026 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
16028 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
16029 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
16031 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16033 if(this.modalTitle.length){
16034 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16037 this.touchViewBodyEl.setHeight(bodyHeight);
16041 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
16043 this.touchViewEl.addClass('in');
16046 if(this._touchViewMask){
16047 Roo.get(document.body).addClass("x-body-masked");
16048 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
16049 this._touchViewMask.setStyle('z-index', 10000);
16050 this._touchViewMask.addClass('show');
16053 this.doTouchViewQuery();
16057 hideTouchView : function()
16059 this.touchViewEl.removeClass('in');
16063 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
16065 this.touchViewEl.setStyle('display', 'none');
16068 if(this._touchViewMask){
16069 this._touchViewMask.removeClass('show');
16070 Roo.get(document.body).removeClass("x-body-masked");
16074 setTouchViewValue : function()
16081 Roo.each(this.tickItems, function(o){
16086 this.hideTouchView();
16089 doTouchViewQuery : function()
16098 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
16102 if(!this.alwaysQuery || this.mode == 'local'){
16103 this.onTouchViewLoad();
16110 onTouchViewBeforeLoad : function(combo,opts)
16116 onTouchViewLoad : function()
16118 if(this.store.getCount() < 1){
16119 this.onTouchViewEmptyResults();
16123 this.clearTouchView();
16125 var rawValue = this.getRawValue();
16127 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
16129 this.tickItems = [];
16131 this.store.data.each(function(d, rowIndex){
16132 var row = this.touchViewListGroup.createChild(template);
16134 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
16135 row.addClass(d.data.cls);
16138 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16141 html : d.data[this.displayField]
16144 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
16145 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
16148 row.removeClass('selected');
16149 if(!this.multiple && this.valueField &&
16150 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
16153 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16154 row.addClass('selected');
16157 if(this.multiple && this.valueField &&
16158 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
16162 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16163 this.tickItems.push(d.data);
16166 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
16170 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
16172 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16174 if(this.modalTitle.length){
16175 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16178 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
16180 if(this.mobile_restrict_height && listHeight < bodyHeight){
16181 this.touchViewBodyEl.setHeight(listHeight);
16186 if(firstChecked && listHeight > bodyHeight){
16187 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
16192 onTouchViewLoadException : function()
16194 this.hideTouchView();
16197 onTouchViewEmptyResults : function()
16199 this.clearTouchView();
16201 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
16203 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
16207 clearTouchView : function()
16209 this.touchViewListGroup.dom.innerHTML = '';
16212 onTouchViewClick : function(e, el, o)
16214 e.preventDefault();
16217 var rowIndex = o.rowIndex;
16219 var r = this.store.getAt(rowIndex);
16221 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
16223 if(!this.multiple){
16224 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
16225 c.dom.removeAttribute('checked');
16228 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16230 this.setFromData(r.data);
16232 var close = this.closeTriggerEl();
16238 this.hideTouchView();
16240 this.fireEvent('select', this, r, rowIndex);
16245 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
16246 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
16247 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
16251 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16252 this.addItem(r.data);
16253 this.tickItems.push(r.data);
16257 getAutoCreateNativeIOS : function()
16260 cls: 'form-group' //input-group,
16265 cls : 'roo-ios-select'
16269 combobox.name = this.name;
16272 if (this.disabled) {
16273 combobox.disabled = true;
16276 var settings = this;
16278 ['xs','sm','md','lg'].map(function(size){
16279 if (settings[size]) {
16280 cfg.cls += ' col-' + size + '-' + settings[size];
16290 initIOSView : function()
16292 this.store.on('load', this.onIOSViewLoad, this);
16297 onIOSViewLoad : function()
16299 if(this.store.getCount() < 1){
16303 this.clearIOSView();
16305 if(this.allowBlank) {
16307 var default_text = '-- SELECT --';
16309 if(this.placeholder.length){
16310 default_text = this.placeholder;
16313 if(this.emptyTitle.length){
16314 default_text += ' - ' + this.emptyTitle + ' -';
16317 var opt = this.inputEl().createChild({
16320 html : default_text
16324 o[this.valueField] = 0;
16325 o[this.displayField] = default_text;
16327 this.ios_options.push({
16334 this.store.data.each(function(d, rowIndex){
16338 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16339 html = d.data[this.displayField];
16344 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16345 value = d.data[this.valueField];
16354 if(this.value == d.data[this.valueField]){
16355 option['selected'] = true;
16358 var opt = this.inputEl().createChild(option);
16360 this.ios_options.push({
16367 this.inputEl().on('change', function(){
16368 this.fireEvent('select', this);
16373 clearIOSView: function()
16375 this.inputEl().dom.innerHTML = '';
16377 this.ios_options = [];
16380 setIOSValue: function(v)
16384 if(!this.ios_options){
16388 Roo.each(this.ios_options, function(opts){
16390 opts.el.dom.removeAttribute('selected');
16392 if(opts.data[this.valueField] != v){
16396 opts.el.dom.setAttribute('selected', true);
16402 * @cfg {Boolean} grow
16406 * @cfg {Number} growMin
16410 * @cfg {Number} growMax
16419 Roo.apply(Roo.bootstrap.ComboBox, {
16423 cls: 'modal-header',
16445 cls: 'list-group-item',
16449 cls: 'roo-combobox-list-group-item-value'
16453 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16467 listItemCheckbox : {
16469 cls: 'list-group-item',
16473 cls: 'roo-combobox-list-group-item-value'
16477 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16493 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16498 cls: 'modal-footer',
16506 cls: 'col-xs-6 text-left',
16509 cls: 'btn btn-danger roo-touch-view-cancel',
16515 cls: 'col-xs-6 text-right',
16518 cls: 'btn btn-success roo-touch-view-ok',
16529 Roo.apply(Roo.bootstrap.ComboBox, {
16531 touchViewTemplate : {
16533 cls: 'modal fade roo-combobox-touch-view',
16537 cls: 'modal-dialog',
16538 style : 'position:fixed', // we have to fix position....
16542 cls: 'modal-content',
16544 Roo.bootstrap.ComboBox.header,
16545 Roo.bootstrap.ComboBox.body,
16546 Roo.bootstrap.ComboBox.footer
16555 * Ext JS Library 1.1.1
16556 * Copyright(c) 2006-2007, Ext JS, LLC.
16558 * Originally Released Under LGPL - original licence link has changed is not relivant.
16561 * <script type="text/javascript">
16566 * @extends Roo.util.Observable
16567 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16568 * This class also supports single and multi selection modes. <br>
16569 * Create a data model bound view:
16571 var store = new Roo.data.Store(...);
16573 var view = new Roo.View({
16575 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16577 singleSelect: true,
16578 selectedClass: "ydataview-selected",
16582 // listen for node click?
16583 view.on("click", function(vw, index, node, e){
16584 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16588 dataModel.load("foobar.xml");
16590 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16592 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16593 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16595 * Note: old style constructor is still suported (container, template, config)
16598 * Create a new View
16599 * @param {Object} config The config object
16602 Roo.View = function(config, depreciated_tpl, depreciated_config){
16604 this.parent = false;
16606 if (typeof(depreciated_tpl) == 'undefined') {
16607 // new way.. - universal constructor.
16608 Roo.apply(this, config);
16609 this.el = Roo.get(this.el);
16612 this.el = Roo.get(config);
16613 this.tpl = depreciated_tpl;
16614 Roo.apply(this, depreciated_config);
16616 this.wrapEl = this.el.wrap().wrap();
16617 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16620 if(typeof(this.tpl) == "string"){
16621 this.tpl = new Roo.Template(this.tpl);
16623 // support xtype ctors..
16624 this.tpl = new Roo.factory(this.tpl, Roo);
16628 this.tpl.compile();
16633 * @event beforeclick
16634 * Fires before a click is processed. Returns false to cancel the default action.
16635 * @param {Roo.View} this
16636 * @param {Number} index The index of the target node
16637 * @param {HTMLElement} node The target node
16638 * @param {Roo.EventObject} e The raw event object
16640 "beforeclick" : true,
16643 * Fires when a template node is clicked.
16644 * @param {Roo.View} this
16645 * @param {Number} index The index of the target node
16646 * @param {HTMLElement} node The target node
16647 * @param {Roo.EventObject} e The raw event object
16652 * Fires when a template node is double clicked.
16653 * @param {Roo.View} this
16654 * @param {Number} index The index of the target node
16655 * @param {HTMLElement} node The target node
16656 * @param {Roo.EventObject} e The raw event object
16660 * @event contextmenu
16661 * Fires when a template node is right clicked.
16662 * @param {Roo.View} this
16663 * @param {Number} index The index of the target node
16664 * @param {HTMLElement} node The target node
16665 * @param {Roo.EventObject} e The raw event object
16667 "contextmenu" : true,
16669 * @event selectionchange
16670 * Fires when the selected nodes change.
16671 * @param {Roo.View} this
16672 * @param {Array} selections Array of the selected nodes
16674 "selectionchange" : true,
16677 * @event beforeselect
16678 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16679 * @param {Roo.View} this
16680 * @param {HTMLElement} node The node to be selected
16681 * @param {Array} selections Array of currently selected nodes
16683 "beforeselect" : true,
16685 * @event preparedata
16686 * Fires on every row to render, to allow you to change the data.
16687 * @param {Roo.View} this
16688 * @param {Object} data to be rendered (change this)
16690 "preparedata" : true
16698 "click": this.onClick,
16699 "dblclick": this.onDblClick,
16700 "contextmenu": this.onContextMenu,
16704 this.selections = [];
16706 this.cmp = new Roo.CompositeElementLite([]);
16708 this.store = Roo.factory(this.store, Roo.data);
16709 this.setStore(this.store, true);
16712 if ( this.footer && this.footer.xtype) {
16714 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16716 this.footer.dataSource = this.store;
16717 this.footer.container = fctr;
16718 this.footer = Roo.factory(this.footer, Roo);
16719 fctr.insertFirst(this.el);
16721 // this is a bit insane - as the paging toolbar seems to detach the el..
16722 // dom.parentNode.parentNode.parentNode
16723 // they get detached?
16727 Roo.View.superclass.constructor.call(this);
16732 Roo.extend(Roo.View, Roo.util.Observable, {
16735 * @cfg {Roo.data.Store} store Data store to load data from.
16740 * @cfg {String|Roo.Element} el The container element.
16745 * @cfg {String|Roo.Template} tpl The template used by this View
16749 * @cfg {String} dataName the named area of the template to use as the data area
16750 * Works with domtemplates roo-name="name"
16754 * @cfg {String} selectedClass The css class to add to selected nodes
16756 selectedClass : "x-view-selected",
16758 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16763 * @cfg {String} text to display on mask (default Loading)
16767 * @cfg {Boolean} multiSelect Allow multiple selection
16769 multiSelect : false,
16771 * @cfg {Boolean} singleSelect Allow single selection
16773 singleSelect: false,
16776 * @cfg {Boolean} toggleSelect - selecting
16778 toggleSelect : false,
16781 * @cfg {Boolean} tickable - selecting
16786 * Returns the element this view is bound to.
16787 * @return {Roo.Element}
16789 getEl : function(){
16790 return this.wrapEl;
16796 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16798 refresh : function(){
16799 //Roo.log('refresh');
16802 // if we are using something like 'domtemplate', then
16803 // the what gets used is:
16804 // t.applySubtemplate(NAME, data, wrapping data..)
16805 // the outer template then get' applied with
16806 // the store 'extra data'
16807 // and the body get's added to the
16808 // roo-name="data" node?
16809 // <span class='roo-tpl-{name}'></span> ?????
16813 this.clearSelections();
16814 this.el.update("");
16816 var records = this.store.getRange();
16817 if(records.length < 1) {
16819 // is this valid?? = should it render a template??
16821 this.el.update(this.emptyText);
16825 if (this.dataName) {
16826 this.el.update(t.apply(this.store.meta)); //????
16827 el = this.el.child('.roo-tpl-' + this.dataName);
16830 for(var i = 0, len = records.length; i < len; i++){
16831 var data = this.prepareData(records[i].data, i, records[i]);
16832 this.fireEvent("preparedata", this, data, i, records[i]);
16834 var d = Roo.apply({}, data);
16837 Roo.apply(d, {'roo-id' : Roo.id()});
16841 Roo.each(this.parent.item, function(item){
16842 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16845 Roo.apply(d, {'roo-data-checked' : 'checked'});
16849 html[html.length] = Roo.util.Format.trim(
16851 t.applySubtemplate(this.dataName, d, this.store.meta) :
16858 el.update(html.join(""));
16859 this.nodes = el.dom.childNodes;
16860 this.updateIndexes(0);
16865 * Function to override to reformat the data that is sent to
16866 * the template for each node.
16867 * DEPRICATED - use the preparedata event handler.
16868 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16869 * a JSON object for an UpdateManager bound view).
16871 prepareData : function(data, index, record)
16873 this.fireEvent("preparedata", this, data, index, record);
16877 onUpdate : function(ds, record){
16878 // Roo.log('on update');
16879 this.clearSelections();
16880 var index = this.store.indexOf(record);
16881 var n = this.nodes[index];
16882 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16883 n.parentNode.removeChild(n);
16884 this.updateIndexes(index, index);
16890 onAdd : function(ds, records, index)
16892 //Roo.log(['on Add', ds, records, index] );
16893 this.clearSelections();
16894 if(this.nodes.length == 0){
16898 var n = this.nodes[index];
16899 for(var i = 0, len = records.length; i < len; i++){
16900 var d = this.prepareData(records[i].data, i, records[i]);
16902 this.tpl.insertBefore(n, d);
16905 this.tpl.append(this.el, d);
16908 this.updateIndexes(index);
16911 onRemove : function(ds, record, index){
16912 // Roo.log('onRemove');
16913 this.clearSelections();
16914 var el = this.dataName ?
16915 this.el.child('.roo-tpl-' + this.dataName) :
16918 el.dom.removeChild(this.nodes[index]);
16919 this.updateIndexes(index);
16923 * Refresh an individual node.
16924 * @param {Number} index
16926 refreshNode : function(index){
16927 this.onUpdate(this.store, this.store.getAt(index));
16930 updateIndexes : function(startIndex, endIndex){
16931 var ns = this.nodes;
16932 startIndex = startIndex || 0;
16933 endIndex = endIndex || ns.length - 1;
16934 for(var i = startIndex; i <= endIndex; i++){
16935 ns[i].nodeIndex = i;
16940 * Changes the data store this view uses and refresh the view.
16941 * @param {Store} store
16943 setStore : function(store, initial){
16944 if(!initial && this.store){
16945 this.store.un("datachanged", this.refresh);
16946 this.store.un("add", this.onAdd);
16947 this.store.un("remove", this.onRemove);
16948 this.store.un("update", this.onUpdate);
16949 this.store.un("clear", this.refresh);
16950 this.store.un("beforeload", this.onBeforeLoad);
16951 this.store.un("load", this.onLoad);
16952 this.store.un("loadexception", this.onLoad);
16956 store.on("datachanged", this.refresh, this);
16957 store.on("add", this.onAdd, this);
16958 store.on("remove", this.onRemove, this);
16959 store.on("update", this.onUpdate, this);
16960 store.on("clear", this.refresh, this);
16961 store.on("beforeload", this.onBeforeLoad, this);
16962 store.on("load", this.onLoad, this);
16963 store.on("loadexception", this.onLoad, this);
16971 * onbeforeLoad - masks the loading area.
16974 onBeforeLoad : function(store,opts)
16976 //Roo.log('onBeforeLoad');
16978 this.el.update("");
16980 this.el.mask(this.mask ? this.mask : "Loading" );
16982 onLoad : function ()
16989 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16990 * @param {HTMLElement} node
16991 * @return {HTMLElement} The template node
16993 findItemFromChild : function(node){
16994 var el = this.dataName ?
16995 this.el.child('.roo-tpl-' + this.dataName,true) :
16998 if(!node || node.parentNode == el){
17001 var p = node.parentNode;
17002 while(p && p != el){
17003 if(p.parentNode == el){
17012 onClick : function(e){
17013 var item = this.findItemFromChild(e.getTarget());
17015 var index = this.indexOf(item);
17016 if(this.onItemClick(item, index, e) !== false){
17017 this.fireEvent("click", this, index, item, e);
17020 this.clearSelections();
17025 onContextMenu : function(e){
17026 var item = this.findItemFromChild(e.getTarget());
17028 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
17033 onDblClick : function(e){
17034 var item = this.findItemFromChild(e.getTarget());
17036 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
17040 onItemClick : function(item, index, e)
17042 if(this.fireEvent("beforeclick", this, index, item, e) === false){
17045 if (this.toggleSelect) {
17046 var m = this.isSelected(item) ? 'unselect' : 'select';
17049 _t[m](item, true, false);
17052 if(this.multiSelect || this.singleSelect){
17053 if(this.multiSelect && e.shiftKey && this.lastSelection){
17054 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
17056 this.select(item, this.multiSelect && e.ctrlKey);
17057 this.lastSelection = item;
17060 if(!this.tickable){
17061 e.preventDefault();
17069 * Get the number of selected nodes.
17072 getSelectionCount : function(){
17073 return this.selections.length;
17077 * Get the currently selected nodes.
17078 * @return {Array} An array of HTMLElements
17080 getSelectedNodes : function(){
17081 return this.selections;
17085 * Get the indexes of the selected nodes.
17088 getSelectedIndexes : function(){
17089 var indexes = [], s = this.selections;
17090 for(var i = 0, len = s.length; i < len; i++){
17091 indexes.push(s[i].nodeIndex);
17097 * Clear all selections
17098 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
17100 clearSelections : function(suppressEvent){
17101 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
17102 this.cmp.elements = this.selections;
17103 this.cmp.removeClass(this.selectedClass);
17104 this.selections = [];
17105 if(!suppressEvent){
17106 this.fireEvent("selectionchange", this, this.selections);
17112 * Returns true if the passed node is selected
17113 * @param {HTMLElement/Number} node The node or node index
17114 * @return {Boolean}
17116 isSelected : function(node){
17117 var s = this.selections;
17121 node = this.getNode(node);
17122 return s.indexOf(node) !== -1;
17127 * @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
17128 * @param {Boolean} keepExisting (optional) true to keep existing selections
17129 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17131 select : function(nodeInfo, keepExisting, suppressEvent){
17132 if(nodeInfo instanceof Array){
17134 this.clearSelections(true);
17136 for(var i = 0, len = nodeInfo.length; i < len; i++){
17137 this.select(nodeInfo[i], true, true);
17141 var node = this.getNode(nodeInfo);
17142 if(!node || this.isSelected(node)){
17143 return; // already selected.
17146 this.clearSelections(true);
17149 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
17150 Roo.fly(node).addClass(this.selectedClass);
17151 this.selections.push(node);
17152 if(!suppressEvent){
17153 this.fireEvent("selectionchange", this, this.selections);
17161 * @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
17162 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
17163 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17165 unselect : function(nodeInfo, keepExisting, suppressEvent)
17167 if(nodeInfo instanceof Array){
17168 Roo.each(this.selections, function(s) {
17169 this.unselect(s, nodeInfo);
17173 var node = this.getNode(nodeInfo);
17174 if(!node || !this.isSelected(node)){
17175 //Roo.log("not selected");
17176 return; // not selected.
17180 Roo.each(this.selections, function(s) {
17182 Roo.fly(node).removeClass(this.selectedClass);
17189 this.selections= ns;
17190 this.fireEvent("selectionchange", this, this.selections);
17194 * Gets a template node.
17195 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17196 * @return {HTMLElement} The node or null if it wasn't found
17198 getNode : function(nodeInfo){
17199 if(typeof nodeInfo == "string"){
17200 return document.getElementById(nodeInfo);
17201 }else if(typeof nodeInfo == "number"){
17202 return this.nodes[nodeInfo];
17208 * Gets a range template nodes.
17209 * @param {Number} startIndex
17210 * @param {Number} endIndex
17211 * @return {Array} An array of nodes
17213 getNodes : function(start, end){
17214 var ns = this.nodes;
17215 start = start || 0;
17216 end = typeof end == "undefined" ? ns.length - 1 : end;
17219 for(var i = start; i <= end; i++){
17223 for(var i = start; i >= end; i--){
17231 * Finds the index of the passed node
17232 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17233 * @return {Number} The index of the node or -1
17235 indexOf : function(node){
17236 node = this.getNode(node);
17237 if(typeof node.nodeIndex == "number"){
17238 return node.nodeIndex;
17240 var ns = this.nodes;
17241 for(var i = 0, len = ns.length; i < len; i++){
17252 * based on jquery fullcalendar
17256 Roo.bootstrap = Roo.bootstrap || {};
17258 * @class Roo.bootstrap.Calendar
17259 * @extends Roo.bootstrap.Component
17260 * Bootstrap Calendar class
17261 * @cfg {Boolean} loadMask (true|false) default false
17262 * @cfg {Object} header generate the user specific header of the calendar, default false
17265 * Create a new Container
17266 * @param {Object} config The config object
17271 Roo.bootstrap.Calendar = function(config){
17272 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
17276 * Fires when a date is selected
17277 * @param {DatePicker} this
17278 * @param {Date} date The selected date
17282 * @event monthchange
17283 * Fires when the displayed month changes
17284 * @param {DatePicker} this
17285 * @param {Date} date The selected month
17287 'monthchange': true,
17289 * @event evententer
17290 * Fires when mouse over an event
17291 * @param {Calendar} this
17292 * @param {event} Event
17294 'evententer': true,
17296 * @event eventleave
17297 * Fires when the mouse leaves an
17298 * @param {Calendar} this
17301 'eventleave': true,
17303 * @event eventclick
17304 * Fires when the mouse click an
17305 * @param {Calendar} this
17314 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
17317 * @cfg {Number} startDay
17318 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17326 getAutoCreate : function(){
17329 var fc_button = function(name, corner, style, content ) {
17330 return Roo.apply({},{
17332 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
17334 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17337 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17348 style : 'width:100%',
17355 cls : 'fc-header-left',
17357 fc_button('prev', 'left', 'arrow', '‹' ),
17358 fc_button('next', 'right', 'arrow', '›' ),
17359 { tag: 'span', cls: 'fc-header-space' },
17360 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
17368 cls : 'fc-header-center',
17372 cls: 'fc-header-title',
17375 html : 'month / year'
17383 cls : 'fc-header-right',
17385 /* fc_button('month', 'left', '', 'month' ),
17386 fc_button('week', '', '', 'week' ),
17387 fc_button('day', 'right', '', 'day' )
17399 header = this.header;
17402 var cal_heads = function() {
17404 // fixme - handle this.
17406 for (var i =0; i < Date.dayNames.length; i++) {
17407 var d = Date.dayNames[i];
17410 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17411 html : d.substring(0,3)
17415 ret[0].cls += ' fc-first';
17416 ret[6].cls += ' fc-last';
17419 var cal_cell = function(n) {
17422 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17427 cls: 'fc-day-number',
17431 cls: 'fc-day-content',
17435 style: 'position: relative;' // height: 17px;
17447 var cal_rows = function() {
17450 for (var r = 0; r < 6; r++) {
17457 for (var i =0; i < Date.dayNames.length; i++) {
17458 var d = Date.dayNames[i];
17459 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17462 row.cn[0].cls+=' fc-first';
17463 row.cn[0].cn[0].style = 'min-height:90px';
17464 row.cn[6].cls+=' fc-last';
17468 ret[0].cls += ' fc-first';
17469 ret[4].cls += ' fc-prev-last';
17470 ret[5].cls += ' fc-last';
17477 cls: 'fc-border-separate',
17478 style : 'width:100%',
17486 cls : 'fc-first fc-last',
17504 cls : 'fc-content',
17505 style : "position: relative;",
17508 cls : 'fc-view fc-view-month fc-grid',
17509 style : 'position: relative',
17510 unselectable : 'on',
17513 cls : 'fc-event-container',
17514 style : 'position:absolute;z-index:8;top:0;left:0;'
17532 initEvents : function()
17535 throw "can not find store for calendar";
17541 style: "text-align:center",
17545 style: "background-color:white;width:50%;margin:250 auto",
17549 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17560 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17562 var size = this.el.select('.fc-content', true).first().getSize();
17563 this.maskEl.setSize(size.width, size.height);
17564 this.maskEl.enableDisplayMode("block");
17565 if(!this.loadMask){
17566 this.maskEl.hide();
17569 this.store = Roo.factory(this.store, Roo.data);
17570 this.store.on('load', this.onLoad, this);
17571 this.store.on('beforeload', this.onBeforeLoad, this);
17575 this.cells = this.el.select('.fc-day',true);
17576 //Roo.log(this.cells);
17577 this.textNodes = this.el.query('.fc-day-number');
17578 this.cells.addClassOnOver('fc-state-hover');
17580 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17581 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17582 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17583 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17585 this.on('monthchange', this.onMonthChange, this);
17587 this.update(new Date().clearTime());
17590 resize : function() {
17591 var sz = this.el.getSize();
17593 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17594 this.el.select('.fc-day-content div',true).setHeight(34);
17599 showPrevMonth : function(e){
17600 this.update(this.activeDate.add("mo", -1));
17602 showToday : function(e){
17603 this.update(new Date().clearTime());
17606 showNextMonth : function(e){
17607 this.update(this.activeDate.add("mo", 1));
17611 showPrevYear : function(){
17612 this.update(this.activeDate.add("y", -1));
17616 showNextYear : function(){
17617 this.update(this.activeDate.add("y", 1));
17622 update : function(date)
17624 var vd = this.activeDate;
17625 this.activeDate = date;
17626 // if(vd && this.el){
17627 // var t = date.getTime();
17628 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17629 // Roo.log('using add remove');
17631 // this.fireEvent('monthchange', this, date);
17633 // this.cells.removeClass("fc-state-highlight");
17634 // this.cells.each(function(c){
17635 // if(c.dateValue == t){
17636 // c.addClass("fc-state-highlight");
17637 // setTimeout(function(){
17638 // try{c.dom.firstChild.focus();}catch(e){}
17648 var days = date.getDaysInMonth();
17650 var firstOfMonth = date.getFirstDateOfMonth();
17651 var startingPos = firstOfMonth.getDay()-this.startDay;
17653 if(startingPos < this.startDay){
17657 var pm = date.add(Date.MONTH, -1);
17658 var prevStart = pm.getDaysInMonth()-startingPos;
17660 this.cells = this.el.select('.fc-day',true);
17661 this.textNodes = this.el.query('.fc-day-number');
17662 this.cells.addClassOnOver('fc-state-hover');
17664 var cells = this.cells.elements;
17665 var textEls = this.textNodes;
17667 Roo.each(cells, function(cell){
17668 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17671 days += startingPos;
17673 // convert everything to numbers so it's fast
17674 var day = 86400000;
17675 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17678 //Roo.log(prevStart);
17680 var today = new Date().clearTime().getTime();
17681 var sel = date.clearTime().getTime();
17682 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17683 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17684 var ddMatch = this.disabledDatesRE;
17685 var ddText = this.disabledDatesText;
17686 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17687 var ddaysText = this.disabledDaysText;
17688 var format = this.format;
17690 var setCellClass = function(cal, cell){
17694 //Roo.log('set Cell Class');
17696 var t = d.getTime();
17700 cell.dateValue = t;
17702 cell.className += " fc-today";
17703 cell.className += " fc-state-highlight";
17704 cell.title = cal.todayText;
17707 // disable highlight in other month..
17708 //cell.className += " fc-state-highlight";
17713 cell.className = " fc-state-disabled";
17714 cell.title = cal.minText;
17718 cell.className = " fc-state-disabled";
17719 cell.title = cal.maxText;
17723 if(ddays.indexOf(d.getDay()) != -1){
17724 cell.title = ddaysText;
17725 cell.className = " fc-state-disabled";
17728 if(ddMatch && format){
17729 var fvalue = d.dateFormat(format);
17730 if(ddMatch.test(fvalue)){
17731 cell.title = ddText.replace("%0", fvalue);
17732 cell.className = " fc-state-disabled";
17736 if (!cell.initialClassName) {
17737 cell.initialClassName = cell.dom.className;
17740 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17745 for(; i < startingPos; i++) {
17746 textEls[i].innerHTML = (++prevStart);
17747 d.setDate(d.getDate()+1);
17749 cells[i].className = "fc-past fc-other-month";
17750 setCellClass(this, cells[i]);
17755 for(; i < days; i++){
17756 intDay = i - startingPos + 1;
17757 textEls[i].innerHTML = (intDay);
17758 d.setDate(d.getDate()+1);
17760 cells[i].className = ''; // "x-date-active";
17761 setCellClass(this, cells[i]);
17765 for(; i < 42; i++) {
17766 textEls[i].innerHTML = (++extraDays);
17767 d.setDate(d.getDate()+1);
17769 cells[i].className = "fc-future fc-other-month";
17770 setCellClass(this, cells[i]);
17773 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17775 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17777 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17778 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17780 if(totalRows != 6){
17781 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17782 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17785 this.fireEvent('monthchange', this, date);
17789 if(!this.internalRender){
17790 var main = this.el.dom.firstChild;
17791 var w = main.offsetWidth;
17792 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17793 Roo.fly(main).setWidth(w);
17794 this.internalRender = true;
17795 // opera does not respect the auto grow header center column
17796 // then, after it gets a width opera refuses to recalculate
17797 // without a second pass
17798 if(Roo.isOpera && !this.secondPass){
17799 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17800 this.secondPass = true;
17801 this.update.defer(10, this, [date]);
17808 findCell : function(dt) {
17809 dt = dt.clearTime().getTime();
17811 this.cells.each(function(c){
17812 //Roo.log("check " +c.dateValue + '?=' + dt);
17813 if(c.dateValue == dt){
17823 findCells : function(ev) {
17824 var s = ev.start.clone().clearTime().getTime();
17826 var e= ev.end.clone().clearTime().getTime();
17829 this.cells.each(function(c){
17830 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17832 if(c.dateValue > e){
17835 if(c.dateValue < s){
17844 // findBestRow: function(cells)
17848 // for (var i =0 ; i < cells.length;i++) {
17849 // ret = Math.max(cells[i].rows || 0,ret);
17856 addItem : function(ev)
17858 // look for vertical location slot in
17859 var cells = this.findCells(ev);
17861 // ev.row = this.findBestRow(cells);
17863 // work out the location.
17867 for(var i =0; i < cells.length; i++) {
17869 cells[i].row = cells[0].row;
17872 cells[i].row = cells[i].row + 1;
17882 if (crow.start.getY() == cells[i].getY()) {
17884 crow.end = cells[i];
17901 cells[0].events.push(ev);
17903 this.calevents.push(ev);
17906 clearEvents: function() {
17908 if(!this.calevents){
17912 Roo.each(this.cells.elements, function(c){
17918 Roo.each(this.calevents, function(e) {
17919 Roo.each(e.els, function(el) {
17920 el.un('mouseenter' ,this.onEventEnter, this);
17921 el.un('mouseleave' ,this.onEventLeave, this);
17926 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17932 renderEvents: function()
17936 this.cells.each(function(c) {
17945 if(c.row != c.events.length){
17946 r = 4 - (4 - (c.row - c.events.length));
17949 c.events = ev.slice(0, r);
17950 c.more = ev.slice(r);
17952 if(c.more.length && c.more.length == 1){
17953 c.events.push(c.more.pop());
17956 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17960 this.cells.each(function(c) {
17962 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17965 for (var e = 0; e < c.events.length; e++){
17966 var ev = c.events[e];
17967 var rows = ev.rows;
17969 for(var i = 0; i < rows.length; i++) {
17971 // how many rows should it span..
17974 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17975 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17977 unselectable : "on",
17980 cls: 'fc-event-inner',
17984 // cls: 'fc-event-time',
17985 // html : cells.length > 1 ? '' : ev.time
17989 cls: 'fc-event-title',
17990 html : String.format('{0}', ev.title)
17997 cls: 'ui-resizable-handle ui-resizable-e',
17998 html : '  '
18005 cfg.cls += ' fc-event-start';
18007 if ((i+1) == rows.length) {
18008 cfg.cls += ' fc-event-end';
18011 var ctr = _this.el.select('.fc-event-container',true).first();
18012 var cg = ctr.createChild(cfg);
18014 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
18015 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
18017 var r = (c.more.length) ? 1 : 0;
18018 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
18019 cg.setWidth(ebox.right - sbox.x -2);
18021 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
18022 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
18023 cg.on('click', _this.onEventClick, _this, ev);
18034 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
18035 style : 'position: absolute',
18036 unselectable : "on",
18039 cls: 'fc-event-inner',
18043 cls: 'fc-event-title',
18051 cls: 'ui-resizable-handle ui-resizable-e',
18052 html : '  '
18058 var ctr = _this.el.select('.fc-event-container',true).first();
18059 var cg = ctr.createChild(cfg);
18061 var sbox = c.select('.fc-day-content',true).first().getBox();
18062 var ebox = c.select('.fc-day-content',true).first().getBox();
18064 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
18065 cg.setWidth(ebox.right - sbox.x -2);
18067 cg.on('click', _this.onMoreEventClick, _this, c.more);
18077 onEventEnter: function (e, el,event,d) {
18078 this.fireEvent('evententer', this, el, event);
18081 onEventLeave: function (e, el,event,d) {
18082 this.fireEvent('eventleave', this, el, event);
18085 onEventClick: function (e, el,event,d) {
18086 this.fireEvent('eventclick', this, el, event);
18089 onMonthChange: function () {
18093 onMoreEventClick: function(e, el, more)
18097 this.calpopover.placement = 'right';
18098 this.calpopover.setTitle('More');
18100 this.calpopover.setContent('');
18102 var ctr = this.calpopover.el.select('.popover-content', true).first();
18104 Roo.each(more, function(m){
18106 cls : 'fc-event-hori fc-event-draggable',
18109 var cg = ctr.createChild(cfg);
18111 cg.on('click', _this.onEventClick, _this, m);
18114 this.calpopover.show(el);
18119 onLoad: function ()
18121 this.calevents = [];
18124 if(this.store.getCount() > 0){
18125 this.store.data.each(function(d){
18128 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
18129 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
18130 time : d.data.start_time,
18131 title : d.data.title,
18132 description : d.data.description,
18133 venue : d.data.venue
18138 this.renderEvents();
18140 if(this.calevents.length && this.loadMask){
18141 this.maskEl.hide();
18145 onBeforeLoad: function()
18147 this.clearEvents();
18149 this.maskEl.show();
18163 * @class Roo.bootstrap.Popover
18164 * @extends Roo.bootstrap.Component
18165 * Bootstrap Popover class
18166 * @cfg {String} html contents of the popover (or false to use children..)
18167 * @cfg {String} title of popover (or false to hide)
18168 * @cfg {String} placement how it is placed
18169 * @cfg {String} trigger click || hover (or false to trigger manually)
18170 * @cfg {String} over what (parent or false to trigger manually.)
18171 * @cfg {Number} delay - delay before showing
18174 * Create a new Popover
18175 * @param {Object} config The config object
18178 Roo.bootstrap.Popover = function(config){
18179 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
18185 * After the popover show
18187 * @param {Roo.bootstrap.Popover} this
18192 * After the popover hide
18194 * @param {Roo.bootstrap.Popover} this
18200 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
18202 title: 'Fill in a title',
18205 placement : 'right',
18206 trigger : 'hover', // hover
18212 can_build_overlaid : false,
18214 getChildContainer : function()
18216 return this.el.select('.popover-content',true).first();
18219 getAutoCreate : function(){
18222 cls : 'popover roo-dynamic',
18223 style: 'display:block',
18229 cls : 'popover-inner',
18233 cls: 'popover-title popover-header',
18237 cls : 'popover-content popover-body',
18248 setTitle: function(str)
18251 this.el.select('.popover-title',true).first().dom.innerHTML = str;
18253 setContent: function(str)
18256 this.el.select('.popover-content',true).first().dom.innerHTML = str;
18258 // as it get's added to the bottom of the page.
18259 onRender : function(ct, position)
18261 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18263 var cfg = Roo.apply({}, this.getAutoCreate());
18267 cfg.cls += ' ' + this.cls;
18270 cfg.style = this.style;
18272 //Roo.log("adding to ");
18273 this.el = Roo.get(document.body).createChild(cfg, position);
18274 // Roo.log(this.el);
18279 initEvents : function()
18281 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
18282 this.el.enableDisplayMode('block');
18284 if (this.over === false) {
18287 if (this.triggers === false) {
18290 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18291 var triggers = this.trigger ? this.trigger.split(' ') : [];
18292 Roo.each(triggers, function(trigger) {
18294 if (trigger == 'click') {
18295 on_el.on('click', this.toggle, this);
18296 } else if (trigger != 'manual') {
18297 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
18298 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
18300 on_el.on(eventIn ,this.enter, this);
18301 on_el.on(eventOut, this.leave, this);
18312 toggle : function () {
18313 this.hoverState == 'in' ? this.leave() : this.enter();
18316 enter : function () {
18318 clearTimeout(this.timeout);
18320 this.hoverState = 'in';
18322 if (!this.delay || !this.delay.show) {
18327 this.timeout = setTimeout(function () {
18328 if (_t.hoverState == 'in') {
18331 }, this.delay.show)
18334 leave : function() {
18335 clearTimeout(this.timeout);
18337 this.hoverState = 'out';
18339 if (!this.delay || !this.delay.hide) {
18344 this.timeout = setTimeout(function () {
18345 if (_t.hoverState == 'out') {
18348 }, this.delay.hide)
18351 show : function (on_el)
18354 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18358 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18359 if (this.html !== false) {
18360 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18362 this.el.removeClass([
18363 'fade','top','bottom', 'left', 'right','in',
18364 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18366 if (!this.title.length) {
18367 this.el.select('.popover-title',true).hide();
18370 var placement = typeof this.placement == 'function' ?
18371 this.placement.call(this, this.el, on_el) :
18374 var autoToken = /\s?auto?\s?/i;
18375 var autoPlace = autoToken.test(placement);
18377 placement = placement.replace(autoToken, '') || 'top';
18381 //this.el.setXY([0,0]);
18383 this.el.dom.style.display='block';
18384 this.el.addClass(placement);
18386 //this.el.appendTo(on_el);
18388 var p = this.getPosition();
18389 var box = this.el.getBox();
18394 var align = Roo.bootstrap.Popover.alignment[placement];
18397 this.el.alignTo(on_el, align[0],align[1]);
18398 //var arrow = this.el.select('.arrow',true).first();
18399 //arrow.set(align[2],
18401 this.el.addClass('in');
18404 if (this.el.hasClass('fade')) {
18408 this.hoverState = 'in';
18410 this.fireEvent('show', this);
18415 this.el.setXY([0,0]);
18416 this.el.removeClass('in');
18418 this.hoverState = null;
18420 this.fireEvent('hide', this);
18425 Roo.bootstrap.Popover.alignment = {
18426 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18427 'right' : ['l-r', [10,0], 'left bs-popover-left'],
18428 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18429 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18440 * @class Roo.bootstrap.Progress
18441 * @extends Roo.bootstrap.Component
18442 * Bootstrap Progress class
18443 * @cfg {Boolean} striped striped of the progress bar
18444 * @cfg {Boolean} active animated of the progress bar
18448 * Create a new Progress
18449 * @param {Object} config The config object
18452 Roo.bootstrap.Progress = function(config){
18453 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18456 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
18461 getAutoCreate : function(){
18469 cfg.cls += ' progress-striped';
18473 cfg.cls += ' active';
18492 * @class Roo.bootstrap.ProgressBar
18493 * @extends Roo.bootstrap.Component
18494 * Bootstrap ProgressBar class
18495 * @cfg {Number} aria_valuenow aria-value now
18496 * @cfg {Number} aria_valuemin aria-value min
18497 * @cfg {Number} aria_valuemax aria-value max
18498 * @cfg {String} label label for the progress bar
18499 * @cfg {String} panel (success | info | warning | danger )
18500 * @cfg {String} role role of the progress bar
18501 * @cfg {String} sr_only text
18505 * Create a new ProgressBar
18506 * @param {Object} config The config object
18509 Roo.bootstrap.ProgressBar = function(config){
18510 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18513 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
18517 aria_valuemax : 100,
18523 getAutoCreate : function()
18528 cls: 'progress-bar',
18529 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18541 cfg.role = this.role;
18544 if(this.aria_valuenow){
18545 cfg['aria-valuenow'] = this.aria_valuenow;
18548 if(this.aria_valuemin){
18549 cfg['aria-valuemin'] = this.aria_valuemin;
18552 if(this.aria_valuemax){
18553 cfg['aria-valuemax'] = this.aria_valuemax;
18556 if(this.label && !this.sr_only){
18557 cfg.html = this.label;
18561 cfg.cls += ' progress-bar-' + this.panel;
18567 update : function(aria_valuenow)
18569 this.aria_valuenow = aria_valuenow;
18571 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18586 * @class Roo.bootstrap.TabGroup
18587 * @extends Roo.bootstrap.Column
18588 * Bootstrap Column class
18589 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18590 * @cfg {Boolean} carousel true to make the group behave like a carousel
18591 * @cfg {Boolean} bullets show bullets for the panels
18592 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18593 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18594 * @cfg {Boolean} showarrow (true|false) show arrow default true
18597 * Create a new TabGroup
18598 * @param {Object} config The config object
18601 Roo.bootstrap.TabGroup = function(config){
18602 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18604 this.navId = Roo.id();
18607 Roo.bootstrap.TabGroup.register(this);
18611 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18614 transition : false,
18619 slideOnTouch : false,
18622 getAutoCreate : function()
18624 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18626 cfg.cls += ' tab-content';
18628 if (this.carousel) {
18629 cfg.cls += ' carousel slide';
18632 cls : 'carousel-inner',
18636 if(this.bullets && !Roo.isTouch){
18639 cls : 'carousel-bullets',
18643 if(this.bullets_cls){
18644 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18651 cfg.cn[0].cn.push(bullets);
18654 if(this.showarrow){
18655 cfg.cn[0].cn.push({
18657 class : 'carousel-arrow',
18661 class : 'carousel-prev',
18665 class : 'fa fa-chevron-left'
18671 class : 'carousel-next',
18675 class : 'fa fa-chevron-right'
18688 initEvents: function()
18690 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18691 // this.el.on("touchstart", this.onTouchStart, this);
18694 if(this.autoslide){
18697 this.slideFn = window.setInterval(function() {
18698 _this.showPanelNext();
18702 if(this.showarrow){
18703 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18704 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18710 // onTouchStart : function(e, el, o)
18712 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18716 // this.showPanelNext();
18720 getChildContainer : function()
18722 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18726 * register a Navigation item
18727 * @param {Roo.bootstrap.NavItem} the navitem to add
18729 register : function(item)
18731 this.tabs.push( item);
18732 item.navId = this.navId; // not really needed..
18737 getActivePanel : function()
18740 Roo.each(this.tabs, function(t) {
18750 getPanelByName : function(n)
18753 Roo.each(this.tabs, function(t) {
18754 if (t.tabId == n) {
18762 indexOfPanel : function(p)
18765 Roo.each(this.tabs, function(t,i) {
18766 if (t.tabId == p.tabId) {
18775 * show a specific panel
18776 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18777 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18779 showPanel : function (pan)
18781 if(this.transition || typeof(pan) == 'undefined'){
18782 Roo.log("waiting for the transitionend");
18786 if (typeof(pan) == 'number') {
18787 pan = this.tabs[pan];
18790 if (typeof(pan) == 'string') {
18791 pan = this.getPanelByName(pan);
18794 var cur = this.getActivePanel();
18797 Roo.log('pan or acitve pan is undefined');
18801 if (pan.tabId == this.getActivePanel().tabId) {
18805 if (false === cur.fireEvent('beforedeactivate')) {
18809 if(this.bullets > 0 && !Roo.isTouch){
18810 this.setActiveBullet(this.indexOfPanel(pan));
18813 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18815 //class="carousel-item carousel-item-next carousel-item-left"
18817 this.transition = true;
18818 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18819 var lr = dir == 'next' ? 'left' : 'right';
18820 pan.el.addClass(dir); // or prev
18821 pan.el.addClass('carousel-item-' + dir); // or prev
18822 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18823 cur.el.addClass(lr); // or right
18824 pan.el.addClass(lr);
18825 cur.el.addClass('carousel-item-' +lr); // or right
18826 pan.el.addClass('carousel-item-' +lr);
18830 cur.el.on('transitionend', function() {
18831 Roo.log("trans end?");
18833 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18834 pan.setActive(true);
18836 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18837 cur.setActive(false);
18839 _this.transition = false;
18841 }, this, { single: true } );
18846 cur.setActive(false);
18847 pan.setActive(true);
18852 showPanelNext : function()
18854 var i = this.indexOfPanel(this.getActivePanel());
18856 if (i >= this.tabs.length - 1 && !this.autoslide) {
18860 if (i >= this.tabs.length - 1 && this.autoslide) {
18864 this.showPanel(this.tabs[i+1]);
18867 showPanelPrev : function()
18869 var i = this.indexOfPanel(this.getActivePanel());
18871 if (i < 1 && !this.autoslide) {
18875 if (i < 1 && this.autoslide) {
18876 i = this.tabs.length;
18879 this.showPanel(this.tabs[i-1]);
18883 addBullet: function()
18885 if(!this.bullets || Roo.isTouch){
18888 var ctr = this.el.select('.carousel-bullets',true).first();
18889 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18890 var bullet = ctr.createChild({
18891 cls : 'bullet bullet-' + i
18892 },ctr.dom.lastChild);
18897 bullet.on('click', (function(e, el, o, ii, t){
18899 e.preventDefault();
18901 this.showPanel(ii);
18903 if(this.autoslide && this.slideFn){
18904 clearInterval(this.slideFn);
18905 this.slideFn = window.setInterval(function() {
18906 _this.showPanelNext();
18910 }).createDelegate(this, [i, bullet], true));
18915 setActiveBullet : function(i)
18921 Roo.each(this.el.select('.bullet', true).elements, function(el){
18922 el.removeClass('selected');
18925 var bullet = this.el.select('.bullet-' + i, true).first();
18931 bullet.addClass('selected');
18942 Roo.apply(Roo.bootstrap.TabGroup, {
18946 * register a Navigation Group
18947 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18949 register : function(navgrp)
18951 this.groups[navgrp.navId] = navgrp;
18955 * fetch a Navigation Group based on the navigation ID
18956 * if one does not exist , it will get created.
18957 * @param {string} the navgroup to add
18958 * @returns {Roo.bootstrap.NavGroup} the navgroup
18960 get: function(navId) {
18961 if (typeof(this.groups[navId]) == 'undefined') {
18962 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18964 return this.groups[navId] ;
18979 * @class Roo.bootstrap.TabPanel
18980 * @extends Roo.bootstrap.Component
18981 * Bootstrap TabPanel class
18982 * @cfg {Boolean} active panel active
18983 * @cfg {String} html panel content
18984 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18985 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18986 * @cfg {String} href click to link..
18990 * Create a new TabPanel
18991 * @param {Object} config The config object
18994 Roo.bootstrap.TabPanel = function(config){
18995 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18999 * Fires when the active status changes
19000 * @param {Roo.bootstrap.TabPanel} this
19001 * @param {Boolean} state the new state
19006 * @event beforedeactivate
19007 * Fires before a tab is de-activated - can be used to do validation on a form.
19008 * @param {Roo.bootstrap.TabPanel} this
19009 * @return {Boolean} false if there is an error
19012 'beforedeactivate': true
19015 this.tabId = this.tabId || Roo.id();
19019 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
19027 getAutoCreate : function(){
19032 // item is needed for carousel - not sure if it has any effect otherwise
19033 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
19034 html: this.html || ''
19038 cfg.cls += ' active';
19042 cfg.tabId = this.tabId;
19050 initEvents: function()
19052 var p = this.parent();
19054 this.navId = this.navId || p.navId;
19056 if (typeof(this.navId) != 'undefined') {
19057 // not really needed.. but just in case.. parent should be a NavGroup.
19058 var tg = Roo.bootstrap.TabGroup.get(this.navId);
19062 var i = tg.tabs.length - 1;
19064 if(this.active && tg.bullets > 0 && i < tg.bullets){
19065 tg.setActiveBullet(i);
19069 this.el.on('click', this.onClick, this);
19072 this.el.on("touchstart", this.onTouchStart, this);
19073 this.el.on("touchmove", this.onTouchMove, this);
19074 this.el.on("touchend", this.onTouchEnd, this);
19079 onRender : function(ct, position)
19081 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
19084 setActive : function(state)
19086 Roo.log("panel - set active " + this.tabId + "=" + state);
19088 this.active = state;
19090 this.el.removeClass('active');
19092 } else if (!this.el.hasClass('active')) {
19093 this.el.addClass('active');
19096 this.fireEvent('changed', this, state);
19099 onClick : function(e)
19101 e.preventDefault();
19103 if(!this.href.length){
19107 window.location.href = this.href;
19116 onTouchStart : function(e)
19118 this.swiping = false;
19120 this.startX = e.browserEvent.touches[0].clientX;
19121 this.startY = e.browserEvent.touches[0].clientY;
19124 onTouchMove : function(e)
19126 this.swiping = true;
19128 this.endX = e.browserEvent.touches[0].clientX;
19129 this.endY = e.browserEvent.touches[0].clientY;
19132 onTouchEnd : function(e)
19139 var tabGroup = this.parent();
19141 if(this.endX > this.startX){ // swiping right
19142 tabGroup.showPanelPrev();
19146 if(this.startX > this.endX){ // swiping left
19147 tabGroup.showPanelNext();
19166 * @class Roo.bootstrap.DateField
19167 * @extends Roo.bootstrap.Input
19168 * Bootstrap DateField class
19169 * @cfg {Number} weekStart default 0
19170 * @cfg {String} viewMode default empty, (months|years)
19171 * @cfg {String} minViewMode default empty, (months|years)
19172 * @cfg {Number} startDate default -Infinity
19173 * @cfg {Number} endDate default Infinity
19174 * @cfg {Boolean} todayHighlight default false
19175 * @cfg {Boolean} todayBtn default false
19176 * @cfg {Boolean} calendarWeeks default false
19177 * @cfg {Object} daysOfWeekDisabled default empty
19178 * @cfg {Boolean} singleMode default false (true | false)
19180 * @cfg {Boolean} keyboardNavigation default true
19181 * @cfg {String} language default en
19184 * Create a new DateField
19185 * @param {Object} config The config object
19188 Roo.bootstrap.DateField = function(config){
19189 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
19193 * Fires when this field show.
19194 * @param {Roo.bootstrap.DateField} this
19195 * @param {Mixed} date The date value
19200 * Fires when this field hide.
19201 * @param {Roo.bootstrap.DateField} this
19202 * @param {Mixed} date The date value
19207 * Fires when select a date.
19208 * @param {Roo.bootstrap.DateField} this
19209 * @param {Mixed} date The date value
19213 * @event beforeselect
19214 * Fires when before select a date.
19215 * @param {Roo.bootstrap.DateField} this
19216 * @param {Mixed} date The date value
19218 beforeselect : true
19222 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
19225 * @cfg {String} format
19226 * The default date format string which can be overriden for localization support. The format must be
19227 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
19231 * @cfg {String} altFormats
19232 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
19233 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
19235 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
19243 todayHighlight : false,
19249 keyboardNavigation: true,
19251 calendarWeeks: false,
19253 startDate: -Infinity,
19257 daysOfWeekDisabled: [],
19261 singleMode : false,
19263 UTCDate: function()
19265 return new Date(Date.UTC.apply(Date, arguments));
19268 UTCToday: function()
19270 var today = new Date();
19271 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
19274 getDate: function() {
19275 var d = this.getUTCDate();
19276 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
19279 getUTCDate: function() {
19283 setDate: function(d) {
19284 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
19287 setUTCDate: function(d) {
19289 this.setValue(this.formatDate(this.date));
19292 onRender: function(ct, position)
19295 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
19297 this.language = this.language || 'en';
19298 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
19299 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
19301 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
19302 this.format = this.format || 'm/d/y';
19303 this.isInline = false;
19304 this.isInput = true;
19305 this.component = this.el.select('.add-on', true).first() || false;
19306 this.component = (this.component && this.component.length === 0) ? false : this.component;
19307 this.hasInput = this.component && this.inputEl().length;
19309 if (typeof(this.minViewMode === 'string')) {
19310 switch (this.minViewMode) {
19312 this.minViewMode = 1;
19315 this.minViewMode = 2;
19318 this.minViewMode = 0;
19323 if (typeof(this.viewMode === 'string')) {
19324 switch (this.viewMode) {
19337 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19339 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19341 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19343 this.picker().on('mousedown', this.onMousedown, this);
19344 this.picker().on('click', this.onClick, this);
19346 this.picker().addClass('datepicker-dropdown');
19348 this.startViewMode = this.viewMode;
19350 if(this.singleMode){
19351 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19352 v.setVisibilityMode(Roo.Element.DISPLAY);
19356 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19357 v.setStyle('width', '189px');
19361 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19362 if(!this.calendarWeeks){
19367 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19368 v.attr('colspan', function(i, val){
19369 return parseInt(val) + 1;
19374 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19376 this.setStartDate(this.startDate);
19377 this.setEndDate(this.endDate);
19379 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19386 if(this.isInline) {
19391 picker : function()
19393 return this.pickerEl;
19394 // return this.el.select('.datepicker', true).first();
19397 fillDow: function()
19399 var dowCnt = this.weekStart;
19408 if(this.calendarWeeks){
19416 while (dowCnt < this.weekStart + 7) {
19420 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19424 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19427 fillMonths: function()
19430 var months = this.picker().select('>.datepicker-months td', true).first();
19432 months.dom.innerHTML = '';
19438 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19441 months.createChild(month);
19448 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;
19450 if (this.date < this.startDate) {
19451 this.viewDate = new Date(this.startDate);
19452 } else if (this.date > this.endDate) {
19453 this.viewDate = new Date(this.endDate);
19455 this.viewDate = new Date(this.date);
19463 var d = new Date(this.viewDate),
19464 year = d.getUTCFullYear(),
19465 month = d.getUTCMonth(),
19466 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19467 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19468 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19469 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19470 currentDate = this.date && this.date.valueOf(),
19471 today = this.UTCToday();
19473 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19475 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19477 // this.picker.select('>tfoot th.today').
19478 // .text(dates[this.language].today)
19479 // .toggle(this.todayBtn !== false);
19481 this.updateNavArrows();
19484 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19486 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19488 prevMonth.setUTCDate(day);
19490 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19492 var nextMonth = new Date(prevMonth);
19494 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19496 nextMonth = nextMonth.valueOf();
19498 var fillMonths = false;
19500 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19502 while(prevMonth.valueOf() <= nextMonth) {
19505 if (prevMonth.getUTCDay() === this.weekStart) {
19507 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19515 if(this.calendarWeeks){
19516 // ISO 8601: First week contains first thursday.
19517 // ISO also states week starts on Monday, but we can be more abstract here.
19519 // Start of current week: based on weekstart/current date
19520 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19521 // Thursday of this week
19522 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19523 // First Thursday of year, year from thursday
19524 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19525 // Calendar week: ms between thursdays, div ms per day, div 7 days
19526 calWeek = (th - yth) / 864e5 / 7 + 1;
19528 fillMonths.cn.push({
19536 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19538 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19541 if (this.todayHighlight &&
19542 prevMonth.getUTCFullYear() == today.getFullYear() &&
19543 prevMonth.getUTCMonth() == today.getMonth() &&
19544 prevMonth.getUTCDate() == today.getDate()) {
19545 clsName += ' today';
19548 if (currentDate && prevMonth.valueOf() === currentDate) {
19549 clsName += ' active';
19552 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19553 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19554 clsName += ' disabled';
19557 fillMonths.cn.push({
19559 cls: 'day ' + clsName,
19560 html: prevMonth.getDate()
19563 prevMonth.setDate(prevMonth.getDate()+1);
19566 var currentYear = this.date && this.date.getUTCFullYear();
19567 var currentMonth = this.date && this.date.getUTCMonth();
19569 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19571 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19572 v.removeClass('active');
19574 if(currentYear === year && k === currentMonth){
19575 v.addClass('active');
19578 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19579 v.addClass('disabled');
19585 year = parseInt(year/10, 10) * 10;
19587 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19589 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19592 for (var i = -1; i < 11; i++) {
19593 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19595 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19603 showMode: function(dir)
19606 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19609 Roo.each(this.picker().select('>div',true).elements, function(v){
19610 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19613 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19618 if(this.isInline) {
19622 this.picker().removeClass(['bottom', 'top']);
19624 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19626 * place to the top of element!
19630 this.picker().addClass('top');
19631 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19636 this.picker().addClass('bottom');
19638 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19641 parseDate : function(value)
19643 if(!value || value instanceof Date){
19646 var v = Date.parseDate(value, this.format);
19647 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19648 v = Date.parseDate(value, 'Y-m-d');
19650 if(!v && this.altFormats){
19651 if(!this.altFormatsArray){
19652 this.altFormatsArray = this.altFormats.split("|");
19654 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19655 v = Date.parseDate(value, this.altFormatsArray[i]);
19661 formatDate : function(date, fmt)
19663 return (!date || !(date instanceof Date)) ?
19664 date : date.dateFormat(fmt || this.format);
19667 onFocus : function()
19669 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19673 onBlur : function()
19675 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19677 var d = this.inputEl().getValue();
19684 showPopup : function()
19686 this.picker().show();
19690 this.fireEvent('showpopup', this, this.date);
19693 hidePopup : function()
19695 if(this.isInline) {
19698 this.picker().hide();
19699 this.viewMode = this.startViewMode;
19702 this.fireEvent('hidepopup', this, this.date);
19706 onMousedown: function(e)
19708 e.stopPropagation();
19709 e.preventDefault();
19714 Roo.bootstrap.DateField.superclass.keyup.call(this);
19718 setValue: function(v)
19720 if(this.fireEvent('beforeselect', this, v) !== false){
19721 var d = new Date(this.parseDate(v) ).clearTime();
19723 if(isNaN(d.getTime())){
19724 this.date = this.viewDate = '';
19725 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19729 v = this.formatDate(d);
19731 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19733 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19737 this.fireEvent('select', this, this.date);
19741 getValue: function()
19743 return this.formatDate(this.date);
19746 fireKey: function(e)
19748 if (!this.picker().isVisible()){
19749 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19755 var dateChanged = false,
19757 newDate, newViewDate;
19762 e.preventDefault();
19766 if (!this.keyboardNavigation) {
19769 dir = e.keyCode == 37 ? -1 : 1;
19772 newDate = this.moveYear(this.date, dir);
19773 newViewDate = this.moveYear(this.viewDate, dir);
19774 } else if (e.shiftKey){
19775 newDate = this.moveMonth(this.date, dir);
19776 newViewDate = this.moveMonth(this.viewDate, dir);
19778 newDate = new Date(this.date);
19779 newDate.setUTCDate(this.date.getUTCDate() + dir);
19780 newViewDate = new Date(this.viewDate);
19781 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19783 if (this.dateWithinRange(newDate)){
19784 this.date = newDate;
19785 this.viewDate = newViewDate;
19786 this.setValue(this.formatDate(this.date));
19788 e.preventDefault();
19789 dateChanged = true;
19794 if (!this.keyboardNavigation) {
19797 dir = e.keyCode == 38 ? -1 : 1;
19799 newDate = this.moveYear(this.date, dir);
19800 newViewDate = this.moveYear(this.viewDate, dir);
19801 } else if (e.shiftKey){
19802 newDate = this.moveMonth(this.date, dir);
19803 newViewDate = this.moveMonth(this.viewDate, dir);
19805 newDate = new Date(this.date);
19806 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19807 newViewDate = new Date(this.viewDate);
19808 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19810 if (this.dateWithinRange(newDate)){
19811 this.date = newDate;
19812 this.viewDate = newViewDate;
19813 this.setValue(this.formatDate(this.date));
19815 e.preventDefault();
19816 dateChanged = true;
19820 this.setValue(this.formatDate(this.date));
19822 e.preventDefault();
19825 this.setValue(this.formatDate(this.date));
19839 onClick: function(e)
19841 e.stopPropagation();
19842 e.preventDefault();
19844 var target = e.getTarget();
19846 if(target.nodeName.toLowerCase() === 'i'){
19847 target = Roo.get(target).dom.parentNode;
19850 var nodeName = target.nodeName;
19851 var className = target.className;
19852 var html = target.innerHTML;
19853 //Roo.log(nodeName);
19855 switch(nodeName.toLowerCase()) {
19857 switch(className) {
19863 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19864 switch(this.viewMode){
19866 this.viewDate = this.moveMonth(this.viewDate, dir);
19870 this.viewDate = this.moveYear(this.viewDate, dir);
19876 var date = new Date();
19877 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19879 this.setValue(this.formatDate(this.date));
19886 if (className.indexOf('disabled') < 0) {
19887 this.viewDate.setUTCDate(1);
19888 if (className.indexOf('month') > -1) {
19889 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19891 var year = parseInt(html, 10) || 0;
19892 this.viewDate.setUTCFullYear(year);
19896 if(this.singleMode){
19897 this.setValue(this.formatDate(this.viewDate));
19908 //Roo.log(className);
19909 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19910 var day = parseInt(html, 10) || 1;
19911 var year = this.viewDate.getUTCFullYear(),
19912 month = this.viewDate.getUTCMonth();
19914 if (className.indexOf('old') > -1) {
19921 } else if (className.indexOf('new') > -1) {
19929 //Roo.log([year,month,day]);
19930 this.date = this.UTCDate(year, month, day,0,0,0,0);
19931 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19933 //Roo.log(this.formatDate(this.date));
19934 this.setValue(this.formatDate(this.date));
19941 setStartDate: function(startDate)
19943 this.startDate = startDate || -Infinity;
19944 if (this.startDate !== -Infinity) {
19945 this.startDate = this.parseDate(this.startDate);
19948 this.updateNavArrows();
19951 setEndDate: function(endDate)
19953 this.endDate = endDate || Infinity;
19954 if (this.endDate !== Infinity) {
19955 this.endDate = this.parseDate(this.endDate);
19958 this.updateNavArrows();
19961 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19963 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19964 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19965 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19967 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19968 return parseInt(d, 10);
19971 this.updateNavArrows();
19974 updateNavArrows: function()
19976 if(this.singleMode){
19980 var d = new Date(this.viewDate),
19981 year = d.getUTCFullYear(),
19982 month = d.getUTCMonth();
19984 Roo.each(this.picker().select('.prev', true).elements, function(v){
19986 switch (this.viewMode) {
19989 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19995 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
20002 Roo.each(this.picker().select('.next', true).elements, function(v){
20004 switch (this.viewMode) {
20007 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
20013 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
20021 moveMonth: function(date, dir)
20026 var new_date = new Date(date.valueOf()),
20027 day = new_date.getUTCDate(),
20028 month = new_date.getUTCMonth(),
20029 mag = Math.abs(dir),
20031 dir = dir > 0 ? 1 : -1;
20034 // If going back one month, make sure month is not current month
20035 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
20037 return new_date.getUTCMonth() == month;
20039 // If going forward one month, make sure month is as expected
20040 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
20042 return new_date.getUTCMonth() != new_month;
20044 new_month = month + dir;
20045 new_date.setUTCMonth(new_month);
20046 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
20047 if (new_month < 0 || new_month > 11) {
20048 new_month = (new_month + 12) % 12;
20051 // For magnitudes >1, move one month at a time...
20052 for (var i=0; i<mag; i++) {
20053 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
20054 new_date = this.moveMonth(new_date, dir);
20056 // ...then reset the day, keeping it in the new month
20057 new_month = new_date.getUTCMonth();
20058 new_date.setUTCDate(day);
20060 return new_month != new_date.getUTCMonth();
20063 // Common date-resetting loop -- if date is beyond end of month, make it
20066 new_date.setUTCDate(--day);
20067 new_date.setUTCMonth(new_month);
20072 moveYear: function(date, dir)
20074 return this.moveMonth(date, dir*12);
20077 dateWithinRange: function(date)
20079 return date >= this.startDate && date <= this.endDate;
20085 this.picker().remove();
20088 validateValue : function(value)
20090 if(this.getVisibilityEl().hasClass('hidden')){
20094 if(value.length < 1) {
20095 if(this.allowBlank){
20101 if(value.length < this.minLength){
20104 if(value.length > this.maxLength){
20108 var vt = Roo.form.VTypes;
20109 if(!vt[this.vtype](value, this)){
20113 if(typeof this.validator == "function"){
20114 var msg = this.validator(value);
20120 if(this.regex && !this.regex.test(value)){
20124 if(typeof(this.parseDate(value)) == 'undefined'){
20128 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
20132 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
20142 this.date = this.viewDate = '';
20144 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20149 Roo.apply(Roo.bootstrap.DateField, {
20160 html: '<i class="fa fa-arrow-left"/>'
20170 html: '<i class="fa fa-arrow-right"/>'
20212 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
20213 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
20214 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
20215 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20216 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
20229 navFnc: 'FullYear',
20234 navFnc: 'FullYear',
20239 Roo.apply(Roo.bootstrap.DateField, {
20243 cls: 'datepicker dropdown-menu roo-dynamic',
20247 cls: 'datepicker-days',
20251 cls: 'table-condensed',
20253 Roo.bootstrap.DateField.head,
20257 Roo.bootstrap.DateField.footer
20264 cls: 'datepicker-months',
20268 cls: 'table-condensed',
20270 Roo.bootstrap.DateField.head,
20271 Roo.bootstrap.DateField.content,
20272 Roo.bootstrap.DateField.footer
20279 cls: 'datepicker-years',
20283 cls: 'table-condensed',
20285 Roo.bootstrap.DateField.head,
20286 Roo.bootstrap.DateField.content,
20287 Roo.bootstrap.DateField.footer
20306 * @class Roo.bootstrap.TimeField
20307 * @extends Roo.bootstrap.Input
20308 * Bootstrap DateField class
20312 * Create a new TimeField
20313 * @param {Object} config The config object
20316 Roo.bootstrap.TimeField = function(config){
20317 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20321 * Fires when this field show.
20322 * @param {Roo.bootstrap.DateField} thisthis
20323 * @param {Mixed} date The date value
20328 * Fires when this field hide.
20329 * @param {Roo.bootstrap.DateField} this
20330 * @param {Mixed} date The date value
20335 * Fires when select a date.
20336 * @param {Roo.bootstrap.DateField} this
20337 * @param {Mixed} date The date value
20343 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
20346 * @cfg {String} format
20347 * The default time format string which can be overriden for localization support. The format must be
20348 * valid according to {@link Date#parseDate} (defaults to 'H:i').
20352 onRender: function(ct, position)
20355 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20357 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20359 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20361 this.pop = this.picker().select('>.datepicker-time',true).first();
20362 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20364 this.picker().on('mousedown', this.onMousedown, this);
20365 this.picker().on('click', this.onClick, this);
20367 this.picker().addClass('datepicker-dropdown');
20372 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20373 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20374 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20375 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20376 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20377 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20381 fireKey: function(e){
20382 if (!this.picker().isVisible()){
20383 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20389 e.preventDefault();
20397 this.onTogglePeriod();
20400 this.onIncrementMinutes();
20403 this.onDecrementMinutes();
20412 onClick: function(e) {
20413 e.stopPropagation();
20414 e.preventDefault();
20417 picker : function()
20419 return this.el.select('.datepicker', true).first();
20422 fillTime: function()
20424 var time = this.pop.select('tbody', true).first();
20426 time.dom.innerHTML = '';
20441 cls: 'hours-up glyphicon glyphicon-chevron-up'
20461 cls: 'minutes-up glyphicon glyphicon-chevron-up'
20482 cls: 'timepicker-hour',
20497 cls: 'timepicker-minute',
20512 cls: 'btn btn-primary period',
20534 cls: 'hours-down glyphicon glyphicon-chevron-down'
20554 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20572 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20579 var hours = this.time.getHours();
20580 var minutes = this.time.getMinutes();
20593 hours = hours - 12;
20597 hours = '0' + hours;
20601 minutes = '0' + minutes;
20604 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20605 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20606 this.pop.select('button', true).first().dom.innerHTML = period;
20612 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20614 var cls = ['bottom'];
20616 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20623 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20628 this.picker().addClass(cls.join('-'));
20632 Roo.each(cls, function(c){
20634 _this.picker().setTop(_this.inputEl().getHeight());
20638 _this.picker().setTop(0 - _this.picker().getHeight());
20643 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20647 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20654 onFocus : function()
20656 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20660 onBlur : function()
20662 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20668 this.picker().show();
20673 this.fireEvent('show', this, this.date);
20678 this.picker().hide();
20681 this.fireEvent('hide', this, this.date);
20684 setTime : function()
20687 this.setValue(this.time.format(this.format));
20689 this.fireEvent('select', this, this.date);
20694 onMousedown: function(e){
20695 e.stopPropagation();
20696 e.preventDefault();
20699 onIncrementHours: function()
20701 Roo.log('onIncrementHours');
20702 this.time = this.time.add(Date.HOUR, 1);
20707 onDecrementHours: function()
20709 Roo.log('onDecrementHours');
20710 this.time = this.time.add(Date.HOUR, -1);
20714 onIncrementMinutes: function()
20716 Roo.log('onIncrementMinutes');
20717 this.time = this.time.add(Date.MINUTE, 1);
20721 onDecrementMinutes: function()
20723 Roo.log('onDecrementMinutes');
20724 this.time = this.time.add(Date.MINUTE, -1);
20728 onTogglePeriod: function()
20730 Roo.log('onTogglePeriod');
20731 this.time = this.time.add(Date.HOUR, 12);
20738 Roo.apply(Roo.bootstrap.TimeField, {
20768 cls: 'btn btn-info ok',
20780 Roo.apply(Roo.bootstrap.TimeField, {
20784 cls: 'datepicker dropdown-menu',
20788 cls: 'datepicker-time',
20792 cls: 'table-condensed',
20794 Roo.bootstrap.TimeField.content,
20795 Roo.bootstrap.TimeField.footer
20814 * @class Roo.bootstrap.MonthField
20815 * @extends Roo.bootstrap.Input
20816 * Bootstrap MonthField class
20818 * @cfg {String} language default en
20821 * Create a new MonthField
20822 * @param {Object} config The config object
20825 Roo.bootstrap.MonthField = function(config){
20826 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20831 * Fires when this field show.
20832 * @param {Roo.bootstrap.MonthField} this
20833 * @param {Mixed} date The date value
20838 * Fires when this field hide.
20839 * @param {Roo.bootstrap.MonthField} this
20840 * @param {Mixed} date The date value
20845 * Fires when select a date.
20846 * @param {Roo.bootstrap.MonthField} this
20847 * @param {String} oldvalue The old value
20848 * @param {String} newvalue The new value
20854 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20856 onRender: function(ct, position)
20859 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20861 this.language = this.language || 'en';
20862 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20863 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20865 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20866 this.isInline = false;
20867 this.isInput = true;
20868 this.component = this.el.select('.add-on', true).first() || false;
20869 this.component = (this.component && this.component.length === 0) ? false : this.component;
20870 this.hasInput = this.component && this.inputEL().length;
20872 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20874 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20876 this.picker().on('mousedown', this.onMousedown, this);
20877 this.picker().on('click', this.onClick, this);
20879 this.picker().addClass('datepicker-dropdown');
20881 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20882 v.setStyle('width', '189px');
20889 if(this.isInline) {
20895 setValue: function(v, suppressEvent)
20897 var o = this.getValue();
20899 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20903 if(suppressEvent !== true){
20904 this.fireEvent('select', this, o, v);
20909 getValue: function()
20914 onClick: function(e)
20916 e.stopPropagation();
20917 e.preventDefault();
20919 var target = e.getTarget();
20921 if(target.nodeName.toLowerCase() === 'i'){
20922 target = Roo.get(target).dom.parentNode;
20925 var nodeName = target.nodeName;
20926 var className = target.className;
20927 var html = target.innerHTML;
20929 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20933 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20935 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20941 picker : function()
20943 return this.pickerEl;
20946 fillMonths: function()
20949 var months = this.picker().select('>.datepicker-months td', true).first();
20951 months.dom.innerHTML = '';
20957 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20960 months.createChild(month);
20969 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20970 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20973 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20974 e.removeClass('active');
20976 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20977 e.addClass('active');
20984 if(this.isInline) {
20988 this.picker().removeClass(['bottom', 'top']);
20990 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20992 * place to the top of element!
20996 this.picker().addClass('top');
20997 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21002 this.picker().addClass('bottom');
21004 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21007 onFocus : function()
21009 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
21013 onBlur : function()
21015 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
21017 var d = this.inputEl().getValue();
21026 this.picker().show();
21027 this.picker().select('>.datepicker-months', true).first().show();
21031 this.fireEvent('show', this, this.date);
21036 if(this.isInline) {
21039 this.picker().hide();
21040 this.fireEvent('hide', this, this.date);
21044 onMousedown: function(e)
21046 e.stopPropagation();
21047 e.preventDefault();
21052 Roo.bootstrap.MonthField.superclass.keyup.call(this);
21056 fireKey: function(e)
21058 if (!this.picker().isVisible()){
21059 if (e.keyCode == 27) {// allow escape to hide and re-show picker
21070 e.preventDefault();
21074 dir = e.keyCode == 37 ? -1 : 1;
21076 this.vIndex = this.vIndex + dir;
21078 if(this.vIndex < 0){
21082 if(this.vIndex > 11){
21086 if(isNaN(this.vIndex)){
21090 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21096 dir = e.keyCode == 38 ? -1 : 1;
21098 this.vIndex = this.vIndex + dir * 4;
21100 if(this.vIndex < 0){
21104 if(this.vIndex > 11){
21108 if(isNaN(this.vIndex)){
21112 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21117 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21118 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21122 e.preventDefault();
21125 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21126 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21142 this.picker().remove();
21147 Roo.apply(Roo.bootstrap.MonthField, {
21166 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21167 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
21172 Roo.apply(Roo.bootstrap.MonthField, {
21176 cls: 'datepicker dropdown-menu roo-dynamic',
21180 cls: 'datepicker-months',
21184 cls: 'table-condensed',
21186 Roo.bootstrap.DateField.content
21206 * @class Roo.bootstrap.CheckBox
21207 * @extends Roo.bootstrap.Input
21208 * Bootstrap CheckBox class
21210 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
21211 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
21212 * @cfg {String} boxLabel The text that appears beside the checkbox
21213 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
21214 * @cfg {Boolean} checked initnal the element
21215 * @cfg {Boolean} inline inline the element (default false)
21216 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
21217 * @cfg {String} tooltip label tooltip
21220 * Create a new CheckBox
21221 * @param {Object} config The config object
21224 Roo.bootstrap.CheckBox = function(config){
21225 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
21230 * Fires when the element is checked or unchecked.
21231 * @param {Roo.bootstrap.CheckBox} this This input
21232 * @param {Boolean} checked The new checked value
21237 * Fires when the element is click.
21238 * @param {Roo.bootstrap.CheckBox} this This input
21245 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
21247 inputType: 'checkbox',
21256 // checkbox success does not make any sense really..
21261 getAutoCreate : function()
21263 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
21269 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
21272 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
21278 type : this.inputType,
21279 value : this.inputValue,
21280 cls : 'roo-' + this.inputType, //'form-box',
21281 placeholder : this.placeholder || ''
21285 if(this.inputType != 'radio'){
21289 cls : 'roo-hidden-value',
21290 value : this.checked ? this.inputValue : this.valueOff
21295 if (this.weight) { // Validity check?
21296 cfg.cls += " " + this.inputType + "-" + this.weight;
21299 if (this.disabled) {
21300 input.disabled=true;
21304 input.checked = this.checked;
21309 input.name = this.name;
21311 if(this.inputType != 'radio'){
21312 hidden.name = this.name;
21313 input.name = '_hidden_' + this.name;
21318 input.cls += ' input-' + this.size;
21323 ['xs','sm','md','lg'].map(function(size){
21324 if (settings[size]) {
21325 cfg.cls += ' col-' + size + '-' + settings[size];
21329 var inputblock = input;
21331 if (this.before || this.after) {
21334 cls : 'input-group',
21339 inputblock.cn.push({
21341 cls : 'input-group-addon',
21346 inputblock.cn.push(input);
21348 if(this.inputType != 'radio'){
21349 inputblock.cn.push(hidden);
21353 inputblock.cn.push({
21355 cls : 'input-group-addon',
21361 var boxLabelCfg = false;
21367 //'for': id, // box label is handled by onclick - so no for...
21369 html: this.boxLabel
21372 boxLabelCfg.tooltip = this.tooltip;
21378 if (align ==='left' && this.fieldLabel.length) {
21379 // Roo.log("left and has label");
21384 cls : 'control-label',
21385 html : this.fieldLabel
21396 cfg.cn[1].cn.push(boxLabelCfg);
21399 if(this.labelWidth > 12){
21400 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21403 if(this.labelWidth < 13 && this.labelmd == 0){
21404 this.labelmd = this.labelWidth;
21407 if(this.labellg > 0){
21408 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21409 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21412 if(this.labelmd > 0){
21413 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21414 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21417 if(this.labelsm > 0){
21418 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21419 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21422 if(this.labelxs > 0){
21423 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21424 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21427 } else if ( this.fieldLabel.length) {
21428 // Roo.log(" label");
21432 tag: this.boxLabel ? 'span' : 'label',
21434 cls: 'control-label box-input-label',
21435 //cls : 'input-group-addon',
21436 html : this.fieldLabel
21443 cfg.cn.push(boxLabelCfg);
21448 // Roo.log(" no label && no align");
21449 cfg.cn = [ inputblock ] ;
21451 cfg.cn.push(boxLabelCfg);
21459 if(this.inputType != 'radio'){
21460 cfg.cn.push(hidden);
21468 * return the real input element.
21470 inputEl: function ()
21472 return this.el.select('input.roo-' + this.inputType,true).first();
21474 hiddenEl: function ()
21476 return this.el.select('input.roo-hidden-value',true).first();
21479 labelEl: function()
21481 return this.el.select('label.control-label',true).first();
21483 /* depricated... */
21487 return this.labelEl();
21490 boxLabelEl: function()
21492 return this.el.select('label.box-label',true).first();
21495 initEvents : function()
21497 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21499 this.inputEl().on('click', this.onClick, this);
21501 if (this.boxLabel) {
21502 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
21505 this.startValue = this.getValue();
21508 Roo.bootstrap.CheckBox.register(this);
21512 onClick : function(e)
21514 if(this.fireEvent('click', this, e) !== false){
21515 this.setChecked(!this.checked);
21520 setChecked : function(state,suppressEvent)
21522 this.startValue = this.getValue();
21524 if(this.inputType == 'radio'){
21526 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21527 e.dom.checked = false;
21530 this.inputEl().dom.checked = true;
21532 this.inputEl().dom.value = this.inputValue;
21534 if(suppressEvent !== true){
21535 this.fireEvent('check', this, true);
21543 this.checked = state;
21545 this.inputEl().dom.checked = state;
21548 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21550 if(suppressEvent !== true){
21551 this.fireEvent('check', this, state);
21557 getValue : function()
21559 if(this.inputType == 'radio'){
21560 return this.getGroupValue();
21563 return this.hiddenEl().dom.value;
21567 getGroupValue : function()
21569 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21573 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21576 setValue : function(v,suppressEvent)
21578 if(this.inputType == 'radio'){
21579 this.setGroupValue(v, suppressEvent);
21583 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21588 setGroupValue : function(v, suppressEvent)
21590 this.startValue = this.getValue();
21592 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21593 e.dom.checked = false;
21595 if(e.dom.value == v){
21596 e.dom.checked = true;
21600 if(suppressEvent !== true){
21601 this.fireEvent('check', this, true);
21609 validate : function()
21611 if(this.getVisibilityEl().hasClass('hidden')){
21617 (this.inputType == 'radio' && this.validateRadio()) ||
21618 (this.inputType == 'checkbox' && this.validateCheckbox())
21624 this.markInvalid();
21628 validateRadio : function()
21630 if(this.getVisibilityEl().hasClass('hidden')){
21634 if(this.allowBlank){
21640 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21641 if(!e.dom.checked){
21653 validateCheckbox : function()
21656 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21657 //return (this.getValue() == this.inputValue) ? true : false;
21660 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21668 for(var i in group){
21669 if(group[i].el.isVisible(true)){
21677 for(var i in group){
21682 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21689 * Mark this field as valid
21691 markValid : function()
21695 this.fireEvent('valid', this);
21697 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21700 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21707 if(this.inputType == 'radio'){
21708 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21709 var fg = e.findParent('.form-group', false, true);
21710 if (Roo.bootstrap.version == 3) {
21711 fg.removeClass([_this.invalidClass, _this.validClass]);
21712 fg.addClass(_this.validClass);
21714 fg.removeClass(['is-valid', 'is-invalid']);
21715 fg.addClass('is-valid');
21723 var fg = this.el.findParent('.form-group', false, true);
21724 if (Roo.bootstrap.version == 3) {
21725 fg.removeClass([this.invalidClass, this.validClass]);
21726 fg.addClass(this.validClass);
21728 fg.removeClass(['is-valid', 'is-invalid']);
21729 fg.addClass('is-valid');
21734 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21740 for(var i in group){
21741 var fg = group[i].el.findParent('.form-group', false, true);
21742 if (Roo.bootstrap.version == 3) {
21743 fg.removeClass([this.invalidClass, this.validClass]);
21744 fg.addClass(this.validClass);
21746 fg.removeClass(['is-valid', 'is-invalid']);
21747 fg.addClass('is-valid');
21753 * Mark this field as invalid
21754 * @param {String} msg The validation message
21756 markInvalid : function(msg)
21758 if(this.allowBlank){
21764 this.fireEvent('invalid', this, msg);
21766 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21769 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21773 label.markInvalid();
21776 if(this.inputType == 'radio'){
21778 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21779 var fg = e.findParent('.form-group', false, true);
21780 if (Roo.bootstrap.version == 3) {
21781 fg.removeClass([_this.invalidClass, _this.validClass]);
21782 fg.addClass(_this.invalidClass);
21784 fg.removeClass(['is-invalid', 'is-valid']);
21785 fg.addClass('is-invalid');
21793 var fg = this.el.findParent('.form-group', false, true);
21794 if (Roo.bootstrap.version == 3) {
21795 fg.removeClass([_this.invalidClass, _this.validClass]);
21796 fg.addClass(_this.invalidClass);
21798 fg.removeClass(['is-invalid', 'is-valid']);
21799 fg.addClass('is-invalid');
21804 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21810 for(var i in group){
21811 var fg = group[i].el.findParent('.form-group', false, true);
21812 if (Roo.bootstrap.version == 3) {
21813 fg.removeClass([_this.invalidClass, _this.validClass]);
21814 fg.addClass(_this.invalidClass);
21816 fg.removeClass(['is-invalid', 'is-valid']);
21817 fg.addClass('is-invalid');
21823 clearInvalid : function()
21825 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21827 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21829 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21831 if (label && label.iconEl) {
21832 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21833 label.iconEl.removeClass(['is-invalid', 'is-valid']);
21837 disable : function()
21839 if(this.inputType != 'radio'){
21840 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21847 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21848 _this.getActionEl().addClass(this.disabledClass);
21849 e.dom.disabled = true;
21853 this.disabled = true;
21854 this.fireEvent("disable", this);
21858 enable : function()
21860 if(this.inputType != 'radio'){
21861 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21868 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21869 _this.getActionEl().removeClass(this.disabledClass);
21870 e.dom.disabled = false;
21874 this.disabled = false;
21875 this.fireEvent("enable", this);
21879 setBoxLabel : function(v)
21884 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21890 Roo.apply(Roo.bootstrap.CheckBox, {
21895 * register a CheckBox Group
21896 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21898 register : function(checkbox)
21900 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21901 this.groups[checkbox.groupId] = {};
21904 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21908 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21912 * fetch a CheckBox Group based on the group ID
21913 * @param {string} the group ID
21914 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21916 get: function(groupId) {
21917 if (typeof(this.groups[groupId]) == 'undefined') {
21921 return this.groups[groupId] ;
21934 * @class Roo.bootstrap.Radio
21935 * @extends Roo.bootstrap.Component
21936 * Bootstrap Radio class
21937 * @cfg {String} boxLabel - the label associated
21938 * @cfg {String} value - the value of radio
21941 * Create a new Radio
21942 * @param {Object} config The config object
21944 Roo.bootstrap.Radio = function(config){
21945 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21949 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21955 getAutoCreate : function()
21959 cls : 'form-group radio',
21964 html : this.boxLabel
21972 initEvents : function()
21974 this.parent().register(this);
21976 this.el.on('click', this.onClick, this);
21980 onClick : function(e)
21982 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21983 this.setChecked(true);
21987 setChecked : function(state, suppressEvent)
21989 this.parent().setValue(this.value, suppressEvent);
21993 setBoxLabel : function(v)
21998 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
22013 * @class Roo.bootstrap.SecurePass
22014 * @extends Roo.bootstrap.Input
22015 * Bootstrap SecurePass class
22019 * Create a new SecurePass
22020 * @param {Object} config The config object
22023 Roo.bootstrap.SecurePass = function (config) {
22024 // these go here, so the translation tool can replace them..
22026 PwdEmpty: "Please type a password, and then retype it to confirm.",
22027 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22028 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22029 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22030 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22031 FNInPwd: "Your password can't contain your first name. Please type a different password.",
22032 LNInPwd: "Your password can't contain your last name. Please type a different password.",
22033 TooWeak: "Your password is Too Weak."
22035 this.meterLabel = "Password strength:";
22036 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
22037 this.meterClass = [
22038 "roo-password-meter-tooweak",
22039 "roo-password-meter-weak",
22040 "roo-password-meter-medium",
22041 "roo-password-meter-strong",
22042 "roo-password-meter-grey"
22047 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
22050 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
22052 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
22054 * PwdEmpty: "Please type a password, and then retype it to confirm.",
22055 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22056 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22057 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22058 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22059 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
22060 * LNInPwd: "Your password can't contain your last name. Please type a different password."
22070 * @cfg {String/Object} Label for the strength meter (defaults to
22071 * 'Password strength:')
22076 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
22077 * ['Weak', 'Medium', 'Strong'])
22080 pwdStrengths: false,
22093 initEvents: function ()
22095 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
22097 if (this.el.is('input[type=password]') && Roo.isSafari) {
22098 this.el.on('keydown', this.SafariOnKeyDown, this);
22101 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
22104 onRender: function (ct, position)
22106 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
22107 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
22108 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
22110 this.trigger.createChild({
22115 cls: 'roo-password-meter-grey col-xs-12',
22118 //width: this.meterWidth + 'px'
22122 cls: 'roo-password-meter-text'
22128 if (this.hideTrigger) {
22129 this.trigger.setDisplayed(false);
22131 this.setSize(this.width || '', this.height || '');
22134 onDestroy: function ()
22136 if (this.trigger) {
22137 this.trigger.removeAllListeners();
22138 this.trigger.remove();
22141 this.wrap.remove();
22143 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
22146 checkStrength: function ()
22148 var pwd = this.inputEl().getValue();
22149 if (pwd == this._lastPwd) {
22154 if (this.ClientSideStrongPassword(pwd)) {
22156 } else if (this.ClientSideMediumPassword(pwd)) {
22158 } else if (this.ClientSideWeakPassword(pwd)) {
22164 Roo.log('strength1: ' + strength);
22166 //var pm = this.trigger.child('div/div/div').dom;
22167 var pm = this.trigger.child('div/div');
22168 pm.removeClass(this.meterClass);
22169 pm.addClass(this.meterClass[strength]);
22172 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22174 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
22176 this._lastPwd = pwd;
22180 Roo.bootstrap.SecurePass.superclass.reset.call(this);
22182 this._lastPwd = '';
22184 var pm = this.trigger.child('div/div');
22185 pm.removeClass(this.meterClass);
22186 pm.addClass('roo-password-meter-grey');
22189 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22192 this.inputEl().dom.type='password';
22195 validateValue: function (value)
22198 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
22201 if (value.length == 0) {
22202 if (this.allowBlank) {
22203 this.clearInvalid();
22207 this.markInvalid(this.errors.PwdEmpty);
22208 this.errorMsg = this.errors.PwdEmpty;
22216 if ('[\x21-\x7e]*'.match(value)) {
22217 this.markInvalid(this.errors.PwdBadChar);
22218 this.errorMsg = this.errors.PwdBadChar;
22221 if (value.length < 6) {
22222 this.markInvalid(this.errors.PwdShort);
22223 this.errorMsg = this.errors.PwdShort;
22226 if (value.length > 16) {
22227 this.markInvalid(this.errors.PwdLong);
22228 this.errorMsg = this.errors.PwdLong;
22232 if (this.ClientSideStrongPassword(value)) {
22234 } else if (this.ClientSideMediumPassword(value)) {
22236 } else if (this.ClientSideWeakPassword(value)) {
22243 if (strength < 2) {
22244 //this.markInvalid(this.errors.TooWeak);
22245 this.errorMsg = this.errors.TooWeak;
22250 console.log('strength2: ' + strength);
22252 //var pm = this.trigger.child('div/div/div').dom;
22254 var pm = this.trigger.child('div/div');
22255 pm.removeClass(this.meterClass);
22256 pm.addClass(this.meterClass[strength]);
22258 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22260 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
22262 this.errorMsg = '';
22266 CharacterSetChecks: function (type)
22269 this.fResult = false;
22272 isctype: function (character, type)
22275 case this.kCapitalLetter:
22276 if (character >= 'A' && character <= 'Z') {
22281 case this.kSmallLetter:
22282 if (character >= 'a' && character <= 'z') {
22288 if (character >= '0' && character <= '9') {
22293 case this.kPunctuation:
22294 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
22305 IsLongEnough: function (pwd, size)
22307 return !(pwd == null || isNaN(size) || pwd.length < size);
22310 SpansEnoughCharacterSets: function (word, nb)
22312 if (!this.IsLongEnough(word, nb))
22317 var characterSetChecks = new Array(
22318 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22319 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22322 for (var index = 0; index < word.length; ++index) {
22323 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22324 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22325 characterSetChecks[nCharSet].fResult = true;
22332 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22333 if (characterSetChecks[nCharSet].fResult) {
22338 if (nCharSets < nb) {
22344 ClientSideStrongPassword: function (pwd)
22346 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22349 ClientSideMediumPassword: function (pwd)
22351 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22354 ClientSideWeakPassword: function (pwd)
22356 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22359 })//<script type="text/javascript">
22362 * Based Ext JS Library 1.1.1
22363 * Copyright(c) 2006-2007, Ext JS, LLC.
22369 * @class Roo.HtmlEditorCore
22370 * @extends Roo.Component
22371 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22373 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22376 Roo.HtmlEditorCore = function(config){
22379 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22384 * @event initialize
22385 * Fires when the editor is fully initialized (including the iframe)
22386 * @param {Roo.HtmlEditorCore} this
22391 * Fires when the editor is first receives the focus. Any insertion must wait
22392 * until after this event.
22393 * @param {Roo.HtmlEditorCore} this
22397 * @event beforesync
22398 * Fires before the textarea is updated with content from the editor iframe. Return false
22399 * to cancel the sync.
22400 * @param {Roo.HtmlEditorCore} this
22401 * @param {String} html
22405 * @event beforepush
22406 * Fires before the iframe editor is updated with content from the textarea. Return false
22407 * to cancel the push.
22408 * @param {Roo.HtmlEditorCore} this
22409 * @param {String} html
22414 * Fires when the textarea is updated with content from the editor iframe.
22415 * @param {Roo.HtmlEditorCore} this
22416 * @param {String} html
22421 * Fires when the iframe editor is updated with content from the textarea.
22422 * @param {Roo.HtmlEditorCore} this
22423 * @param {String} html
22428 * @event editorevent
22429 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22430 * @param {Roo.HtmlEditorCore} this
22436 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22438 // defaults : white / black...
22439 this.applyBlacklists();
22446 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
22450 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
22456 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22461 * @cfg {Number} height (in pixels)
22465 * @cfg {Number} width (in pixels)
22470 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22473 stylesheets: false,
22478 // private properties
22479 validationEvent : false,
22481 initialized : false,
22483 sourceEditMode : false,
22484 onFocus : Roo.emptyFn,
22486 hideMode:'offsets',
22490 // blacklist + whitelisted elements..
22497 * Protected method that will not generally be called directly. It
22498 * is called when the editor initializes the iframe with HTML contents. Override this method if you
22499 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22501 getDocMarkup : function(){
22505 // inherit styels from page...??
22506 if (this.stylesheets === false) {
22508 Roo.get(document.head).select('style').each(function(node) {
22509 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22512 Roo.get(document.head).select('link').each(function(node) {
22513 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22516 } else if (!this.stylesheets.length) {
22518 st = '<style type="text/css">' +
22519 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22522 st = '<style type="text/css">' +
22527 st += '<style type="text/css">' +
22528 'IMG { cursor: pointer } ' +
22531 var cls = 'roo-htmleditor-body';
22533 if(this.bodyCls.length){
22534 cls += ' ' + this.bodyCls;
22537 return '<html><head>' + st +
22538 //<style type="text/css">' +
22539 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22541 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
22545 onRender : function(ct, position)
22548 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22549 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22552 this.el.dom.style.border = '0 none';
22553 this.el.dom.setAttribute('tabIndex', -1);
22554 this.el.addClass('x-hidden hide');
22558 if(Roo.isIE){ // fix IE 1px bogus margin
22559 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22563 this.frameId = Roo.id();
22567 var iframe = this.owner.wrap.createChild({
22569 cls: 'form-control', // bootstrap..
22571 name: this.frameId,
22572 frameBorder : 'no',
22573 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
22578 this.iframe = iframe.dom;
22580 this.assignDocWin();
22582 this.doc.designMode = 'on';
22585 this.doc.write(this.getDocMarkup());
22589 var task = { // must defer to wait for browser to be ready
22591 //console.log("run task?" + this.doc.readyState);
22592 this.assignDocWin();
22593 if(this.doc.body || this.doc.readyState == 'complete'){
22595 this.doc.designMode="on";
22599 Roo.TaskMgr.stop(task);
22600 this.initEditor.defer(10, this);
22607 Roo.TaskMgr.start(task);
22612 onResize : function(w, h)
22614 Roo.log('resize: ' +w + ',' + h );
22615 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22619 if(typeof w == 'number'){
22621 this.iframe.style.width = w + 'px';
22623 if(typeof h == 'number'){
22625 this.iframe.style.height = h + 'px';
22627 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22634 * Toggles the editor between standard and source edit mode.
22635 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22637 toggleSourceEdit : function(sourceEditMode){
22639 this.sourceEditMode = sourceEditMode === true;
22641 if(this.sourceEditMode){
22643 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22646 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22647 //this.iframe.className = '';
22650 //this.setSize(this.owner.wrap.getSize());
22651 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22658 * Protected method that will not generally be called directly. If you need/want
22659 * custom HTML cleanup, this is the method you should override.
22660 * @param {String} html The HTML to be cleaned
22661 * return {String} The cleaned HTML
22663 cleanHtml : function(html){
22664 html = String(html);
22665 if(html.length > 5){
22666 if(Roo.isSafari){ // strip safari nonsense
22667 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22670 if(html == ' '){
22677 * HTML Editor -> Textarea
22678 * Protected method that will not generally be called directly. Syncs the contents
22679 * of the editor iframe with the textarea.
22681 syncValue : function(){
22682 if(this.initialized){
22683 var bd = (this.doc.body || this.doc.documentElement);
22684 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22685 var html = bd.innerHTML;
22687 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22688 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22690 html = '<div style="'+m[0]+'">' + html + '</div>';
22693 html = this.cleanHtml(html);
22694 // fix up the special chars.. normaly like back quotes in word...
22695 // however we do not want to do this with chinese..
22696 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
22698 var cc = match.charCodeAt();
22700 // Get the character value, handling surrogate pairs
22701 if (match.length == 2) {
22702 // It's a surrogate pair, calculate the Unicode code point
22703 var high = match.charCodeAt(0) - 0xD800;
22704 var low = match.charCodeAt(1) - 0xDC00;
22705 cc = (high * 0x400) + low + 0x10000;
22707 (cc >= 0x4E00 && cc < 0xA000 ) ||
22708 (cc >= 0x3400 && cc < 0x4E00 ) ||
22709 (cc >= 0xf900 && cc < 0xfb00 )
22714 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
22715 return "&#" + cc + ";";
22722 if(this.owner.fireEvent('beforesync', this, html) !== false){
22723 this.el.dom.value = html;
22724 this.owner.fireEvent('sync', this, html);
22730 * Protected method that will not generally be called directly. Pushes the value of the textarea
22731 * into the iframe editor.
22733 pushValue : function(){
22734 if(this.initialized){
22735 var v = this.el.dom.value.trim();
22737 // if(v.length < 1){
22741 if(this.owner.fireEvent('beforepush', this, v) !== false){
22742 var d = (this.doc.body || this.doc.documentElement);
22744 this.cleanUpPaste();
22745 this.el.dom.value = d.innerHTML;
22746 this.owner.fireEvent('push', this, v);
22752 deferFocus : function(){
22753 this.focus.defer(10, this);
22757 focus : function(){
22758 if(this.win && !this.sourceEditMode){
22765 assignDocWin: function()
22767 var iframe = this.iframe;
22770 this.doc = iframe.contentWindow.document;
22771 this.win = iframe.contentWindow;
22773 // if (!Roo.get(this.frameId)) {
22776 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22777 // this.win = Roo.get(this.frameId).dom.contentWindow;
22779 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22783 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22784 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22789 initEditor : function(){
22790 //console.log("INIT EDITOR");
22791 this.assignDocWin();
22795 this.doc.designMode="on";
22797 this.doc.write(this.getDocMarkup());
22800 var dbody = (this.doc.body || this.doc.documentElement);
22801 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22802 // this copies styles from the containing element into thsi one..
22803 // not sure why we need all of this..
22804 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22806 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22807 //ss['background-attachment'] = 'fixed'; // w3c
22808 dbody.bgProperties = 'fixed'; // ie
22809 //Roo.DomHelper.applyStyles(dbody, ss);
22810 Roo.EventManager.on(this.doc, {
22811 //'mousedown': this.onEditorEvent,
22812 'mouseup': this.onEditorEvent,
22813 'dblclick': this.onEditorEvent,
22814 'click': this.onEditorEvent,
22815 'keyup': this.onEditorEvent,
22820 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22822 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22823 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22825 this.initialized = true;
22827 this.owner.fireEvent('initialize', this);
22832 onDestroy : function(){
22838 //for (var i =0; i < this.toolbars.length;i++) {
22839 // // fixme - ask toolbars for heights?
22840 // this.toolbars[i].onDestroy();
22843 //this.wrap.dom.innerHTML = '';
22844 //this.wrap.remove();
22849 onFirstFocus : function(){
22851 this.assignDocWin();
22854 this.activated = true;
22857 if(Roo.isGecko){ // prevent silly gecko errors
22859 var s = this.win.getSelection();
22860 if(!s.focusNode || s.focusNode.nodeType != 3){
22861 var r = s.getRangeAt(0);
22862 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22867 this.execCmd('useCSS', true);
22868 this.execCmd('styleWithCSS', false);
22871 this.owner.fireEvent('activate', this);
22875 adjustFont: function(btn){
22876 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22877 //if(Roo.isSafari){ // safari
22880 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22881 if(Roo.isSafari){ // safari
22882 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22883 v = (v < 10) ? 10 : v;
22884 v = (v > 48) ? 48 : v;
22885 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22890 v = Math.max(1, v+adjust);
22892 this.execCmd('FontSize', v );
22895 onEditorEvent : function(e)
22897 this.owner.fireEvent('editorevent', this, e);
22898 // this.updateToolbar();
22899 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22902 insertTag : function(tg)
22904 // could be a bit smarter... -> wrap the current selected tRoo..
22905 if (tg.toLowerCase() == 'span' ||
22906 tg.toLowerCase() == 'code' ||
22907 tg.toLowerCase() == 'sup' ||
22908 tg.toLowerCase() == 'sub'
22911 range = this.createRange(this.getSelection());
22912 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22913 wrappingNode.appendChild(range.extractContents());
22914 range.insertNode(wrappingNode);
22921 this.execCmd("formatblock", tg);
22925 insertText : function(txt)
22929 var range = this.createRange();
22930 range.deleteContents();
22931 //alert(Sender.getAttribute('label'));
22933 range.insertNode(this.doc.createTextNode(txt));
22939 * Executes a Midas editor command on the editor document and performs necessary focus and
22940 * toolbar updates. <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 relayCmd : function(cmd, value){
22946 this.execCmd(cmd, value);
22947 this.owner.fireEvent('editorevent', this);
22948 //this.updateToolbar();
22949 this.owner.deferFocus();
22953 * Executes a Midas editor command directly on the editor document.
22954 * For visual commands, you should use {@link #relayCmd} instead.
22955 * <b>This should only be called after the editor is initialized.</b>
22956 * @param {String} cmd The Midas command
22957 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22959 execCmd : function(cmd, value){
22960 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22967 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22969 * @param {String} text | dom node..
22971 insertAtCursor : function(text)
22974 if(!this.activated){
22980 var r = this.doc.selection.createRange();
22991 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22995 // from jquery ui (MIT licenced)
22997 var win = this.win;
22999 if (win.getSelection && win.getSelection().getRangeAt) {
23000 range = win.getSelection().getRangeAt(0);
23001 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
23002 range.insertNode(node);
23003 } else if (win.document.selection && win.document.selection.createRange) {
23004 // no firefox support
23005 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23006 win.document.selection.createRange().pasteHTML(txt);
23008 // no firefox support
23009 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23010 this.execCmd('InsertHTML', txt);
23019 mozKeyPress : function(e){
23021 var c = e.getCharCode(), cmd;
23024 c = String.fromCharCode(c).toLowerCase();
23038 this.cleanUpPaste.defer(100, this);
23046 e.preventDefault();
23054 fixKeys : function(){ // load time branching for fastest keydown performance
23056 return function(e){
23057 var k = e.getKey(), r;
23060 r = this.doc.selection.createRange();
23063 r.pasteHTML('    ');
23070 r = this.doc.selection.createRange();
23072 var target = r.parentElement();
23073 if(!target || target.tagName.toLowerCase() != 'li'){
23075 r.pasteHTML('<br />');
23081 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23082 this.cleanUpPaste.defer(100, this);
23088 }else if(Roo.isOpera){
23089 return function(e){
23090 var k = e.getKey();
23094 this.execCmd('InsertHTML','    ');
23097 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23098 this.cleanUpPaste.defer(100, this);
23103 }else if(Roo.isSafari){
23104 return function(e){
23105 var k = e.getKey();
23109 this.execCmd('InsertText','\t');
23113 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23114 this.cleanUpPaste.defer(100, this);
23122 getAllAncestors: function()
23124 var p = this.getSelectedNode();
23127 a.push(p); // push blank onto stack..
23128 p = this.getParentElement();
23132 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
23136 a.push(this.doc.body);
23140 lastSelNode : false,
23143 getSelection : function()
23145 this.assignDocWin();
23146 return Roo.isIE ? this.doc.selection : this.win.getSelection();
23149 getSelectedNode: function()
23151 // this may only work on Gecko!!!
23153 // should we cache this!!!!
23158 var range = this.createRange(this.getSelection()).cloneRange();
23161 var parent = range.parentElement();
23163 var testRange = range.duplicate();
23164 testRange.moveToElementText(parent);
23165 if (testRange.inRange(range)) {
23168 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
23171 parent = parent.parentElement;
23176 // is ancestor a text element.
23177 var ac = range.commonAncestorContainer;
23178 if (ac.nodeType == 3) {
23179 ac = ac.parentNode;
23182 var ar = ac.childNodes;
23185 var other_nodes = [];
23186 var has_other_nodes = false;
23187 for (var i=0;i<ar.length;i++) {
23188 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
23191 // fullly contained node.
23193 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
23198 // probably selected..
23199 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
23200 other_nodes.push(ar[i]);
23204 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
23209 has_other_nodes = true;
23211 if (!nodes.length && other_nodes.length) {
23212 nodes= other_nodes;
23214 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
23220 createRange: function(sel)
23222 // this has strange effects when using with
23223 // top toolbar - not sure if it's a great idea.
23224 //this.editor.contentWindow.focus();
23225 if (typeof sel != "undefined") {
23227 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
23229 return this.doc.createRange();
23232 return this.doc.createRange();
23235 getParentElement: function()
23238 this.assignDocWin();
23239 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
23241 var range = this.createRange(sel);
23244 var p = range.commonAncestorContainer;
23245 while (p.nodeType == 3) { // text node
23256 * Range intersection.. the hard stuff...
23260 * [ -- selected range --- ]
23264 * if end is before start or hits it. fail.
23265 * if start is after end or hits it fail.
23267 * if either hits (but other is outside. - then it's not
23273 // @see http://www.thismuchiknow.co.uk/?p=64.
23274 rangeIntersectsNode : function(range, node)
23276 var nodeRange = node.ownerDocument.createRange();
23278 nodeRange.selectNode(node);
23280 nodeRange.selectNodeContents(node);
23283 var rangeStartRange = range.cloneRange();
23284 rangeStartRange.collapse(true);
23286 var rangeEndRange = range.cloneRange();
23287 rangeEndRange.collapse(false);
23289 var nodeStartRange = nodeRange.cloneRange();
23290 nodeStartRange.collapse(true);
23292 var nodeEndRange = nodeRange.cloneRange();
23293 nodeEndRange.collapse(false);
23295 return rangeStartRange.compareBoundaryPoints(
23296 Range.START_TO_START, nodeEndRange) == -1 &&
23297 rangeEndRange.compareBoundaryPoints(
23298 Range.START_TO_START, nodeStartRange) == 1;
23302 rangeCompareNode : function(range, node)
23304 var nodeRange = node.ownerDocument.createRange();
23306 nodeRange.selectNode(node);
23308 nodeRange.selectNodeContents(node);
23312 range.collapse(true);
23314 nodeRange.collapse(true);
23316 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23317 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
23319 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23321 var nodeIsBefore = ss == 1;
23322 var nodeIsAfter = ee == -1;
23324 if (nodeIsBefore && nodeIsAfter) {
23327 if (!nodeIsBefore && nodeIsAfter) {
23328 return 1; //right trailed.
23331 if (nodeIsBefore && !nodeIsAfter) {
23332 return 2; // left trailed.
23338 // private? - in a new class?
23339 cleanUpPaste : function()
23341 // cleans up the whole document..
23342 Roo.log('cleanuppaste');
23344 this.cleanUpChildren(this.doc.body);
23345 var clean = this.cleanWordChars(this.doc.body.innerHTML);
23346 if (clean != this.doc.body.innerHTML) {
23347 this.doc.body.innerHTML = clean;
23352 cleanWordChars : function(input) {// change the chars to hex code
23353 var he = Roo.HtmlEditorCore;
23355 var output = input;
23356 Roo.each(he.swapCodes, function(sw) {
23357 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23359 output = output.replace(swapper, sw[1]);
23366 cleanUpChildren : function (n)
23368 if (!n.childNodes.length) {
23371 for (var i = n.childNodes.length-1; i > -1 ; i--) {
23372 this.cleanUpChild(n.childNodes[i]);
23379 cleanUpChild : function (node)
23382 //console.log(node);
23383 if (node.nodeName == "#text") {
23384 // clean up silly Windows -- stuff?
23387 if (node.nodeName == "#comment") {
23388 node.parentNode.removeChild(node);
23389 // clean up silly Windows -- stuff?
23392 var lcname = node.tagName.toLowerCase();
23393 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23394 // whitelist of tags..
23396 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23398 node.parentNode.removeChild(node);
23403 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23405 // spans with no attributes - just remove them..
23406 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
23407 remove_keep_children = true;
23410 // remove <a name=....> as rendering on yahoo mailer is borked with this.
23411 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23413 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23414 // remove_keep_children = true;
23417 if (remove_keep_children) {
23418 this.cleanUpChildren(node);
23419 // inserts everything just before this node...
23420 while (node.childNodes.length) {
23421 var cn = node.childNodes[0];
23422 node.removeChild(cn);
23423 node.parentNode.insertBefore(cn, node);
23425 node.parentNode.removeChild(node);
23429 if (!node.attributes || !node.attributes.length) {
23434 this.cleanUpChildren(node);
23438 function cleanAttr(n,v)
23441 if (v.match(/^\./) || v.match(/^\//)) {
23444 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23447 if (v.match(/^#/)) {
23450 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23451 node.removeAttribute(n);
23455 var cwhite = this.cwhite;
23456 var cblack = this.cblack;
23458 function cleanStyle(n,v)
23460 if (v.match(/expression/)) { //XSS?? should we even bother..
23461 node.removeAttribute(n);
23465 var parts = v.split(/;/);
23468 Roo.each(parts, function(p) {
23469 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23473 var l = p.split(':').shift().replace(/\s+/g,'');
23474 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23476 if ( cwhite.length && cblack.indexOf(l) > -1) {
23477 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23478 //node.removeAttribute(n);
23482 // only allow 'c whitelisted system attributes'
23483 if ( cwhite.length && cwhite.indexOf(l) < 0) {
23484 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23485 //node.removeAttribute(n);
23495 if (clean.length) {
23496 node.setAttribute(n, clean.join(';'));
23498 node.removeAttribute(n);
23504 for (var i = node.attributes.length-1; i > -1 ; i--) {
23505 var a = node.attributes[i];
23508 if (a.name.toLowerCase().substr(0,2)=='on') {
23509 node.removeAttribute(a.name);
23512 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23513 node.removeAttribute(a.name);
23516 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23517 cleanAttr(a.name,a.value); // fixme..
23520 if (a.name == 'style') {
23521 cleanStyle(a.name,a.value);
23524 /// clean up MS crap..
23525 // tecnically this should be a list of valid class'es..
23528 if (a.name == 'class') {
23529 if (a.value.match(/^Mso/)) {
23530 node.removeAttribute('class');
23533 if (a.value.match(/^body$/)) {
23534 node.removeAttribute('class');
23545 this.cleanUpChildren(node);
23551 * Clean up MS wordisms...
23553 cleanWord : function(node)
23556 this.cleanWord(this.doc.body);
23561 node.nodeName == 'SPAN' &&
23562 !node.hasAttributes() &&
23563 node.childNodes.length == 1 &&
23564 node.firstChild.nodeName == "#text"
23566 var textNode = node.firstChild;
23567 node.removeChild(textNode);
23568 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
23569 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
23571 node.parentNode.insertBefore(textNode, node);
23572 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
23573 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
23575 node.parentNode.removeChild(node);
23578 if (node.nodeName == "#text") {
23579 // clean up silly Windows -- stuff?
23582 if (node.nodeName == "#comment") {
23583 node.parentNode.removeChild(node);
23584 // clean up silly Windows -- stuff?
23588 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23589 node.parentNode.removeChild(node);
23592 //Roo.log(node.tagName);
23593 // remove - but keep children..
23594 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
23595 //Roo.log('-- removed');
23596 while (node.childNodes.length) {
23597 var cn = node.childNodes[0];
23598 node.removeChild(cn);
23599 node.parentNode.insertBefore(cn, node);
23600 // move node to parent - and clean it..
23601 this.cleanWord(cn);
23603 node.parentNode.removeChild(node);
23604 /// no need to iterate chidlren = it's got none..
23605 //this.iterateChildren(node, this.cleanWord);
23609 if (node.className.length) {
23611 var cn = node.className.split(/\W+/);
23613 Roo.each(cn, function(cls) {
23614 if (cls.match(/Mso[a-zA-Z]+/)) {
23619 node.className = cna.length ? cna.join(' ') : '';
23621 node.removeAttribute("class");
23625 if (node.hasAttribute("lang")) {
23626 node.removeAttribute("lang");
23629 if (node.hasAttribute("style")) {
23631 var styles = node.getAttribute("style").split(";");
23633 Roo.each(styles, function(s) {
23634 if (!s.match(/:/)) {
23637 var kv = s.split(":");
23638 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23641 // what ever is left... we allow.
23644 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23645 if (!nstyle.length) {
23646 node.removeAttribute('style');
23649 this.iterateChildren(node, this.cleanWord);
23655 * iterateChildren of a Node, calling fn each time, using this as the scole..
23656 * @param {DomNode} node node to iterate children of.
23657 * @param {Function} fn method of this class to call on each item.
23659 iterateChildren : function(node, fn)
23661 if (!node.childNodes.length) {
23664 for (var i = node.childNodes.length-1; i > -1 ; i--) {
23665 fn.call(this, node.childNodes[i])
23671 * cleanTableWidths.
23673 * Quite often pasting from word etc.. results in tables with column and widths.
23674 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23677 cleanTableWidths : function(node)
23682 this.cleanTableWidths(this.doc.body);
23687 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23690 Roo.log(node.tagName);
23691 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23692 this.iterateChildren(node, this.cleanTableWidths);
23695 if (node.hasAttribute('width')) {
23696 node.removeAttribute('width');
23700 if (node.hasAttribute("style")) {
23703 var styles = node.getAttribute("style").split(";");
23705 Roo.each(styles, function(s) {
23706 if (!s.match(/:/)) {
23709 var kv = s.split(":");
23710 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23713 // what ever is left... we allow.
23716 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23717 if (!nstyle.length) {
23718 node.removeAttribute('style');
23722 this.iterateChildren(node, this.cleanTableWidths);
23730 domToHTML : function(currentElement, depth, nopadtext) {
23732 depth = depth || 0;
23733 nopadtext = nopadtext || false;
23735 if (!currentElement) {
23736 return this.domToHTML(this.doc.body);
23739 //Roo.log(currentElement);
23741 var allText = false;
23742 var nodeName = currentElement.nodeName;
23743 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23745 if (nodeName == '#text') {
23747 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23752 if (nodeName != 'BODY') {
23755 // Prints the node tagName, such as <A>, <IMG>, etc
23758 for(i = 0; i < currentElement.attributes.length;i++) {
23760 var aname = currentElement.attributes.item(i).name;
23761 if (!currentElement.attributes.item(i).value.length) {
23764 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23767 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23776 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23779 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23784 // Traverse the tree
23786 var currentElementChild = currentElement.childNodes.item(i);
23787 var allText = true;
23788 var innerHTML = '';
23790 while (currentElementChild) {
23791 // Formatting code (indent the tree so it looks nice on the screen)
23792 var nopad = nopadtext;
23793 if (lastnode == 'SPAN') {
23797 if (currentElementChild.nodeName == '#text') {
23798 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23799 toadd = nopadtext ? toadd : toadd.trim();
23800 if (!nopad && toadd.length > 80) {
23801 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23803 innerHTML += toadd;
23806 currentElementChild = currentElement.childNodes.item(i);
23812 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23814 // Recursively traverse the tree structure of the child node
23815 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23816 lastnode = currentElementChild.nodeName;
23818 currentElementChild=currentElement.childNodes.item(i);
23824 // The remaining code is mostly for formatting the tree
23825 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23830 ret+= "</"+tagName+">";
23836 applyBlacklists : function()
23838 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23839 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23843 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23844 if (b.indexOf(tag) > -1) {
23847 this.white.push(tag);
23851 Roo.each(w, function(tag) {
23852 if (b.indexOf(tag) > -1) {
23855 if (this.white.indexOf(tag) > -1) {
23858 this.white.push(tag);
23863 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23864 if (w.indexOf(tag) > -1) {
23867 this.black.push(tag);
23871 Roo.each(b, function(tag) {
23872 if (w.indexOf(tag) > -1) {
23875 if (this.black.indexOf(tag) > -1) {
23878 this.black.push(tag);
23883 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23884 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23888 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23889 if (b.indexOf(tag) > -1) {
23892 this.cwhite.push(tag);
23896 Roo.each(w, function(tag) {
23897 if (b.indexOf(tag) > -1) {
23900 if (this.cwhite.indexOf(tag) > -1) {
23903 this.cwhite.push(tag);
23908 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23909 if (w.indexOf(tag) > -1) {
23912 this.cblack.push(tag);
23916 Roo.each(b, function(tag) {
23917 if (w.indexOf(tag) > -1) {
23920 if (this.cblack.indexOf(tag) > -1) {
23923 this.cblack.push(tag);
23928 setStylesheets : function(stylesheets)
23930 if(typeof(stylesheets) == 'string'){
23931 Roo.get(this.iframe.contentDocument.head).createChild({
23933 rel : 'stylesheet',
23942 Roo.each(stylesheets, function(s) {
23947 Roo.get(_this.iframe.contentDocument.head).createChild({
23949 rel : 'stylesheet',
23958 removeStylesheets : function()
23962 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23967 setStyle : function(style)
23969 Roo.get(this.iframe.contentDocument.head).createChild({
23978 // hide stuff that is not compatible
23992 * @event specialkey
23996 * @cfg {String} fieldClass @hide
23999 * @cfg {String} focusClass @hide
24002 * @cfg {String} autoCreate @hide
24005 * @cfg {String} inputType @hide
24008 * @cfg {String} invalidClass @hide
24011 * @cfg {String} invalidText @hide
24014 * @cfg {String} msgFx @hide
24017 * @cfg {String} validateOnBlur @hide
24021 Roo.HtmlEditorCore.white = [
24022 'area', 'br', 'img', 'input', 'hr', 'wbr',
24024 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
24025 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
24026 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
24027 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
24028 'table', 'ul', 'xmp',
24030 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
24033 'dir', 'menu', 'ol', 'ul', 'dl',
24039 Roo.HtmlEditorCore.black = [
24040 // 'embed', 'object', // enable - backend responsiblity to clean thiese
24042 'base', 'basefont', 'bgsound', 'blink', 'body',
24043 'frame', 'frameset', 'head', 'html', 'ilayer',
24044 'iframe', 'layer', 'link', 'meta', 'object',
24045 'script', 'style' ,'title', 'xml' // clean later..
24047 Roo.HtmlEditorCore.clean = [
24048 'script', 'style', 'title', 'xml'
24050 Roo.HtmlEditorCore.remove = [
24055 Roo.HtmlEditorCore.ablack = [
24059 Roo.HtmlEditorCore.aclean = [
24060 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24064 Roo.HtmlEditorCore.pwhite= [
24065 'http', 'https', 'mailto'
24068 // white listed style attributes.
24069 Roo.HtmlEditorCore.cwhite= [
24070 // 'text-align', /// default is to allow most things..
24076 // black listed style attributes.
24077 Roo.HtmlEditorCore.cblack= [
24078 // 'font-size' -- this can be set by the project
24082 Roo.HtmlEditorCore.swapCodes =[
24101 * @class Roo.bootstrap.HtmlEditor
24102 * @extends Roo.bootstrap.TextArea
24103 * Bootstrap HtmlEditor class
24106 * Create a new HtmlEditor
24107 * @param {Object} config The config object
24110 Roo.bootstrap.HtmlEditor = function(config){
24111 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
24112 if (!this.toolbars) {
24113 this.toolbars = [];
24116 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
24119 * @event initialize
24120 * Fires when the editor is fully initialized (including the iframe)
24121 * @param {HtmlEditor} this
24126 * Fires when the editor is first receives the focus. Any insertion must wait
24127 * until after this event.
24128 * @param {HtmlEditor} this
24132 * @event beforesync
24133 * Fires before the textarea is updated with content from the editor iframe. Return false
24134 * to cancel the sync.
24135 * @param {HtmlEditor} this
24136 * @param {String} html
24140 * @event beforepush
24141 * Fires before the iframe editor is updated with content from the textarea. Return false
24142 * to cancel the push.
24143 * @param {HtmlEditor} this
24144 * @param {String} html
24149 * Fires when the textarea is updated with content from the editor iframe.
24150 * @param {HtmlEditor} this
24151 * @param {String} html
24156 * Fires when the iframe editor is updated with content from the textarea.
24157 * @param {HtmlEditor} this
24158 * @param {String} html
24162 * @event editmodechange
24163 * Fires when the editor switches edit modes
24164 * @param {HtmlEditor} this
24165 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24167 editmodechange: true,
24169 * @event editorevent
24170 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24171 * @param {HtmlEditor} this
24175 * @event firstfocus
24176 * Fires when on first focus - needed by toolbars..
24177 * @param {HtmlEditor} this
24182 * Auto save the htmlEditor value as a file into Events
24183 * @param {HtmlEditor} this
24187 * @event savedpreview
24188 * preview the saved version of htmlEditor
24189 * @param {HtmlEditor} this
24196 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
24200 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24205 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
24210 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24215 * @cfg {Number} height (in pixels)
24219 * @cfg {Number} width (in pixels)
24224 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24227 stylesheets: false,
24232 // private properties
24233 validationEvent : false,
24235 initialized : false,
24238 onFocus : Roo.emptyFn,
24240 hideMode:'offsets',
24242 tbContainer : false,
24246 toolbarContainer :function() {
24247 return this.wrap.select('.x-html-editor-tb',true).first();
24251 * Protected method that will not generally be called directly. It
24252 * is called when the editor creates its toolbar. Override this method if you need to
24253 * add custom toolbar buttons.
24254 * @param {HtmlEditor} editor
24256 createToolbar : function(){
24257 Roo.log('renewing');
24258 Roo.log("create toolbars");
24260 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
24261 this.toolbars[0].render(this.toolbarContainer());
24265 // if (!editor.toolbars || !editor.toolbars.length) {
24266 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
24269 // for (var i =0 ; i < editor.toolbars.length;i++) {
24270 // editor.toolbars[i] = Roo.factory(
24271 // typeof(editor.toolbars[i]) == 'string' ?
24272 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
24273 // Roo.bootstrap.HtmlEditor);
24274 // editor.toolbars[i].init(editor);
24280 onRender : function(ct, position)
24282 // Roo.log("Call onRender: " + this.xtype);
24284 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
24286 this.wrap = this.inputEl().wrap({
24287 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24290 this.editorcore.onRender(ct, position);
24292 if (this.resizable) {
24293 this.resizeEl = new Roo.Resizable(this.wrap, {
24297 minHeight : this.height,
24298 height: this.height,
24299 handles : this.resizable,
24302 resize : function(r, w, h) {
24303 _t.onResize(w,h); // -something
24309 this.createToolbar(this);
24312 if(!this.width && this.resizable){
24313 this.setSize(this.wrap.getSize());
24315 if (this.resizeEl) {
24316 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24317 // should trigger onReize..
24323 onResize : function(w, h)
24325 Roo.log('resize: ' +w + ',' + h );
24326 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
24330 if(this.inputEl() ){
24331 if(typeof w == 'number'){
24332 var aw = w - this.wrap.getFrameWidth('lr');
24333 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24336 if(typeof h == 'number'){
24337 var tbh = -11; // fixme it needs to tool bar size!
24338 for (var i =0; i < this.toolbars.length;i++) {
24339 // fixme - ask toolbars for heights?
24340 tbh += this.toolbars[i].el.getHeight();
24341 //if (this.toolbars[i].footer) {
24342 // tbh += this.toolbars[i].footer.el.getHeight();
24350 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24351 ah -= 5; // knock a few pixes off for look..
24352 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24356 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24357 this.editorcore.onResize(ew,eh);
24362 * Toggles the editor between standard and source edit mode.
24363 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24365 toggleSourceEdit : function(sourceEditMode)
24367 this.editorcore.toggleSourceEdit(sourceEditMode);
24369 if(this.editorcore.sourceEditMode){
24370 Roo.log('editor - showing textarea');
24373 // Roo.log(this.syncValue());
24375 this.inputEl().removeClass(['hide', 'x-hidden']);
24376 this.inputEl().dom.removeAttribute('tabIndex');
24377 this.inputEl().focus();
24379 Roo.log('editor - hiding textarea');
24381 // Roo.log(this.pushValue());
24384 this.inputEl().addClass(['hide', 'x-hidden']);
24385 this.inputEl().dom.setAttribute('tabIndex', -1);
24386 //this.deferFocus();
24389 if(this.resizable){
24390 this.setSize(this.wrap.getSize());
24393 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24396 // private (for BoxComponent)
24397 adjustSize : Roo.BoxComponent.prototype.adjustSize,
24399 // private (for BoxComponent)
24400 getResizeEl : function(){
24404 // private (for BoxComponent)
24405 getPositionEl : function(){
24410 initEvents : function(){
24411 this.originalValue = this.getValue();
24415 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24418 // markInvalid : Roo.emptyFn,
24420 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24423 // clearInvalid : Roo.emptyFn,
24425 setValue : function(v){
24426 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24427 this.editorcore.pushValue();
24432 deferFocus : function(){
24433 this.focus.defer(10, this);
24437 focus : function(){
24438 this.editorcore.focus();
24444 onDestroy : function(){
24450 for (var i =0; i < this.toolbars.length;i++) {
24451 // fixme - ask toolbars for heights?
24452 this.toolbars[i].onDestroy();
24455 this.wrap.dom.innerHTML = '';
24456 this.wrap.remove();
24461 onFirstFocus : function(){
24462 //Roo.log("onFirstFocus");
24463 this.editorcore.onFirstFocus();
24464 for (var i =0; i < this.toolbars.length;i++) {
24465 this.toolbars[i].onFirstFocus();
24471 syncValue : function()
24473 this.editorcore.syncValue();
24476 pushValue : function()
24478 this.editorcore.pushValue();
24482 // hide stuff that is not compatible
24496 * @event specialkey
24500 * @cfg {String} fieldClass @hide
24503 * @cfg {String} focusClass @hide
24506 * @cfg {String} autoCreate @hide
24509 * @cfg {String} inputType @hide
24513 * @cfg {String} invalidText @hide
24516 * @cfg {String} msgFx @hide
24519 * @cfg {String} validateOnBlur @hide
24528 Roo.namespace('Roo.bootstrap.htmleditor');
24530 * @class Roo.bootstrap.HtmlEditorToolbar1
24536 new Roo.bootstrap.HtmlEditor({
24539 new Roo.bootstrap.HtmlEditorToolbar1({
24540 disable : { fonts: 1 , format: 1, ..., ... , ...],
24546 * @cfg {Object} disable List of elements to disable..
24547 * @cfg {Array} btns List of additional buttons.
24551 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24554 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24557 Roo.apply(this, config);
24559 // default disabled, based on 'good practice'..
24560 this.disable = this.disable || {};
24561 Roo.applyIf(this.disable, {
24564 specialElements : true
24566 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24568 this.editor = config.editor;
24569 this.editorcore = config.editor.editorcore;
24571 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24573 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24574 // dont call parent... till later.
24576 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
24581 editorcore : false,
24586 "h1","h2","h3","h4","h5","h6",
24588 "abbr", "acronym", "address", "cite", "samp", "var",
24592 onRender : function(ct, position)
24594 // Roo.log("Call onRender: " + this.xtype);
24596 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24598 this.el.dom.style.marginBottom = '0';
24600 var editorcore = this.editorcore;
24601 var editor= this.editor;
24604 var btn = function(id,cmd , toggle, handler, html){
24606 var event = toggle ? 'toggle' : 'click';
24611 xns: Roo.bootstrap,
24615 enableToggle:toggle !== false,
24617 pressed : toggle ? false : null,
24620 a.listeners[toggle ? 'toggle' : 'click'] = function() {
24621 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
24627 // var cb_box = function...
24632 xns: Roo.bootstrap,
24637 xns: Roo.bootstrap,
24641 Roo.each(this.formats, function(f) {
24642 style.menu.items.push({
24644 xns: Roo.bootstrap,
24645 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24650 editorcore.insertTag(this.tagname);
24657 children.push(style);
24659 btn('bold',false,true);
24660 btn('italic',false,true);
24661 btn('align-left', 'justifyleft',true);
24662 btn('align-center', 'justifycenter',true);
24663 btn('align-right' , 'justifyright',true);
24664 btn('link', false, false, function(btn) {
24665 //Roo.log("create link?");
24666 var url = prompt(this.createLinkText, this.defaultLinkValue);
24667 if(url && url != 'http:/'+'/'){
24668 this.editorcore.relayCmd('createlink', url);
24671 btn('list','insertunorderedlist',true);
24672 btn('pencil', false,true, function(btn){
24674 this.toggleSourceEdit(btn.pressed);
24677 if (this.editor.btns.length > 0) {
24678 for (var i = 0; i<this.editor.btns.length; i++) {
24679 children.push(this.editor.btns[i]);
24687 xns: Roo.bootstrap,
24692 xns: Roo.bootstrap,
24697 cog.menu.items.push({
24699 xns: Roo.bootstrap,
24700 html : Clean styles,
24705 editorcore.insertTag(this.tagname);
24714 this.xtype = 'NavSimplebar';
24716 for(var i=0;i< children.length;i++) {
24718 this.buttons.add(this.addxtypeChild(children[i]));
24722 editor.on('editorevent', this.updateToolbar, this);
24724 onBtnClick : function(id)
24726 this.editorcore.relayCmd(id);
24727 this.editorcore.focus();
24731 * Protected method that will not generally be called directly. It triggers
24732 * a toolbar update by reading the markup state of the current selection in the editor.
24734 updateToolbar: function(){
24736 if(!this.editorcore.activated){
24737 this.editor.onFirstFocus(); // is this neeed?
24741 var btns = this.buttons;
24742 var doc = this.editorcore.doc;
24743 btns.get('bold').setActive(doc.queryCommandState('bold'));
24744 btns.get('italic').setActive(doc.queryCommandState('italic'));
24745 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24747 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24748 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24749 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24751 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24752 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24755 var ans = this.editorcore.getAllAncestors();
24756 if (this.formatCombo) {
24759 var store = this.formatCombo.store;
24760 this.formatCombo.setValue("");
24761 for (var i =0; i < ans.length;i++) {
24762 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24764 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24772 // hides menus... - so this cant be on a menu...
24773 Roo.bootstrap.MenuMgr.hideAll();
24775 Roo.bootstrap.MenuMgr.hideAll();
24776 //this.editorsyncValue();
24778 onFirstFocus: function() {
24779 this.buttons.each(function(item){
24783 toggleSourceEdit : function(sourceEditMode){
24786 if(sourceEditMode){
24787 Roo.log("disabling buttons");
24788 this.buttons.each( function(item){
24789 if(item.cmd != 'pencil'){
24795 Roo.log("enabling buttons");
24796 if(this.editorcore.initialized){
24797 this.buttons.each( function(item){
24803 Roo.log("calling toggole on editor");
24804 // tell the editor that it's been pressed..
24805 this.editor.toggleSourceEdit(sourceEditMode);
24815 * @class Roo.bootstrap.Table.AbstractSelectionModel
24816 * @extends Roo.util.Observable
24817 * Abstract base class for grid SelectionModels. It provides the interface that should be
24818 * implemented by descendant classes. This class should not be directly instantiated.
24821 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24822 this.locked = false;
24823 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24827 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24828 /** @ignore Called by the grid automatically. Do not call directly. */
24829 init : function(grid){
24835 * Locks the selections.
24838 this.locked = true;
24842 * Unlocks the selections.
24844 unlock : function(){
24845 this.locked = false;
24849 * Returns true if the selections are locked.
24850 * @return {Boolean}
24852 isLocked : function(){
24853 return this.locked;
24857 initEvents : function ()
24863 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24864 * @class Roo.bootstrap.Table.RowSelectionModel
24865 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24866 * It supports multiple selections and keyboard selection/navigation.
24868 * @param {Object} config
24871 Roo.bootstrap.Table.RowSelectionModel = function(config){
24872 Roo.apply(this, config);
24873 this.selections = new Roo.util.MixedCollection(false, function(o){
24878 this.lastActive = false;
24882 * @event selectionchange
24883 * Fires when the selection changes
24884 * @param {SelectionModel} this
24886 "selectionchange" : true,
24888 * @event afterselectionchange
24889 * Fires after the selection changes (eg. by key press or clicking)
24890 * @param {SelectionModel} this
24892 "afterselectionchange" : true,
24894 * @event beforerowselect
24895 * Fires when a row is selected being selected, return false to cancel.
24896 * @param {SelectionModel} this
24897 * @param {Number} rowIndex The selected index
24898 * @param {Boolean} keepExisting False if other selections will be cleared
24900 "beforerowselect" : true,
24903 * Fires when a row is selected.
24904 * @param {SelectionModel} this
24905 * @param {Number} rowIndex The selected index
24906 * @param {Roo.data.Record} r The record
24908 "rowselect" : true,
24910 * @event rowdeselect
24911 * Fires when a row is deselected.
24912 * @param {SelectionModel} this
24913 * @param {Number} rowIndex The selected index
24915 "rowdeselect" : true
24917 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24918 this.locked = false;
24921 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24923 * @cfg {Boolean} singleSelect
24924 * True to allow selection of only one row at a time (defaults to false)
24926 singleSelect : false,
24929 initEvents : function()
24932 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24933 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24934 //}else{ // allow click to work like normal
24935 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24937 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24938 this.grid.on("rowclick", this.handleMouseDown, this);
24940 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24941 "up" : function(e){
24943 this.selectPrevious(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);
24956 "down" : function(e){
24958 this.selectNext(e.shiftKey);
24959 }else if(this.last !== false && this.lastActive !== false){
24960 var last = this.last;
24961 this.selectRange(this.last, this.lastActive+1);
24962 this.grid.getView().focusRow(this.lastActive);
24963 if(last !== false){
24967 this.selectFirstRow();
24969 this.fireEvent("afterselectionchange", this);
24973 this.grid.store.on('load', function(){
24974 this.selections.clear();
24977 var view = this.grid.view;
24978 view.on("refresh", this.onRefresh, this);
24979 view.on("rowupdated", this.onRowUpdated, this);
24980 view.on("rowremoved", this.onRemove, this);
24985 onRefresh : function()
24987 var ds = this.grid.store, i, v = this.grid.view;
24988 var s = this.selections;
24989 s.each(function(r){
24990 if((i = ds.indexOfId(r.id)) != -1){
24999 onRemove : function(v, index, r){
25000 this.selections.remove(r);
25004 onRowUpdated : function(v, index, r){
25005 if(this.isSelected(r)){
25006 v.onRowSelect(index);
25012 * @param {Array} records The records to select
25013 * @param {Boolean} keepExisting (optional) True to keep existing selections
25015 selectRecords : function(records, keepExisting)
25018 this.clearSelections();
25020 var ds = this.grid.store;
25021 for(var i = 0, len = records.length; i < len; i++){
25022 this.selectRow(ds.indexOf(records[i]), true);
25027 * Gets the number of selected rows.
25030 getCount : function(){
25031 return this.selections.length;
25035 * Selects the first row in the grid.
25037 selectFirstRow : function(){
25042 * Select the last row.
25043 * @param {Boolean} keepExisting (optional) True to keep existing selections
25045 selectLastRow : function(keepExisting){
25046 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
25047 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
25051 * Selects the row immediately following the last selected row.
25052 * @param {Boolean} keepExisting (optional) True to keep existing selections
25054 selectNext : function(keepExisting)
25056 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
25057 this.selectRow(this.last+1, keepExisting);
25058 this.grid.getView().focusRow(this.last);
25063 * Selects the row that precedes the last selected row.
25064 * @param {Boolean} keepExisting (optional) True to keep existing selections
25066 selectPrevious : function(keepExisting){
25068 this.selectRow(this.last-1, keepExisting);
25069 this.grid.getView().focusRow(this.last);
25074 * Returns the selected records
25075 * @return {Array} Array of selected records
25077 getSelections : function(){
25078 return [].concat(this.selections.items);
25082 * Returns the first selected record.
25085 getSelected : function(){
25086 return this.selections.itemAt(0);
25091 * Clears all selections.
25093 clearSelections : function(fast)
25099 var ds = this.grid.store;
25100 var s = this.selections;
25101 s.each(function(r){
25102 this.deselectRow(ds.indexOfId(r.id));
25106 this.selections.clear();
25113 * Selects all rows.
25115 selectAll : function(){
25119 this.selections.clear();
25120 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
25121 this.selectRow(i, true);
25126 * Returns True if there is a selection.
25127 * @return {Boolean}
25129 hasSelection : function(){
25130 return this.selections.length > 0;
25134 * Returns True if the specified row is selected.
25135 * @param {Number/Record} record The record or index of the record to check
25136 * @return {Boolean}
25138 isSelected : function(index){
25139 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
25140 return (r && this.selections.key(r.id) ? true : false);
25144 * Returns True if the specified record id is selected.
25145 * @param {String} id The id of record to check
25146 * @return {Boolean}
25148 isIdSelected : function(id){
25149 return (this.selections.key(id) ? true : false);
25154 handleMouseDBClick : function(e, t){
25158 handleMouseDown : function(e, t)
25160 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
25161 if(this.isLocked() || rowIndex < 0 ){
25164 if(e.shiftKey && this.last !== false){
25165 var last = this.last;
25166 this.selectRange(last, rowIndex, e.ctrlKey);
25167 this.last = last; // reset the last
25171 var isSelected = this.isSelected(rowIndex);
25172 //Roo.log("select row:" + rowIndex);
25174 this.deselectRow(rowIndex);
25176 this.selectRow(rowIndex, true);
25180 if(e.button !== 0 && isSelected){
25181 alert('rowIndex 2: ' + rowIndex);
25182 view.focusRow(rowIndex);
25183 }else if(e.ctrlKey && isSelected){
25184 this.deselectRow(rowIndex);
25185 }else if(!isSelected){
25186 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
25187 view.focusRow(rowIndex);
25191 this.fireEvent("afterselectionchange", this);
25194 handleDragableRowClick : function(grid, rowIndex, e)
25196 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
25197 this.selectRow(rowIndex, false);
25198 grid.view.focusRow(rowIndex);
25199 this.fireEvent("afterselectionchange", this);
25204 * Selects multiple rows.
25205 * @param {Array} rows Array of the indexes of the row to select
25206 * @param {Boolean} keepExisting (optional) True to keep existing selections
25208 selectRows : function(rows, keepExisting){
25210 this.clearSelections();
25212 for(var i = 0, len = rows.length; i < len; i++){
25213 this.selectRow(rows[i], true);
25218 * Selects a range of rows. All rows in between startRow and endRow are also selected.
25219 * @param {Number} startRow The index of the first row in the range
25220 * @param {Number} endRow The index of the last row in the range
25221 * @param {Boolean} keepExisting (optional) True to retain existing selections
25223 selectRange : function(startRow, endRow, keepExisting){
25228 this.clearSelections();
25230 if(startRow <= endRow){
25231 for(var i = startRow; i <= endRow; i++){
25232 this.selectRow(i, true);
25235 for(var i = startRow; i >= endRow; i--){
25236 this.selectRow(i, true);
25242 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
25243 * @param {Number} startRow The index of the first row in the range
25244 * @param {Number} endRow The index of the last row in the range
25246 deselectRange : function(startRow, endRow, preventViewNotify){
25250 for(var i = startRow; i <= endRow; i++){
25251 this.deselectRow(i, preventViewNotify);
25257 * @param {Number} row The index of the row to select
25258 * @param {Boolean} keepExisting (optional) True to keep existing selections
25260 selectRow : function(index, keepExisting, preventViewNotify)
25262 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
25265 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
25266 if(!keepExisting || this.singleSelect){
25267 this.clearSelections();
25270 var r = this.grid.store.getAt(index);
25271 //console.log('selectRow - record id :' + r.id);
25273 this.selections.add(r);
25274 this.last = this.lastActive = index;
25275 if(!preventViewNotify){
25276 var proxy = new Roo.Element(
25277 this.grid.getRowDom(index)
25279 proxy.addClass('bg-info info');
25281 this.fireEvent("rowselect", this, index, r);
25282 this.fireEvent("selectionchange", this);
25288 * @param {Number} row The index of the row to deselect
25290 deselectRow : function(index, preventViewNotify)
25295 if(this.last == index){
25298 if(this.lastActive == index){
25299 this.lastActive = false;
25302 var r = this.grid.store.getAt(index);
25307 this.selections.remove(r);
25308 //.console.log('deselectRow - record id :' + r.id);
25309 if(!preventViewNotify){
25311 var proxy = new Roo.Element(
25312 this.grid.getRowDom(index)
25314 proxy.removeClass('bg-info info');
25316 this.fireEvent("rowdeselect", this, index);
25317 this.fireEvent("selectionchange", this);
25321 restoreLast : function(){
25323 this.last = this._last;
25328 acceptsNav : function(row, col, cm){
25329 return !cm.isHidden(col) && cm.isCellEditable(col, row);
25333 onEditorKey : function(field, e){
25334 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25339 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25341 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25343 }else if(k == e.ENTER && !e.ctrlKey){
25347 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25349 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25351 }else if(k == e.ESC){
25355 g.startEditing(newCell[0], newCell[1]);
25361 * Ext JS Library 1.1.1
25362 * Copyright(c) 2006-2007, Ext JS, LLC.
25364 * Originally Released Under LGPL - original licence link has changed is not relivant.
25367 * <script type="text/javascript">
25371 * @class Roo.bootstrap.PagingToolbar
25372 * @extends Roo.bootstrap.NavSimplebar
25373 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25375 * Create a new PagingToolbar
25376 * @param {Object} config The config object
25377 * @param {Roo.data.Store} store
25379 Roo.bootstrap.PagingToolbar = function(config)
25381 // old args format still supported... - xtype is prefered..
25382 // created from xtype...
25384 this.ds = config.dataSource;
25386 if (config.store && !this.ds) {
25387 this.store= Roo.factory(config.store, Roo.data);
25388 this.ds = this.store;
25389 this.ds.xmodule = this.xmodule || false;
25392 this.toolbarItems = [];
25393 if (config.items) {
25394 this.toolbarItems = config.items;
25397 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25402 this.bind(this.ds);
25405 if (Roo.bootstrap.version == 4) {
25406 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25408 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25413 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25415 * @cfg {Roo.data.Store} dataSource
25416 * The underlying data store providing the paged data
25419 * @cfg {String/HTMLElement/Element} container
25420 * container The id or element that will contain the toolbar
25423 * @cfg {Boolean} displayInfo
25424 * True to display the displayMsg (defaults to false)
25427 * @cfg {Number} pageSize
25428 * The number of records to display per page (defaults to 20)
25432 * @cfg {String} displayMsg
25433 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25435 displayMsg : 'Displaying {0} - {1} of {2}',
25437 * @cfg {String} emptyMsg
25438 * The message to display when no records are found (defaults to "No data to display")
25440 emptyMsg : 'No data to display',
25442 * Customizable piece of the default paging text (defaults to "Page")
25445 beforePageText : "Page",
25447 * Customizable piece of the default paging text (defaults to "of %0")
25450 afterPageText : "of {0}",
25452 * Customizable piece of the default paging text (defaults to "First Page")
25455 firstText : "First Page",
25457 * Customizable piece of the default paging text (defaults to "Previous Page")
25460 prevText : "Previous Page",
25462 * Customizable piece of the default paging text (defaults to "Next Page")
25465 nextText : "Next Page",
25467 * Customizable piece of the default paging text (defaults to "Last Page")
25470 lastText : "Last Page",
25472 * Customizable piece of the default paging text (defaults to "Refresh")
25475 refreshText : "Refresh",
25479 onRender : function(ct, position)
25481 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25482 this.navgroup.parentId = this.id;
25483 this.navgroup.onRender(this.el, null);
25484 // add the buttons to the navgroup
25486 if(this.displayInfo){
25487 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25488 this.displayEl = this.el.select('.x-paging-info', true).first();
25489 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25490 // this.displayEl = navel.el.select('span',true).first();
25496 Roo.each(_this.buttons, function(e){ // this might need to use render????
25497 Roo.factory(e).render(_this.el);
25501 Roo.each(_this.toolbarItems, function(e) {
25502 _this.navgroup.addItem(e);
25506 this.first = this.navgroup.addItem({
25507 tooltip: this.firstText,
25508 cls: "prev btn-outline-secondary",
25509 html : ' <i class="fa fa-step-backward"></i>',
25511 preventDefault: true,
25512 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25515 this.prev = this.navgroup.addItem({
25516 tooltip: this.prevText,
25517 cls: "prev btn-outline-secondary",
25518 html : ' <i class="fa fa-backward"></i>',
25520 preventDefault: true,
25521 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
25523 //this.addSeparator();
25526 var field = this.navgroup.addItem( {
25528 cls : 'x-paging-position btn-outline-secondary',
25530 html : this.beforePageText +
25531 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25532 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
25535 this.field = field.el.select('input', true).first();
25536 this.field.on("keydown", this.onPagingKeydown, this);
25537 this.field.on("focus", function(){this.dom.select();});
25540 this.afterTextEl = field.el.select('.x-paging-after',true).first();
25541 //this.field.setHeight(18);
25542 //this.addSeparator();
25543 this.next = this.navgroup.addItem({
25544 tooltip: this.nextText,
25545 cls: "next btn-outline-secondary",
25546 html : ' <i class="fa fa-forward"></i>',
25548 preventDefault: true,
25549 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
25551 this.last = this.navgroup.addItem({
25552 tooltip: this.lastText,
25553 html : ' <i class="fa fa-step-forward"></i>',
25554 cls: "next btn-outline-secondary",
25556 preventDefault: true,
25557 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
25559 //this.addSeparator();
25560 this.loading = this.navgroup.addItem({
25561 tooltip: this.refreshText,
25562 cls: "btn-outline-secondary",
25563 html : ' <i class="fa fa-refresh"></i>',
25564 preventDefault: true,
25565 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25571 updateInfo : function(){
25572 if(this.displayEl){
25573 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25574 var msg = count == 0 ?
25578 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
25580 this.displayEl.update(msg);
25585 onLoad : function(ds, r, o)
25587 this.cursor = o.params.start ? o.params.start : 0;
25589 var d = this.getPageData(),
25594 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25595 this.field.dom.value = ap;
25596 this.first.setDisabled(ap == 1);
25597 this.prev.setDisabled(ap == 1);
25598 this.next.setDisabled(ap == ps);
25599 this.last.setDisabled(ap == ps);
25600 this.loading.enable();
25605 getPageData : function(){
25606 var total = this.ds.getTotalCount();
25609 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25610 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25615 onLoadError : function(){
25616 this.loading.enable();
25620 onPagingKeydown : function(e){
25621 var k = e.getKey();
25622 var d = this.getPageData();
25624 var v = this.field.dom.value, pageNum;
25625 if(!v || isNaN(pageNum = parseInt(v, 10))){
25626 this.field.dom.value = d.activePage;
25629 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25630 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25633 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))
25635 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25636 this.field.dom.value = pageNum;
25637 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25640 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25642 var v = this.field.dom.value, pageNum;
25643 var increment = (e.shiftKey) ? 10 : 1;
25644 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25647 if(!v || isNaN(pageNum = parseInt(v, 10))) {
25648 this.field.dom.value = d.activePage;
25651 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25653 this.field.dom.value = parseInt(v, 10) + increment;
25654 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25655 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25662 beforeLoad : function(){
25664 this.loading.disable();
25669 onClick : function(which){
25678 ds.load({params:{start: 0, limit: this.pageSize}});
25681 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25684 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25687 var total = ds.getTotalCount();
25688 var extra = total % this.pageSize;
25689 var lastStart = extra ? (total - extra) : total-this.pageSize;
25690 ds.load({params:{start: lastStart, limit: this.pageSize}});
25693 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25699 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25700 * @param {Roo.data.Store} store The data store to unbind
25702 unbind : function(ds){
25703 ds.un("beforeload", this.beforeLoad, this);
25704 ds.un("load", this.onLoad, this);
25705 ds.un("loadexception", this.onLoadError, this);
25706 ds.un("remove", this.updateInfo, this);
25707 ds.un("add", this.updateInfo, this);
25708 this.ds = undefined;
25712 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25713 * @param {Roo.data.Store} store The data store to bind
25715 bind : function(ds){
25716 ds.on("beforeload", this.beforeLoad, this);
25717 ds.on("load", this.onLoad, this);
25718 ds.on("loadexception", this.onLoadError, this);
25719 ds.on("remove", this.updateInfo, this);
25720 ds.on("add", this.updateInfo, this);
25731 * @class Roo.bootstrap.MessageBar
25732 * @extends Roo.bootstrap.Component
25733 * Bootstrap MessageBar class
25734 * @cfg {String} html contents of the MessageBar
25735 * @cfg {String} weight (info | success | warning | danger) default info
25736 * @cfg {String} beforeClass insert the bar before the given class
25737 * @cfg {Boolean} closable (true | false) default false
25738 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25741 * Create a new Element
25742 * @param {Object} config The config object
25745 Roo.bootstrap.MessageBar = function(config){
25746 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25749 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25755 beforeClass: 'bootstrap-sticky-wrap',
25757 getAutoCreate : function(){
25761 cls: 'alert alert-dismissable alert-' + this.weight,
25766 html: this.html || ''
25772 cfg.cls += ' alert-messages-fixed';
25786 onRender : function(ct, position)
25788 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25791 var cfg = Roo.apply({}, this.getAutoCreate());
25795 cfg.cls += ' ' + this.cls;
25798 cfg.style = this.style;
25800 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25802 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25805 this.el.select('>button.close').on('click', this.hide, this);
25811 if (!this.rendered) {
25817 this.fireEvent('show', this);
25823 if (!this.rendered) {
25829 this.fireEvent('hide', this);
25832 update : function()
25834 // var e = this.el.dom.firstChild;
25836 // if(this.closable){
25837 // e = e.nextSibling;
25840 // e.data = this.html || '';
25842 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25858 * @class Roo.bootstrap.Graph
25859 * @extends Roo.bootstrap.Component
25860 * Bootstrap Graph class
25864 @cfg {String} graphtype bar | vbar | pie
25865 @cfg {number} g_x coodinator | centre x (pie)
25866 @cfg {number} g_y coodinator | centre y (pie)
25867 @cfg {number} g_r radius (pie)
25868 @cfg {number} g_height height of the chart (respected by all elements in the set)
25869 @cfg {number} g_width width of the chart (respected by all elements in the set)
25870 @cfg {Object} title The title of the chart
25873 -opts (object) options for the chart
25875 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25876 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25878 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.
25879 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25881 o stretch (boolean)
25883 -opts (object) options for the pie
25886 o startAngle (number)
25887 o endAngle (number)
25891 * Create a new Input
25892 * @param {Object} config The config object
25895 Roo.bootstrap.Graph = function(config){
25896 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25902 * The img click event for the img.
25903 * @param {Roo.EventObject} e
25909 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25920 //g_colors: this.colors,
25927 getAutoCreate : function(){
25938 onRender : function(ct,position){
25941 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25943 if (typeof(Raphael) == 'undefined') {
25944 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25948 this.raphael = Raphael(this.el.dom);
25950 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25951 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25952 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25953 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25955 r.text(160, 10, "Single Series Chart").attr(txtattr);
25956 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25957 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25958 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25960 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25961 r.barchart(330, 10, 300, 220, data1);
25962 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25963 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25966 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25967 // r.barchart(30, 30, 560, 250, xdata, {
25968 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25969 // axis : "0 0 1 1",
25970 // axisxlabels : xdata
25971 // //yvalues : cols,
25974 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25976 // this.load(null,xdata,{
25977 // axis : "0 0 1 1",
25978 // axisxlabels : xdata
25983 load : function(graphtype,xdata,opts)
25985 this.raphael.clear();
25987 graphtype = this.graphtype;
25992 var r = this.raphael,
25993 fin = function () {
25994 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25996 fout = function () {
25997 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25999 pfin = function() {
26000 this.sector.stop();
26001 this.sector.scale(1.1, 1.1, this.cx, this.cy);
26004 this.label[0].stop();
26005 this.label[0].attr({ r: 7.5 });
26006 this.label[1].attr({ "font-weight": 800 });
26009 pfout = function() {
26010 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
26013 this.label[0].animate({ r: 5 }, 500, "bounce");
26014 this.label[1].attr({ "font-weight": 400 });
26020 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26023 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26026 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
26027 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
26029 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
26036 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
26041 setTitle: function(o)
26046 initEvents: function() {
26049 this.el.on('click', this.onClick, this);
26053 onClick : function(e)
26055 Roo.log('img onclick');
26056 this.fireEvent('click', this, e);
26068 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26071 * @class Roo.bootstrap.dash.NumberBox
26072 * @extends Roo.bootstrap.Component
26073 * Bootstrap NumberBox class
26074 * @cfg {String} headline Box headline
26075 * @cfg {String} content Box content
26076 * @cfg {String} icon Box icon
26077 * @cfg {String} footer Footer text
26078 * @cfg {String} fhref Footer href
26081 * Create a new NumberBox
26082 * @param {Object} config The config object
26086 Roo.bootstrap.dash.NumberBox = function(config){
26087 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
26091 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
26100 getAutoCreate : function(){
26104 cls : 'small-box ',
26112 cls : 'roo-headline',
26113 html : this.headline
26117 cls : 'roo-content',
26118 html : this.content
26132 cls : 'ion ' + this.icon
26141 cls : 'small-box-footer',
26142 href : this.fhref || '#',
26146 cfg.cn.push(footer);
26153 onRender : function(ct,position){
26154 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
26161 setHeadline: function (value)
26163 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
26166 setFooter: function (value, href)
26168 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
26171 this.el.select('a.small-box-footer',true).first().attr('href', href);
26176 setContent: function (value)
26178 this.el.select('.roo-content',true).first().dom.innerHTML = value;
26181 initEvents: function()
26195 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26198 * @class Roo.bootstrap.dash.TabBox
26199 * @extends Roo.bootstrap.Component
26200 * Bootstrap TabBox class
26201 * @cfg {String} title Title of the TabBox
26202 * @cfg {String} icon Icon of the TabBox
26203 * @cfg {Boolean} showtabs (true|false) show the tabs default true
26204 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
26207 * Create a new TabBox
26208 * @param {Object} config The config object
26212 Roo.bootstrap.dash.TabBox = function(config){
26213 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
26218 * When a pane is added
26219 * @param {Roo.bootstrap.dash.TabPane} pane
26223 * @event activatepane
26224 * When a pane is activated
26225 * @param {Roo.bootstrap.dash.TabPane} pane
26227 "activatepane" : true
26235 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
26240 tabScrollable : false,
26242 getChildContainer : function()
26244 return this.el.select('.tab-content', true).first();
26247 getAutoCreate : function(){
26251 cls: 'pull-left header',
26259 cls: 'fa ' + this.icon
26265 cls: 'nav nav-tabs pull-right',
26271 if(this.tabScrollable){
26278 cls: 'nav nav-tabs pull-right',
26289 cls: 'nav-tabs-custom',
26294 cls: 'tab-content no-padding',
26302 initEvents : function()
26304 //Roo.log('add add pane handler');
26305 this.on('addpane', this.onAddPane, this);
26308 * Updates the box title
26309 * @param {String} html to set the title to.
26311 setTitle : function(value)
26313 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
26315 onAddPane : function(pane)
26317 this.panes.push(pane);
26318 //Roo.log('addpane');
26320 // tabs are rendere left to right..
26321 if(!this.showtabs){
26325 var ctr = this.el.select('.nav-tabs', true).first();
26328 var existing = ctr.select('.nav-tab',true);
26329 var qty = existing.getCount();;
26332 var tab = ctr.createChild({
26334 cls : 'nav-tab' + (qty ? '' : ' active'),
26342 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26345 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26347 pane.el.addClass('active');
26352 onTabClick : function(ev,un,ob,pane)
26354 //Roo.log('tab - prev default');
26355 ev.preventDefault();
26358 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26359 pane.tab.addClass('active');
26360 //Roo.log(pane.title);
26361 this.getChildContainer().select('.tab-pane',true).removeClass('active');
26362 // technically we should have a deactivate event.. but maybe add later.
26363 // and it should not de-activate the selected tab...
26364 this.fireEvent('activatepane', pane);
26365 pane.el.addClass('active');
26366 pane.fireEvent('activate');
26371 getActivePane : function()
26374 Roo.each(this.panes, function(p) {
26375 if(p.el.hasClass('active')){
26396 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26398 * @class Roo.bootstrap.TabPane
26399 * @extends Roo.bootstrap.Component
26400 * Bootstrap TabPane class
26401 * @cfg {Boolean} active (false | true) Default false
26402 * @cfg {String} title title of panel
26406 * Create a new TabPane
26407 * @param {Object} config The config object
26410 Roo.bootstrap.dash.TabPane = function(config){
26411 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26417 * When a pane is activated
26418 * @param {Roo.bootstrap.dash.TabPane} pane
26425 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
26430 // the tabBox that this is attached to.
26433 getAutoCreate : function()
26441 cfg.cls += ' active';
26446 initEvents : function()
26448 //Roo.log('trigger add pane handler');
26449 this.parent().fireEvent('addpane', this)
26453 * Updates the tab title
26454 * @param {String} html to set the title to.
26456 setTitle: function(str)
26462 this.tab.select('a', true).first().dom.innerHTML = str;
26479 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26482 * @class Roo.bootstrap.menu.Menu
26483 * @extends Roo.bootstrap.Component
26484 * Bootstrap Menu class - container for Menu
26485 * @cfg {String} html Text of the menu
26486 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26487 * @cfg {String} icon Font awesome icon
26488 * @cfg {String} pos Menu align to (top | bottom) default bottom
26492 * Create a new Menu
26493 * @param {Object} config The config object
26497 Roo.bootstrap.menu.Menu = function(config){
26498 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26502 * @event beforeshow
26503 * Fires before this menu is displayed
26504 * @param {Roo.bootstrap.menu.Menu} this
26508 * @event beforehide
26509 * Fires before this menu is hidden
26510 * @param {Roo.bootstrap.menu.Menu} this
26515 * Fires after this menu is displayed
26516 * @param {Roo.bootstrap.menu.Menu} this
26521 * Fires after this menu is hidden
26522 * @param {Roo.bootstrap.menu.Menu} this
26527 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26528 * @param {Roo.bootstrap.menu.Menu} this
26529 * @param {Roo.EventObject} e
26536 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
26540 weight : 'default',
26545 getChildContainer : function() {
26546 if(this.isSubMenu){
26550 return this.el.select('ul.dropdown-menu', true).first();
26553 getAutoCreate : function()
26558 cls : 'roo-menu-text',
26566 cls : 'fa ' + this.icon
26577 cls : 'dropdown-button btn btn-' + this.weight,
26582 cls : 'dropdown-toggle btn btn-' + this.weight,
26592 cls : 'dropdown-menu'
26598 if(this.pos == 'top'){
26599 cfg.cls += ' dropup';
26602 if(this.isSubMenu){
26605 cls : 'dropdown-menu'
26612 onRender : function(ct, position)
26614 this.isSubMenu = ct.hasClass('dropdown-submenu');
26616 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26619 initEvents : function()
26621 if(this.isSubMenu){
26625 this.hidden = true;
26627 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26628 this.triggerEl.on('click', this.onTriggerPress, this);
26630 this.buttonEl = this.el.select('button.dropdown-button', true).first();
26631 this.buttonEl.on('click', this.onClick, this);
26637 if(this.isSubMenu){
26641 return this.el.select('ul.dropdown-menu', true).first();
26644 onClick : function(e)
26646 this.fireEvent("click", this, e);
26649 onTriggerPress : function(e)
26651 if (this.isVisible()) {
26658 isVisible : function(){
26659 return !this.hidden;
26664 this.fireEvent("beforeshow", this);
26666 this.hidden = false;
26667 this.el.addClass('open');
26669 Roo.get(document).on("mouseup", this.onMouseUp, this);
26671 this.fireEvent("show", this);
26678 this.fireEvent("beforehide", this);
26680 this.hidden = true;
26681 this.el.removeClass('open');
26683 Roo.get(document).un("mouseup", this.onMouseUp);
26685 this.fireEvent("hide", this);
26688 onMouseUp : function()
26702 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26705 * @class Roo.bootstrap.menu.Item
26706 * @extends Roo.bootstrap.Component
26707 * Bootstrap MenuItem class
26708 * @cfg {Boolean} submenu (true | false) default false
26709 * @cfg {String} html text of the item
26710 * @cfg {String} href the link
26711 * @cfg {Boolean} disable (true | false) default false
26712 * @cfg {Boolean} preventDefault (true | false) default true
26713 * @cfg {String} icon Font awesome icon
26714 * @cfg {String} pos Submenu align to (left | right) default right
26718 * Create a new Item
26719 * @param {Object} config The config object
26723 Roo.bootstrap.menu.Item = function(config){
26724 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26728 * Fires when the mouse is hovering over this menu
26729 * @param {Roo.bootstrap.menu.Item} this
26730 * @param {Roo.EventObject} e
26735 * Fires when the mouse exits this menu
26736 * @param {Roo.bootstrap.menu.Item} this
26737 * @param {Roo.EventObject} e
26743 * The raw click event for the entire grid.
26744 * @param {Roo.EventObject} e
26750 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26755 preventDefault: true,
26760 getAutoCreate : function()
26765 cls : 'roo-menu-item-text',
26773 cls : 'fa ' + this.icon
26782 href : this.href || '#',
26789 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26793 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26795 if(this.pos == 'left'){
26796 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26803 initEvents : function()
26805 this.el.on('mouseover', this.onMouseOver, this);
26806 this.el.on('mouseout', this.onMouseOut, this);
26808 this.el.select('a', true).first().on('click', this.onClick, this);
26812 onClick : function(e)
26814 if(this.preventDefault){
26815 e.preventDefault();
26818 this.fireEvent("click", this, e);
26821 onMouseOver : function(e)
26823 if(this.submenu && this.pos == 'left'){
26824 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26827 this.fireEvent("mouseover", this, e);
26830 onMouseOut : function(e)
26832 this.fireEvent("mouseout", this, e);
26844 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26847 * @class Roo.bootstrap.menu.Separator
26848 * @extends Roo.bootstrap.Component
26849 * Bootstrap Separator class
26852 * Create a new Separator
26853 * @param {Object} config The config object
26857 Roo.bootstrap.menu.Separator = function(config){
26858 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26861 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26863 getAutoCreate : function(){
26884 * @class Roo.bootstrap.Tooltip
26885 * Bootstrap Tooltip class
26886 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26887 * to determine which dom element triggers the tooltip.
26889 * It needs to add support for additional attributes like tooltip-position
26892 * Create a new Toolti
26893 * @param {Object} config The config object
26896 Roo.bootstrap.Tooltip = function(config){
26897 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26899 this.alignment = Roo.bootstrap.Tooltip.alignment;
26901 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26902 this.alignment = config.alignment;
26907 Roo.apply(Roo.bootstrap.Tooltip, {
26909 * @function init initialize tooltip monitoring.
26913 currentTip : false,
26914 currentRegion : false,
26920 Roo.get(document).on('mouseover', this.enter ,this);
26921 Roo.get(document).on('mouseout', this.leave, this);
26924 this.currentTip = new Roo.bootstrap.Tooltip();
26927 enter : function(ev)
26929 var dom = ev.getTarget();
26931 //Roo.log(['enter',dom]);
26932 var el = Roo.fly(dom);
26933 if (this.currentEl) {
26935 //Roo.log(this.currentEl);
26936 //Roo.log(this.currentEl.contains(dom));
26937 if (this.currentEl == el) {
26940 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26946 if (this.currentTip.el) {
26947 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26951 if(!el || el.dom == document){
26957 // you can not look for children, as if el is the body.. then everythign is the child..
26958 if (!el.attr('tooltip')) { //
26959 if (!el.select("[tooltip]").elements.length) {
26962 // is the mouse over this child...?
26963 bindEl = el.select("[tooltip]").first();
26964 var xy = ev.getXY();
26965 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26966 //Roo.log("not in region.");
26969 //Roo.log("child element over..");
26972 this.currentEl = bindEl;
26973 this.currentTip.bind(bindEl);
26974 this.currentRegion = Roo.lib.Region.getRegion(dom);
26975 this.currentTip.enter();
26978 leave : function(ev)
26980 var dom = ev.getTarget();
26981 //Roo.log(['leave',dom]);
26982 if (!this.currentEl) {
26987 if (dom != this.currentEl.dom) {
26990 var xy = ev.getXY();
26991 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26994 // only activate leave if mouse cursor is outside... bounding box..
26999 if (this.currentTip) {
27000 this.currentTip.leave();
27002 //Roo.log('clear currentEl');
27003 this.currentEl = false;
27008 'left' : ['r-l', [-2,0], 'right'],
27009 'right' : ['l-r', [2,0], 'left'],
27010 'bottom' : ['t-b', [0,2], 'top'],
27011 'top' : [ 'b-t', [0,-2], 'bottom']
27017 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
27022 delay : null, // can be { show : 300 , hide: 500}
27026 hoverState : null, //???
27028 placement : 'bottom',
27032 getAutoCreate : function(){
27039 cls : 'tooltip-arrow'
27042 cls : 'tooltip-inner'
27049 bind : function(el)
27055 enter : function () {
27057 if (this.timeout != null) {
27058 clearTimeout(this.timeout);
27061 this.hoverState = 'in';
27062 //Roo.log("enter - show");
27063 if (!this.delay || !this.delay.show) {
27068 this.timeout = setTimeout(function () {
27069 if (_t.hoverState == 'in') {
27072 }, this.delay.show);
27076 clearTimeout(this.timeout);
27078 this.hoverState = 'out';
27079 if (!this.delay || !this.delay.hide) {
27085 this.timeout = setTimeout(function () {
27086 //Roo.log("leave - timeout");
27088 if (_t.hoverState == 'out') {
27090 Roo.bootstrap.Tooltip.currentEl = false;
27095 show : function (msg)
27098 this.render(document.body);
27101 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
27103 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
27105 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
27107 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
27109 var placement = typeof this.placement == 'function' ?
27110 this.placement.call(this, this.el, on_el) :
27113 var autoToken = /\s?auto?\s?/i;
27114 var autoPlace = autoToken.test(placement);
27116 placement = placement.replace(autoToken, '') || 'top';
27120 //this.el.setXY([0,0]);
27122 //this.el.dom.style.display='block';
27124 //this.el.appendTo(on_el);
27126 var p = this.getPosition();
27127 var box = this.el.getBox();
27133 var align = this.alignment[placement];
27135 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
27137 if(placement == 'top' || placement == 'bottom'){
27139 placement = 'right';
27142 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
27143 placement = 'left';
27146 var scroll = Roo.select('body', true).first().getScroll();
27148 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
27152 align = this.alignment[placement];
27155 this.el.alignTo(this.bindEl, align[0],align[1]);
27156 //var arrow = this.el.select('.arrow',true).first();
27157 //arrow.set(align[2],
27159 this.el.addClass(placement);
27161 this.el.addClass('in fade');
27163 this.hoverState = null;
27165 if (this.el.hasClass('fade')) {
27176 //this.el.setXY([0,0]);
27177 this.el.removeClass('in');
27193 * @class Roo.bootstrap.LocationPicker
27194 * @extends Roo.bootstrap.Component
27195 * Bootstrap LocationPicker class
27196 * @cfg {Number} latitude Position when init default 0
27197 * @cfg {Number} longitude Position when init default 0
27198 * @cfg {Number} zoom default 15
27199 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
27200 * @cfg {Boolean} mapTypeControl default false
27201 * @cfg {Boolean} disableDoubleClickZoom default false
27202 * @cfg {Boolean} scrollwheel default true
27203 * @cfg {Boolean} streetViewControl default false
27204 * @cfg {Number} radius default 0
27205 * @cfg {String} locationName
27206 * @cfg {Boolean} draggable default true
27207 * @cfg {Boolean} enableAutocomplete default false
27208 * @cfg {Boolean} enableReverseGeocode default true
27209 * @cfg {String} markerTitle
27212 * Create a new LocationPicker
27213 * @param {Object} config The config object
27217 Roo.bootstrap.LocationPicker = function(config){
27219 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
27224 * Fires when the picker initialized.
27225 * @param {Roo.bootstrap.LocationPicker} this
27226 * @param {Google Location} location
27230 * @event positionchanged
27231 * Fires when the picker position changed.
27232 * @param {Roo.bootstrap.LocationPicker} this
27233 * @param {Google Location} location
27235 positionchanged : true,
27238 * Fires when the map resize.
27239 * @param {Roo.bootstrap.LocationPicker} this
27244 * Fires when the map show.
27245 * @param {Roo.bootstrap.LocationPicker} this
27250 * Fires when the map hide.
27251 * @param {Roo.bootstrap.LocationPicker} this
27256 * Fires when click the map.
27257 * @param {Roo.bootstrap.LocationPicker} this
27258 * @param {Map event} e
27262 * @event mapRightClick
27263 * Fires when right click the map.
27264 * @param {Roo.bootstrap.LocationPicker} this
27265 * @param {Map event} e
27267 mapRightClick : true,
27269 * @event markerClick
27270 * Fires when click the marker.
27271 * @param {Roo.bootstrap.LocationPicker} this
27272 * @param {Map event} e
27274 markerClick : true,
27276 * @event markerRightClick
27277 * Fires when right click the marker.
27278 * @param {Roo.bootstrap.LocationPicker} this
27279 * @param {Map event} e
27281 markerRightClick : true,
27283 * @event OverlayViewDraw
27284 * Fires when OverlayView Draw
27285 * @param {Roo.bootstrap.LocationPicker} this
27287 OverlayViewDraw : true,
27289 * @event OverlayViewOnAdd
27290 * Fires when OverlayView Draw
27291 * @param {Roo.bootstrap.LocationPicker} this
27293 OverlayViewOnAdd : true,
27295 * @event OverlayViewOnRemove
27296 * Fires when OverlayView Draw
27297 * @param {Roo.bootstrap.LocationPicker} this
27299 OverlayViewOnRemove : true,
27301 * @event OverlayViewShow
27302 * Fires when OverlayView Draw
27303 * @param {Roo.bootstrap.LocationPicker} this
27304 * @param {Pixel} cpx
27306 OverlayViewShow : true,
27308 * @event OverlayViewHide
27309 * Fires when OverlayView Draw
27310 * @param {Roo.bootstrap.LocationPicker} this
27312 OverlayViewHide : true,
27314 * @event loadexception
27315 * Fires when load google lib failed.
27316 * @param {Roo.bootstrap.LocationPicker} this
27318 loadexception : true
27323 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
27325 gMapContext: false,
27331 mapTypeControl: false,
27332 disableDoubleClickZoom: false,
27334 streetViewControl: false,
27338 enableAutocomplete: false,
27339 enableReverseGeocode: true,
27342 getAutoCreate: function()
27347 cls: 'roo-location-picker'
27353 initEvents: function(ct, position)
27355 if(!this.el.getWidth() || this.isApplied()){
27359 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27364 initial: function()
27366 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27367 this.fireEvent('loadexception', this);
27371 if(!this.mapTypeId){
27372 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27375 this.gMapContext = this.GMapContext();
27377 this.initOverlayView();
27379 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27383 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27384 _this.setPosition(_this.gMapContext.marker.position);
27387 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27388 _this.fireEvent('mapClick', this, event);
27392 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27393 _this.fireEvent('mapRightClick', this, event);
27397 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27398 _this.fireEvent('markerClick', this, event);
27402 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27403 _this.fireEvent('markerRightClick', this, event);
27407 this.setPosition(this.gMapContext.location);
27409 this.fireEvent('initial', this, this.gMapContext.location);
27412 initOverlayView: function()
27416 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27420 _this.fireEvent('OverlayViewDraw', _this);
27425 _this.fireEvent('OverlayViewOnAdd', _this);
27428 onRemove: function()
27430 _this.fireEvent('OverlayViewOnRemove', _this);
27433 show: function(cpx)
27435 _this.fireEvent('OverlayViewShow', _this, cpx);
27440 _this.fireEvent('OverlayViewHide', _this);
27446 fromLatLngToContainerPixel: function(event)
27448 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27451 isApplied: function()
27453 return this.getGmapContext() == false ? false : true;
27456 getGmapContext: function()
27458 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27461 GMapContext: function()
27463 var position = new google.maps.LatLng(this.latitude, this.longitude);
27465 var _map = new google.maps.Map(this.el.dom, {
27468 mapTypeId: this.mapTypeId,
27469 mapTypeControl: this.mapTypeControl,
27470 disableDoubleClickZoom: this.disableDoubleClickZoom,
27471 scrollwheel: this.scrollwheel,
27472 streetViewControl: this.streetViewControl,
27473 locationName: this.locationName,
27474 draggable: this.draggable,
27475 enableAutocomplete: this.enableAutocomplete,
27476 enableReverseGeocode: this.enableReverseGeocode
27479 var _marker = new google.maps.Marker({
27480 position: position,
27482 title: this.markerTitle,
27483 draggable: this.draggable
27490 location: position,
27491 radius: this.radius,
27492 locationName: this.locationName,
27493 addressComponents: {
27494 formatted_address: null,
27495 addressLine1: null,
27496 addressLine2: null,
27498 streetNumber: null,
27502 stateOrProvince: null
27505 domContainer: this.el.dom,
27506 geodecoder: new google.maps.Geocoder()
27510 drawCircle: function(center, radius, options)
27512 if (this.gMapContext.circle != null) {
27513 this.gMapContext.circle.setMap(null);
27517 options = Roo.apply({}, options, {
27518 strokeColor: "#0000FF",
27519 strokeOpacity: .35,
27521 fillColor: "#0000FF",
27525 options.map = this.gMapContext.map;
27526 options.radius = radius;
27527 options.center = center;
27528 this.gMapContext.circle = new google.maps.Circle(options);
27529 return this.gMapContext.circle;
27535 setPosition: function(location)
27537 this.gMapContext.location = location;
27538 this.gMapContext.marker.setPosition(location);
27539 this.gMapContext.map.panTo(location);
27540 this.drawCircle(location, this.gMapContext.radius, {});
27544 if (this.gMapContext.settings.enableReverseGeocode) {
27545 this.gMapContext.geodecoder.geocode({
27546 latLng: this.gMapContext.location
27547 }, function(results, status) {
27549 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27550 _this.gMapContext.locationName = results[0].formatted_address;
27551 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27553 _this.fireEvent('positionchanged', this, location);
27560 this.fireEvent('positionchanged', this, location);
27565 google.maps.event.trigger(this.gMapContext.map, "resize");
27567 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27569 this.fireEvent('resize', this);
27572 setPositionByLatLng: function(latitude, longitude)
27574 this.setPosition(new google.maps.LatLng(latitude, longitude));
27577 getCurrentPosition: function()
27580 latitude: this.gMapContext.location.lat(),
27581 longitude: this.gMapContext.location.lng()
27585 getAddressName: function()
27587 return this.gMapContext.locationName;
27590 getAddressComponents: function()
27592 return this.gMapContext.addressComponents;
27595 address_component_from_google_geocode: function(address_components)
27599 for (var i = 0; i < address_components.length; i++) {
27600 var component = address_components[i];
27601 if (component.types.indexOf("postal_code") >= 0) {
27602 result.postalCode = component.short_name;
27603 } else if (component.types.indexOf("street_number") >= 0) {
27604 result.streetNumber = component.short_name;
27605 } else if (component.types.indexOf("route") >= 0) {
27606 result.streetName = component.short_name;
27607 } else if (component.types.indexOf("neighborhood") >= 0) {
27608 result.city = component.short_name;
27609 } else if (component.types.indexOf("locality") >= 0) {
27610 result.city = component.short_name;
27611 } else if (component.types.indexOf("sublocality") >= 0) {
27612 result.district = component.short_name;
27613 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27614 result.stateOrProvince = component.short_name;
27615 } else if (component.types.indexOf("country") >= 0) {
27616 result.country = component.short_name;
27620 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27621 result.addressLine2 = "";
27625 setZoomLevel: function(zoom)
27627 this.gMapContext.map.setZoom(zoom);
27640 this.fireEvent('show', this);
27651 this.fireEvent('hide', this);
27656 Roo.apply(Roo.bootstrap.LocationPicker, {
27658 OverlayView : function(map, options)
27660 options = options || {};
27667 * @class Roo.bootstrap.Alert
27668 * @extends Roo.bootstrap.Component
27669 * Bootstrap Alert class - shows an alert area box
27671 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27672 Enter a valid email address
27675 * @cfg {String} title The title of alert
27676 * @cfg {String} html The content of alert
27677 * @cfg {String} weight ( success | info | warning | danger )
27678 * @cfg {String} faicon font-awesomeicon
27681 * Create a new alert
27682 * @param {Object} config The config object
27686 Roo.bootstrap.Alert = function(config){
27687 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27691 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27698 getAutoCreate : function()
27707 cls : 'roo-alert-icon'
27712 cls : 'roo-alert-title',
27717 cls : 'roo-alert-text',
27724 cfg.cn[0].cls += ' fa ' + this.faicon;
27728 cfg.cls += ' alert-' + this.weight;
27734 initEvents: function()
27736 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27739 setTitle : function(str)
27741 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27744 setText : function(str)
27746 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27749 setWeight : function(weight)
27752 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27755 this.weight = weight;
27757 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27760 setIcon : function(icon)
27763 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27766 this.faicon = icon;
27768 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27789 * @class Roo.bootstrap.UploadCropbox
27790 * @extends Roo.bootstrap.Component
27791 * Bootstrap UploadCropbox class
27792 * @cfg {String} emptyText show when image has been loaded
27793 * @cfg {String} rotateNotify show when image too small to rotate
27794 * @cfg {Number} errorTimeout default 3000
27795 * @cfg {Number} minWidth default 300
27796 * @cfg {Number} minHeight default 300
27797 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27798 * @cfg {Boolean} isDocument (true|false) default false
27799 * @cfg {String} url action url
27800 * @cfg {String} paramName default 'imageUpload'
27801 * @cfg {String} method default POST
27802 * @cfg {Boolean} loadMask (true|false) default true
27803 * @cfg {Boolean} loadingText default 'Loading...'
27806 * Create a new UploadCropbox
27807 * @param {Object} config The config object
27810 Roo.bootstrap.UploadCropbox = function(config){
27811 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27815 * @event beforeselectfile
27816 * Fire before select file
27817 * @param {Roo.bootstrap.UploadCropbox} this
27819 "beforeselectfile" : true,
27822 * Fire after initEvent
27823 * @param {Roo.bootstrap.UploadCropbox} this
27828 * Fire after initEvent
27829 * @param {Roo.bootstrap.UploadCropbox} this
27830 * @param {String} data
27835 * Fire when preparing the file data
27836 * @param {Roo.bootstrap.UploadCropbox} this
27837 * @param {Object} file
27842 * Fire when get exception
27843 * @param {Roo.bootstrap.UploadCropbox} this
27844 * @param {XMLHttpRequest} xhr
27846 "exception" : true,
27848 * @event beforeloadcanvas
27849 * Fire before load the canvas
27850 * @param {Roo.bootstrap.UploadCropbox} this
27851 * @param {String} src
27853 "beforeloadcanvas" : true,
27856 * Fire when trash image
27857 * @param {Roo.bootstrap.UploadCropbox} this
27862 * Fire when download the image
27863 * @param {Roo.bootstrap.UploadCropbox} this
27867 * @event footerbuttonclick
27868 * Fire when footerbuttonclick
27869 * @param {Roo.bootstrap.UploadCropbox} this
27870 * @param {String} type
27872 "footerbuttonclick" : true,
27876 * @param {Roo.bootstrap.UploadCropbox} this
27881 * Fire when rotate the image
27882 * @param {Roo.bootstrap.UploadCropbox} this
27883 * @param {String} pos
27888 * Fire when inspect the file
27889 * @param {Roo.bootstrap.UploadCropbox} this
27890 * @param {Object} file
27895 * Fire when xhr upload the file
27896 * @param {Roo.bootstrap.UploadCropbox} this
27897 * @param {Object} data
27902 * Fire when arrange the file data
27903 * @param {Roo.bootstrap.UploadCropbox} this
27904 * @param {Object} formData
27909 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27912 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27914 emptyText : 'Click to upload image',
27915 rotateNotify : 'Image is too small to rotate',
27916 errorTimeout : 3000,
27930 cropType : 'image/jpeg',
27932 canvasLoaded : false,
27933 isDocument : false,
27935 paramName : 'imageUpload',
27937 loadingText : 'Loading...',
27940 getAutoCreate : function()
27944 cls : 'roo-upload-cropbox',
27948 cls : 'roo-upload-cropbox-selector',
27953 cls : 'roo-upload-cropbox-body',
27954 style : 'cursor:pointer',
27958 cls : 'roo-upload-cropbox-preview'
27962 cls : 'roo-upload-cropbox-thumb'
27966 cls : 'roo-upload-cropbox-empty-notify',
27967 html : this.emptyText
27971 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27972 html : this.rotateNotify
27978 cls : 'roo-upload-cropbox-footer',
27981 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27991 onRender : function(ct, position)
27993 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27995 if (this.buttons.length) {
27997 Roo.each(this.buttons, function(bb) {
27999 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
28001 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
28007 this.maskEl = this.el;
28011 initEvents : function()
28013 this.urlAPI = (window.createObjectURL && window) ||
28014 (window.URL && URL.revokeObjectURL && URL) ||
28015 (window.webkitURL && webkitURL);
28017 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
28018 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28020 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
28021 this.selectorEl.hide();
28023 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
28024 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28026 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
28027 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28028 this.thumbEl.hide();
28030 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
28031 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28033 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
28034 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28035 this.errorEl.hide();
28037 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
28038 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28039 this.footerEl.hide();
28041 this.setThumbBoxSize();
28047 this.fireEvent('initial', this);
28054 window.addEventListener("resize", function() { _this.resize(); } );
28056 this.bodyEl.on('click', this.beforeSelectFile, this);
28059 this.bodyEl.on('touchstart', this.onTouchStart, this);
28060 this.bodyEl.on('touchmove', this.onTouchMove, this);
28061 this.bodyEl.on('touchend', this.onTouchEnd, this);
28065 this.bodyEl.on('mousedown', this.onMouseDown, this);
28066 this.bodyEl.on('mousemove', this.onMouseMove, this);
28067 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
28068 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
28069 Roo.get(document).on('mouseup', this.onMouseUp, this);
28072 this.selectorEl.on('change', this.onFileSelected, this);
28078 this.baseScale = 1;
28080 this.baseRotate = 1;
28081 this.dragable = false;
28082 this.pinching = false;
28085 this.cropData = false;
28086 this.notifyEl.dom.innerHTML = this.emptyText;
28088 this.selectorEl.dom.value = '';
28092 resize : function()
28094 if(this.fireEvent('resize', this) != false){
28095 this.setThumbBoxPosition();
28096 this.setCanvasPosition();
28100 onFooterButtonClick : function(e, el, o, type)
28103 case 'rotate-left' :
28104 this.onRotateLeft(e);
28106 case 'rotate-right' :
28107 this.onRotateRight(e);
28110 this.beforeSelectFile(e);
28125 this.fireEvent('footerbuttonclick', this, type);
28128 beforeSelectFile : function(e)
28130 e.preventDefault();
28132 if(this.fireEvent('beforeselectfile', this) != false){
28133 this.selectorEl.dom.click();
28137 onFileSelected : function(e)
28139 e.preventDefault();
28141 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28145 var file = this.selectorEl.dom.files[0];
28147 if(this.fireEvent('inspect', this, file) != false){
28148 this.prepare(file);
28153 trash : function(e)
28155 this.fireEvent('trash', this);
28158 download : function(e)
28160 this.fireEvent('download', this);
28163 loadCanvas : function(src)
28165 if(this.fireEvent('beforeloadcanvas', this, src) != false){
28169 this.imageEl = document.createElement('img');
28173 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
28175 this.imageEl.src = src;
28179 onLoadCanvas : function()
28181 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
28182 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
28184 this.bodyEl.un('click', this.beforeSelectFile, this);
28186 this.notifyEl.hide();
28187 this.thumbEl.show();
28188 this.footerEl.show();
28190 this.baseRotateLevel();
28192 if(this.isDocument){
28193 this.setThumbBoxSize();
28196 this.setThumbBoxPosition();
28198 this.baseScaleLevel();
28204 this.canvasLoaded = true;
28207 this.maskEl.unmask();
28212 setCanvasPosition : function()
28214 if(!this.canvasEl){
28218 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
28219 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
28221 this.previewEl.setLeft(pw);
28222 this.previewEl.setTop(ph);
28226 onMouseDown : function(e)
28230 this.dragable = true;
28231 this.pinching = false;
28233 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
28234 this.dragable = false;
28238 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28239 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28243 onMouseMove : function(e)
28247 if(!this.canvasLoaded){
28251 if (!this.dragable){
28255 var minX = Math.ceil(this.thumbEl.getLeft(true));
28256 var minY = Math.ceil(this.thumbEl.getTop(true));
28258 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
28259 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
28261 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28262 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28264 x = x - this.mouseX;
28265 y = y - this.mouseY;
28267 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
28268 var bgY = Math.ceil(y + this.previewEl.getTop(true));
28270 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
28271 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
28273 this.previewEl.setLeft(bgX);
28274 this.previewEl.setTop(bgY);
28276 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28277 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28280 onMouseUp : function(e)
28284 this.dragable = false;
28287 onMouseWheel : function(e)
28291 this.startScale = this.scale;
28293 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
28295 if(!this.zoomable()){
28296 this.scale = this.startScale;
28305 zoomable : function()
28307 var minScale = this.thumbEl.getWidth() / this.minWidth;
28309 if(this.minWidth < this.minHeight){
28310 minScale = this.thumbEl.getHeight() / this.minHeight;
28313 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
28314 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
28318 (this.rotate == 0 || this.rotate == 180) &&
28320 width > this.imageEl.OriginWidth ||
28321 height > this.imageEl.OriginHeight ||
28322 (width < this.minWidth && height < this.minHeight)
28330 (this.rotate == 90 || this.rotate == 270) &&
28332 width > this.imageEl.OriginWidth ||
28333 height > this.imageEl.OriginHeight ||
28334 (width < this.minHeight && height < this.minWidth)
28341 !this.isDocument &&
28342 (this.rotate == 0 || this.rotate == 180) &&
28344 width < this.minWidth ||
28345 width > this.imageEl.OriginWidth ||
28346 height < this.minHeight ||
28347 height > this.imageEl.OriginHeight
28354 !this.isDocument &&
28355 (this.rotate == 90 || this.rotate == 270) &&
28357 width < this.minHeight ||
28358 width > this.imageEl.OriginWidth ||
28359 height < this.minWidth ||
28360 height > this.imageEl.OriginHeight
28370 onRotateLeft : function(e)
28372 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28374 var minScale = this.thumbEl.getWidth() / this.minWidth;
28376 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28377 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28379 this.startScale = this.scale;
28381 while (this.getScaleLevel() < minScale){
28383 this.scale = this.scale + 1;
28385 if(!this.zoomable()){
28390 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28391 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28396 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28403 this.scale = this.startScale;
28405 this.onRotateFail();
28410 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28412 if(this.isDocument){
28413 this.setThumbBoxSize();
28414 this.setThumbBoxPosition();
28415 this.setCanvasPosition();
28420 this.fireEvent('rotate', this, 'left');
28424 onRotateRight : function(e)
28426 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28428 var minScale = this.thumbEl.getWidth() / this.minWidth;
28430 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28431 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28433 this.startScale = this.scale;
28435 while (this.getScaleLevel() < minScale){
28437 this.scale = this.scale + 1;
28439 if(!this.zoomable()){
28444 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28445 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28450 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28457 this.scale = this.startScale;
28459 this.onRotateFail();
28464 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28466 if(this.isDocument){
28467 this.setThumbBoxSize();
28468 this.setThumbBoxPosition();
28469 this.setCanvasPosition();
28474 this.fireEvent('rotate', this, 'right');
28477 onRotateFail : function()
28479 this.errorEl.show(true);
28483 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28488 this.previewEl.dom.innerHTML = '';
28490 var canvasEl = document.createElement("canvas");
28492 var contextEl = canvasEl.getContext("2d");
28494 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28495 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28496 var center = this.imageEl.OriginWidth / 2;
28498 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28499 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28500 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28501 center = this.imageEl.OriginHeight / 2;
28504 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28506 contextEl.translate(center, center);
28507 contextEl.rotate(this.rotate * Math.PI / 180);
28509 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28511 this.canvasEl = document.createElement("canvas");
28513 this.contextEl = this.canvasEl.getContext("2d");
28515 switch (this.rotate) {
28518 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28519 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28521 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28526 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28527 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28529 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28530 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);
28534 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28539 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28540 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28542 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28543 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);
28547 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);
28552 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28553 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28555 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28556 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28560 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);
28567 this.previewEl.appendChild(this.canvasEl);
28569 this.setCanvasPosition();
28574 if(!this.canvasLoaded){
28578 var imageCanvas = document.createElement("canvas");
28580 var imageContext = imageCanvas.getContext("2d");
28582 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28583 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28585 var center = imageCanvas.width / 2;
28587 imageContext.translate(center, center);
28589 imageContext.rotate(this.rotate * Math.PI / 180);
28591 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28593 var canvas = document.createElement("canvas");
28595 var context = canvas.getContext("2d");
28597 canvas.width = this.minWidth;
28598 canvas.height = this.minHeight;
28600 switch (this.rotate) {
28603 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28604 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28606 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28607 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28609 var targetWidth = this.minWidth - 2 * x;
28610 var targetHeight = this.minHeight - 2 * y;
28614 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28615 scale = targetWidth / width;
28618 if(x > 0 && y == 0){
28619 scale = targetHeight / height;
28622 if(x > 0 && y > 0){
28623 scale = targetWidth / width;
28625 if(width < height){
28626 scale = targetHeight / height;
28630 context.scale(scale, scale);
28632 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28633 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28635 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28636 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28638 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28643 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28644 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28646 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28647 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28649 var targetWidth = this.minWidth - 2 * x;
28650 var targetHeight = this.minHeight - 2 * y;
28654 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28655 scale = targetWidth / width;
28658 if(x > 0 && y == 0){
28659 scale = targetHeight / height;
28662 if(x > 0 && y > 0){
28663 scale = targetWidth / width;
28665 if(width < height){
28666 scale = targetHeight / height;
28670 context.scale(scale, scale);
28672 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28673 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28675 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28676 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28678 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28680 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28685 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28686 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28688 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28689 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28691 var targetWidth = this.minWidth - 2 * x;
28692 var targetHeight = this.minHeight - 2 * y;
28696 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28697 scale = targetWidth / width;
28700 if(x > 0 && y == 0){
28701 scale = targetHeight / height;
28704 if(x > 0 && y > 0){
28705 scale = targetWidth / width;
28707 if(width < height){
28708 scale = targetHeight / height;
28712 context.scale(scale, scale);
28714 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28715 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28717 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28718 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28720 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28721 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28723 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28728 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28729 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28731 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28732 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28734 var targetWidth = this.minWidth - 2 * x;
28735 var targetHeight = this.minHeight - 2 * y;
28739 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28740 scale = targetWidth / width;
28743 if(x > 0 && y == 0){
28744 scale = targetHeight / height;
28747 if(x > 0 && y > 0){
28748 scale = targetWidth / width;
28750 if(width < height){
28751 scale = targetHeight / height;
28755 context.scale(scale, scale);
28757 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28758 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28760 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28761 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28763 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28765 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28772 this.cropData = canvas.toDataURL(this.cropType);
28774 if(this.fireEvent('crop', this, this.cropData) !== false){
28775 this.process(this.file, this.cropData);
28782 setThumbBoxSize : function()
28786 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28787 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28788 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28790 this.minWidth = width;
28791 this.minHeight = height;
28793 if(this.rotate == 90 || this.rotate == 270){
28794 this.minWidth = height;
28795 this.minHeight = width;
28800 width = Math.ceil(this.minWidth * height / this.minHeight);
28802 if(this.minWidth > this.minHeight){
28804 height = Math.ceil(this.minHeight * width / this.minWidth);
28807 this.thumbEl.setStyle({
28808 width : width + 'px',
28809 height : height + 'px'
28816 setThumbBoxPosition : function()
28818 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28819 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28821 this.thumbEl.setLeft(x);
28822 this.thumbEl.setTop(y);
28826 baseRotateLevel : function()
28828 this.baseRotate = 1;
28831 typeof(this.exif) != 'undefined' &&
28832 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28833 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28835 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28838 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28842 baseScaleLevel : function()
28846 if(this.isDocument){
28848 if(this.baseRotate == 6 || this.baseRotate == 8){
28850 height = this.thumbEl.getHeight();
28851 this.baseScale = height / this.imageEl.OriginWidth;
28853 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28854 width = this.thumbEl.getWidth();
28855 this.baseScale = width / this.imageEl.OriginHeight;
28861 height = this.thumbEl.getHeight();
28862 this.baseScale = height / this.imageEl.OriginHeight;
28864 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28865 width = this.thumbEl.getWidth();
28866 this.baseScale = width / this.imageEl.OriginWidth;
28872 if(this.baseRotate == 6 || this.baseRotate == 8){
28874 width = this.thumbEl.getHeight();
28875 this.baseScale = width / this.imageEl.OriginHeight;
28877 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28878 height = this.thumbEl.getWidth();
28879 this.baseScale = height / this.imageEl.OriginHeight;
28882 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28883 height = this.thumbEl.getWidth();
28884 this.baseScale = height / this.imageEl.OriginHeight;
28886 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28887 width = this.thumbEl.getHeight();
28888 this.baseScale = width / this.imageEl.OriginWidth;
28895 width = this.thumbEl.getWidth();
28896 this.baseScale = width / this.imageEl.OriginWidth;
28898 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28899 height = this.thumbEl.getHeight();
28900 this.baseScale = height / this.imageEl.OriginHeight;
28903 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28905 height = this.thumbEl.getHeight();
28906 this.baseScale = height / this.imageEl.OriginHeight;
28908 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28909 width = this.thumbEl.getWidth();
28910 this.baseScale = width / this.imageEl.OriginWidth;
28918 getScaleLevel : function()
28920 return this.baseScale * Math.pow(1.1, this.scale);
28923 onTouchStart : function(e)
28925 if(!this.canvasLoaded){
28926 this.beforeSelectFile(e);
28930 var touches = e.browserEvent.touches;
28936 if(touches.length == 1){
28937 this.onMouseDown(e);
28941 if(touches.length != 2){
28947 for(var i = 0, finger; finger = touches[i]; i++){
28948 coords.push(finger.pageX, finger.pageY);
28951 var x = Math.pow(coords[0] - coords[2], 2);
28952 var y = Math.pow(coords[1] - coords[3], 2);
28954 this.startDistance = Math.sqrt(x + y);
28956 this.startScale = this.scale;
28958 this.pinching = true;
28959 this.dragable = false;
28963 onTouchMove : function(e)
28965 if(!this.pinching && !this.dragable){
28969 var touches = e.browserEvent.touches;
28976 this.onMouseMove(e);
28982 for(var i = 0, finger; finger = touches[i]; i++){
28983 coords.push(finger.pageX, finger.pageY);
28986 var x = Math.pow(coords[0] - coords[2], 2);
28987 var y = Math.pow(coords[1] - coords[3], 2);
28989 this.endDistance = Math.sqrt(x + y);
28991 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28993 if(!this.zoomable()){
28994 this.scale = this.startScale;
29002 onTouchEnd : function(e)
29004 this.pinching = false;
29005 this.dragable = false;
29009 process : function(file, crop)
29012 this.maskEl.mask(this.loadingText);
29015 this.xhr = new XMLHttpRequest();
29017 file.xhr = this.xhr;
29019 this.xhr.open(this.method, this.url, true);
29022 "Accept": "application/json",
29023 "Cache-Control": "no-cache",
29024 "X-Requested-With": "XMLHttpRequest"
29027 for (var headerName in headers) {
29028 var headerValue = headers[headerName];
29030 this.xhr.setRequestHeader(headerName, headerValue);
29036 this.xhr.onload = function()
29038 _this.xhrOnLoad(_this.xhr);
29041 this.xhr.onerror = function()
29043 _this.xhrOnError(_this.xhr);
29046 var formData = new FormData();
29048 formData.append('returnHTML', 'NO');
29051 formData.append('crop', crop);
29054 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
29055 formData.append(this.paramName, file, file.name);
29058 if(typeof(file.filename) != 'undefined'){
29059 formData.append('filename', file.filename);
29062 if(typeof(file.mimetype) != 'undefined'){
29063 formData.append('mimetype', file.mimetype);
29066 if(this.fireEvent('arrange', this, formData) != false){
29067 this.xhr.send(formData);
29071 xhrOnLoad : function(xhr)
29074 this.maskEl.unmask();
29077 if (xhr.readyState !== 4) {
29078 this.fireEvent('exception', this, xhr);
29082 var response = Roo.decode(xhr.responseText);
29084 if(!response.success){
29085 this.fireEvent('exception', this, xhr);
29089 var response = Roo.decode(xhr.responseText);
29091 this.fireEvent('upload', this, response);
29095 xhrOnError : function()
29098 this.maskEl.unmask();
29101 Roo.log('xhr on error');
29103 var response = Roo.decode(xhr.responseText);
29109 prepare : function(file)
29112 this.maskEl.mask(this.loadingText);
29118 if(typeof(file) === 'string'){
29119 this.loadCanvas(file);
29123 if(!file || !this.urlAPI){
29128 this.cropType = file.type;
29132 if(this.fireEvent('prepare', this, this.file) != false){
29134 var reader = new FileReader();
29136 reader.onload = function (e) {
29137 if (e.target.error) {
29138 Roo.log(e.target.error);
29142 var buffer = e.target.result,
29143 dataView = new DataView(buffer),
29145 maxOffset = dataView.byteLength - 4,
29149 if (dataView.getUint16(0) === 0xffd8) {
29150 while (offset < maxOffset) {
29151 markerBytes = dataView.getUint16(offset);
29153 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
29154 markerLength = dataView.getUint16(offset + 2) + 2;
29155 if (offset + markerLength > dataView.byteLength) {
29156 Roo.log('Invalid meta data: Invalid segment size.');
29160 if(markerBytes == 0xffe1){
29161 _this.parseExifData(
29168 offset += markerLength;
29178 var url = _this.urlAPI.createObjectURL(_this.file);
29180 _this.loadCanvas(url);
29185 reader.readAsArrayBuffer(this.file);
29191 parseExifData : function(dataView, offset, length)
29193 var tiffOffset = offset + 10,
29197 if (dataView.getUint32(offset + 4) !== 0x45786966) {
29198 // No Exif data, might be XMP data instead
29202 // Check for the ASCII code for "Exif" (0x45786966):
29203 if (dataView.getUint32(offset + 4) !== 0x45786966) {
29204 // No Exif data, might be XMP data instead
29207 if (tiffOffset + 8 > dataView.byteLength) {
29208 Roo.log('Invalid Exif data: Invalid segment size.');
29211 // Check for the two null bytes:
29212 if (dataView.getUint16(offset + 8) !== 0x0000) {
29213 Roo.log('Invalid Exif data: Missing byte alignment offset.');
29216 // Check the byte alignment:
29217 switch (dataView.getUint16(tiffOffset)) {
29219 littleEndian = true;
29222 littleEndian = false;
29225 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
29228 // Check for the TIFF tag marker (0x002A):
29229 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
29230 Roo.log('Invalid Exif data: Missing TIFF marker.');
29233 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
29234 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
29236 this.parseExifTags(
29239 tiffOffset + dirOffset,
29244 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
29249 if (dirOffset + 6 > dataView.byteLength) {
29250 Roo.log('Invalid Exif data: Invalid directory offset.');
29253 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
29254 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
29255 if (dirEndOffset + 4 > dataView.byteLength) {
29256 Roo.log('Invalid Exif data: Invalid directory size.');
29259 for (i = 0; i < tagsNumber; i += 1) {
29263 dirOffset + 2 + 12 * i, // tag offset
29267 // Return the offset to the next directory:
29268 return dataView.getUint32(dirEndOffset, littleEndian);
29271 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
29273 var tag = dataView.getUint16(offset, littleEndian);
29275 this.exif[tag] = this.getExifValue(
29279 dataView.getUint16(offset + 2, littleEndian), // tag type
29280 dataView.getUint32(offset + 4, littleEndian), // tag length
29285 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
29287 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
29296 Roo.log('Invalid Exif data: Invalid tag type.');
29300 tagSize = tagType.size * length;
29301 // Determine if the value is contained in the dataOffset bytes,
29302 // or if the value at the dataOffset is a pointer to the actual data:
29303 dataOffset = tagSize > 4 ?
29304 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
29305 if (dataOffset + tagSize > dataView.byteLength) {
29306 Roo.log('Invalid Exif data: Invalid data offset.');
29309 if (length === 1) {
29310 return tagType.getValue(dataView, dataOffset, littleEndian);
29313 for (i = 0; i < length; i += 1) {
29314 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
29317 if (tagType.ascii) {
29319 // Concatenate the chars:
29320 for (i = 0; i < values.length; i += 1) {
29322 // Ignore the terminating NULL byte(s):
29323 if (c === '\u0000') {
29335 Roo.apply(Roo.bootstrap.UploadCropbox, {
29337 'Orientation': 0x0112
29341 1: 0, //'top-left',
29343 3: 180, //'bottom-right',
29344 // 4: 'bottom-left',
29346 6: 90, //'right-top',
29347 // 7: 'right-bottom',
29348 8: 270 //'left-bottom'
29352 // byte, 8-bit unsigned int:
29354 getValue: function (dataView, dataOffset) {
29355 return dataView.getUint8(dataOffset);
29359 // ascii, 8-bit byte:
29361 getValue: function (dataView, dataOffset) {
29362 return String.fromCharCode(dataView.getUint8(dataOffset));
29367 // short, 16 bit int:
29369 getValue: function (dataView, dataOffset, littleEndian) {
29370 return dataView.getUint16(dataOffset, littleEndian);
29374 // long, 32 bit int:
29376 getValue: function (dataView, dataOffset, littleEndian) {
29377 return dataView.getUint32(dataOffset, littleEndian);
29381 // rational = two long values, first is numerator, second is denominator:
29383 getValue: function (dataView, dataOffset, littleEndian) {
29384 return dataView.getUint32(dataOffset, littleEndian) /
29385 dataView.getUint32(dataOffset + 4, littleEndian);
29389 // slong, 32 bit signed int:
29391 getValue: function (dataView, dataOffset, littleEndian) {
29392 return dataView.getInt32(dataOffset, littleEndian);
29396 // srational, two slongs, first is numerator, second is denominator:
29398 getValue: function (dataView, dataOffset, littleEndian) {
29399 return dataView.getInt32(dataOffset, littleEndian) /
29400 dataView.getInt32(dataOffset + 4, littleEndian);
29410 cls : 'btn-group roo-upload-cropbox-rotate-left',
29411 action : 'rotate-left',
29415 cls : 'btn btn-default',
29416 html : '<i class="fa fa-undo"></i>'
29422 cls : 'btn-group roo-upload-cropbox-picture',
29423 action : 'picture',
29427 cls : 'btn btn-default',
29428 html : '<i class="fa fa-picture-o"></i>'
29434 cls : 'btn-group roo-upload-cropbox-rotate-right',
29435 action : 'rotate-right',
29439 cls : 'btn btn-default',
29440 html : '<i class="fa fa-repeat"></i>'
29448 cls : 'btn-group roo-upload-cropbox-rotate-left',
29449 action : 'rotate-left',
29453 cls : 'btn btn-default',
29454 html : '<i class="fa fa-undo"></i>'
29460 cls : 'btn-group roo-upload-cropbox-download',
29461 action : 'download',
29465 cls : 'btn btn-default',
29466 html : '<i class="fa fa-download"></i>'
29472 cls : 'btn-group roo-upload-cropbox-crop',
29477 cls : 'btn btn-default',
29478 html : '<i class="fa fa-crop"></i>'
29484 cls : 'btn-group roo-upload-cropbox-trash',
29489 cls : 'btn btn-default',
29490 html : '<i class="fa fa-trash"></i>'
29496 cls : 'btn-group roo-upload-cropbox-rotate-right',
29497 action : 'rotate-right',
29501 cls : 'btn btn-default',
29502 html : '<i class="fa fa-repeat"></i>'
29510 cls : 'btn-group roo-upload-cropbox-rotate-left',
29511 action : 'rotate-left',
29515 cls : 'btn btn-default',
29516 html : '<i class="fa fa-undo"></i>'
29522 cls : 'btn-group roo-upload-cropbox-rotate-right',
29523 action : 'rotate-right',
29527 cls : 'btn btn-default',
29528 html : '<i class="fa fa-repeat"></i>'
29541 * @class Roo.bootstrap.DocumentManager
29542 * @extends Roo.bootstrap.Component
29543 * Bootstrap DocumentManager class
29544 * @cfg {String} paramName default 'imageUpload'
29545 * @cfg {String} toolTipName default 'filename'
29546 * @cfg {String} method default POST
29547 * @cfg {String} url action url
29548 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29549 * @cfg {Boolean} multiple multiple upload default true
29550 * @cfg {Number} thumbSize default 300
29551 * @cfg {String} fieldLabel
29552 * @cfg {Number} labelWidth default 4
29553 * @cfg {String} labelAlign (left|top) default left
29554 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29555 * @cfg {Number} labellg set the width of label (1-12)
29556 * @cfg {Number} labelmd set the width of label (1-12)
29557 * @cfg {Number} labelsm set the width of label (1-12)
29558 * @cfg {Number} labelxs set the width of label (1-12)
29561 * Create a new DocumentManager
29562 * @param {Object} config The config object
29565 Roo.bootstrap.DocumentManager = function(config){
29566 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29569 this.delegates = [];
29574 * Fire when initial the DocumentManager
29575 * @param {Roo.bootstrap.DocumentManager} this
29580 * inspect selected file
29581 * @param {Roo.bootstrap.DocumentManager} this
29582 * @param {File} file
29587 * Fire when xhr load exception
29588 * @param {Roo.bootstrap.DocumentManager} this
29589 * @param {XMLHttpRequest} xhr
29591 "exception" : true,
29593 * @event afterupload
29594 * Fire when xhr load exception
29595 * @param {Roo.bootstrap.DocumentManager} this
29596 * @param {XMLHttpRequest} xhr
29598 "afterupload" : true,
29601 * prepare the form data
29602 * @param {Roo.bootstrap.DocumentManager} this
29603 * @param {Object} formData
29608 * Fire when remove the file
29609 * @param {Roo.bootstrap.DocumentManager} this
29610 * @param {Object} file
29615 * Fire after refresh the file
29616 * @param {Roo.bootstrap.DocumentManager} this
29621 * Fire after click the image
29622 * @param {Roo.bootstrap.DocumentManager} this
29623 * @param {Object} file
29628 * Fire when upload a image and editable set to true
29629 * @param {Roo.bootstrap.DocumentManager} this
29630 * @param {Object} file
29634 * @event beforeselectfile
29635 * Fire before select file
29636 * @param {Roo.bootstrap.DocumentManager} this
29638 "beforeselectfile" : true,
29641 * Fire before process file
29642 * @param {Roo.bootstrap.DocumentManager} this
29643 * @param {Object} file
29647 * @event previewrendered
29648 * Fire when preview rendered
29649 * @param {Roo.bootstrap.DocumentManager} this
29650 * @param {Object} file
29652 "previewrendered" : true,
29655 "previewResize" : true
29660 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
29669 paramName : 'imageUpload',
29670 toolTipName : 'filename',
29673 labelAlign : 'left',
29683 getAutoCreate : function()
29685 var managerWidget = {
29687 cls : 'roo-document-manager',
29691 cls : 'roo-document-manager-selector',
29696 cls : 'roo-document-manager-uploader',
29700 cls : 'roo-document-manager-upload-btn',
29701 html : '<i class="fa fa-plus"></i>'
29712 cls : 'column col-md-12',
29717 if(this.fieldLabel.length){
29722 cls : 'column col-md-12',
29723 html : this.fieldLabel
29727 cls : 'column col-md-12',
29732 if(this.labelAlign == 'left'){
29737 html : this.fieldLabel
29746 if(this.labelWidth > 12){
29747 content[0].style = "width: " + this.labelWidth + 'px';
29750 if(this.labelWidth < 13 && this.labelmd == 0){
29751 this.labelmd = this.labelWidth;
29754 if(this.labellg > 0){
29755 content[0].cls += ' col-lg-' + this.labellg;
29756 content[1].cls += ' col-lg-' + (12 - this.labellg);
29759 if(this.labelmd > 0){
29760 content[0].cls += ' col-md-' + this.labelmd;
29761 content[1].cls += ' col-md-' + (12 - this.labelmd);
29764 if(this.labelsm > 0){
29765 content[0].cls += ' col-sm-' + this.labelsm;
29766 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29769 if(this.labelxs > 0){
29770 content[0].cls += ' col-xs-' + this.labelxs;
29771 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29779 cls : 'row clearfix',
29787 initEvents : function()
29789 this.managerEl = this.el.select('.roo-document-manager', true).first();
29790 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29792 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29793 this.selectorEl.hide();
29796 this.selectorEl.attr('multiple', 'multiple');
29799 this.selectorEl.on('change', this.onFileSelected, this);
29801 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29802 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29804 this.uploader.on('click', this.onUploaderClick, this);
29806 this.renderProgressDialog();
29810 window.addEventListener("resize", function() { _this.refresh(); } );
29812 this.fireEvent('initial', this);
29815 renderProgressDialog : function()
29819 this.progressDialog = new Roo.bootstrap.Modal({
29820 cls : 'roo-document-manager-progress-dialog',
29821 allow_close : false,
29832 btnclick : function() {
29833 _this.uploadCancel();
29839 this.progressDialog.render(Roo.get(document.body));
29841 this.progress = new Roo.bootstrap.Progress({
29842 cls : 'roo-document-manager-progress',
29847 this.progress.render(this.progressDialog.getChildContainer());
29849 this.progressBar = new Roo.bootstrap.ProgressBar({
29850 cls : 'roo-document-manager-progress-bar',
29853 aria_valuemax : 12,
29857 this.progressBar.render(this.progress.getChildContainer());
29860 onUploaderClick : function(e)
29862 e.preventDefault();
29864 if(this.fireEvent('beforeselectfile', this) != false){
29865 this.selectorEl.dom.click();
29870 onFileSelected : function(e)
29872 e.preventDefault();
29874 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29878 Roo.each(this.selectorEl.dom.files, function(file){
29879 if(this.fireEvent('inspect', this, file) != false){
29880 this.files.push(file);
29890 this.selectorEl.dom.value = '';
29892 if(!this.files || !this.files.length){
29896 if(this.boxes > 0 && this.files.length > this.boxes){
29897 this.files = this.files.slice(0, this.boxes);
29900 this.uploader.show();
29902 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29903 this.uploader.hide();
29912 Roo.each(this.files, function(file){
29914 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29915 var f = this.renderPreview(file);
29920 if(file.type.indexOf('image') != -1){
29921 this.delegates.push(
29923 _this.process(file);
29924 }).createDelegate(this)
29932 _this.process(file);
29933 }).createDelegate(this)
29938 this.files = files;
29940 this.delegates = this.delegates.concat(docs);
29942 if(!this.delegates.length){
29947 this.progressBar.aria_valuemax = this.delegates.length;
29954 arrange : function()
29956 if(!this.delegates.length){
29957 this.progressDialog.hide();
29962 var delegate = this.delegates.shift();
29964 this.progressDialog.show();
29966 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29968 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29973 refresh : function()
29975 this.uploader.show();
29977 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29978 this.uploader.hide();
29981 Roo.isTouch ? this.closable(false) : this.closable(true);
29983 this.fireEvent('refresh', this);
29986 onRemove : function(e, el, o)
29988 e.preventDefault();
29990 this.fireEvent('remove', this, o);
29994 remove : function(o)
29998 Roo.each(this.files, function(file){
29999 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
30008 this.files = files;
30015 Roo.each(this.files, function(file){
30020 file.target.remove();
30029 onClick : function(e, el, o)
30031 e.preventDefault();
30033 this.fireEvent('click', this, o);
30037 closable : function(closable)
30039 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
30041 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30053 xhrOnLoad : function(xhr)
30055 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30059 if (xhr.readyState !== 4) {
30061 this.fireEvent('exception', this, xhr);
30065 var response = Roo.decode(xhr.responseText);
30067 if(!response.success){
30069 this.fireEvent('exception', this, xhr);
30073 var file = this.renderPreview(response.data);
30075 this.files.push(file);
30079 this.fireEvent('afterupload', this, xhr);
30083 xhrOnError : function(xhr)
30085 Roo.log('xhr on error');
30087 var response = Roo.decode(xhr.responseText);
30094 process : function(file)
30096 if(this.fireEvent('process', this, file) !== false){
30097 if(this.editable && file.type.indexOf('image') != -1){
30098 this.fireEvent('edit', this, file);
30102 this.uploadStart(file, false);
30109 uploadStart : function(file, crop)
30111 this.xhr = new XMLHttpRequest();
30113 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30118 file.xhr = this.xhr;
30120 this.managerEl.createChild({
30122 cls : 'roo-document-manager-loading',
30126 tooltip : file.name,
30127 cls : 'roo-document-manager-thumb',
30128 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30134 this.xhr.open(this.method, this.url, true);
30137 "Accept": "application/json",
30138 "Cache-Control": "no-cache",
30139 "X-Requested-With": "XMLHttpRequest"
30142 for (var headerName in headers) {
30143 var headerValue = headers[headerName];
30145 this.xhr.setRequestHeader(headerName, headerValue);
30151 this.xhr.onload = function()
30153 _this.xhrOnLoad(_this.xhr);
30156 this.xhr.onerror = function()
30158 _this.xhrOnError(_this.xhr);
30161 var formData = new FormData();
30163 formData.append('returnHTML', 'NO');
30166 formData.append('crop', crop);
30169 formData.append(this.paramName, file, file.name);
30176 if(this.fireEvent('prepare', this, formData, options) != false){
30178 if(options.manually){
30182 this.xhr.send(formData);
30186 this.uploadCancel();
30189 uploadCancel : function()
30195 this.delegates = [];
30197 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30204 renderPreview : function(file)
30206 if(typeof(file.target) != 'undefined' && file.target){
30210 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
30212 var previewEl = this.managerEl.createChild({
30214 cls : 'roo-document-manager-preview',
30218 tooltip : file[this.toolTipName],
30219 cls : 'roo-document-manager-thumb',
30220 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
30225 html : '<i class="fa fa-times-circle"></i>'
30230 var close = previewEl.select('button.close', true).first();
30232 close.on('click', this.onRemove, this, file);
30234 file.target = previewEl;
30236 var image = previewEl.select('img', true).first();
30240 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
30242 image.on('click', this.onClick, this, file);
30244 this.fireEvent('previewrendered', this, file);
30250 onPreviewLoad : function(file, image)
30252 if(typeof(file.target) == 'undefined' || !file.target){
30256 var width = image.dom.naturalWidth || image.dom.width;
30257 var height = image.dom.naturalHeight || image.dom.height;
30259 if(!this.previewResize) {
30263 if(width > height){
30264 file.target.addClass('wide');
30268 file.target.addClass('tall');
30273 uploadFromSource : function(file, crop)
30275 this.xhr = new XMLHttpRequest();
30277 this.managerEl.createChild({
30279 cls : 'roo-document-manager-loading',
30283 tooltip : file.name,
30284 cls : 'roo-document-manager-thumb',
30285 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30291 this.xhr.open(this.method, this.url, true);
30294 "Accept": "application/json",
30295 "Cache-Control": "no-cache",
30296 "X-Requested-With": "XMLHttpRequest"
30299 for (var headerName in headers) {
30300 var headerValue = headers[headerName];
30302 this.xhr.setRequestHeader(headerName, headerValue);
30308 this.xhr.onload = function()
30310 _this.xhrOnLoad(_this.xhr);
30313 this.xhr.onerror = function()
30315 _this.xhrOnError(_this.xhr);
30318 var formData = new FormData();
30320 formData.append('returnHTML', 'NO');
30322 formData.append('crop', crop);
30324 if(typeof(file.filename) != 'undefined'){
30325 formData.append('filename', file.filename);
30328 if(typeof(file.mimetype) != 'undefined'){
30329 formData.append('mimetype', file.mimetype);
30334 if(this.fireEvent('prepare', this, formData) != false){
30335 this.xhr.send(formData);
30345 * @class Roo.bootstrap.DocumentViewer
30346 * @extends Roo.bootstrap.Component
30347 * Bootstrap DocumentViewer class
30348 * @cfg {Boolean} showDownload (true|false) show download button (default true)
30349 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30352 * Create a new DocumentViewer
30353 * @param {Object} config The config object
30356 Roo.bootstrap.DocumentViewer = function(config){
30357 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30362 * Fire after initEvent
30363 * @param {Roo.bootstrap.DocumentViewer} this
30369 * @param {Roo.bootstrap.DocumentViewer} this
30374 * Fire after download button
30375 * @param {Roo.bootstrap.DocumentViewer} this
30380 * Fire after trash button
30381 * @param {Roo.bootstrap.DocumentViewer} this
30388 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
30390 showDownload : true,
30394 getAutoCreate : function()
30398 cls : 'roo-document-viewer',
30402 cls : 'roo-document-viewer-body',
30406 cls : 'roo-document-viewer-thumb',
30410 cls : 'roo-document-viewer-image'
30418 cls : 'roo-document-viewer-footer',
30421 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30425 cls : 'btn-group roo-document-viewer-download',
30429 cls : 'btn btn-default',
30430 html : '<i class="fa fa-download"></i>'
30436 cls : 'btn-group roo-document-viewer-trash',
30440 cls : 'btn btn-default',
30441 html : '<i class="fa fa-trash"></i>'
30454 initEvents : function()
30456 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30457 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30459 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30460 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30462 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30463 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30465 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30466 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30468 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30469 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30471 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30472 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30474 this.bodyEl.on('click', this.onClick, this);
30475 this.downloadBtn.on('click', this.onDownload, this);
30476 this.trashBtn.on('click', this.onTrash, this);
30478 this.downloadBtn.hide();
30479 this.trashBtn.hide();
30481 if(this.showDownload){
30482 this.downloadBtn.show();
30485 if(this.showTrash){
30486 this.trashBtn.show();
30489 if(!this.showDownload && !this.showTrash) {
30490 this.footerEl.hide();
30495 initial : function()
30497 this.fireEvent('initial', this);
30501 onClick : function(e)
30503 e.preventDefault();
30505 this.fireEvent('click', this);
30508 onDownload : function(e)
30510 e.preventDefault();
30512 this.fireEvent('download', this);
30515 onTrash : function(e)
30517 e.preventDefault();
30519 this.fireEvent('trash', this);
30531 * @class Roo.bootstrap.NavProgressBar
30532 * @extends Roo.bootstrap.Component
30533 * Bootstrap NavProgressBar class
30536 * Create a new nav progress bar
30537 * @param {Object} config The config object
30540 Roo.bootstrap.NavProgressBar = function(config){
30541 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30543 this.bullets = this.bullets || [];
30545 // Roo.bootstrap.NavProgressBar.register(this);
30549 * Fires when the active item changes
30550 * @param {Roo.bootstrap.NavProgressBar} this
30551 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30552 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
30559 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
30564 getAutoCreate : function()
30566 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30570 cls : 'roo-navigation-bar-group',
30574 cls : 'roo-navigation-top-bar'
30578 cls : 'roo-navigation-bullets-bar',
30582 cls : 'roo-navigation-bar'
30589 cls : 'roo-navigation-bottom-bar'
30599 initEvents: function()
30604 onRender : function(ct, position)
30606 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30608 if(this.bullets.length){
30609 Roo.each(this.bullets, function(b){
30618 addItem : function(cfg)
30620 var item = new Roo.bootstrap.NavProgressItem(cfg);
30622 item.parentId = this.id;
30623 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30626 var top = new Roo.bootstrap.Element({
30628 cls : 'roo-navigation-bar-text'
30631 var bottom = new Roo.bootstrap.Element({
30633 cls : 'roo-navigation-bar-text'
30636 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30637 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30639 var topText = new Roo.bootstrap.Element({
30641 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30644 var bottomText = new Roo.bootstrap.Element({
30646 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30649 topText.onRender(top.el, null);
30650 bottomText.onRender(bottom.el, null);
30653 item.bottomEl = bottom;
30656 this.barItems.push(item);
30661 getActive : function()
30663 var active = false;
30665 Roo.each(this.barItems, function(v){
30667 if (!v.isActive()) {
30679 setActiveItem : function(item)
30683 Roo.each(this.barItems, function(v){
30684 if (v.rid == item.rid) {
30688 if (v.isActive()) {
30689 v.setActive(false);
30694 item.setActive(true);
30696 this.fireEvent('changed', this, item, prev);
30699 getBarItem: function(rid)
30703 Roo.each(this.barItems, function(e) {
30704 if (e.rid != rid) {
30715 indexOfItem : function(item)
30719 Roo.each(this.barItems, function(v, i){
30721 if (v.rid != item.rid) {
30732 setActiveNext : function()
30734 var i = this.indexOfItem(this.getActive());
30736 if (i > this.barItems.length) {
30740 this.setActiveItem(this.barItems[i+1]);
30743 setActivePrev : function()
30745 var i = this.indexOfItem(this.getActive());
30751 this.setActiveItem(this.barItems[i-1]);
30754 format : function()
30756 if(!this.barItems.length){
30760 var width = 100 / this.barItems.length;
30762 Roo.each(this.barItems, function(i){
30763 i.el.setStyle('width', width + '%');
30764 i.topEl.el.setStyle('width', width + '%');
30765 i.bottomEl.el.setStyle('width', width + '%');
30774 * Nav Progress Item
30779 * @class Roo.bootstrap.NavProgressItem
30780 * @extends Roo.bootstrap.Component
30781 * Bootstrap NavProgressItem class
30782 * @cfg {String} rid the reference id
30783 * @cfg {Boolean} active (true|false) Is item active default false
30784 * @cfg {Boolean} disabled (true|false) Is item active default false
30785 * @cfg {String} html
30786 * @cfg {String} position (top|bottom) text position default bottom
30787 * @cfg {String} icon show icon instead of number
30790 * Create a new NavProgressItem
30791 * @param {Object} config The config object
30793 Roo.bootstrap.NavProgressItem = function(config){
30794 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30799 * The raw click event for the entire grid.
30800 * @param {Roo.bootstrap.NavProgressItem} this
30801 * @param {Roo.EventObject} e
30808 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30814 position : 'bottom',
30817 getAutoCreate : function()
30819 var iconCls = 'roo-navigation-bar-item-icon';
30821 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30825 cls: 'roo-navigation-bar-item',
30835 cfg.cls += ' active';
30838 cfg.cls += ' disabled';
30844 disable : function()
30846 this.setDisabled(true);
30849 enable : function()
30851 this.setDisabled(false);
30854 initEvents: function()
30856 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30858 this.iconEl.on('click', this.onClick, this);
30861 onClick : function(e)
30863 e.preventDefault();
30869 if(this.fireEvent('click', this, e) === false){
30873 this.parent().setActiveItem(this);
30876 isActive: function ()
30878 return this.active;
30881 setActive : function(state)
30883 if(this.active == state){
30887 this.active = state;
30890 this.el.addClass('active');
30894 this.el.removeClass('active');
30899 setDisabled : function(state)
30901 if(this.disabled == state){
30905 this.disabled = state;
30908 this.el.addClass('disabled');
30912 this.el.removeClass('disabled');
30915 tooltipEl : function()
30917 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30930 * @class Roo.bootstrap.FieldLabel
30931 * @extends Roo.bootstrap.Component
30932 * Bootstrap FieldLabel class
30933 * @cfg {String} html contents of the element
30934 * @cfg {String} tag tag of the element default label
30935 * @cfg {String} cls class of the element
30936 * @cfg {String} target label target
30937 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30938 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30939 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30940 * @cfg {String} iconTooltip default "This field is required"
30941 * @cfg {String} indicatorpos (left|right) default left
30944 * Create a new FieldLabel
30945 * @param {Object} config The config object
30948 Roo.bootstrap.FieldLabel = function(config){
30949 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30954 * Fires after the field has been marked as invalid.
30955 * @param {Roo.form.FieldLabel} this
30956 * @param {String} msg The validation message
30961 * Fires after the field has been validated with no errors.
30962 * @param {Roo.form.FieldLabel} this
30968 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30975 invalidClass : 'has-warning',
30976 validClass : 'has-success',
30977 iconTooltip : 'This field is required',
30978 indicatorpos : 'left',
30980 getAutoCreate : function(){
30983 if (!this.allowBlank) {
30989 cls : 'roo-bootstrap-field-label ' + this.cls,
30994 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30995 tooltip : this.iconTooltip
31004 if(this.indicatorpos == 'right'){
31007 cls : 'roo-bootstrap-field-label ' + this.cls,
31016 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
31017 tooltip : this.iconTooltip
31026 initEvents: function()
31028 Roo.bootstrap.Element.superclass.initEvents.call(this);
31030 this.indicator = this.indicatorEl();
31032 if(this.indicator){
31033 this.indicator.removeClass('visible');
31034 this.indicator.addClass('invisible');
31037 Roo.bootstrap.FieldLabel.register(this);
31040 indicatorEl : function()
31042 var indicator = this.el.select('i.roo-required-indicator',true).first();
31053 * Mark this field as valid
31055 markValid : function()
31057 if(this.indicator){
31058 this.indicator.removeClass('visible');
31059 this.indicator.addClass('invisible');
31061 if (Roo.bootstrap.version == 3) {
31062 this.el.removeClass(this.invalidClass);
31063 this.el.addClass(this.validClass);
31065 this.el.removeClass('is-invalid');
31066 this.el.addClass('is-valid');
31070 this.fireEvent('valid', this);
31074 * Mark this field as invalid
31075 * @param {String} msg The validation message
31077 markInvalid : function(msg)
31079 if(this.indicator){
31080 this.indicator.removeClass('invisible');
31081 this.indicator.addClass('visible');
31083 if (Roo.bootstrap.version == 3) {
31084 this.el.removeClass(this.validClass);
31085 this.el.addClass(this.invalidClass);
31087 this.el.removeClass('is-valid');
31088 this.el.addClass('is-invalid');
31092 this.fireEvent('invalid', this, msg);
31098 Roo.apply(Roo.bootstrap.FieldLabel, {
31103 * register a FieldLabel Group
31104 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
31106 register : function(label)
31108 if(this.groups.hasOwnProperty(label.target)){
31112 this.groups[label.target] = label;
31116 * fetch a FieldLabel Group based on the target
31117 * @param {string} target
31118 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
31120 get: function(target) {
31121 if (typeof(this.groups[target]) == 'undefined') {
31125 return this.groups[target] ;
31134 * page DateSplitField.
31140 * @class Roo.bootstrap.DateSplitField
31141 * @extends Roo.bootstrap.Component
31142 * Bootstrap DateSplitField class
31143 * @cfg {string} fieldLabel - the label associated
31144 * @cfg {Number} labelWidth set the width of label (0-12)
31145 * @cfg {String} labelAlign (top|left)
31146 * @cfg {Boolean} dayAllowBlank (true|false) default false
31147 * @cfg {Boolean} monthAllowBlank (true|false) default false
31148 * @cfg {Boolean} yearAllowBlank (true|false) default false
31149 * @cfg {string} dayPlaceholder
31150 * @cfg {string} monthPlaceholder
31151 * @cfg {string} yearPlaceholder
31152 * @cfg {string} dayFormat default 'd'
31153 * @cfg {string} monthFormat default 'm'
31154 * @cfg {string} yearFormat default 'Y'
31155 * @cfg {Number} labellg set the width of label (1-12)
31156 * @cfg {Number} labelmd set the width of label (1-12)
31157 * @cfg {Number} labelsm set the width of label (1-12)
31158 * @cfg {Number} labelxs set the width of label (1-12)
31162 * Create a new DateSplitField
31163 * @param {Object} config The config object
31166 Roo.bootstrap.DateSplitField = function(config){
31167 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
31173 * getting the data of years
31174 * @param {Roo.bootstrap.DateSplitField} this
31175 * @param {Object} years
31180 * getting the data of days
31181 * @param {Roo.bootstrap.DateSplitField} this
31182 * @param {Object} days
31187 * Fires after the field has been marked as invalid.
31188 * @param {Roo.form.Field} this
31189 * @param {String} msg The validation message
31194 * Fires after the field has been validated with no errors.
31195 * @param {Roo.form.Field} this
31201 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
31204 labelAlign : 'top',
31206 dayAllowBlank : false,
31207 monthAllowBlank : false,
31208 yearAllowBlank : false,
31209 dayPlaceholder : '',
31210 monthPlaceholder : '',
31211 yearPlaceholder : '',
31215 isFormField : true,
31221 getAutoCreate : function()
31225 cls : 'row roo-date-split-field-group',
31230 cls : 'form-hidden-field roo-date-split-field-group-value',
31236 var labelCls = 'col-md-12';
31237 var contentCls = 'col-md-4';
31239 if(this.fieldLabel){
31243 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
31247 html : this.fieldLabel
31252 if(this.labelAlign == 'left'){
31254 if(this.labelWidth > 12){
31255 label.style = "width: " + this.labelWidth + 'px';
31258 if(this.labelWidth < 13 && this.labelmd == 0){
31259 this.labelmd = this.labelWidth;
31262 if(this.labellg > 0){
31263 labelCls = ' col-lg-' + this.labellg;
31264 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
31267 if(this.labelmd > 0){
31268 labelCls = ' col-md-' + this.labelmd;
31269 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
31272 if(this.labelsm > 0){
31273 labelCls = ' col-sm-' + this.labelsm;
31274 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
31277 if(this.labelxs > 0){
31278 labelCls = ' col-xs-' + this.labelxs;
31279 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
31283 label.cls += ' ' + labelCls;
31285 cfg.cn.push(label);
31288 Roo.each(['day', 'month', 'year'], function(t){
31291 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
31298 inputEl: function ()
31300 return this.el.select('.roo-date-split-field-group-value', true).first();
31303 onRender : function(ct, position)
31307 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31309 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
31311 this.dayField = new Roo.bootstrap.ComboBox({
31312 allowBlank : this.dayAllowBlank,
31313 alwaysQuery : true,
31314 displayField : 'value',
31317 forceSelection : true,
31319 placeholder : this.dayPlaceholder,
31320 selectOnFocus : true,
31321 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31322 triggerAction : 'all',
31324 valueField : 'value',
31325 store : new Roo.data.SimpleStore({
31326 data : (function() {
31328 _this.fireEvent('days', _this, days);
31331 fields : [ 'value' ]
31334 select : function (_self, record, index)
31336 _this.setValue(_this.getValue());
31341 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31343 this.monthField = new Roo.bootstrap.MonthField({
31344 after : '<i class=\"fa fa-calendar\"></i>',
31345 allowBlank : this.monthAllowBlank,
31346 placeholder : this.monthPlaceholder,
31349 render : function (_self)
31351 this.el.select('span.input-group-addon', true).first().on('click', function(e){
31352 e.preventDefault();
31356 select : function (_self, oldvalue, newvalue)
31358 _this.setValue(_this.getValue());
31363 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31365 this.yearField = new Roo.bootstrap.ComboBox({
31366 allowBlank : this.yearAllowBlank,
31367 alwaysQuery : true,
31368 displayField : 'value',
31371 forceSelection : true,
31373 placeholder : this.yearPlaceholder,
31374 selectOnFocus : true,
31375 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31376 triggerAction : 'all',
31378 valueField : 'value',
31379 store : new Roo.data.SimpleStore({
31380 data : (function() {
31382 _this.fireEvent('years', _this, years);
31385 fields : [ 'value' ]
31388 select : function (_self, record, index)
31390 _this.setValue(_this.getValue());
31395 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31398 setValue : function(v, format)
31400 this.inputEl.dom.value = v;
31402 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31404 var d = Date.parseDate(v, f);
31411 this.setDay(d.format(this.dayFormat));
31412 this.setMonth(d.format(this.monthFormat));
31413 this.setYear(d.format(this.yearFormat));
31420 setDay : function(v)
31422 this.dayField.setValue(v);
31423 this.inputEl.dom.value = this.getValue();
31428 setMonth : function(v)
31430 this.monthField.setValue(v, true);
31431 this.inputEl.dom.value = this.getValue();
31436 setYear : function(v)
31438 this.yearField.setValue(v);
31439 this.inputEl.dom.value = this.getValue();
31444 getDay : function()
31446 return this.dayField.getValue();
31449 getMonth : function()
31451 return this.monthField.getValue();
31454 getYear : function()
31456 return this.yearField.getValue();
31459 getValue : function()
31461 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31463 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31473 this.inputEl.dom.value = '';
31478 validate : function()
31480 var d = this.dayField.validate();
31481 var m = this.monthField.validate();
31482 var y = this.yearField.validate();
31487 (!this.dayAllowBlank && !d) ||
31488 (!this.monthAllowBlank && !m) ||
31489 (!this.yearAllowBlank && !y)
31494 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31503 this.markInvalid();
31508 markValid : function()
31511 var label = this.el.select('label', true).first();
31512 var icon = this.el.select('i.fa-star', true).first();
31518 this.fireEvent('valid', this);
31522 * Mark this field as invalid
31523 * @param {String} msg The validation message
31525 markInvalid : function(msg)
31528 var label = this.el.select('label', true).first();
31529 var icon = this.el.select('i.fa-star', true).first();
31531 if(label && !icon){
31532 this.el.select('.roo-date-split-field-label', true).createChild({
31534 cls : 'text-danger fa fa-lg fa-star',
31535 tooltip : 'This field is required',
31536 style : 'margin-right:5px;'
31540 this.fireEvent('invalid', this, msg);
31543 clearInvalid : function()
31545 var label = this.el.select('label', true).first();
31546 var icon = this.el.select('i.fa-star', true).first();
31552 this.fireEvent('valid', this);
31555 getName: function()
31565 * http://masonry.desandro.com
31567 * The idea is to render all the bricks based on vertical width...
31569 * The original code extends 'outlayer' - we might need to use that....
31575 * @class Roo.bootstrap.LayoutMasonry
31576 * @extends Roo.bootstrap.Component
31577 * Bootstrap Layout Masonry class
31580 * Create a new Element
31581 * @param {Object} config The config object
31584 Roo.bootstrap.LayoutMasonry = function(config){
31586 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31590 Roo.bootstrap.LayoutMasonry.register(this);
31596 * Fire after layout the items
31597 * @param {Roo.bootstrap.LayoutMasonry} this
31598 * @param {Roo.EventObject} e
31605 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
31608 * @cfg {Boolean} isLayoutInstant = no animation?
31610 isLayoutInstant : false, // needed?
31613 * @cfg {Number} boxWidth width of the columns
31618 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
31623 * @cfg {Number} padWidth padding below box..
31628 * @cfg {Number} gutter gutter width..
31633 * @cfg {Number} maxCols maximum number of columns
31639 * @cfg {Boolean} isAutoInitial defalut true
31641 isAutoInitial : true,
31646 * @cfg {Boolean} isHorizontal defalut false
31648 isHorizontal : false,
31650 currentSize : null,
31656 bricks: null, //CompositeElement
31660 _isLayoutInited : false,
31662 // isAlternative : false, // only use for vertical layout...
31665 * @cfg {Number} alternativePadWidth padding below box..
31667 alternativePadWidth : 50,
31669 selectedBrick : [],
31671 getAutoCreate : function(){
31673 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31677 cls: 'blog-masonary-wrapper ' + this.cls,
31679 cls : 'mas-boxes masonary'
31686 getChildContainer: function( )
31688 if (this.boxesEl) {
31689 return this.boxesEl;
31692 this.boxesEl = this.el.select('.mas-boxes').first();
31694 return this.boxesEl;
31698 initEvents : function()
31702 if(this.isAutoInitial){
31703 Roo.log('hook children rendered');
31704 this.on('childrenrendered', function() {
31705 Roo.log('children rendered');
31711 initial : function()
31713 this.selectedBrick = [];
31715 this.currentSize = this.el.getBox(true);
31717 Roo.EventManager.onWindowResize(this.resize, this);
31719 if(!this.isAutoInitial){
31727 //this.layout.defer(500,this);
31731 resize : function()
31733 var cs = this.el.getBox(true);
31736 this.currentSize.width == cs.width &&
31737 this.currentSize.x == cs.x &&
31738 this.currentSize.height == cs.height &&
31739 this.currentSize.y == cs.y
31741 Roo.log("no change in with or X or Y");
31745 this.currentSize = cs;
31751 layout : function()
31753 this._resetLayout();
31755 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31757 this.layoutItems( isInstant );
31759 this._isLayoutInited = true;
31761 this.fireEvent('layout', this);
31765 _resetLayout : function()
31767 if(this.isHorizontal){
31768 this.horizontalMeasureColumns();
31772 this.verticalMeasureColumns();
31776 verticalMeasureColumns : function()
31778 this.getContainerWidth();
31780 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31781 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31785 var boxWidth = this.boxWidth + this.padWidth;
31787 if(this.containerWidth < this.boxWidth){
31788 boxWidth = this.containerWidth
31791 var containerWidth = this.containerWidth;
31793 var cols = Math.floor(containerWidth / boxWidth);
31795 this.cols = Math.max( cols, 1 );
31797 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31799 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31801 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31803 this.colWidth = boxWidth + avail - this.padWidth;
31805 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31806 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31809 horizontalMeasureColumns : function()
31811 this.getContainerWidth();
31813 var boxWidth = this.boxWidth;
31815 if(this.containerWidth < boxWidth){
31816 boxWidth = this.containerWidth;
31819 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31821 this.el.setHeight(boxWidth);
31825 getContainerWidth : function()
31827 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31830 layoutItems : function( isInstant )
31832 Roo.log(this.bricks);
31834 var items = Roo.apply([], this.bricks);
31836 if(this.isHorizontal){
31837 this._horizontalLayoutItems( items , isInstant );
31841 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31842 // this._verticalAlternativeLayoutItems( items , isInstant );
31846 this._verticalLayoutItems( items , isInstant );
31850 _verticalLayoutItems : function ( items , isInstant)
31852 if ( !items || !items.length ) {
31857 ['xs', 'xs', 'xs', 'tall'],
31858 ['xs', 'xs', 'tall'],
31859 ['xs', 'xs', 'sm'],
31860 ['xs', 'xs', 'xs'],
31866 ['sm', 'xs', 'xs'],
31870 ['tall', 'xs', 'xs', 'xs'],
31871 ['tall', 'xs', 'xs'],
31883 Roo.each(items, function(item, k){
31885 switch (item.size) {
31886 // these layouts take up a full box,
31897 boxes.push([item]);
31920 var filterPattern = function(box, length)
31928 var pattern = box.slice(0, length);
31932 Roo.each(pattern, function(i){
31933 format.push(i.size);
31936 Roo.each(standard, function(s){
31938 if(String(s) != String(format)){
31947 if(!match && length == 1){
31952 filterPattern(box, length - 1);
31956 queue.push(pattern);
31958 box = box.slice(length, box.length);
31960 filterPattern(box, 4);
31966 Roo.each(boxes, function(box, k){
31972 if(box.length == 1){
31977 filterPattern(box, 4);
31981 this._processVerticalLayoutQueue( queue, isInstant );
31985 // _verticalAlternativeLayoutItems : function( items , isInstant )
31987 // if ( !items || !items.length ) {
31991 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31995 _horizontalLayoutItems : function ( items , isInstant)
31997 if ( !items || !items.length || items.length < 3) {
32003 var eItems = items.slice(0, 3);
32005 items = items.slice(3, items.length);
32008 ['xs', 'xs', 'xs', 'wide'],
32009 ['xs', 'xs', 'wide'],
32010 ['xs', 'xs', 'sm'],
32011 ['xs', 'xs', 'xs'],
32017 ['sm', 'xs', 'xs'],
32021 ['wide', 'xs', 'xs', 'xs'],
32022 ['wide', 'xs', 'xs'],
32035 Roo.each(items, function(item, k){
32037 switch (item.size) {
32048 boxes.push([item]);
32072 var filterPattern = function(box, length)
32080 var pattern = box.slice(0, length);
32084 Roo.each(pattern, function(i){
32085 format.push(i.size);
32088 Roo.each(standard, function(s){
32090 if(String(s) != String(format)){
32099 if(!match && length == 1){
32104 filterPattern(box, length - 1);
32108 queue.push(pattern);
32110 box = box.slice(length, box.length);
32112 filterPattern(box, 4);
32118 Roo.each(boxes, function(box, k){
32124 if(box.length == 1){
32129 filterPattern(box, 4);
32136 var pos = this.el.getBox(true);
32140 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32142 var hit_end = false;
32144 Roo.each(queue, function(box){
32148 Roo.each(box, function(b){
32150 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32160 Roo.each(box, function(b){
32162 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32165 mx = Math.max(mx, b.x);
32169 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
32173 Roo.each(box, function(b){
32175 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32189 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
32192 /** Sets position of item in DOM
32193 * @param {Element} item
32194 * @param {Number} x - horizontal position
32195 * @param {Number} y - vertical position
32196 * @param {Boolean} isInstant - disables transitions
32198 _processVerticalLayoutQueue : function( queue, isInstant )
32200 var pos = this.el.getBox(true);
32205 for (var i = 0; i < this.cols; i++){
32209 Roo.each(queue, function(box, k){
32211 var col = k % this.cols;
32213 Roo.each(box, function(b,kk){
32215 b.el.position('absolute');
32217 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32218 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32220 if(b.size == 'md-left' || b.size == 'md-right'){
32221 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32222 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32225 b.el.setWidth(width);
32226 b.el.setHeight(height);
32228 b.el.select('iframe',true).setSize(width,height);
32232 for (var i = 0; i < this.cols; i++){
32234 if(maxY[i] < maxY[col]){
32239 col = Math.min(col, i);
32243 x = pos.x + col * (this.colWidth + this.padWidth);
32247 var positions = [];
32249 switch (box.length){
32251 positions = this.getVerticalOneBoxColPositions(x, y, box);
32254 positions = this.getVerticalTwoBoxColPositions(x, y, box);
32257 positions = this.getVerticalThreeBoxColPositions(x, y, box);
32260 positions = this.getVerticalFourBoxColPositions(x, y, box);
32266 Roo.each(box, function(b,kk){
32268 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32270 var sz = b.el.getSize();
32272 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
32280 for (var i = 0; i < this.cols; i++){
32281 mY = Math.max(mY, maxY[i]);
32284 this.el.setHeight(mY - pos.y);
32288 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
32290 // var pos = this.el.getBox(true);
32293 // var maxX = pos.right;
32295 // var maxHeight = 0;
32297 // Roo.each(items, function(item, k){
32301 // item.el.position('absolute');
32303 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
32305 // item.el.setWidth(width);
32307 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
32309 // item.el.setHeight(height);
32312 // item.el.setXY([x, y], isInstant ? false : true);
32314 // item.el.setXY([maxX - width, y], isInstant ? false : true);
32317 // y = y + height + this.alternativePadWidth;
32319 // maxHeight = maxHeight + height + this.alternativePadWidth;
32323 // this.el.setHeight(maxHeight);
32327 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32329 var pos = this.el.getBox(true);
32334 var maxX = pos.right;
32336 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32338 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32340 Roo.each(queue, function(box, k){
32342 Roo.each(box, function(b, kk){
32344 b.el.position('absolute');
32346 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32347 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32349 if(b.size == 'md-left' || b.size == 'md-right'){
32350 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32351 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32354 b.el.setWidth(width);
32355 b.el.setHeight(height);
32363 var positions = [];
32365 switch (box.length){
32367 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32370 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32373 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32376 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32382 Roo.each(box, function(b,kk){
32384 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32386 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32394 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32396 Roo.each(eItems, function(b,k){
32398 b.size = (k == 0) ? 'sm' : 'xs';
32399 b.x = (k == 0) ? 2 : 1;
32400 b.y = (k == 0) ? 2 : 1;
32402 b.el.position('absolute');
32404 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32406 b.el.setWidth(width);
32408 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32410 b.el.setHeight(height);
32414 var positions = [];
32417 x : maxX - this.unitWidth * 2 - this.gutter,
32422 x : maxX - this.unitWidth,
32423 y : minY + (this.unitWidth + this.gutter) * 2
32427 x : maxX - this.unitWidth * 3 - this.gutter * 2,
32431 Roo.each(eItems, function(b,k){
32433 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32439 getVerticalOneBoxColPositions : function(x, y, box)
32443 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32445 if(box[0].size == 'md-left'){
32449 if(box[0].size == 'md-right'){
32454 x : x + (this.unitWidth + this.gutter) * rand,
32461 getVerticalTwoBoxColPositions : function(x, y, box)
32465 if(box[0].size == 'xs'){
32469 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32473 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32487 x : x + (this.unitWidth + this.gutter) * 2,
32488 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32495 getVerticalThreeBoxColPositions : function(x, y, box)
32499 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32507 x : x + (this.unitWidth + this.gutter) * 1,
32512 x : x + (this.unitWidth + this.gutter) * 2,
32520 if(box[0].size == 'xs' && box[1].size == 'xs'){
32529 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32533 x : x + (this.unitWidth + this.gutter) * 1,
32547 x : x + (this.unitWidth + this.gutter) * 2,
32552 x : x + (this.unitWidth + this.gutter) * 2,
32553 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32560 getVerticalFourBoxColPositions : function(x, y, box)
32564 if(box[0].size == 'xs'){
32573 y : y + (this.unitHeight + this.gutter) * 1
32578 y : y + (this.unitHeight + this.gutter) * 2
32582 x : x + (this.unitWidth + this.gutter) * 1,
32596 x : x + (this.unitWidth + this.gutter) * 2,
32601 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32602 y : y + (this.unitHeight + this.gutter) * 1
32606 x : x + (this.unitWidth + this.gutter) * 2,
32607 y : y + (this.unitWidth + this.gutter) * 2
32614 getHorizontalOneBoxColPositions : function(maxX, minY, box)
32618 if(box[0].size == 'md-left'){
32620 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32627 if(box[0].size == 'md-right'){
32629 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32630 y : minY + (this.unitWidth + this.gutter) * 1
32636 var rand = Math.floor(Math.random() * (4 - box[0].y));
32639 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32640 y : minY + (this.unitWidth + this.gutter) * rand
32647 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32651 if(box[0].size == 'xs'){
32654 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32659 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32660 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32668 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32673 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32674 y : minY + (this.unitWidth + this.gutter) * 2
32681 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32685 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32688 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32693 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32694 y : minY + (this.unitWidth + this.gutter) * 1
32698 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32699 y : minY + (this.unitWidth + this.gutter) * 2
32706 if(box[0].size == 'xs' && box[1].size == 'xs'){
32709 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32714 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32719 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32720 y : minY + (this.unitWidth + this.gutter) * 1
32728 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32733 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32734 y : minY + (this.unitWidth + this.gutter) * 2
32738 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32739 y : minY + (this.unitWidth + this.gutter) * 2
32746 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32750 if(box[0].size == 'xs'){
32753 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32758 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32763 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),
32768 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32769 y : minY + (this.unitWidth + this.gutter) * 1
32777 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32782 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32783 y : minY + (this.unitWidth + this.gutter) * 2
32787 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32788 y : minY + (this.unitWidth + this.gutter) * 2
32792 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),
32793 y : minY + (this.unitWidth + this.gutter) * 2
32801 * remove a Masonry Brick
32802 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32804 removeBrick : function(brick_id)
32810 for (var i = 0; i<this.bricks.length; i++) {
32811 if (this.bricks[i].id == brick_id) {
32812 this.bricks.splice(i,1);
32813 this.el.dom.removeChild(Roo.get(brick_id).dom);
32820 * adds a Masonry Brick
32821 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32823 addBrick : function(cfg)
32825 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32826 //this.register(cn);
32827 cn.parentId = this.id;
32828 cn.render(this.el);
32833 * register a Masonry Brick
32834 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32837 register : function(brick)
32839 this.bricks.push(brick);
32840 brick.masonryId = this.id;
32844 * clear all the Masonry Brick
32846 clearAll : function()
32849 //this.getChildContainer().dom.innerHTML = "";
32850 this.el.dom.innerHTML = '';
32853 getSelected : function()
32855 if (!this.selectedBrick) {
32859 return this.selectedBrick;
32863 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32867 * register a Masonry Layout
32868 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32871 register : function(layout)
32873 this.groups[layout.id] = layout;
32876 * fetch a Masonry Layout based on the masonry layout ID
32877 * @param {string} the masonry layout to add
32878 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32881 get: function(layout_id) {
32882 if (typeof(this.groups[layout_id]) == 'undefined') {
32885 return this.groups[layout_id] ;
32897 * http://masonry.desandro.com
32899 * The idea is to render all the bricks based on vertical width...
32901 * The original code extends 'outlayer' - we might need to use that....
32907 * @class Roo.bootstrap.LayoutMasonryAuto
32908 * @extends Roo.bootstrap.Component
32909 * Bootstrap Layout Masonry class
32912 * Create a new Element
32913 * @param {Object} config The config object
32916 Roo.bootstrap.LayoutMasonryAuto = function(config){
32917 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32920 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32923 * @cfg {Boolean} isFitWidth - resize the width..
32925 isFitWidth : false, // options..
32927 * @cfg {Boolean} isOriginLeft = left align?
32929 isOriginLeft : true,
32931 * @cfg {Boolean} isOriginTop = top align?
32933 isOriginTop : false,
32935 * @cfg {Boolean} isLayoutInstant = no animation?
32937 isLayoutInstant : false, // needed?
32939 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32941 isResizingContainer : true,
32943 * @cfg {Number} columnWidth width of the columns
32949 * @cfg {Number} maxCols maximum number of columns
32954 * @cfg {Number} padHeight padding below box..
32960 * @cfg {Boolean} isAutoInitial defalut true
32963 isAutoInitial : true,
32969 initialColumnWidth : 0,
32970 currentSize : null,
32972 colYs : null, // array.
32979 bricks: null, //CompositeElement
32980 cols : 0, // array?
32981 // element : null, // wrapped now this.el
32982 _isLayoutInited : null,
32985 getAutoCreate : function(){
32989 cls: 'blog-masonary-wrapper ' + this.cls,
32991 cls : 'mas-boxes masonary'
32998 getChildContainer: function( )
33000 if (this.boxesEl) {
33001 return this.boxesEl;
33004 this.boxesEl = this.el.select('.mas-boxes').first();
33006 return this.boxesEl;
33010 initEvents : function()
33014 if(this.isAutoInitial){
33015 Roo.log('hook children rendered');
33016 this.on('childrenrendered', function() {
33017 Roo.log('children rendered');
33024 initial : function()
33026 this.reloadItems();
33028 this.currentSize = this.el.getBox(true);
33030 /// was window resize... - let's see if this works..
33031 Roo.EventManager.onWindowResize(this.resize, this);
33033 if(!this.isAutoInitial){
33038 this.layout.defer(500,this);
33041 reloadItems: function()
33043 this.bricks = this.el.select('.masonry-brick', true);
33045 this.bricks.each(function(b) {
33046 //Roo.log(b.getSize());
33047 if (!b.attr('originalwidth')) {
33048 b.attr('originalwidth', b.getSize().width);
33053 Roo.log(this.bricks.elements.length);
33056 resize : function()
33059 var cs = this.el.getBox(true);
33061 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
33062 Roo.log("no change in with or X");
33065 this.currentSize = cs;
33069 layout : function()
33072 this._resetLayout();
33073 //this._manageStamps();
33075 // don't animate first layout
33076 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33077 this.layoutItems( isInstant );
33079 // flag for initalized
33080 this._isLayoutInited = true;
33083 layoutItems : function( isInstant )
33085 //var items = this._getItemsForLayout( this.items );
33086 // original code supports filtering layout items.. we just ignore it..
33088 this._layoutItems( this.bricks , isInstant );
33090 this._postLayout();
33092 _layoutItems : function ( items , isInstant)
33094 //this.fireEvent( 'layout', this, items );
33097 if ( !items || !items.elements.length ) {
33098 // no items, emit event with empty array
33103 items.each(function(item) {
33104 Roo.log("layout item");
33106 // get x/y object from method
33107 var position = this._getItemLayoutPosition( item );
33109 position.item = item;
33110 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
33111 queue.push( position );
33114 this._processLayoutQueue( queue );
33116 /** Sets position of item in DOM
33117 * @param {Element} item
33118 * @param {Number} x - horizontal position
33119 * @param {Number} y - vertical position
33120 * @param {Boolean} isInstant - disables transitions
33122 _processLayoutQueue : function( queue )
33124 for ( var i=0, len = queue.length; i < len; i++ ) {
33125 var obj = queue[i];
33126 obj.item.position('absolute');
33127 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
33133 * Any logic you want to do after each layout,
33134 * i.e. size the container
33136 _postLayout : function()
33138 this.resizeContainer();
33141 resizeContainer : function()
33143 if ( !this.isResizingContainer ) {
33146 var size = this._getContainerSize();
33148 this.el.setSize(size.width,size.height);
33149 this.boxesEl.setSize(size.width,size.height);
33155 _resetLayout : function()
33157 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
33158 this.colWidth = this.el.getWidth();
33159 //this.gutter = this.el.getWidth();
33161 this.measureColumns();
33167 this.colYs.push( 0 );
33173 measureColumns : function()
33175 this.getContainerWidth();
33176 // if columnWidth is 0, default to outerWidth of first item
33177 if ( !this.columnWidth ) {
33178 var firstItem = this.bricks.first();
33179 Roo.log(firstItem);
33180 this.columnWidth = this.containerWidth;
33181 if (firstItem && firstItem.attr('originalwidth') ) {
33182 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
33184 // columnWidth fall back to item of first element
33185 Roo.log("set column width?");
33186 this.initialColumnWidth = this.columnWidth ;
33188 // if first elem has no width, default to size of container
33193 if (this.initialColumnWidth) {
33194 this.columnWidth = this.initialColumnWidth;
33199 // column width is fixed at the top - however if container width get's smaller we should
33202 // this bit calcs how man columns..
33204 var columnWidth = this.columnWidth += this.gutter;
33206 // calculate columns
33207 var containerWidth = this.containerWidth + this.gutter;
33209 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
33210 // fix rounding errors, typically with gutters
33211 var excess = columnWidth - containerWidth % columnWidth;
33214 // if overshoot is less than a pixel, round up, otherwise floor it
33215 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
33216 cols = Math[ mathMethod ]( cols );
33217 this.cols = Math.max( cols, 1 );
33218 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33220 // padding positioning..
33221 var totalColWidth = this.cols * this.columnWidth;
33222 var padavail = this.containerWidth - totalColWidth;
33223 // so for 2 columns - we need 3 'pads'
33225 var padNeeded = (1+this.cols) * this.padWidth;
33227 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
33229 this.columnWidth += padExtra
33230 //this.padWidth = Math.floor(padavail / ( this.cols));
33232 // adjust colum width so that padding is fixed??
33234 // we have 3 columns ... total = width * 3
33235 // we have X left over... that should be used by
33237 //if (this.expandC) {
33245 getContainerWidth : function()
33247 /* // container is parent if fit width
33248 var container = this.isFitWidth ? this.element.parentNode : this.element;
33249 // check that this.size and size are there
33250 // IE8 triggers resize on body size change, so they might not be
33252 var size = getSize( container ); //FIXME
33253 this.containerWidth = size && size.innerWidth; //FIXME
33256 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33260 _getItemLayoutPosition : function( item ) // what is item?
33262 // we resize the item to our columnWidth..
33264 item.setWidth(this.columnWidth);
33265 item.autoBoxAdjust = false;
33267 var sz = item.getSize();
33269 // how many columns does this brick span
33270 var remainder = this.containerWidth % this.columnWidth;
33272 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
33273 // round if off by 1 pixel, otherwise use ceil
33274 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
33275 colSpan = Math.min( colSpan, this.cols );
33277 // normally this should be '1' as we dont' currently allow multi width columns..
33279 var colGroup = this._getColGroup( colSpan );
33280 // get the minimum Y value from the columns
33281 var minimumY = Math.min.apply( Math, colGroup );
33282 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
33284 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
33286 // position the brick
33288 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
33289 y: this.currentSize.y + minimumY + this.padHeight
33293 // apply setHeight to necessary columns
33294 var setHeight = minimumY + sz.height + this.padHeight;
33295 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
33297 var setSpan = this.cols + 1 - colGroup.length;
33298 for ( var i = 0; i < setSpan; i++ ) {
33299 this.colYs[ shortColIndex + i ] = setHeight ;
33306 * @param {Number} colSpan - number of columns the element spans
33307 * @returns {Array} colGroup
33309 _getColGroup : function( colSpan )
33311 if ( colSpan < 2 ) {
33312 // if brick spans only one column, use all the column Ys
33317 // how many different places could this brick fit horizontally
33318 var groupCount = this.cols + 1 - colSpan;
33319 // for each group potential horizontal position
33320 for ( var i = 0; i < groupCount; i++ ) {
33321 // make an array of colY values for that one group
33322 var groupColYs = this.colYs.slice( i, i + colSpan );
33323 // and get the max value of the array
33324 colGroup[i] = Math.max.apply( Math, groupColYs );
33329 _manageStamp : function( stamp )
33331 var stampSize = stamp.getSize();
33332 var offset = stamp.getBox();
33333 // get the columns that this stamp affects
33334 var firstX = this.isOriginLeft ? offset.x : offset.right;
33335 var lastX = firstX + stampSize.width;
33336 var firstCol = Math.floor( firstX / this.columnWidth );
33337 firstCol = Math.max( 0, firstCol );
33339 var lastCol = Math.floor( lastX / this.columnWidth );
33340 // lastCol should not go over if multiple of columnWidth #425
33341 lastCol -= lastX % this.columnWidth ? 0 : 1;
33342 lastCol = Math.min( this.cols - 1, lastCol );
33344 // set colYs to bottom of the stamp
33345 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33348 for ( var i = firstCol; i <= lastCol; i++ ) {
33349 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33354 _getContainerSize : function()
33356 this.maxY = Math.max.apply( Math, this.colYs );
33361 if ( this.isFitWidth ) {
33362 size.width = this._getContainerFitWidth();
33368 _getContainerFitWidth : function()
33370 var unusedCols = 0;
33371 // count unused columns
33374 if ( this.colYs[i] !== 0 ) {
33379 // fit container to columns that have been used
33380 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33383 needsResizeLayout : function()
33385 var previousWidth = this.containerWidth;
33386 this.getContainerWidth();
33387 return previousWidth !== this.containerWidth;
33402 * @class Roo.bootstrap.MasonryBrick
33403 * @extends Roo.bootstrap.Component
33404 * Bootstrap MasonryBrick class
33407 * Create a new MasonryBrick
33408 * @param {Object} config The config object
33411 Roo.bootstrap.MasonryBrick = function(config){
33413 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33415 Roo.bootstrap.MasonryBrick.register(this);
33421 * When a MasonryBrick is clcik
33422 * @param {Roo.bootstrap.MasonryBrick} this
33423 * @param {Roo.EventObject} e
33429 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
33432 * @cfg {String} title
33436 * @cfg {String} html
33440 * @cfg {String} bgimage
33444 * @cfg {String} videourl
33448 * @cfg {String} cls
33452 * @cfg {String} href
33456 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33461 * @cfg {String} placetitle (center|bottom)
33466 * @cfg {Boolean} isFitContainer defalut true
33468 isFitContainer : true,
33471 * @cfg {Boolean} preventDefault defalut false
33473 preventDefault : false,
33476 * @cfg {Boolean} inverse defalut false
33478 maskInverse : false,
33480 getAutoCreate : function()
33482 if(!this.isFitContainer){
33483 return this.getSplitAutoCreate();
33486 var cls = 'masonry-brick masonry-brick-full';
33488 if(this.href.length){
33489 cls += ' masonry-brick-link';
33492 if(this.bgimage.length){
33493 cls += ' masonry-brick-image';
33496 if(this.maskInverse){
33497 cls += ' mask-inverse';
33500 if(!this.html.length && !this.maskInverse && !this.videourl.length){
33501 cls += ' enable-mask';
33505 cls += ' masonry-' + this.size + '-brick';
33508 if(this.placetitle.length){
33510 switch (this.placetitle) {
33512 cls += ' masonry-center-title';
33515 cls += ' masonry-bottom-title';
33522 if(!this.html.length && !this.bgimage.length){
33523 cls += ' masonry-center-title';
33526 if(!this.html.length && this.bgimage.length){
33527 cls += ' masonry-bottom-title';
33532 cls += ' ' + this.cls;
33536 tag: (this.href.length) ? 'a' : 'div',
33541 cls: 'masonry-brick-mask'
33545 cls: 'masonry-brick-paragraph',
33551 if(this.href.length){
33552 cfg.href = this.href;
33555 var cn = cfg.cn[1].cn;
33557 if(this.title.length){
33560 cls: 'masonry-brick-title',
33565 if(this.html.length){
33568 cls: 'masonry-brick-text',
33573 if (!this.title.length && !this.html.length) {
33574 cfg.cn[1].cls += ' hide';
33577 if(this.bgimage.length){
33580 cls: 'masonry-brick-image-view',
33585 if(this.videourl.length){
33586 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33587 // youtube support only?
33590 cls: 'masonry-brick-image-view',
33593 allowfullscreen : true
33601 getSplitAutoCreate : function()
33603 var cls = 'masonry-brick masonry-brick-split';
33605 if(this.href.length){
33606 cls += ' masonry-brick-link';
33609 if(this.bgimage.length){
33610 cls += ' masonry-brick-image';
33614 cls += ' masonry-' + this.size + '-brick';
33617 switch (this.placetitle) {
33619 cls += ' masonry-center-title';
33622 cls += ' masonry-bottom-title';
33625 if(!this.bgimage.length){
33626 cls += ' masonry-center-title';
33629 if(this.bgimage.length){
33630 cls += ' masonry-bottom-title';
33636 cls += ' ' + this.cls;
33640 tag: (this.href.length) ? 'a' : 'div',
33645 cls: 'masonry-brick-split-head',
33649 cls: 'masonry-brick-paragraph',
33656 cls: 'masonry-brick-split-body',
33662 if(this.href.length){
33663 cfg.href = this.href;
33666 if(this.title.length){
33667 cfg.cn[0].cn[0].cn.push({
33669 cls: 'masonry-brick-title',
33674 if(this.html.length){
33675 cfg.cn[1].cn.push({
33677 cls: 'masonry-brick-text',
33682 if(this.bgimage.length){
33683 cfg.cn[0].cn.push({
33685 cls: 'masonry-brick-image-view',
33690 if(this.videourl.length){
33691 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33692 // youtube support only?
33693 cfg.cn[0].cn.cn.push({
33695 cls: 'masonry-brick-image-view',
33698 allowfullscreen : true
33705 initEvents: function()
33707 switch (this.size) {
33740 this.el.on('touchstart', this.onTouchStart, this);
33741 this.el.on('touchmove', this.onTouchMove, this);
33742 this.el.on('touchend', this.onTouchEnd, this);
33743 this.el.on('contextmenu', this.onContextMenu, this);
33745 this.el.on('mouseenter' ,this.enter, this);
33746 this.el.on('mouseleave', this.leave, this);
33747 this.el.on('click', this.onClick, this);
33750 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33751 this.parent().bricks.push(this);
33756 onClick: function(e, el)
33758 var time = this.endTimer - this.startTimer;
33759 // Roo.log(e.preventDefault());
33762 e.preventDefault();
33767 if(!this.preventDefault){
33771 e.preventDefault();
33773 if (this.activeClass != '') {
33774 this.selectBrick();
33777 this.fireEvent('click', this, e);
33780 enter: function(e, el)
33782 e.preventDefault();
33784 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33788 if(this.bgimage.length && this.html.length){
33789 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33793 leave: function(e, el)
33795 e.preventDefault();
33797 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33801 if(this.bgimage.length && this.html.length){
33802 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33806 onTouchStart: function(e, el)
33808 // e.preventDefault();
33810 this.touchmoved = false;
33812 if(!this.isFitContainer){
33816 if(!this.bgimage.length || !this.html.length){
33820 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33822 this.timer = new Date().getTime();
33826 onTouchMove: function(e, el)
33828 this.touchmoved = true;
33831 onContextMenu : function(e,el)
33833 e.preventDefault();
33834 e.stopPropagation();
33838 onTouchEnd: function(e, el)
33840 // e.preventDefault();
33842 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33849 if(!this.bgimage.length || !this.html.length){
33851 if(this.href.length){
33852 window.location.href = this.href;
33858 if(!this.isFitContainer){
33862 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33864 window.location.href = this.href;
33867 //selection on single brick only
33868 selectBrick : function() {
33870 if (!this.parentId) {
33874 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33875 var index = m.selectedBrick.indexOf(this.id);
33878 m.selectedBrick.splice(index,1);
33879 this.el.removeClass(this.activeClass);
33883 for(var i = 0; i < m.selectedBrick.length; i++) {
33884 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33885 b.el.removeClass(b.activeClass);
33888 m.selectedBrick = [];
33890 m.selectedBrick.push(this.id);
33891 this.el.addClass(this.activeClass);
33895 isSelected : function(){
33896 return this.el.hasClass(this.activeClass);
33901 Roo.apply(Roo.bootstrap.MasonryBrick, {
33904 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33906 * register a Masonry Brick
33907 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33910 register : function(brick)
33912 //this.groups[brick.id] = brick;
33913 this.groups.add(brick.id, brick);
33916 * fetch a masonry brick based on the masonry brick ID
33917 * @param {string} the masonry brick to add
33918 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33921 get: function(brick_id)
33923 // if (typeof(this.groups[brick_id]) == 'undefined') {
33926 // return this.groups[brick_id] ;
33928 if(this.groups.key(brick_id)) {
33929 return this.groups.key(brick_id);
33947 * @class Roo.bootstrap.Brick
33948 * @extends Roo.bootstrap.Component
33949 * Bootstrap Brick class
33952 * Create a new Brick
33953 * @param {Object} config The config object
33956 Roo.bootstrap.Brick = function(config){
33957 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33963 * When a Brick is click
33964 * @param {Roo.bootstrap.Brick} this
33965 * @param {Roo.EventObject} e
33971 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33974 * @cfg {String} title
33978 * @cfg {String} html
33982 * @cfg {String} bgimage
33986 * @cfg {String} cls
33990 * @cfg {String} href
33994 * @cfg {String} video
33998 * @cfg {Boolean} square
34002 getAutoCreate : function()
34004 var cls = 'roo-brick';
34006 if(this.href.length){
34007 cls += ' roo-brick-link';
34010 if(this.bgimage.length){
34011 cls += ' roo-brick-image';
34014 if(!this.html.length && !this.bgimage.length){
34015 cls += ' roo-brick-center-title';
34018 if(!this.html.length && this.bgimage.length){
34019 cls += ' roo-brick-bottom-title';
34023 cls += ' ' + this.cls;
34027 tag: (this.href.length) ? 'a' : 'div',
34032 cls: 'roo-brick-paragraph',
34038 if(this.href.length){
34039 cfg.href = this.href;
34042 var cn = cfg.cn[0].cn;
34044 if(this.title.length){
34047 cls: 'roo-brick-title',
34052 if(this.html.length){
34055 cls: 'roo-brick-text',
34062 if(this.bgimage.length){
34065 cls: 'roo-brick-image-view',
34073 initEvents: function()
34075 if(this.title.length || this.html.length){
34076 this.el.on('mouseenter' ,this.enter, this);
34077 this.el.on('mouseleave', this.leave, this);
34080 Roo.EventManager.onWindowResize(this.resize, this);
34082 if(this.bgimage.length){
34083 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
34084 this.imageEl.on('load', this.onImageLoad, this);
34091 onImageLoad : function()
34096 resize : function()
34098 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
34100 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
34102 if(this.bgimage.length){
34103 var image = this.el.select('.roo-brick-image-view', true).first();
34105 image.setWidth(paragraph.getWidth());
34108 image.setHeight(paragraph.getWidth());
34111 this.el.setHeight(image.getHeight());
34112 paragraph.setHeight(image.getHeight());
34118 enter: function(e, el)
34120 e.preventDefault();
34122 if(this.bgimage.length){
34123 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
34124 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
34128 leave: function(e, el)
34130 e.preventDefault();
34132 if(this.bgimage.length){
34133 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
34134 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
34149 * @class Roo.bootstrap.NumberField
34150 * @extends Roo.bootstrap.Input
34151 * Bootstrap NumberField class
34157 * Create a new NumberField
34158 * @param {Object} config The config object
34161 Roo.bootstrap.NumberField = function(config){
34162 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
34165 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
34168 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
34170 allowDecimals : true,
34172 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
34174 decimalSeparator : ".",
34176 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
34178 decimalPrecision : 2,
34180 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
34182 allowNegative : true,
34185 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
34189 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
34191 minValue : Number.NEGATIVE_INFINITY,
34193 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
34195 maxValue : Number.MAX_VALUE,
34197 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
34199 minText : "The minimum value for this field is {0}",
34201 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
34203 maxText : "The maximum value for this field is {0}",
34205 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
34206 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
34208 nanText : "{0} is not a valid number",
34210 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
34212 thousandsDelimiter : false,
34214 * @cfg {String} valueAlign alignment of value
34216 valueAlign : "left",
34218 getAutoCreate : function()
34220 var hiddenInput = {
34224 cls: 'hidden-number-input'
34228 hiddenInput.name = this.name;
34233 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
34235 this.name = hiddenInput.name;
34237 if(cfg.cn.length > 0) {
34238 cfg.cn.push(hiddenInput);
34245 initEvents : function()
34247 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
34249 var allowed = "0123456789";
34251 if(this.allowDecimals){
34252 allowed += this.decimalSeparator;
34255 if(this.allowNegative){
34259 if(this.thousandsDelimiter) {
34263 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
34265 var keyPress = function(e){
34267 var k = e.getKey();
34269 var c = e.getCharCode();
34272 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
34273 allowed.indexOf(String.fromCharCode(c)) === -1
34279 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
34283 if(allowed.indexOf(String.fromCharCode(c)) === -1){
34288 this.el.on("keypress", keyPress, this);
34291 validateValue : function(value)
34294 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
34298 var num = this.parseValue(value);
34301 this.markInvalid(String.format(this.nanText, value));
34305 if(num < this.minValue){
34306 this.markInvalid(String.format(this.minText, this.minValue));
34310 if(num > this.maxValue){
34311 this.markInvalid(String.format(this.maxText, this.maxValue));
34318 getValue : function()
34320 var v = this.hiddenEl().getValue();
34322 return this.fixPrecision(this.parseValue(v));
34325 parseValue : function(value)
34327 if(this.thousandsDelimiter) {
34329 r = new RegExp(",", "g");
34330 value = value.replace(r, "");
34333 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34334 return isNaN(value) ? '' : value;
34337 fixPrecision : function(value)
34339 if(this.thousandsDelimiter) {
34341 r = new RegExp(",", "g");
34342 value = value.replace(r, "");
34345 var nan = isNaN(value);
34347 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34348 return nan ? '' : value;
34350 return parseFloat(value).toFixed(this.decimalPrecision);
34353 setValue : function(v)
34355 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34361 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34363 this.inputEl().dom.value = (v == '') ? '' :
34364 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34366 if(!this.allowZero && v === '0') {
34367 this.hiddenEl().dom.value = '';
34368 this.inputEl().dom.value = '';
34375 decimalPrecisionFcn : function(v)
34377 return Math.floor(v);
34380 beforeBlur : function()
34382 var v = this.parseValue(this.getRawValue());
34384 if(v || v === 0 || v === ''){
34389 hiddenEl : function()
34391 return this.el.select('input.hidden-number-input',true).first();
34403 * @class Roo.bootstrap.DocumentSlider
34404 * @extends Roo.bootstrap.Component
34405 * Bootstrap DocumentSlider class
34408 * Create a new DocumentViewer
34409 * @param {Object} config The config object
34412 Roo.bootstrap.DocumentSlider = function(config){
34413 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34420 * Fire after initEvent
34421 * @param {Roo.bootstrap.DocumentSlider} this
34426 * Fire after update
34427 * @param {Roo.bootstrap.DocumentSlider} this
34433 * @param {Roo.bootstrap.DocumentSlider} this
34439 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
34445 getAutoCreate : function()
34449 cls : 'roo-document-slider',
34453 cls : 'roo-document-slider-header',
34457 cls : 'roo-document-slider-header-title'
34463 cls : 'roo-document-slider-body',
34467 cls : 'roo-document-slider-prev',
34471 cls : 'fa fa-chevron-left'
34477 cls : 'roo-document-slider-thumb',
34481 cls : 'roo-document-slider-image'
34487 cls : 'roo-document-slider-next',
34491 cls : 'fa fa-chevron-right'
34503 initEvents : function()
34505 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34506 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34508 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34509 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34511 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34512 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34514 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34515 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34517 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34518 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34520 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34521 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34523 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34524 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34526 this.thumbEl.on('click', this.onClick, this);
34528 this.prevIndicator.on('click', this.prev, this);
34530 this.nextIndicator.on('click', this.next, this);
34534 initial : function()
34536 if(this.files.length){
34537 this.indicator = 1;
34541 this.fireEvent('initial', this);
34544 update : function()
34546 this.imageEl.attr('src', this.files[this.indicator - 1]);
34548 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34550 this.prevIndicator.show();
34552 if(this.indicator == 1){
34553 this.prevIndicator.hide();
34556 this.nextIndicator.show();
34558 if(this.indicator == this.files.length){
34559 this.nextIndicator.hide();
34562 this.thumbEl.scrollTo('top');
34564 this.fireEvent('update', this);
34567 onClick : function(e)
34569 e.preventDefault();
34571 this.fireEvent('click', this);
34576 e.preventDefault();
34578 this.indicator = Math.max(1, this.indicator - 1);
34585 e.preventDefault();
34587 this.indicator = Math.min(this.files.length, this.indicator + 1);
34601 * @class Roo.bootstrap.RadioSet
34602 * @extends Roo.bootstrap.Input
34603 * Bootstrap RadioSet class
34604 * @cfg {String} indicatorpos (left|right) default left
34605 * @cfg {Boolean} inline (true|false) inline the element (default true)
34606 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34608 * Create a new RadioSet
34609 * @param {Object} config The config object
34612 Roo.bootstrap.RadioSet = function(config){
34614 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34618 Roo.bootstrap.RadioSet.register(this);
34623 * Fires when the element is checked or unchecked.
34624 * @param {Roo.bootstrap.RadioSet} this This radio
34625 * @param {Roo.bootstrap.Radio} item The checked item
34630 * Fires when the element is click.
34631 * @param {Roo.bootstrap.RadioSet} this This radio set
34632 * @param {Roo.bootstrap.Radio} item The checked item
34633 * @param {Roo.EventObject} e The event object
34640 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
34648 indicatorpos : 'left',
34650 getAutoCreate : function()
34654 cls : 'roo-radio-set-label',
34658 html : this.fieldLabel
34662 if (Roo.bootstrap.version == 3) {
34665 if(this.indicatorpos == 'left'){
34668 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34669 tooltip : 'This field is required'
34674 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34675 tooltip : 'This field is required'
34681 cls : 'roo-radio-set-items'
34684 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34686 if (align === 'left' && this.fieldLabel.length) {
34689 cls : "roo-radio-set-right",
34695 if(this.labelWidth > 12){
34696 label.style = "width: " + this.labelWidth + 'px';
34699 if(this.labelWidth < 13 && this.labelmd == 0){
34700 this.labelmd = this.labelWidth;
34703 if(this.labellg > 0){
34704 label.cls += ' col-lg-' + this.labellg;
34705 items.cls += ' col-lg-' + (12 - this.labellg);
34708 if(this.labelmd > 0){
34709 label.cls += ' col-md-' + this.labelmd;
34710 items.cls += ' col-md-' + (12 - this.labelmd);
34713 if(this.labelsm > 0){
34714 label.cls += ' col-sm-' + this.labelsm;
34715 items.cls += ' col-sm-' + (12 - this.labelsm);
34718 if(this.labelxs > 0){
34719 label.cls += ' col-xs-' + this.labelxs;
34720 items.cls += ' col-xs-' + (12 - this.labelxs);
34726 cls : 'roo-radio-set',
34730 cls : 'roo-radio-set-input',
34733 value : this.value ? this.value : ''
34740 if(this.weight.length){
34741 cfg.cls += ' roo-radio-' + this.weight;
34745 cfg.cls += ' roo-radio-set-inline';
34749 ['xs','sm','md','lg'].map(function(size){
34750 if (settings[size]) {
34751 cfg.cls += ' col-' + size + '-' + settings[size];
34759 initEvents : function()
34761 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34762 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34764 if(!this.fieldLabel.length){
34765 this.labelEl.hide();
34768 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34769 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34771 this.indicator = this.indicatorEl();
34773 if(this.indicator){
34774 this.indicator.addClass('invisible');
34777 this.originalValue = this.getValue();
34781 inputEl: function ()
34783 return this.el.select('.roo-radio-set-input', true).first();
34786 getChildContainer : function()
34788 return this.itemsEl;
34791 register : function(item)
34793 this.radioes.push(item);
34797 validate : function()
34799 if(this.getVisibilityEl().hasClass('hidden')){
34805 Roo.each(this.radioes, function(i){
34814 if(this.allowBlank) {
34818 if(this.disabled || valid){
34823 this.markInvalid();
34828 markValid : function()
34830 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34831 this.indicatorEl().removeClass('visible');
34832 this.indicatorEl().addClass('invisible');
34836 if (Roo.bootstrap.version == 3) {
34837 this.el.removeClass([this.invalidClass, this.validClass]);
34838 this.el.addClass(this.validClass);
34840 this.el.removeClass(['is-invalid','is-valid']);
34841 this.el.addClass(['is-valid']);
34843 this.fireEvent('valid', this);
34846 markInvalid : function(msg)
34848 if(this.allowBlank || this.disabled){
34852 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34853 this.indicatorEl().removeClass('invisible');
34854 this.indicatorEl().addClass('visible');
34856 if (Roo.bootstrap.version == 3) {
34857 this.el.removeClass([this.invalidClass, this.validClass]);
34858 this.el.addClass(this.invalidClass);
34860 this.el.removeClass(['is-invalid','is-valid']);
34861 this.el.addClass(['is-invalid']);
34864 this.fireEvent('invalid', this, msg);
34868 setValue : function(v, suppressEvent)
34870 if(this.value === v){
34877 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34880 Roo.each(this.radioes, function(i){
34882 i.el.removeClass('checked');
34885 Roo.each(this.radioes, function(i){
34887 if(i.value === v || i.value.toString() === v.toString()){
34889 i.el.addClass('checked');
34891 if(suppressEvent !== true){
34892 this.fireEvent('check', this, i);
34903 clearInvalid : function(){
34905 if(!this.el || this.preventMark){
34909 this.el.removeClass([this.invalidClass]);
34911 this.fireEvent('valid', this);
34916 Roo.apply(Roo.bootstrap.RadioSet, {
34920 register : function(set)
34922 this.groups[set.name] = set;
34925 get: function(name)
34927 if (typeof(this.groups[name]) == 'undefined') {
34931 return this.groups[name] ;
34937 * Ext JS Library 1.1.1
34938 * Copyright(c) 2006-2007, Ext JS, LLC.
34940 * Originally Released Under LGPL - original licence link has changed is not relivant.
34943 * <script type="text/javascript">
34948 * @class Roo.bootstrap.SplitBar
34949 * @extends Roo.util.Observable
34950 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34954 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34955 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34956 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34957 split.minSize = 100;
34958 split.maxSize = 600;
34959 split.animate = true;
34960 split.on('moved', splitterMoved);
34963 * Create a new SplitBar
34964 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34965 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34966 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34967 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34968 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34969 position of the SplitBar).
34971 Roo.bootstrap.SplitBar = function(cfg){
34976 // dragElement : elm
34977 // resizingElement: el,
34979 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34980 // placement : Roo.bootstrap.SplitBar.LEFT ,
34981 // existingProxy ???
34984 this.el = Roo.get(cfg.dragElement, true);
34985 this.el.dom.unselectable = "on";
34987 this.resizingEl = Roo.get(cfg.resizingElement, true);
34991 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34992 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34995 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34998 * The minimum size of the resizing element. (Defaults to 0)
35004 * The maximum size of the resizing element. (Defaults to 2000)
35007 this.maxSize = 2000;
35010 * Whether to animate the transition to the new size
35013 this.animate = false;
35016 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
35019 this.useShim = false;
35024 if(!cfg.existingProxy){
35026 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
35028 this.proxy = Roo.get(cfg.existingProxy).dom;
35031 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
35034 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
35037 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
35040 this.dragSpecs = {};
35043 * @private The adapter to use to positon and resize elements
35045 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35046 this.adapter.init(this);
35048 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35050 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
35051 this.el.addClass("roo-splitbar-h");
35054 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
35055 this.el.addClass("roo-splitbar-v");
35061 * Fires when the splitter is moved (alias for {@link #event-moved})
35062 * @param {Roo.bootstrap.SplitBar} this
35063 * @param {Number} newSize the new width or height
35068 * Fires when the splitter is moved
35069 * @param {Roo.bootstrap.SplitBar} this
35070 * @param {Number} newSize the new width or height
35074 * @event beforeresize
35075 * Fires before the splitter is dragged
35076 * @param {Roo.bootstrap.SplitBar} this
35078 "beforeresize" : true,
35080 "beforeapply" : true
35083 Roo.util.Observable.call(this);
35086 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
35087 onStartProxyDrag : function(x, y){
35088 this.fireEvent("beforeresize", this);
35090 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
35092 o.enableDisplayMode("block");
35093 // all splitbars share the same overlay
35094 Roo.bootstrap.SplitBar.prototype.overlay = o;
35096 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
35097 this.overlay.show();
35098 Roo.get(this.proxy).setDisplayed("block");
35099 var size = this.adapter.getElementSize(this);
35100 this.activeMinSize = this.getMinimumSize();;
35101 this.activeMaxSize = this.getMaximumSize();;
35102 var c1 = size - this.activeMinSize;
35103 var c2 = Math.max(this.activeMaxSize - size, 0);
35104 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35105 this.dd.resetConstraints();
35106 this.dd.setXConstraint(
35107 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
35108 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
35110 this.dd.setYConstraint(0, 0);
35112 this.dd.resetConstraints();
35113 this.dd.setXConstraint(0, 0);
35114 this.dd.setYConstraint(
35115 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
35116 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
35119 this.dragSpecs.startSize = size;
35120 this.dragSpecs.startPoint = [x, y];
35121 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
35125 * @private Called after the drag operation by the DDProxy
35127 onEndProxyDrag : function(e){
35128 Roo.get(this.proxy).setDisplayed(false);
35129 var endPoint = Roo.lib.Event.getXY(e);
35131 this.overlay.hide();
35134 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35135 newSize = this.dragSpecs.startSize +
35136 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
35137 endPoint[0] - this.dragSpecs.startPoint[0] :
35138 this.dragSpecs.startPoint[0] - endPoint[0]
35141 newSize = this.dragSpecs.startSize +
35142 (this.placement == Roo.bootstrap.SplitBar.TOP ?
35143 endPoint[1] - this.dragSpecs.startPoint[1] :
35144 this.dragSpecs.startPoint[1] - endPoint[1]
35147 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
35148 if(newSize != this.dragSpecs.startSize){
35149 if(this.fireEvent('beforeapply', this, newSize) !== false){
35150 this.adapter.setElementSize(this, newSize);
35151 this.fireEvent("moved", this, newSize);
35152 this.fireEvent("resize", this, newSize);
35158 * Get the adapter this SplitBar uses
35159 * @return The adapter object
35161 getAdapter : function(){
35162 return this.adapter;
35166 * Set the adapter this SplitBar uses
35167 * @param {Object} adapter A SplitBar adapter object
35169 setAdapter : function(adapter){
35170 this.adapter = adapter;
35171 this.adapter.init(this);
35175 * Gets the minimum size for the resizing element
35176 * @return {Number} The minimum size
35178 getMinimumSize : function(){
35179 return this.minSize;
35183 * Sets the minimum size for the resizing element
35184 * @param {Number} minSize The minimum size
35186 setMinimumSize : function(minSize){
35187 this.minSize = minSize;
35191 * Gets the maximum size for the resizing element
35192 * @return {Number} The maximum size
35194 getMaximumSize : function(){
35195 return this.maxSize;
35199 * Sets the maximum size for the resizing element
35200 * @param {Number} maxSize The maximum size
35202 setMaximumSize : function(maxSize){
35203 this.maxSize = maxSize;
35207 * Sets the initialize size for the resizing element
35208 * @param {Number} size The initial size
35210 setCurrentSize : function(size){
35211 var oldAnimate = this.animate;
35212 this.animate = false;
35213 this.adapter.setElementSize(this, size);
35214 this.animate = oldAnimate;
35218 * Destroy this splitbar.
35219 * @param {Boolean} removeEl True to remove the element
35221 destroy : function(removeEl){
35223 this.shim.remove();
35226 this.proxy.parentNode.removeChild(this.proxy);
35234 * @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.
35236 Roo.bootstrap.SplitBar.createProxy = function(dir){
35237 var proxy = new Roo.Element(document.createElement("div"));
35238 proxy.unselectable();
35239 var cls = 'roo-splitbar-proxy';
35240 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
35241 document.body.appendChild(proxy.dom);
35246 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
35247 * Default Adapter. It assumes the splitter and resizing element are not positioned
35248 * elements and only gets/sets the width of the element. Generally used for table based layouts.
35250 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
35253 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
35254 // do nothing for now
35255 init : function(s){
35259 * Called before drag operations to get the current size of the resizing element.
35260 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35262 getElementSize : function(s){
35263 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35264 return s.resizingEl.getWidth();
35266 return s.resizingEl.getHeight();
35271 * Called after drag operations to set the size of the resizing element.
35272 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35273 * @param {Number} newSize The new size to set
35274 * @param {Function} onComplete A function to be invoked when resizing is complete
35276 setElementSize : function(s, newSize, onComplete){
35277 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35279 s.resizingEl.setWidth(newSize);
35281 onComplete(s, newSize);
35284 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
35289 s.resizingEl.setHeight(newSize);
35291 onComplete(s, newSize);
35294 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
35301 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
35302 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
35303 * Adapter that moves the splitter element to align with the resized sizing element.
35304 * Used with an absolute positioned SplitBar.
35305 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
35306 * document.body, make sure you assign an id to the body element.
35308 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
35309 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35310 this.container = Roo.get(container);
35313 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
35314 init : function(s){
35315 this.basic.init(s);
35318 getElementSize : function(s){
35319 return this.basic.getElementSize(s);
35322 setElementSize : function(s, newSize, onComplete){
35323 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
35326 moveSplitter : function(s){
35327 var yes = Roo.bootstrap.SplitBar;
35328 switch(s.placement){
35330 s.el.setX(s.resizingEl.getRight());
35333 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35336 s.el.setY(s.resizingEl.getBottom());
35339 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35346 * Orientation constant - Create a vertical SplitBar
35350 Roo.bootstrap.SplitBar.VERTICAL = 1;
35353 * Orientation constant - Create a horizontal SplitBar
35357 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35360 * Placement constant - The resizing element is to the left of the splitter element
35364 Roo.bootstrap.SplitBar.LEFT = 1;
35367 * Placement constant - The resizing element is to the right of the splitter element
35371 Roo.bootstrap.SplitBar.RIGHT = 2;
35374 * Placement constant - The resizing element is positioned above the splitter element
35378 Roo.bootstrap.SplitBar.TOP = 3;
35381 * Placement constant - The resizing element is positioned under splitter element
35385 Roo.bootstrap.SplitBar.BOTTOM = 4;
35386 Roo.namespace("Roo.bootstrap.layout");/*
35388 * Ext JS Library 1.1.1
35389 * Copyright(c) 2006-2007, Ext JS, LLC.
35391 * Originally Released Under LGPL - original licence link has changed is not relivant.
35394 * <script type="text/javascript">
35398 * @class Roo.bootstrap.layout.Manager
35399 * @extends Roo.bootstrap.Component
35400 * Base class for layout managers.
35402 Roo.bootstrap.layout.Manager = function(config)
35404 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35410 /** false to disable window resize monitoring @type Boolean */
35411 this.monitorWindowResize = true;
35416 * Fires when a layout is performed.
35417 * @param {Roo.LayoutManager} this
35421 * @event regionresized
35422 * Fires when the user resizes a region.
35423 * @param {Roo.LayoutRegion} region The resized region
35424 * @param {Number} newSize The new size (width for east/west, height for north/south)
35426 "regionresized" : true,
35428 * @event regioncollapsed
35429 * Fires when a region is collapsed.
35430 * @param {Roo.LayoutRegion} region The collapsed region
35432 "regioncollapsed" : true,
35434 * @event regionexpanded
35435 * Fires when a region is expanded.
35436 * @param {Roo.LayoutRegion} region The expanded region
35438 "regionexpanded" : true
35440 this.updating = false;
35443 this.el = Roo.get(config.el);
35449 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35454 monitorWindowResize : true,
35460 onRender : function(ct, position)
35463 this.el = Roo.get(ct);
35466 //this.fireEvent('render',this);
35470 initEvents: function()
35474 // ie scrollbar fix
35475 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35476 document.body.scroll = "no";
35477 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35478 this.el.position('relative');
35480 this.id = this.el.id;
35481 this.el.addClass("roo-layout-container");
35482 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35483 if(this.el.dom != document.body ) {
35484 this.el.on('resize', this.layout,this);
35485 this.el.on('show', this.layout,this);
35491 * Returns true if this layout is currently being updated
35492 * @return {Boolean}
35494 isUpdating : function(){
35495 return this.updating;
35499 * Suspend the LayoutManager from doing auto-layouts while
35500 * making multiple add or remove calls
35502 beginUpdate : function(){
35503 this.updating = true;
35507 * Restore auto-layouts and optionally disable the manager from performing a layout
35508 * @param {Boolean} noLayout true to disable a layout update
35510 endUpdate : function(noLayout){
35511 this.updating = false;
35517 layout: function(){
35521 onRegionResized : function(region, newSize){
35522 this.fireEvent("regionresized", region, newSize);
35526 onRegionCollapsed : function(region){
35527 this.fireEvent("regioncollapsed", region);
35530 onRegionExpanded : function(region){
35531 this.fireEvent("regionexpanded", region);
35535 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35536 * performs box-model adjustments.
35537 * @return {Object} The size as an object {width: (the width), height: (the height)}
35539 getViewSize : function()
35542 if(this.el.dom != document.body){
35543 size = this.el.getSize();
35545 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35547 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35548 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35553 * Returns the Element this layout is bound to.
35554 * @return {Roo.Element}
35556 getEl : function(){
35561 * Returns the specified region.
35562 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35563 * @return {Roo.LayoutRegion}
35565 getRegion : function(target){
35566 return this.regions[target.toLowerCase()];
35569 onWindowResize : function(){
35570 if(this.monitorWindowResize){
35577 * Ext JS Library 1.1.1
35578 * Copyright(c) 2006-2007, Ext JS, LLC.
35580 * Originally Released Under LGPL - original licence link has changed is not relivant.
35583 * <script type="text/javascript">
35586 * @class Roo.bootstrap.layout.Border
35587 * @extends Roo.bootstrap.layout.Manager
35588 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35589 * please see: examples/bootstrap/nested.html<br><br>
35591 <b>The container the layout is rendered into can be either the body element or any other element.
35592 If it is not the body element, the container needs to either be an absolute positioned element,
35593 or you will need to add "position:relative" to the css of the container. You will also need to specify
35594 the container size if it is not the body element.</b>
35597 * Create a new Border
35598 * @param {Object} config Configuration options
35600 Roo.bootstrap.layout.Border = function(config){
35601 config = config || {};
35602 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35606 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35607 if(config[region]){
35608 config[region].region = region;
35609 this.addRegion(config[region]);
35615 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
35617 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35619 parent : false, // this might point to a 'nest' or a ???
35622 * Creates and adds a new region if it doesn't already exist.
35623 * @param {String} target The target region key (north, south, east, west or center).
35624 * @param {Object} config The regions config object
35625 * @return {BorderLayoutRegion} The new region
35627 addRegion : function(config)
35629 if(!this.regions[config.region]){
35630 var r = this.factory(config);
35631 this.bindRegion(r);
35633 return this.regions[config.region];
35637 bindRegion : function(r){
35638 this.regions[r.config.region] = r;
35640 r.on("visibilitychange", this.layout, this);
35641 r.on("paneladded", this.layout, this);
35642 r.on("panelremoved", this.layout, this);
35643 r.on("invalidated", this.layout, this);
35644 r.on("resized", this.onRegionResized, this);
35645 r.on("collapsed", this.onRegionCollapsed, this);
35646 r.on("expanded", this.onRegionExpanded, this);
35650 * Performs a layout update.
35652 layout : function()
35654 if(this.updating) {
35658 // render all the rebions if they have not been done alreayd?
35659 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35660 if(this.regions[region] && !this.regions[region].bodyEl){
35661 this.regions[region].onRender(this.el)
35665 var size = this.getViewSize();
35666 var w = size.width;
35667 var h = size.height;
35672 //var x = 0, y = 0;
35674 var rs = this.regions;
35675 var north = rs["north"];
35676 var south = rs["south"];
35677 var west = rs["west"];
35678 var east = rs["east"];
35679 var center = rs["center"];
35680 //if(this.hideOnLayout){ // not supported anymore
35681 //c.el.setStyle("display", "none");
35683 if(north && north.isVisible()){
35684 var b = north.getBox();
35685 var m = north.getMargins();
35686 b.width = w - (m.left+m.right);
35689 centerY = b.height + b.y + m.bottom;
35690 centerH -= centerY;
35691 north.updateBox(this.safeBox(b));
35693 if(south && south.isVisible()){
35694 var b = south.getBox();
35695 var m = south.getMargins();
35696 b.width = w - (m.left+m.right);
35698 var totalHeight = (b.height + m.top + m.bottom);
35699 b.y = h - totalHeight + m.top;
35700 centerH -= totalHeight;
35701 south.updateBox(this.safeBox(b));
35703 if(west && west.isVisible()){
35704 var b = west.getBox();
35705 var m = west.getMargins();
35706 b.height = centerH - (m.top+m.bottom);
35708 b.y = centerY + m.top;
35709 var totalWidth = (b.width + m.left + m.right);
35710 centerX += totalWidth;
35711 centerW -= totalWidth;
35712 west.updateBox(this.safeBox(b));
35714 if(east && east.isVisible()){
35715 var b = east.getBox();
35716 var m = east.getMargins();
35717 b.height = centerH - (m.top+m.bottom);
35718 var totalWidth = (b.width + m.left + m.right);
35719 b.x = w - totalWidth + m.left;
35720 b.y = centerY + m.top;
35721 centerW -= totalWidth;
35722 east.updateBox(this.safeBox(b));
35725 var m = center.getMargins();
35727 x: centerX + m.left,
35728 y: centerY + m.top,
35729 width: centerW - (m.left+m.right),
35730 height: centerH - (m.top+m.bottom)
35732 //if(this.hideOnLayout){
35733 //center.el.setStyle("display", "block");
35735 center.updateBox(this.safeBox(centerBox));
35738 this.fireEvent("layout", this);
35742 safeBox : function(box){
35743 box.width = Math.max(0, box.width);
35744 box.height = Math.max(0, box.height);
35749 * Adds a ContentPanel (or subclass) to this layout.
35750 * @param {String} target The target region key (north, south, east, west or center).
35751 * @param {Roo.ContentPanel} panel The panel to add
35752 * @return {Roo.ContentPanel} The added panel
35754 add : function(target, panel){
35756 target = target.toLowerCase();
35757 return this.regions[target].add(panel);
35761 * Remove a ContentPanel (or subclass) to this layout.
35762 * @param {String} target The target region key (north, south, east, west or center).
35763 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35764 * @return {Roo.ContentPanel} The removed panel
35766 remove : function(target, panel){
35767 target = target.toLowerCase();
35768 return this.regions[target].remove(panel);
35772 * Searches all regions for a panel with the specified id
35773 * @param {String} panelId
35774 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35776 findPanel : function(panelId){
35777 var rs = this.regions;
35778 for(var target in rs){
35779 if(typeof rs[target] != "function"){
35780 var p = rs[target].getPanel(panelId);
35790 * Searches all regions for a panel with the specified id and activates (shows) it.
35791 * @param {String/ContentPanel} panelId The panels id or the panel itself
35792 * @return {Roo.ContentPanel} The shown panel or null
35794 showPanel : function(panelId) {
35795 var rs = this.regions;
35796 for(var target in rs){
35797 var r = rs[target];
35798 if(typeof r != "function"){
35799 if(r.hasPanel(panelId)){
35800 return r.showPanel(panelId);
35808 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35809 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35812 restoreState : function(provider){
35814 provider = Roo.state.Manager;
35816 var sm = new Roo.LayoutStateManager();
35817 sm.init(this, provider);
35823 * Adds a xtype elements to the layout.
35827 xtype : 'ContentPanel',
35834 xtype : 'NestedLayoutPanel',
35840 items : [ ... list of content panels or nested layout panels.. ]
35844 * @param {Object} cfg Xtype definition of item to add.
35846 addxtype : function(cfg)
35848 // basically accepts a pannel...
35849 // can accept a layout region..!?!?
35850 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35853 // theory? children can only be panels??
35855 //if (!cfg.xtype.match(/Panel$/)) {
35860 if (typeof(cfg.region) == 'undefined') {
35861 Roo.log("Failed to add Panel, region was not set");
35865 var region = cfg.region;
35871 xitems = cfg.items;
35876 if ( region == 'center') {
35877 Roo.log("Center: " + cfg.title);
35883 case 'Content': // ContentPanel (el, cfg)
35884 case 'Scroll': // ContentPanel (el, cfg)
35886 cfg.autoCreate = cfg.autoCreate || true;
35887 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35889 // var el = this.el.createChild();
35890 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35893 this.add(region, ret);
35897 case 'TreePanel': // our new panel!
35898 cfg.el = this.el.createChild();
35899 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35900 this.add(region, ret);
35905 // create a new Layout (which is a Border Layout...
35907 var clayout = cfg.layout;
35908 clayout.el = this.el.createChild();
35909 clayout.items = clayout.items || [];
35913 // replace this exitems with the clayout ones..
35914 xitems = clayout.items;
35916 // force background off if it's in center...
35917 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35918 cfg.background = false;
35920 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35923 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35924 //console.log('adding nested layout panel ' + cfg.toSource());
35925 this.add(region, ret);
35926 nb = {}; /// find first...
35931 // needs grid and region
35933 //var el = this.getRegion(region).el.createChild();
35935 *var el = this.el.createChild();
35936 // create the grid first...
35937 cfg.grid.container = el;
35938 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35941 if (region == 'center' && this.active ) {
35942 cfg.background = false;
35945 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35947 this.add(region, ret);
35949 if (cfg.background) {
35950 // render grid on panel activation (if panel background)
35951 ret.on('activate', function(gp) {
35952 if (!gp.grid.rendered) {
35953 // gp.grid.render(el);
35957 // cfg.grid.render(el);
35963 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35964 // it was the old xcomponent building that caused this before.
35965 // espeically if border is the top element in the tree.
35975 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35977 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35978 this.add(region, ret);
35982 throw "Can not add '" + cfg.xtype + "' to Border";
35988 this.beginUpdate();
35992 Roo.each(xitems, function(i) {
35993 region = nb && i.region ? i.region : false;
35995 var add = ret.addxtype(i);
35998 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35999 if (!i.background) {
36000 abn[region] = nb[region] ;
36007 // make the last non-background panel active..
36008 //if (nb) { Roo.log(abn); }
36011 for(var r in abn) {
36012 region = this.getRegion(r);
36014 // tried using nb[r], but it does not work..
36016 region.showPanel(abn[r]);
36027 factory : function(cfg)
36030 var validRegions = Roo.bootstrap.layout.Border.regions;
36032 var target = cfg.region;
36035 var r = Roo.bootstrap.layout;
36039 return new r.North(cfg);
36041 return new r.South(cfg);
36043 return new r.East(cfg);
36045 return new r.West(cfg);
36047 return new r.Center(cfg);
36049 throw 'Layout region "'+target+'" not supported.';
36056 * Ext JS Library 1.1.1
36057 * Copyright(c) 2006-2007, Ext JS, LLC.
36059 * Originally Released Under LGPL - original licence link has changed is not relivant.
36062 * <script type="text/javascript">
36066 * @class Roo.bootstrap.layout.Basic
36067 * @extends Roo.util.Observable
36068 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
36069 * and does not have a titlebar, tabs or any other features. All it does is size and position
36070 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
36071 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
36072 * @cfg {string} region the region that it inhabits..
36073 * @cfg {bool} skipConfig skip config?
36077 Roo.bootstrap.layout.Basic = function(config){
36079 this.mgr = config.mgr;
36081 this.position = config.region;
36083 var skipConfig = config.skipConfig;
36087 * @scope Roo.BasicLayoutRegion
36091 * @event beforeremove
36092 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
36093 * @param {Roo.LayoutRegion} this
36094 * @param {Roo.ContentPanel} panel The panel
36095 * @param {Object} e The cancel event object
36097 "beforeremove" : true,
36099 * @event invalidated
36100 * Fires when the layout for this region is changed.
36101 * @param {Roo.LayoutRegion} this
36103 "invalidated" : true,
36105 * @event visibilitychange
36106 * Fires when this region is shown or hidden
36107 * @param {Roo.LayoutRegion} this
36108 * @param {Boolean} visibility true or false
36110 "visibilitychange" : true,
36112 * @event paneladded
36113 * Fires when a panel is added.
36114 * @param {Roo.LayoutRegion} this
36115 * @param {Roo.ContentPanel} panel The panel
36117 "paneladded" : true,
36119 * @event panelremoved
36120 * Fires when a panel is removed.
36121 * @param {Roo.LayoutRegion} this
36122 * @param {Roo.ContentPanel} panel The panel
36124 "panelremoved" : true,
36126 * @event beforecollapse
36127 * Fires when this region before collapse.
36128 * @param {Roo.LayoutRegion} this
36130 "beforecollapse" : true,
36133 * Fires when this region is collapsed.
36134 * @param {Roo.LayoutRegion} this
36136 "collapsed" : true,
36139 * Fires when this region is expanded.
36140 * @param {Roo.LayoutRegion} this
36145 * Fires when this region is slid into view.
36146 * @param {Roo.LayoutRegion} this
36148 "slideshow" : true,
36151 * Fires when this region slides out of view.
36152 * @param {Roo.LayoutRegion} this
36154 "slidehide" : true,
36156 * @event panelactivated
36157 * Fires when a panel is activated.
36158 * @param {Roo.LayoutRegion} this
36159 * @param {Roo.ContentPanel} panel The activated panel
36161 "panelactivated" : true,
36164 * Fires when the user resizes this region.
36165 * @param {Roo.LayoutRegion} this
36166 * @param {Number} newSize The new size (width for east/west, height for north/south)
36170 /** A collection of panels in this region. @type Roo.util.MixedCollection */
36171 this.panels = new Roo.util.MixedCollection();
36172 this.panels.getKey = this.getPanelId.createDelegate(this);
36174 this.activePanel = null;
36175 // ensure listeners are added...
36177 if (config.listeners || config.events) {
36178 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
36179 listeners : config.listeners || {},
36180 events : config.events || {}
36184 if(skipConfig !== true){
36185 this.applyConfig(config);
36189 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
36191 getPanelId : function(p){
36195 applyConfig : function(config){
36196 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36197 this.config = config;
36202 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
36203 * the width, for horizontal (north, south) the height.
36204 * @param {Number} newSize The new width or height
36206 resizeTo : function(newSize){
36207 var el = this.el ? this.el :
36208 (this.activePanel ? this.activePanel.getEl() : null);
36210 switch(this.position){
36213 el.setWidth(newSize);
36214 this.fireEvent("resized", this, newSize);
36218 el.setHeight(newSize);
36219 this.fireEvent("resized", this, newSize);
36225 getBox : function(){
36226 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
36229 getMargins : function(){
36230 return this.margins;
36233 updateBox : function(box){
36235 var el = this.activePanel.getEl();
36236 el.dom.style.left = box.x + "px";
36237 el.dom.style.top = box.y + "px";
36238 this.activePanel.setSize(box.width, box.height);
36242 * Returns the container element for this region.
36243 * @return {Roo.Element}
36245 getEl : function(){
36246 return this.activePanel;
36250 * Returns true if this region is currently visible.
36251 * @return {Boolean}
36253 isVisible : function(){
36254 return this.activePanel ? true : false;
36257 setActivePanel : function(panel){
36258 panel = this.getPanel(panel);
36259 if(this.activePanel && this.activePanel != panel){
36260 this.activePanel.setActiveState(false);
36261 this.activePanel.getEl().setLeftTop(-10000,-10000);
36263 this.activePanel = panel;
36264 panel.setActiveState(true);
36266 panel.setSize(this.box.width, this.box.height);
36268 this.fireEvent("panelactivated", this, panel);
36269 this.fireEvent("invalidated");
36273 * Show the specified panel.
36274 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
36275 * @return {Roo.ContentPanel} The shown panel or null
36277 showPanel : function(panel){
36278 panel = this.getPanel(panel);
36280 this.setActivePanel(panel);
36286 * Get the active panel for this region.
36287 * @return {Roo.ContentPanel} The active panel or null
36289 getActivePanel : function(){
36290 return this.activePanel;
36294 * Add the passed ContentPanel(s)
36295 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36296 * @return {Roo.ContentPanel} The panel added (if only one was added)
36298 add : function(panel){
36299 if(arguments.length > 1){
36300 for(var i = 0, len = arguments.length; i < len; i++) {
36301 this.add(arguments[i]);
36305 if(this.hasPanel(panel)){
36306 this.showPanel(panel);
36309 var el = panel.getEl();
36310 if(el.dom.parentNode != this.mgr.el.dom){
36311 this.mgr.el.dom.appendChild(el.dom);
36313 if(panel.setRegion){
36314 panel.setRegion(this);
36316 this.panels.add(panel);
36317 el.setStyle("position", "absolute");
36318 if(!panel.background){
36319 this.setActivePanel(panel);
36320 if(this.config.initialSize && this.panels.getCount()==1){
36321 this.resizeTo(this.config.initialSize);
36324 this.fireEvent("paneladded", this, panel);
36329 * Returns true if the panel is in this region.
36330 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36331 * @return {Boolean}
36333 hasPanel : function(panel){
36334 if(typeof panel == "object"){ // must be panel obj
36335 panel = panel.getId();
36337 return this.getPanel(panel) ? true : false;
36341 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36342 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36343 * @param {Boolean} preservePanel Overrides the config preservePanel option
36344 * @return {Roo.ContentPanel} The panel that was removed
36346 remove : function(panel, preservePanel){
36347 panel = this.getPanel(panel);
36352 this.fireEvent("beforeremove", this, panel, e);
36353 if(e.cancel === true){
36356 var panelId = panel.getId();
36357 this.panels.removeKey(panelId);
36362 * Returns the panel specified or null if it's not in this region.
36363 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36364 * @return {Roo.ContentPanel}
36366 getPanel : function(id){
36367 if(typeof id == "object"){ // must be panel obj
36370 return this.panels.get(id);
36374 * Returns this regions position (north/south/east/west/center).
36377 getPosition: function(){
36378 return this.position;
36382 * Ext JS Library 1.1.1
36383 * Copyright(c) 2006-2007, Ext JS, LLC.
36385 * Originally Released Under LGPL - original licence link has changed is not relivant.
36388 * <script type="text/javascript">
36392 * @class Roo.bootstrap.layout.Region
36393 * @extends Roo.bootstrap.layout.Basic
36394 * This class represents a region in a layout manager.
36396 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36397 * @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})
36398 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
36399 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
36400 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
36401 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
36402 * @cfg {String} title The title for the region (overrides panel titles)
36403 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
36404 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36405 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
36406 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36407 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
36408 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36409 * the space available, similar to FireFox 1.5 tabs (defaults to false)
36410 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
36411 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
36412 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
36414 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
36415 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
36416 * @cfg {Boolean} disableTabTips True to disable tab tooltips
36417 * @cfg {Number} width For East/West panels
36418 * @cfg {Number} height For North/South panels
36419 * @cfg {Boolean} split To show the splitter
36420 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
36422 * @cfg {string} cls Extra CSS classes to add to region
36424 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
36425 * @cfg {string} region the region that it inhabits..
36428 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
36429 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
36431 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
36432 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
36433 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
36435 Roo.bootstrap.layout.Region = function(config)
36437 this.applyConfig(config);
36439 var mgr = config.mgr;
36440 var pos = config.region;
36441 config.skipConfig = true;
36442 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36445 this.onRender(mgr.el);
36448 this.visible = true;
36449 this.collapsed = false;
36450 this.unrendered_panels = [];
36453 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36455 position: '', // set by wrapper (eg. north/south etc..)
36456 unrendered_panels : null, // unrendered panels.
36458 tabPosition : false,
36460 mgr: false, // points to 'Border'
36463 createBody : function(){
36464 /** This region's body element
36465 * @type Roo.Element */
36466 this.bodyEl = this.el.createChild({
36468 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36472 onRender: function(ctr, pos)
36474 var dh = Roo.DomHelper;
36475 /** This region's container element
36476 * @type Roo.Element */
36477 this.el = dh.append(ctr.dom, {
36479 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36481 /** This region's title element
36482 * @type Roo.Element */
36484 this.titleEl = dh.append(this.el.dom, {
36486 unselectable: "on",
36487 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36489 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
36490 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36494 this.titleEl.enableDisplayMode();
36495 /** This region's title text element
36496 * @type HTMLElement */
36497 this.titleTextEl = this.titleEl.dom.firstChild;
36498 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36500 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36501 this.closeBtn.enableDisplayMode();
36502 this.closeBtn.on("click", this.closeClicked, this);
36503 this.closeBtn.hide();
36505 this.createBody(this.config);
36506 if(this.config.hideWhenEmpty){
36508 this.on("paneladded", this.validateVisibility, this);
36509 this.on("panelremoved", this.validateVisibility, this);
36511 if(this.autoScroll){
36512 this.bodyEl.setStyle("overflow", "auto");
36514 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36516 //if(c.titlebar !== false){
36517 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36518 this.titleEl.hide();
36520 this.titleEl.show();
36521 if(this.config.title){
36522 this.titleTextEl.innerHTML = this.config.title;
36526 if(this.config.collapsed){
36527 this.collapse(true);
36529 if(this.config.hidden){
36533 if (this.unrendered_panels && this.unrendered_panels.length) {
36534 for (var i =0;i< this.unrendered_panels.length; i++) {
36535 this.add(this.unrendered_panels[i]);
36537 this.unrendered_panels = null;
36543 applyConfig : function(c)
36546 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36547 var dh = Roo.DomHelper;
36548 if(c.titlebar !== false){
36549 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36550 this.collapseBtn.on("click", this.collapse, this);
36551 this.collapseBtn.enableDisplayMode();
36553 if(c.showPin === true || this.showPin){
36554 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36555 this.stickBtn.enableDisplayMode();
36556 this.stickBtn.on("click", this.expand, this);
36557 this.stickBtn.hide();
36562 /** This region's collapsed element
36563 * @type Roo.Element */
36566 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36567 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36570 if(c.floatable !== false){
36571 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36572 this.collapsedEl.on("click", this.collapseClick, this);
36575 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36576 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36577 id: "message", unselectable: "on", style:{"float":"left"}});
36578 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36580 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36581 this.expandBtn.on("click", this.expand, this);
36585 if(this.collapseBtn){
36586 this.collapseBtn.setVisible(c.collapsible == true);
36589 this.cmargins = c.cmargins || this.cmargins ||
36590 (this.position == "west" || this.position == "east" ?
36591 {top: 0, left: 2, right:2, bottom: 0} :
36592 {top: 2, left: 0, right:0, bottom: 2});
36594 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36597 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36599 this.autoScroll = c.autoScroll || false;
36604 this.duration = c.duration || .30;
36605 this.slideDuration = c.slideDuration || .45;
36610 * Returns true if this region is currently visible.
36611 * @return {Boolean}
36613 isVisible : function(){
36614 return this.visible;
36618 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36619 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
36621 //setCollapsedTitle : function(title){
36622 // title = title || " ";
36623 // if(this.collapsedTitleTextEl){
36624 // this.collapsedTitleTextEl.innerHTML = title;
36628 getBox : function(){
36630 // if(!this.collapsed){
36631 b = this.el.getBox(false, true);
36633 // b = this.collapsedEl.getBox(false, true);
36638 getMargins : function(){
36639 return this.margins;
36640 //return this.collapsed ? this.cmargins : this.margins;
36643 highlight : function(){
36644 this.el.addClass("x-layout-panel-dragover");
36647 unhighlight : function(){
36648 this.el.removeClass("x-layout-panel-dragover");
36651 updateBox : function(box)
36653 if (!this.bodyEl) {
36654 return; // not rendered yet..
36658 if(!this.collapsed){
36659 this.el.dom.style.left = box.x + "px";
36660 this.el.dom.style.top = box.y + "px";
36661 this.updateBody(box.width, box.height);
36663 this.collapsedEl.dom.style.left = box.x + "px";
36664 this.collapsedEl.dom.style.top = box.y + "px";
36665 this.collapsedEl.setSize(box.width, box.height);
36668 this.tabs.autoSizeTabs();
36672 updateBody : function(w, h)
36675 this.el.setWidth(w);
36676 w -= this.el.getBorderWidth("rl");
36677 if(this.config.adjustments){
36678 w += this.config.adjustments[0];
36681 if(h !== null && h > 0){
36682 this.el.setHeight(h);
36683 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36684 h -= this.el.getBorderWidth("tb");
36685 if(this.config.adjustments){
36686 h += this.config.adjustments[1];
36688 this.bodyEl.setHeight(h);
36690 h = this.tabs.syncHeight(h);
36693 if(this.panelSize){
36694 w = w !== null ? w : this.panelSize.width;
36695 h = h !== null ? h : this.panelSize.height;
36697 if(this.activePanel){
36698 var el = this.activePanel.getEl();
36699 w = w !== null ? w : el.getWidth();
36700 h = h !== null ? h : el.getHeight();
36701 this.panelSize = {width: w, height: h};
36702 this.activePanel.setSize(w, h);
36704 if(Roo.isIE && this.tabs){
36705 this.tabs.el.repaint();
36710 * Returns the container element for this region.
36711 * @return {Roo.Element}
36713 getEl : function(){
36718 * Hides this region.
36721 //if(!this.collapsed){
36722 this.el.dom.style.left = "-2000px";
36725 // this.collapsedEl.dom.style.left = "-2000px";
36726 // this.collapsedEl.hide();
36728 this.visible = false;
36729 this.fireEvent("visibilitychange", this, false);
36733 * Shows this region if it was previously hidden.
36736 //if(!this.collapsed){
36739 // this.collapsedEl.show();
36741 this.visible = true;
36742 this.fireEvent("visibilitychange", this, true);
36745 closeClicked : function(){
36746 if(this.activePanel){
36747 this.remove(this.activePanel);
36751 collapseClick : function(e){
36753 e.stopPropagation();
36756 e.stopPropagation();
36762 * Collapses this region.
36763 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36766 collapse : function(skipAnim, skipCheck = false){
36767 if(this.collapsed) {
36771 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36773 this.collapsed = true;
36775 this.split.el.hide();
36777 if(this.config.animate && skipAnim !== true){
36778 this.fireEvent("invalidated", this);
36779 this.animateCollapse();
36781 this.el.setLocation(-20000,-20000);
36783 this.collapsedEl.show();
36784 this.fireEvent("collapsed", this);
36785 this.fireEvent("invalidated", this);
36791 animateCollapse : function(){
36796 * Expands this region if it was previously collapsed.
36797 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36798 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36801 expand : function(e, skipAnim){
36803 e.stopPropagation();
36805 if(!this.collapsed || this.el.hasActiveFx()) {
36809 this.afterSlideIn();
36812 this.collapsed = false;
36813 if(this.config.animate && skipAnim !== true){
36814 this.animateExpand();
36818 this.split.el.show();
36820 this.collapsedEl.setLocation(-2000,-2000);
36821 this.collapsedEl.hide();
36822 this.fireEvent("invalidated", this);
36823 this.fireEvent("expanded", this);
36827 animateExpand : function(){
36831 initTabs : function()
36833 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36835 var ts = new Roo.bootstrap.panel.Tabs({
36836 el: this.bodyEl.dom,
36838 tabPosition: this.tabPosition ? this.tabPosition : 'top',
36839 disableTooltips: this.config.disableTabTips,
36840 toolbar : this.config.toolbar
36843 if(this.config.hideTabs){
36844 ts.stripWrap.setDisplayed(false);
36847 ts.resizeTabs = this.config.resizeTabs === true;
36848 ts.minTabWidth = this.config.minTabWidth || 40;
36849 ts.maxTabWidth = this.config.maxTabWidth || 250;
36850 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36851 ts.monitorResize = false;
36852 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36853 ts.bodyEl.addClass('roo-layout-tabs-body');
36854 this.panels.each(this.initPanelAsTab, this);
36857 initPanelAsTab : function(panel){
36858 var ti = this.tabs.addTab(
36862 this.config.closeOnTab && panel.isClosable(),
36865 if(panel.tabTip !== undefined){
36866 ti.setTooltip(panel.tabTip);
36868 ti.on("activate", function(){
36869 this.setActivePanel(panel);
36872 if(this.config.closeOnTab){
36873 ti.on("beforeclose", function(t, e){
36875 this.remove(panel);
36879 panel.tabItem = ti;
36884 updatePanelTitle : function(panel, title)
36886 if(this.activePanel == panel){
36887 this.updateTitle(title);
36890 var ti = this.tabs.getTab(panel.getEl().id);
36892 if(panel.tabTip !== undefined){
36893 ti.setTooltip(panel.tabTip);
36898 updateTitle : function(title){
36899 if(this.titleTextEl && !this.config.title){
36900 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36904 setActivePanel : function(panel)
36906 panel = this.getPanel(panel);
36907 if(this.activePanel && this.activePanel != panel){
36908 if(this.activePanel.setActiveState(false) === false){
36912 this.activePanel = panel;
36913 panel.setActiveState(true);
36914 if(this.panelSize){
36915 panel.setSize(this.panelSize.width, this.panelSize.height);
36918 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36920 this.updateTitle(panel.getTitle());
36922 this.fireEvent("invalidated", this);
36924 this.fireEvent("panelactivated", this, panel);
36928 * Shows the specified panel.
36929 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36930 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36932 showPanel : function(panel)
36934 panel = this.getPanel(panel);
36937 var tab = this.tabs.getTab(panel.getEl().id);
36938 if(tab.isHidden()){
36939 this.tabs.unhideTab(tab.id);
36943 this.setActivePanel(panel);
36950 * Get the active panel for this region.
36951 * @return {Roo.ContentPanel} The active panel or null
36953 getActivePanel : function(){
36954 return this.activePanel;
36957 validateVisibility : function(){
36958 if(this.panels.getCount() < 1){
36959 this.updateTitle(" ");
36960 this.closeBtn.hide();
36963 if(!this.isVisible()){
36970 * Adds the passed ContentPanel(s) to this region.
36971 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36972 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36974 add : function(panel)
36976 if(arguments.length > 1){
36977 for(var i = 0, len = arguments.length; i < len; i++) {
36978 this.add(arguments[i]);
36983 // if we have not been rendered yet, then we can not really do much of this..
36984 if (!this.bodyEl) {
36985 this.unrendered_panels.push(panel);
36992 if(this.hasPanel(panel)){
36993 this.showPanel(panel);
36996 panel.setRegion(this);
36997 this.panels.add(panel);
36998 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36999 // sinle panel - no tab...?? would it not be better to render it with the tabs,
37000 // and hide them... ???
37001 this.bodyEl.dom.appendChild(panel.getEl().dom);
37002 if(panel.background !== true){
37003 this.setActivePanel(panel);
37005 this.fireEvent("paneladded", this, panel);
37012 this.initPanelAsTab(panel);
37016 if(panel.background !== true){
37017 this.tabs.activate(panel.getEl().id);
37019 this.fireEvent("paneladded", this, panel);
37024 * Hides the tab for the specified panel.
37025 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37027 hidePanel : function(panel){
37028 if(this.tabs && (panel = this.getPanel(panel))){
37029 this.tabs.hideTab(panel.getEl().id);
37034 * Unhides the tab for a previously hidden panel.
37035 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37037 unhidePanel : function(panel){
37038 if(this.tabs && (panel = this.getPanel(panel))){
37039 this.tabs.unhideTab(panel.getEl().id);
37043 clearPanels : function(){
37044 while(this.panels.getCount() > 0){
37045 this.remove(this.panels.first());
37050 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37051 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37052 * @param {Boolean} preservePanel Overrides the config preservePanel option
37053 * @return {Roo.ContentPanel} The panel that was removed
37055 remove : function(panel, preservePanel)
37057 panel = this.getPanel(panel);
37062 this.fireEvent("beforeremove", this, panel, e);
37063 if(e.cancel === true){
37066 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
37067 var panelId = panel.getId();
37068 this.panels.removeKey(panelId);
37070 document.body.appendChild(panel.getEl().dom);
37073 this.tabs.removeTab(panel.getEl().id);
37074 }else if (!preservePanel){
37075 this.bodyEl.dom.removeChild(panel.getEl().dom);
37077 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
37078 var p = this.panels.first();
37079 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
37080 tempEl.appendChild(p.getEl().dom);
37081 this.bodyEl.update("");
37082 this.bodyEl.dom.appendChild(p.getEl().dom);
37084 this.updateTitle(p.getTitle());
37086 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
37087 this.setActivePanel(p);
37089 panel.setRegion(null);
37090 if(this.activePanel == panel){
37091 this.activePanel = null;
37093 if(this.config.autoDestroy !== false && preservePanel !== true){
37094 try{panel.destroy();}catch(e){}
37096 this.fireEvent("panelremoved", this, panel);
37101 * Returns the TabPanel component used by this region
37102 * @return {Roo.TabPanel}
37104 getTabs : function(){
37108 createTool : function(parentEl, className){
37109 var btn = Roo.DomHelper.append(parentEl, {
37111 cls: "x-layout-tools-button",
37114 cls: "roo-layout-tools-button-inner " + className,
37118 btn.addClassOnOver("roo-layout-tools-button-over");
37123 * Ext JS Library 1.1.1
37124 * Copyright(c) 2006-2007, Ext JS, LLC.
37126 * Originally Released Under LGPL - original licence link has changed is not relivant.
37129 * <script type="text/javascript">
37135 * @class Roo.SplitLayoutRegion
37136 * @extends Roo.LayoutRegion
37137 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
37139 Roo.bootstrap.layout.Split = function(config){
37140 this.cursor = config.cursor;
37141 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
37144 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
37146 splitTip : "Drag to resize.",
37147 collapsibleSplitTip : "Drag to resize. Double click to hide.",
37148 useSplitTips : false,
37150 applyConfig : function(config){
37151 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
37154 onRender : function(ctr,pos) {
37156 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
37157 if(!this.config.split){
37162 var splitEl = Roo.DomHelper.append(ctr.dom, {
37164 id: this.el.id + "-split",
37165 cls: "roo-layout-split roo-layout-split-"+this.position,
37168 /** The SplitBar for this region
37169 * @type Roo.SplitBar */
37170 // does not exist yet...
37171 Roo.log([this.position, this.orientation]);
37173 this.split = new Roo.bootstrap.SplitBar({
37174 dragElement : splitEl,
37175 resizingElement: this.el,
37176 orientation : this.orientation
37179 this.split.on("moved", this.onSplitMove, this);
37180 this.split.useShim = this.config.useShim === true;
37181 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
37182 if(this.useSplitTips){
37183 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
37185 //if(config.collapsible){
37186 // this.split.el.on("dblclick", this.collapse, this);
37189 if(typeof this.config.minSize != "undefined"){
37190 this.split.minSize = this.config.minSize;
37192 if(typeof this.config.maxSize != "undefined"){
37193 this.split.maxSize = this.config.maxSize;
37195 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
37196 this.hideSplitter();
37201 getHMaxSize : function(){
37202 var cmax = this.config.maxSize || 10000;
37203 var center = this.mgr.getRegion("center");
37204 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
37207 getVMaxSize : function(){
37208 var cmax = this.config.maxSize || 10000;
37209 var center = this.mgr.getRegion("center");
37210 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
37213 onSplitMove : function(split, newSize){
37214 this.fireEvent("resized", this, newSize);
37218 * Returns the {@link Roo.SplitBar} for this region.
37219 * @return {Roo.SplitBar}
37221 getSplitBar : function(){
37226 this.hideSplitter();
37227 Roo.bootstrap.layout.Split.superclass.hide.call(this);
37230 hideSplitter : function(){
37232 this.split.el.setLocation(-2000,-2000);
37233 this.split.el.hide();
37239 this.split.el.show();
37241 Roo.bootstrap.layout.Split.superclass.show.call(this);
37244 beforeSlide: function(){
37245 if(Roo.isGecko){// firefox overflow auto bug workaround
37246 this.bodyEl.clip();
37248 this.tabs.bodyEl.clip();
37250 if(this.activePanel){
37251 this.activePanel.getEl().clip();
37253 if(this.activePanel.beforeSlide){
37254 this.activePanel.beforeSlide();
37260 afterSlide : function(){
37261 if(Roo.isGecko){// firefox overflow auto bug workaround
37262 this.bodyEl.unclip();
37264 this.tabs.bodyEl.unclip();
37266 if(this.activePanel){
37267 this.activePanel.getEl().unclip();
37268 if(this.activePanel.afterSlide){
37269 this.activePanel.afterSlide();
37275 initAutoHide : function(){
37276 if(this.autoHide !== false){
37277 if(!this.autoHideHd){
37278 var st = new Roo.util.DelayedTask(this.slideIn, this);
37279 this.autoHideHd = {
37280 "mouseout": function(e){
37281 if(!e.within(this.el, true)){
37285 "mouseover" : function(e){
37291 this.el.on(this.autoHideHd);
37295 clearAutoHide : function(){
37296 if(this.autoHide !== false){
37297 this.el.un("mouseout", this.autoHideHd.mouseout);
37298 this.el.un("mouseover", this.autoHideHd.mouseover);
37302 clearMonitor : function(){
37303 Roo.get(document).un("click", this.slideInIf, this);
37306 // these names are backwards but not changed for compat
37307 slideOut : function(){
37308 if(this.isSlid || this.el.hasActiveFx()){
37311 this.isSlid = true;
37312 if(this.collapseBtn){
37313 this.collapseBtn.hide();
37315 this.closeBtnState = this.closeBtn.getStyle('display');
37316 this.closeBtn.hide();
37318 this.stickBtn.show();
37321 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37322 this.beforeSlide();
37323 this.el.setStyle("z-index", 10001);
37324 this.el.slideIn(this.getSlideAnchor(), {
37325 callback: function(){
37327 this.initAutoHide();
37328 Roo.get(document).on("click", this.slideInIf, this);
37329 this.fireEvent("slideshow", this);
37336 afterSlideIn : function(){
37337 this.clearAutoHide();
37338 this.isSlid = false;
37339 this.clearMonitor();
37340 this.el.setStyle("z-index", "");
37341 if(this.collapseBtn){
37342 this.collapseBtn.show();
37344 this.closeBtn.setStyle('display', this.closeBtnState);
37346 this.stickBtn.hide();
37348 this.fireEvent("slidehide", this);
37351 slideIn : function(cb){
37352 if(!this.isSlid || this.el.hasActiveFx()){
37356 this.isSlid = false;
37357 this.beforeSlide();
37358 this.el.slideOut(this.getSlideAnchor(), {
37359 callback: function(){
37360 this.el.setLeftTop(-10000, -10000);
37362 this.afterSlideIn();
37370 slideInIf : function(e){
37371 if(!e.within(this.el)){
37376 animateCollapse : function(){
37377 this.beforeSlide();
37378 this.el.setStyle("z-index", 20000);
37379 var anchor = this.getSlideAnchor();
37380 this.el.slideOut(anchor, {
37381 callback : function(){
37382 this.el.setStyle("z-index", "");
37383 this.collapsedEl.slideIn(anchor, {duration:.3});
37385 this.el.setLocation(-10000,-10000);
37387 this.fireEvent("collapsed", this);
37394 animateExpand : function(){
37395 this.beforeSlide();
37396 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37397 this.el.setStyle("z-index", 20000);
37398 this.collapsedEl.hide({
37401 this.el.slideIn(this.getSlideAnchor(), {
37402 callback : function(){
37403 this.el.setStyle("z-index", "");
37406 this.split.el.show();
37408 this.fireEvent("invalidated", this);
37409 this.fireEvent("expanded", this);
37437 getAnchor : function(){
37438 return this.anchors[this.position];
37441 getCollapseAnchor : function(){
37442 return this.canchors[this.position];
37445 getSlideAnchor : function(){
37446 return this.sanchors[this.position];
37449 getAlignAdj : function(){
37450 var cm = this.cmargins;
37451 switch(this.position){
37467 getExpandAdj : function(){
37468 var c = this.collapsedEl, cm = this.cmargins;
37469 switch(this.position){
37471 return [-(cm.right+c.getWidth()+cm.left), 0];
37474 return [cm.right+c.getWidth()+cm.left, 0];
37477 return [0, -(cm.top+cm.bottom+c.getHeight())];
37480 return [0, cm.top+cm.bottom+c.getHeight()];
37486 * Ext JS Library 1.1.1
37487 * Copyright(c) 2006-2007, Ext JS, LLC.
37489 * Originally Released Under LGPL - original licence link has changed is not relivant.
37492 * <script type="text/javascript">
37495 * These classes are private internal classes
37497 Roo.bootstrap.layout.Center = function(config){
37498 config.region = "center";
37499 Roo.bootstrap.layout.Region.call(this, config);
37500 this.visible = true;
37501 this.minWidth = config.minWidth || 20;
37502 this.minHeight = config.minHeight || 20;
37505 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37507 // center panel can't be hidden
37511 // center panel can't be hidden
37514 getMinWidth: function(){
37515 return this.minWidth;
37518 getMinHeight: function(){
37519 return this.minHeight;
37533 Roo.bootstrap.layout.North = function(config)
37535 config.region = 'north';
37536 config.cursor = 'n-resize';
37538 Roo.bootstrap.layout.Split.call(this, config);
37542 this.split.placement = Roo.bootstrap.SplitBar.TOP;
37543 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37544 this.split.el.addClass("roo-layout-split-v");
37546 var size = config.initialSize || config.height;
37547 if(typeof size != "undefined"){
37548 this.el.setHeight(size);
37551 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37553 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37557 getBox : function(){
37558 if(this.collapsed){
37559 return this.collapsedEl.getBox();
37561 var box = this.el.getBox();
37563 box.height += this.split.el.getHeight();
37568 updateBox : function(box){
37569 if(this.split && !this.collapsed){
37570 box.height -= this.split.el.getHeight();
37571 this.split.el.setLeft(box.x);
37572 this.split.el.setTop(box.y+box.height);
37573 this.split.el.setWidth(box.width);
37575 if(this.collapsed){
37576 this.updateBody(box.width, null);
37578 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37586 Roo.bootstrap.layout.South = function(config){
37587 config.region = 'south';
37588 config.cursor = 's-resize';
37589 Roo.bootstrap.layout.Split.call(this, config);
37591 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37592 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37593 this.split.el.addClass("roo-layout-split-v");
37595 var size = config.initialSize || config.height;
37596 if(typeof size != "undefined"){
37597 this.el.setHeight(size);
37601 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37602 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37603 getBox : function(){
37604 if(this.collapsed){
37605 return this.collapsedEl.getBox();
37607 var box = this.el.getBox();
37609 var sh = this.split.el.getHeight();
37616 updateBox : function(box){
37617 if(this.split && !this.collapsed){
37618 var sh = this.split.el.getHeight();
37621 this.split.el.setLeft(box.x);
37622 this.split.el.setTop(box.y-sh);
37623 this.split.el.setWidth(box.width);
37625 if(this.collapsed){
37626 this.updateBody(box.width, null);
37628 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37632 Roo.bootstrap.layout.East = function(config){
37633 config.region = "east";
37634 config.cursor = "e-resize";
37635 Roo.bootstrap.layout.Split.call(this, config);
37637 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37638 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37639 this.split.el.addClass("roo-layout-split-h");
37641 var size = config.initialSize || config.width;
37642 if(typeof size != "undefined"){
37643 this.el.setWidth(size);
37646 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37647 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37648 getBox : function(){
37649 if(this.collapsed){
37650 return this.collapsedEl.getBox();
37652 var box = this.el.getBox();
37654 var sw = this.split.el.getWidth();
37661 updateBox : function(box){
37662 if(this.split && !this.collapsed){
37663 var sw = this.split.el.getWidth();
37665 this.split.el.setLeft(box.x);
37666 this.split.el.setTop(box.y);
37667 this.split.el.setHeight(box.height);
37670 if(this.collapsed){
37671 this.updateBody(null, box.height);
37673 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37677 Roo.bootstrap.layout.West = function(config){
37678 config.region = "west";
37679 config.cursor = "w-resize";
37681 Roo.bootstrap.layout.Split.call(this, config);
37683 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37684 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37685 this.split.el.addClass("roo-layout-split-h");
37689 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37690 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37692 onRender: function(ctr, pos)
37694 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37695 var size = this.config.initialSize || this.config.width;
37696 if(typeof size != "undefined"){
37697 this.el.setWidth(size);
37701 getBox : function(){
37702 if(this.collapsed){
37703 return this.collapsedEl.getBox();
37705 var box = this.el.getBox();
37707 box.width += this.split.el.getWidth();
37712 updateBox : function(box){
37713 if(this.split && !this.collapsed){
37714 var sw = this.split.el.getWidth();
37716 this.split.el.setLeft(box.x+box.width);
37717 this.split.el.setTop(box.y);
37718 this.split.el.setHeight(box.height);
37720 if(this.collapsed){
37721 this.updateBody(null, box.height);
37723 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37725 });Roo.namespace("Roo.bootstrap.panel");/*
37727 * Ext JS Library 1.1.1
37728 * Copyright(c) 2006-2007, Ext JS, LLC.
37730 * Originally Released Under LGPL - original licence link has changed is not relivant.
37733 * <script type="text/javascript">
37736 * @class Roo.ContentPanel
37737 * @extends Roo.util.Observable
37738 * A basic ContentPanel element.
37739 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37740 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37741 * @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
37742 * @cfg {Boolean} closable True if the panel can be closed/removed
37743 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37744 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37745 * @cfg {Toolbar} toolbar A toolbar for this panel
37746 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37747 * @cfg {String} title The title for this panel
37748 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37749 * @cfg {String} url Calls {@link #setUrl} with this value
37750 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37751 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37752 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37753 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37754 * @cfg {Boolean} badges render the badges
37757 * Create a new ContentPanel.
37758 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37759 * @param {String/Object} config A string to set only the title or a config object
37760 * @param {String} content (optional) Set the HTML content for this panel
37761 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37763 Roo.bootstrap.panel.Content = function( config){
37765 this.tpl = config.tpl || false;
37767 var el = config.el;
37768 var content = config.content;
37770 if(config.autoCreate){ // xtype is available if this is called from factory
37773 this.el = Roo.get(el);
37774 if(!this.el && config && config.autoCreate){
37775 if(typeof config.autoCreate == "object"){
37776 if(!config.autoCreate.id){
37777 config.autoCreate.id = config.id||el;
37779 this.el = Roo.DomHelper.append(document.body,
37780 config.autoCreate, true);
37782 var elcfg = { tag: "div",
37783 cls: "roo-layout-inactive-content",
37787 elcfg.html = config.html;
37791 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37794 this.closable = false;
37795 this.loaded = false;
37796 this.active = false;
37799 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37801 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37803 this.wrapEl = this.el; //this.el.wrap();
37805 if (config.toolbar.items) {
37806 ti = config.toolbar.items ;
37807 delete config.toolbar.items ;
37811 this.toolbar.render(this.wrapEl, 'before');
37812 for(var i =0;i < ti.length;i++) {
37813 // Roo.log(['add child', items[i]]);
37814 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37816 this.toolbar.items = nitems;
37817 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37818 delete config.toolbar;
37822 // xtype created footer. - not sure if will work as we normally have to render first..
37823 if (this.footer && !this.footer.el && this.footer.xtype) {
37824 if (!this.wrapEl) {
37825 this.wrapEl = this.el.wrap();
37828 this.footer.container = this.wrapEl.createChild();
37830 this.footer = Roo.factory(this.footer, Roo);
37835 if(typeof config == "string"){
37836 this.title = config;
37838 Roo.apply(this, config);
37842 this.resizeEl = Roo.get(this.resizeEl, true);
37844 this.resizeEl = this.el;
37846 // handle view.xtype
37854 * Fires when this panel is activated.
37855 * @param {Roo.ContentPanel} this
37859 * @event deactivate
37860 * Fires when this panel is activated.
37861 * @param {Roo.ContentPanel} this
37863 "deactivate" : true,
37867 * Fires when this panel is resized if fitToFrame is true.
37868 * @param {Roo.ContentPanel} this
37869 * @param {Number} width The width after any component adjustments
37870 * @param {Number} height The height after any component adjustments
37876 * Fires when this tab is created
37877 * @param {Roo.ContentPanel} this
37888 if(this.autoScroll){
37889 this.resizeEl.setStyle("overflow", "auto");
37891 // fix randome scrolling
37892 //this.el.on('scroll', function() {
37893 // Roo.log('fix random scolling');
37894 // this.scrollTo('top',0);
37897 content = content || this.content;
37899 this.setContent(content);
37901 if(config && config.url){
37902 this.setUrl(this.url, this.params, this.loadOnce);
37907 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37909 if (this.view && typeof(this.view.xtype) != 'undefined') {
37910 this.view.el = this.el.appendChild(document.createElement("div"));
37911 this.view = Roo.factory(this.view);
37912 this.view.render && this.view.render(false, '');
37916 this.fireEvent('render', this);
37919 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37923 setRegion : function(region){
37924 this.region = region;
37925 this.setActiveClass(region && !this.background);
37929 setActiveClass: function(state)
37932 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37933 this.el.setStyle('position','relative');
37935 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37936 this.el.setStyle('position', 'absolute');
37941 * Returns the toolbar for this Panel if one was configured.
37942 * @return {Roo.Toolbar}
37944 getToolbar : function(){
37945 return this.toolbar;
37948 setActiveState : function(active)
37950 this.active = active;
37951 this.setActiveClass(active);
37953 if(this.fireEvent("deactivate", this) === false){
37958 this.fireEvent("activate", this);
37962 * Updates this panel's element
37963 * @param {String} content The new content
37964 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37966 setContent : function(content, loadScripts){
37967 this.el.update(content, loadScripts);
37970 ignoreResize : function(w, h){
37971 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37974 this.lastSize = {width: w, height: h};
37979 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37980 * @return {Roo.UpdateManager} The UpdateManager
37982 getUpdateManager : function(){
37983 return this.el.getUpdateManager();
37986 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37987 * @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:
37990 url: "your-url.php",
37991 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37992 callback: yourFunction,
37993 scope: yourObject, //(optional scope)
37996 text: "Loading...",
38001 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
38002 * 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.
38003 * @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}
38004 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
38005 * @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.
38006 * @return {Roo.ContentPanel} this
38009 var um = this.el.getUpdateManager();
38010 um.update.apply(um, arguments);
38016 * 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.
38017 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
38018 * @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)
38019 * @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)
38020 * @return {Roo.UpdateManager} The UpdateManager
38022 setUrl : function(url, params, loadOnce){
38023 if(this.refreshDelegate){
38024 this.removeListener("activate", this.refreshDelegate);
38026 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38027 this.on("activate", this.refreshDelegate);
38028 return this.el.getUpdateManager();
38031 _handleRefresh : function(url, params, loadOnce){
38032 if(!loadOnce || !this.loaded){
38033 var updater = this.el.getUpdateManager();
38034 updater.update(url, params, this._setLoaded.createDelegate(this));
38038 _setLoaded : function(){
38039 this.loaded = true;
38043 * Returns this panel's id
38046 getId : function(){
38051 * Returns this panel's element - used by regiosn to add.
38052 * @return {Roo.Element}
38054 getEl : function(){
38055 return this.wrapEl || this.el;
38060 adjustForComponents : function(width, height)
38062 //Roo.log('adjustForComponents ');
38063 if(this.resizeEl != this.el){
38064 width -= this.el.getFrameWidth('lr');
38065 height -= this.el.getFrameWidth('tb');
38068 var te = this.toolbar.getEl();
38069 te.setWidth(width);
38070 height -= te.getHeight();
38073 var te = this.footer.getEl();
38074 te.setWidth(width);
38075 height -= te.getHeight();
38079 if(this.adjustments){
38080 width += this.adjustments[0];
38081 height += this.adjustments[1];
38083 return {"width": width, "height": height};
38086 setSize : function(width, height){
38087 if(this.fitToFrame && !this.ignoreResize(width, height)){
38088 if(this.fitContainer && this.resizeEl != this.el){
38089 this.el.setSize(width, height);
38091 var size = this.adjustForComponents(width, height);
38092 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
38093 this.fireEvent('resize', this, size.width, size.height);
38098 * Returns this panel's title
38101 getTitle : function(){
38103 if (typeof(this.title) != 'object') {
38108 for (var k in this.title) {
38109 if (!this.title.hasOwnProperty(k)) {
38113 if (k.indexOf('-') >= 0) {
38114 var s = k.split('-');
38115 for (var i = 0; i<s.length; i++) {
38116 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
38119 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
38126 * Set this panel's title
38127 * @param {String} title
38129 setTitle : function(title){
38130 this.title = title;
38132 this.region.updatePanelTitle(this, title);
38137 * Returns true is this panel was configured to be closable
38138 * @return {Boolean}
38140 isClosable : function(){
38141 return this.closable;
38144 beforeSlide : function(){
38146 this.resizeEl.clip();
38149 afterSlide : function(){
38151 this.resizeEl.unclip();
38155 * Force a content refresh from the URL specified in the {@link #setUrl} method.
38156 * Will fail silently if the {@link #setUrl} method has not been called.
38157 * This does not activate the panel, just updates its content.
38159 refresh : function(){
38160 if(this.refreshDelegate){
38161 this.loaded = false;
38162 this.refreshDelegate();
38167 * Destroys this panel
38169 destroy : function(){
38170 this.el.removeAllListeners();
38171 var tempEl = document.createElement("span");
38172 tempEl.appendChild(this.el.dom);
38173 tempEl.innerHTML = "";
38179 * form - if the content panel contains a form - this is a reference to it.
38180 * @type {Roo.form.Form}
38184 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
38185 * This contains a reference to it.
38191 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
38201 * @param {Object} cfg Xtype definition of item to add.
38205 getChildContainer: function () {
38206 return this.getEl();
38211 var ret = new Roo.factory(cfg);
38216 if (cfg.xtype.match(/^Form$/)) {
38219 //if (this.footer) {
38220 // el = this.footer.container.insertSibling(false, 'before');
38222 el = this.el.createChild();
38225 this.form = new Roo.form.Form(cfg);
38228 if ( this.form.allItems.length) {
38229 this.form.render(el.dom);
38233 // should only have one of theses..
38234 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
38235 // views.. should not be just added - used named prop 'view''
38237 cfg.el = this.el.appendChild(document.createElement("div"));
38240 var ret = new Roo.factory(cfg);
38242 ret.render && ret.render(false, ''); // render blank..
38252 * @class Roo.bootstrap.panel.Grid
38253 * @extends Roo.bootstrap.panel.Content
38255 * Create a new GridPanel.
38256 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
38257 * @param {Object} config A the config object
38263 Roo.bootstrap.panel.Grid = function(config)
38267 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
38268 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
38270 config.el = this.wrapper;
38271 //this.el = this.wrapper;
38273 if (config.container) {
38274 // ctor'ed from a Border/panel.grid
38277 this.wrapper.setStyle("overflow", "hidden");
38278 this.wrapper.addClass('roo-grid-container');
38283 if(config.toolbar){
38284 var tool_el = this.wrapper.createChild();
38285 this.toolbar = Roo.factory(config.toolbar);
38287 if (config.toolbar.items) {
38288 ti = config.toolbar.items ;
38289 delete config.toolbar.items ;
38293 this.toolbar.render(tool_el);
38294 for(var i =0;i < ti.length;i++) {
38295 // Roo.log(['add child', items[i]]);
38296 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38298 this.toolbar.items = nitems;
38300 delete config.toolbar;
38303 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
38304 config.grid.scrollBody = true;;
38305 config.grid.monitorWindowResize = false; // turn off autosizing
38306 config.grid.autoHeight = false;
38307 config.grid.autoWidth = false;
38309 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
38311 if (config.background) {
38312 // render grid on panel activation (if panel background)
38313 this.on('activate', function(gp) {
38314 if (!gp.grid.rendered) {
38315 gp.grid.render(this.wrapper);
38316 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
38321 this.grid.render(this.wrapper);
38322 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
38325 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
38326 // ??? needed ??? config.el = this.wrapper;
38331 // xtype created footer. - not sure if will work as we normally have to render first..
38332 if (this.footer && !this.footer.el && this.footer.xtype) {
38334 var ctr = this.grid.getView().getFooterPanel(true);
38335 this.footer.dataSource = this.grid.dataSource;
38336 this.footer = Roo.factory(this.footer, Roo);
38337 this.footer.render(ctr);
38347 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38348 getId : function(){
38349 return this.grid.id;
38353 * Returns the grid for this panel
38354 * @return {Roo.bootstrap.Table}
38356 getGrid : function(){
38360 setSize : function(width, height){
38361 if(!this.ignoreResize(width, height)){
38362 var grid = this.grid;
38363 var size = this.adjustForComponents(width, height);
38364 var gridel = grid.getGridEl();
38365 gridel.setSize(size.width, size.height);
38367 var thd = grid.getGridEl().select('thead',true).first();
38368 var tbd = grid.getGridEl().select('tbody', true).first();
38370 tbd.setSize(width, height - thd.getHeight());
38379 beforeSlide : function(){
38380 this.grid.getView().scroller.clip();
38383 afterSlide : function(){
38384 this.grid.getView().scroller.unclip();
38387 destroy : function(){
38388 this.grid.destroy();
38390 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
38395 * @class Roo.bootstrap.panel.Nest
38396 * @extends Roo.bootstrap.panel.Content
38398 * Create a new Panel, that can contain a layout.Border.
38401 * @param {Roo.BorderLayout} layout The layout for this panel
38402 * @param {String/Object} config A string to set only the title or a config object
38404 Roo.bootstrap.panel.Nest = function(config)
38406 // construct with only one argument..
38407 /* FIXME - implement nicer consturctors
38408 if (layout.layout) {
38410 layout = config.layout;
38411 delete config.layout;
38413 if (layout.xtype && !layout.getEl) {
38414 // then layout needs constructing..
38415 layout = Roo.factory(layout, Roo);
38419 config.el = config.layout.getEl();
38421 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38423 config.layout.monitorWindowResize = false; // turn off autosizing
38424 this.layout = config.layout;
38425 this.layout.getEl().addClass("roo-layout-nested-layout");
38426 this.layout.parent = this;
38433 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38435 setSize : function(width, height){
38436 if(!this.ignoreResize(width, height)){
38437 var size = this.adjustForComponents(width, height);
38438 var el = this.layout.getEl();
38439 if (size.height < 1) {
38440 el.setWidth(size.width);
38442 el.setSize(size.width, size.height);
38444 var touch = el.dom.offsetWidth;
38445 this.layout.layout();
38446 // ie requires a double layout on the first pass
38447 if(Roo.isIE && !this.initialized){
38448 this.initialized = true;
38449 this.layout.layout();
38454 // activate all subpanels if not currently active..
38456 setActiveState : function(active){
38457 this.active = active;
38458 this.setActiveClass(active);
38461 this.fireEvent("deactivate", this);
38465 this.fireEvent("activate", this);
38466 // not sure if this should happen before or after..
38467 if (!this.layout) {
38468 return; // should not happen..
38471 for (var r in this.layout.regions) {
38472 reg = this.layout.getRegion(r);
38473 if (reg.getActivePanel()) {
38474 //reg.showPanel(reg.getActivePanel()); // force it to activate..
38475 reg.setActivePanel(reg.getActivePanel());
38478 if (!reg.panels.length) {
38481 reg.showPanel(reg.getPanel(0));
38490 * Returns the nested BorderLayout for this panel
38491 * @return {Roo.BorderLayout}
38493 getLayout : function(){
38494 return this.layout;
38498 * Adds a xtype elements to the layout of the nested panel
38502 xtype : 'ContentPanel',
38509 xtype : 'NestedLayoutPanel',
38515 items : [ ... list of content panels or nested layout panels.. ]
38519 * @param {Object} cfg Xtype definition of item to add.
38521 addxtype : function(cfg) {
38522 return this.layout.addxtype(cfg);
38527 * Ext JS Library 1.1.1
38528 * Copyright(c) 2006-2007, Ext JS, LLC.
38530 * Originally Released Under LGPL - original licence link has changed is not relivant.
38533 * <script type="text/javascript">
38536 * @class Roo.TabPanel
38537 * @extends Roo.util.Observable
38538 * A lightweight tab container.
38542 // basic tabs 1, built from existing content
38543 var tabs = new Roo.TabPanel("tabs1");
38544 tabs.addTab("script", "View Script");
38545 tabs.addTab("markup", "View Markup");
38546 tabs.activate("script");
38548 // more advanced tabs, built from javascript
38549 var jtabs = new Roo.TabPanel("jtabs");
38550 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38552 // set up the UpdateManager
38553 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38554 var updater = tab2.getUpdateManager();
38555 updater.setDefaultUrl("ajax1.htm");
38556 tab2.on('activate', updater.refresh, updater, true);
38558 // Use setUrl for Ajax loading
38559 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38560 tab3.setUrl("ajax2.htm", null, true);
38563 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38566 jtabs.activate("jtabs-1");
38569 * Create a new TabPanel.
38570 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38571 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38573 Roo.bootstrap.panel.Tabs = function(config){
38575 * The container element for this TabPanel.
38576 * @type Roo.Element
38578 this.el = Roo.get(config.el);
38581 if(typeof config == "boolean"){
38582 this.tabPosition = config ? "bottom" : "top";
38584 Roo.apply(this, config);
38588 if(this.tabPosition == "bottom"){
38589 // if tabs are at the bottom = create the body first.
38590 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38591 this.el.addClass("roo-tabs-bottom");
38593 // next create the tabs holders
38595 if (this.tabPosition == "west"){
38597 var reg = this.region; // fake it..
38599 if (!reg.mgr.parent) {
38602 reg = reg.mgr.parent.region;
38604 Roo.log("got nest?");
38606 if (reg.mgr.getRegion('west')) {
38607 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38608 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38609 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38610 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38611 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38619 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38620 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38621 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38622 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38627 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38630 // finally - if tabs are at the top, then create the body last..
38631 if(this.tabPosition != "bottom"){
38632 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38633 * @type Roo.Element
38635 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38636 this.el.addClass("roo-tabs-top");
38640 this.bodyEl.setStyle("position", "relative");
38642 this.active = null;
38643 this.activateDelegate = this.activate.createDelegate(this);
38648 * Fires when the active tab changes
38649 * @param {Roo.TabPanel} this
38650 * @param {Roo.TabPanelItem} activePanel The new active tab
38654 * @event beforetabchange
38655 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38656 * @param {Roo.TabPanel} this
38657 * @param {Object} e Set cancel to true on this object to cancel the tab change
38658 * @param {Roo.TabPanelItem} tab The tab being changed to
38660 "beforetabchange" : true
38663 Roo.EventManager.onWindowResize(this.onResize, this);
38664 this.cpad = this.el.getPadding("lr");
38665 this.hiddenCount = 0;
38668 // toolbar on the tabbar support...
38669 if (this.toolbar) {
38670 alert("no toolbar support yet");
38671 this.toolbar = false;
38673 var tcfg = this.toolbar;
38674 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
38675 this.toolbar = new Roo.Toolbar(tcfg);
38676 if (Roo.isSafari) {
38677 var tbl = tcfg.container.child('table', true);
38678 tbl.setAttribute('width', '100%');
38686 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38689 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38691 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38693 tabPosition : "top",
38695 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38697 currentTabWidth : 0,
38699 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38703 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38707 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38709 preferredTabWidth : 175,
38711 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38713 resizeTabs : false,
38715 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38717 monitorResize : true,
38719 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
38721 toolbar : false, // set by caller..
38723 region : false, /// set by caller
38725 disableTooltips : true, // not used yet...
38728 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38729 * @param {String} id The id of the div to use <b>or create</b>
38730 * @param {String} text The text for the tab
38731 * @param {String} content (optional) Content to put in the TabPanelItem body
38732 * @param {Boolean} closable (optional) True to create a close icon on the tab
38733 * @return {Roo.TabPanelItem} The created TabPanelItem
38735 addTab : function(id, text, content, closable, tpl)
38737 var item = new Roo.bootstrap.panel.TabItem({
38741 closable : closable,
38744 this.addTabItem(item);
38746 item.setContent(content);
38752 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38753 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38754 * @return {Roo.TabPanelItem}
38756 getTab : function(id){
38757 return this.items[id];
38761 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38762 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38764 hideTab : function(id){
38765 var t = this.items[id];
38768 this.hiddenCount++;
38769 this.autoSizeTabs();
38774 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38775 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38777 unhideTab : function(id){
38778 var t = this.items[id];
38780 t.setHidden(false);
38781 this.hiddenCount--;
38782 this.autoSizeTabs();
38787 * Adds an existing {@link Roo.TabPanelItem}.
38788 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38790 addTabItem : function(item)
38792 this.items[item.id] = item;
38793 this.items.push(item);
38794 this.autoSizeTabs();
38795 // if(this.resizeTabs){
38796 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38797 // this.autoSizeTabs();
38799 // item.autoSize();
38804 * Removes a {@link Roo.TabPanelItem}.
38805 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38807 removeTab : function(id){
38808 var items = this.items;
38809 var tab = items[id];
38810 if(!tab) { return; }
38811 var index = items.indexOf(tab);
38812 if(this.active == tab && items.length > 1){
38813 var newTab = this.getNextAvailable(index);
38818 this.stripEl.dom.removeChild(tab.pnode.dom);
38819 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38820 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38822 items.splice(index, 1);
38823 delete this.items[tab.id];
38824 tab.fireEvent("close", tab);
38825 tab.purgeListeners();
38826 this.autoSizeTabs();
38829 getNextAvailable : function(start){
38830 var items = this.items;
38832 // look for a next tab that will slide over to
38833 // replace the one being removed
38834 while(index < items.length){
38835 var item = items[++index];
38836 if(item && !item.isHidden()){
38840 // if one isn't found select the previous tab (on the left)
38843 var item = items[--index];
38844 if(item && !item.isHidden()){
38852 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38853 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38855 disableTab : function(id){
38856 var tab = this.items[id];
38857 if(tab && this.active != tab){
38863 * Enables a {@link Roo.TabPanelItem} that is disabled.
38864 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38866 enableTab : function(id){
38867 var tab = this.items[id];
38872 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38873 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38874 * @return {Roo.TabPanelItem} The TabPanelItem.
38876 activate : function(id)
38878 //Roo.log('activite:' + id);
38880 var tab = this.items[id];
38884 if(tab == this.active || tab.disabled){
38888 this.fireEvent("beforetabchange", this, e, tab);
38889 if(e.cancel !== true && !tab.disabled){
38891 this.active.hide();
38893 this.active = this.items[id];
38894 this.active.show();
38895 this.fireEvent("tabchange", this, this.active);
38901 * Gets the active {@link Roo.TabPanelItem}.
38902 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38904 getActiveTab : function(){
38905 return this.active;
38909 * Updates the tab body element to fit the height of the container element
38910 * for overflow scrolling
38911 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38913 syncHeight : function(targetHeight){
38914 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38915 var bm = this.bodyEl.getMargins();
38916 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38917 this.bodyEl.setHeight(newHeight);
38921 onResize : function(){
38922 if(this.monitorResize){
38923 this.autoSizeTabs();
38928 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38930 beginUpdate : function(){
38931 this.updating = true;
38935 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38937 endUpdate : function(){
38938 this.updating = false;
38939 this.autoSizeTabs();
38943 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38945 autoSizeTabs : function()
38947 var count = this.items.length;
38948 var vcount = count - this.hiddenCount;
38951 this.stripEl.hide();
38953 this.stripEl.show();
38956 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38961 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38962 var availWidth = Math.floor(w / vcount);
38963 var b = this.stripBody;
38964 if(b.getWidth() > w){
38965 var tabs = this.items;
38966 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38967 if(availWidth < this.minTabWidth){
38968 /*if(!this.sleft){ // incomplete scrolling code
38969 this.createScrollButtons();
38972 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38975 if(this.currentTabWidth < this.preferredTabWidth){
38976 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38982 * Returns the number of tabs in this TabPanel.
38985 getCount : function(){
38986 return this.items.length;
38990 * Resizes all the tabs to the passed width
38991 * @param {Number} The new width
38993 setTabWidth : function(width){
38994 this.currentTabWidth = width;
38995 for(var i = 0, len = this.items.length; i < len; i++) {
38996 if(!this.items[i].isHidden()) {
38997 this.items[i].setWidth(width);
39003 * Destroys this TabPanel
39004 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
39006 destroy : function(removeEl){
39007 Roo.EventManager.removeResizeListener(this.onResize, this);
39008 for(var i = 0, len = this.items.length; i < len; i++){
39009 this.items[i].purgeListeners();
39011 if(removeEl === true){
39012 this.el.update("");
39017 createStrip : function(container)
39019 var strip = document.createElement("nav");
39020 strip.className = Roo.bootstrap.version == 4 ?
39021 "navbar-light bg-light" :
39022 "navbar navbar-default"; //"x-tabs-wrap";
39023 container.appendChild(strip);
39027 createStripList : function(strip)
39029 // div wrapper for retard IE
39030 // returns the "tr" element.
39031 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
39032 //'<div class="x-tabs-strip-wrap">'+
39033 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
39034 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
39035 return strip.firstChild; //.firstChild.firstChild.firstChild;
39037 createBody : function(container)
39039 var body = document.createElement("div");
39040 Roo.id(body, "tab-body");
39041 //Roo.fly(body).addClass("x-tabs-body");
39042 Roo.fly(body).addClass("tab-content");
39043 container.appendChild(body);
39046 createItemBody :function(bodyEl, id){
39047 var body = Roo.getDom(id);
39049 body = document.createElement("div");
39052 //Roo.fly(body).addClass("x-tabs-item-body");
39053 Roo.fly(body).addClass("tab-pane");
39054 bodyEl.insertBefore(body, bodyEl.firstChild);
39058 createStripElements : function(stripEl, text, closable, tpl)
39060 var td = document.createElement("li"); // was td..
39061 td.className = 'nav-item';
39063 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
39066 stripEl.appendChild(td);
39068 td.className = "x-tabs-closable";
39069 if(!this.closeTpl){
39070 this.closeTpl = new Roo.Template(
39071 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39072 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
39073 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
39076 var el = this.closeTpl.overwrite(td, {"text": text});
39077 var close = el.getElementsByTagName("div")[0];
39078 var inner = el.getElementsByTagName("em")[0];
39079 return {"el": el, "close": close, "inner": inner};
39082 // not sure what this is..
39083 // if(!this.tabTpl){
39084 //this.tabTpl = new Roo.Template(
39085 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39086 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
39088 // this.tabTpl = new Roo.Template(
39089 // '<a href="#">' +
39090 // '<span unselectable="on"' +
39091 // (this.disableTooltips ? '' : ' title="{text}"') +
39092 // ' >{text}</span></a>'
39098 var template = tpl || this.tabTpl || false;
39101 template = new Roo.Template(
39102 Roo.bootstrap.version == 4 ?
39104 '<a class="nav-link" href="#" unselectable="on"' +
39105 (this.disableTooltips ? '' : ' title="{text}"') +
39108 '<a class="nav-link" href="#">' +
39109 '<span unselectable="on"' +
39110 (this.disableTooltips ? '' : ' title="{text}"') +
39111 ' >{text}</span></a>'
39116 switch (typeof(template)) {
39120 template = new Roo.Template(template);
39126 var el = template.overwrite(td, {"text": text});
39128 var inner = el.getElementsByTagName("span")[0];
39130 return {"el": el, "inner": inner};
39138 * @class Roo.TabPanelItem
39139 * @extends Roo.util.Observable
39140 * Represents an individual item (tab plus body) in a TabPanel.
39141 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
39142 * @param {String} id The id of this TabPanelItem
39143 * @param {String} text The text for the tab of this TabPanelItem
39144 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
39146 Roo.bootstrap.panel.TabItem = function(config){
39148 * The {@link Roo.TabPanel} this TabPanelItem belongs to
39149 * @type Roo.TabPanel
39151 this.tabPanel = config.panel;
39153 * The id for this TabPanelItem
39156 this.id = config.id;
39158 this.disabled = false;
39160 this.text = config.text;
39162 this.loaded = false;
39163 this.closable = config.closable;
39166 * The body element for this TabPanelItem.
39167 * @type Roo.Element
39169 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
39170 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
39171 this.bodyEl.setStyle("display", "block");
39172 this.bodyEl.setStyle("zoom", "1");
39173 //this.hideAction();
39175 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
39177 this.el = Roo.get(els.el);
39178 this.inner = Roo.get(els.inner, true);
39179 this.textEl = Roo.bootstrap.version == 4 ?
39180 this.el : Roo.get(this.el.dom.firstChild, true);
39182 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
39183 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
39186 // this.el.on("mousedown", this.onTabMouseDown, this);
39187 this.el.on("click", this.onTabClick, this);
39189 if(config.closable){
39190 var c = Roo.get(els.close, true);
39191 c.dom.title = this.closeText;
39192 c.addClassOnOver("close-over");
39193 c.on("click", this.closeClick, this);
39199 * Fires when this tab becomes the active tab.
39200 * @param {Roo.TabPanel} tabPanel The parent TabPanel
39201 * @param {Roo.TabPanelItem} this
39205 * @event beforeclose
39206 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
39207 * @param {Roo.TabPanelItem} this
39208 * @param {Object} e Set cancel to true on this object to cancel the close.
39210 "beforeclose": true,
39213 * Fires when this tab is closed.
39214 * @param {Roo.TabPanelItem} this
39218 * @event deactivate
39219 * Fires when this tab is no longer the active tab.
39220 * @param {Roo.TabPanel} tabPanel The parent TabPanel
39221 * @param {Roo.TabPanelItem} this
39223 "deactivate" : true
39225 this.hidden = false;
39227 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
39230 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
39232 purgeListeners : function(){
39233 Roo.util.Observable.prototype.purgeListeners.call(this);
39234 this.el.removeAllListeners();
39237 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
39240 this.status_node.addClass("active");
39243 this.tabPanel.stripWrap.repaint();
39245 this.fireEvent("activate", this.tabPanel, this);
39249 * Returns true if this tab is the active tab.
39250 * @return {Boolean}
39252 isActive : function(){
39253 return this.tabPanel.getActiveTab() == this;
39257 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
39260 this.status_node.removeClass("active");
39262 this.fireEvent("deactivate", this.tabPanel, this);
39265 hideAction : function(){
39266 this.bodyEl.hide();
39267 this.bodyEl.setStyle("position", "absolute");
39268 this.bodyEl.setLeft("-20000px");
39269 this.bodyEl.setTop("-20000px");
39272 showAction : function(){
39273 this.bodyEl.setStyle("position", "relative");
39274 this.bodyEl.setTop("");
39275 this.bodyEl.setLeft("");
39276 this.bodyEl.show();
39280 * Set the tooltip for the tab.
39281 * @param {String} tooltip The tab's tooltip
39283 setTooltip : function(text){
39284 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
39285 this.textEl.dom.qtip = text;
39286 this.textEl.dom.removeAttribute('title');
39288 this.textEl.dom.title = text;
39292 onTabClick : function(e){
39293 e.preventDefault();
39294 this.tabPanel.activate(this.id);
39297 onTabMouseDown : function(e){
39298 e.preventDefault();
39299 this.tabPanel.activate(this.id);
39302 getWidth : function(){
39303 return this.inner.getWidth();
39306 setWidth : function(width){
39307 var iwidth = width - this.linode.getPadding("lr");
39308 this.inner.setWidth(iwidth);
39309 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
39310 this.linode.setWidth(width);
39314 * Show or hide the tab
39315 * @param {Boolean} hidden True to hide or false to show.
39317 setHidden : function(hidden){
39318 this.hidden = hidden;
39319 this.linode.setStyle("display", hidden ? "none" : "");
39323 * Returns true if this tab is "hidden"
39324 * @return {Boolean}
39326 isHidden : function(){
39327 return this.hidden;
39331 * Returns the text for this tab
39334 getText : function(){
39338 autoSize : function(){
39339 //this.el.beginMeasure();
39340 this.textEl.setWidth(1);
39342 * #2804 [new] Tabs in Roojs
39343 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39345 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39346 //this.el.endMeasure();
39350 * Sets the text for the tab (Note: this also sets the tooltip text)
39351 * @param {String} text The tab's text and tooltip
39353 setText : function(text){
39355 this.textEl.update(text);
39356 this.setTooltip(text);
39357 //if(!this.tabPanel.resizeTabs){
39358 // this.autoSize();
39362 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39364 activate : function(){
39365 this.tabPanel.activate(this.id);
39369 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39371 disable : function(){
39372 if(this.tabPanel.active != this){
39373 this.disabled = true;
39374 this.status_node.addClass("disabled");
39379 * Enables this TabPanelItem if it was previously disabled.
39381 enable : function(){
39382 this.disabled = false;
39383 this.status_node.removeClass("disabled");
39387 * Sets the content for this TabPanelItem.
39388 * @param {String} content The content
39389 * @param {Boolean} loadScripts true to look for and load scripts
39391 setContent : function(content, loadScripts){
39392 this.bodyEl.update(content, loadScripts);
39396 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39397 * @return {Roo.UpdateManager} The UpdateManager
39399 getUpdateManager : function(){
39400 return this.bodyEl.getUpdateManager();
39404 * Set a URL to be used to load the content for this TabPanelItem.
39405 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39406 * @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)
39407 * @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)
39408 * @return {Roo.UpdateManager} The UpdateManager
39410 setUrl : function(url, params, loadOnce){
39411 if(this.refreshDelegate){
39412 this.un('activate', this.refreshDelegate);
39414 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39415 this.on("activate", this.refreshDelegate);
39416 return this.bodyEl.getUpdateManager();
39420 _handleRefresh : function(url, params, loadOnce){
39421 if(!loadOnce || !this.loaded){
39422 var updater = this.bodyEl.getUpdateManager();
39423 updater.update(url, params, this._setLoaded.createDelegate(this));
39428 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
39429 * Will fail silently if the setUrl method has not been called.
39430 * This does not activate the panel, just updates its content.
39432 refresh : function(){
39433 if(this.refreshDelegate){
39434 this.loaded = false;
39435 this.refreshDelegate();
39440 _setLoaded : function(){
39441 this.loaded = true;
39445 closeClick : function(e){
39448 this.fireEvent("beforeclose", this, o);
39449 if(o.cancel !== true){
39450 this.tabPanel.removeTab(this.id);
39454 * The text displayed in the tooltip for the close icon.
39457 closeText : "Close this tab"
39460 * This script refer to:
39461 * Title: International Telephone Input
39462 * Author: Jack O'Connor
39463 * Code version: v12.1.12
39464 * Availability: https://github.com/jackocnr/intl-tel-input.git
39467 Roo.bootstrap.PhoneInputData = function() {
39470 "Afghanistan (افغانستان)",
39475 "Albania (Shqipëri)",
39480 "Algeria (الجزائر)",
39505 "Antigua and Barbuda",
39515 "Armenia (Հայաստան)",
39531 "Austria (Österreich)",
39536 "Azerbaijan (Azərbaycan)",
39546 "Bahrain (البحرين)",
39551 "Bangladesh (বাংলাদেশ)",
39561 "Belarus (Беларусь)",
39566 "Belgium (België)",
39596 "Bosnia and Herzegovina (Босна и Херцеговина)",
39611 "British Indian Ocean Territory",
39616 "British Virgin Islands",
39626 "Bulgaria (България)",
39636 "Burundi (Uburundi)",
39641 "Cambodia (កម្ពុជា)",
39646 "Cameroon (Cameroun)",
39655 ["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"]
39658 "Cape Verde (Kabu Verdi)",
39663 "Caribbean Netherlands",
39674 "Central African Republic (République centrafricaine)",
39694 "Christmas Island",
39700 "Cocos (Keeling) Islands",
39711 "Comoros (جزر القمر)",
39716 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39721 "Congo (Republic) (Congo-Brazzaville)",
39741 "Croatia (Hrvatska)",
39762 "Czech Republic (Česká republika)",
39767 "Denmark (Danmark)",
39782 "Dominican Republic (República Dominicana)",
39786 ["809", "829", "849"]
39804 "Equatorial Guinea (Guinea Ecuatorial)",
39824 "Falkland Islands (Islas Malvinas)",
39829 "Faroe Islands (Føroyar)",
39850 "French Guiana (Guyane française)",
39855 "French Polynesia (Polynésie française)",
39870 "Georgia (საქართველო)",
39875 "Germany (Deutschland)",
39895 "Greenland (Kalaallit Nunaat)",
39932 "Guinea-Bissau (Guiné Bissau)",
39957 "Hungary (Magyarország)",
39962 "Iceland (Ísland)",
39982 "Iraq (العراق)",
39998 "Israel (ישראל)",
40025 "Jordan (الأردن)",
40030 "Kazakhstan (Казахстан)",
40051 "Kuwait (الكويت)",
40056 "Kyrgyzstan (Кыргызстан)",
40066 "Latvia (Latvija)",
40071 "Lebanon (لبنان)",
40086 "Libya (ليبيا)",
40096 "Lithuania (Lietuva)",
40111 "Macedonia (FYROM) (Македонија)",
40116 "Madagascar (Madagasikara)",
40146 "Marshall Islands",
40156 "Mauritania (موريتانيا)",
40161 "Mauritius (Moris)",
40182 "Moldova (Republica Moldova)",
40192 "Mongolia (Монгол)",
40197 "Montenegro (Crna Gora)",
40207 "Morocco (المغرب)",
40213 "Mozambique (Moçambique)",
40218 "Myanmar (Burma) (မြန်မာ)",
40223 "Namibia (Namibië)",
40238 "Netherlands (Nederland)",
40243 "New Caledonia (Nouvelle-Calédonie)",
40278 "North Korea (조선 민주주의 인민 공화국)",
40283 "Northern Mariana Islands",
40299 "Pakistan (پاکستان)",
40309 "Palestine (فلسطين)",
40319 "Papua New Guinea",
40361 "Réunion (La Réunion)",
40367 "Romania (România)",
40383 "Saint Barthélemy",
40394 "Saint Kitts and Nevis",
40404 "Saint Martin (Saint-Martin (partie française))",
40410 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40415 "Saint Vincent and the Grenadines",
40430 "São Tomé and Príncipe (São Tomé e Príncipe)",
40435 "Saudi Arabia (المملكة العربية السعودية)",
40440 "Senegal (Sénégal)",
40470 "Slovakia (Slovensko)",
40475 "Slovenia (Slovenija)",
40485 "Somalia (Soomaaliya)",
40495 "South Korea (대한민국)",
40500 "South Sudan (جنوب السودان)",
40510 "Sri Lanka (ශ්රී ලංකාව)",
40515 "Sudan (السودان)",
40525 "Svalbard and Jan Mayen",
40536 "Sweden (Sverige)",
40541 "Switzerland (Schweiz)",
40546 "Syria (سوريا)",
40591 "Trinidad and Tobago",
40596 "Tunisia (تونس)",
40601 "Turkey (Türkiye)",
40611 "Turks and Caicos Islands",
40621 "U.S. Virgin Islands",
40631 "Ukraine (Україна)",
40636 "United Arab Emirates (الإمارات العربية المتحدة)",
40658 "Uzbekistan (Oʻzbekiston)",
40668 "Vatican City (Città del Vaticano)",
40679 "Vietnam (Việt Nam)",
40684 "Wallis and Futuna (Wallis-et-Futuna)",
40689 "Western Sahara (الصحراء الغربية)",
40695 "Yemen (اليمن)",
40719 * This script refer to:
40720 * Title: International Telephone Input
40721 * Author: Jack O'Connor
40722 * Code version: v12.1.12
40723 * Availability: https://github.com/jackocnr/intl-tel-input.git
40727 * @class Roo.bootstrap.PhoneInput
40728 * @extends Roo.bootstrap.TriggerField
40729 * An input with International dial-code selection
40731 * @cfg {String} defaultDialCode default '+852'
40732 * @cfg {Array} preferedCountries default []
40735 * Create a new PhoneInput.
40736 * @param {Object} config Configuration options
40739 Roo.bootstrap.PhoneInput = function(config) {
40740 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40743 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40745 listWidth: undefined,
40747 selectedClass: 'active',
40749 invalidClass : "has-warning",
40751 validClass: 'has-success',
40753 allowed: '0123456789',
40758 * @cfg {String} defaultDialCode The default dial code when initializing the input
40760 defaultDialCode: '+852',
40763 * @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
40765 preferedCountries: false,
40767 getAutoCreate : function()
40769 var data = Roo.bootstrap.PhoneInputData();
40770 var align = this.labelAlign || this.parentLabelAlign();
40773 this.allCountries = [];
40774 this.dialCodeMapping = [];
40776 for (var i = 0; i < data.length; i++) {
40778 this.allCountries[i] = {
40782 priority: c[3] || 0,
40783 areaCodes: c[4] || null
40785 this.dialCodeMapping[c[2]] = {
40788 priority: c[3] || 0,
40789 areaCodes: c[4] || null
40801 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40802 maxlength: this.max_length,
40803 cls : 'form-control tel-input',
40804 autocomplete: 'new-password'
40807 var hiddenInput = {
40810 cls: 'hidden-tel-input'
40814 hiddenInput.name = this.name;
40817 if (this.disabled) {
40818 input.disabled = true;
40821 var flag_container = {
40838 cls: this.hasFeedback ? 'has-feedback' : '',
40844 cls: 'dial-code-holder',
40851 cls: 'roo-select2-container input-group',
40858 if (this.fieldLabel.length) {
40861 tooltip: 'This field is required'
40867 cls: 'control-label',
40873 html: this.fieldLabel
40876 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40882 if(this.indicatorpos == 'right') {
40883 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40890 if(align == 'left') {
40898 if(this.labelWidth > 12){
40899 label.style = "width: " + this.labelWidth + 'px';
40901 if(this.labelWidth < 13 && this.labelmd == 0){
40902 this.labelmd = this.labelWidth;
40904 if(this.labellg > 0){
40905 label.cls += ' col-lg-' + this.labellg;
40906 input.cls += ' col-lg-' + (12 - this.labellg);
40908 if(this.labelmd > 0){
40909 label.cls += ' col-md-' + this.labelmd;
40910 container.cls += ' col-md-' + (12 - this.labelmd);
40912 if(this.labelsm > 0){
40913 label.cls += ' col-sm-' + this.labelsm;
40914 container.cls += ' col-sm-' + (12 - this.labelsm);
40916 if(this.labelxs > 0){
40917 label.cls += ' col-xs-' + this.labelxs;
40918 container.cls += ' col-xs-' + (12 - this.labelxs);
40928 var settings = this;
40930 ['xs','sm','md','lg'].map(function(size){
40931 if (settings[size]) {
40932 cfg.cls += ' col-' + size + '-' + settings[size];
40936 this.store = new Roo.data.Store({
40937 proxy : new Roo.data.MemoryProxy({}),
40938 reader : new Roo.data.JsonReader({
40949 'name' : 'dialCode',
40953 'name' : 'priority',
40957 'name' : 'areaCodes',
40964 if(!this.preferedCountries) {
40965 this.preferedCountries = [
40972 var p = this.preferedCountries.reverse();
40975 for (var i = 0; i < p.length; i++) {
40976 for (var j = 0; j < this.allCountries.length; j++) {
40977 if(this.allCountries[j].iso2 == p[i]) {
40978 var t = this.allCountries[j];
40979 this.allCountries.splice(j,1);
40980 this.allCountries.unshift(t);
40986 this.store.proxy.data = {
40988 data: this.allCountries
40994 initEvents : function()
40997 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40999 this.indicator = this.indicatorEl();
41000 this.flag = this.flagEl();
41001 this.dialCodeHolder = this.dialCodeHolderEl();
41003 this.trigger = this.el.select('div.flag-box',true).first();
41004 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41009 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41010 _this.list.setWidth(lw);
41013 this.list.on('mouseover', this.onViewOver, this);
41014 this.list.on('mousemove', this.onViewMove, this);
41015 this.inputEl().on("keyup", this.onKeyUp, this);
41016 this.inputEl().on("keypress", this.onKeyPress, this);
41018 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
41020 this.view = new Roo.View(this.list, this.tpl, {
41021 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41024 this.view.on('click', this.onViewClick, this);
41025 this.setValue(this.defaultDialCode);
41028 onTriggerClick : function(e)
41030 Roo.log('trigger click');
41035 if(this.isExpanded()){
41037 this.hasFocus = false;
41039 this.store.load({});
41040 this.hasFocus = true;
41045 isExpanded : function()
41047 return this.list.isVisible();
41050 collapse : function()
41052 if(!this.isExpanded()){
41056 Roo.get(document).un('mousedown', this.collapseIf, this);
41057 Roo.get(document).un('mousewheel', this.collapseIf, this);
41058 this.fireEvent('collapse', this);
41062 expand : function()
41066 if(this.isExpanded() || !this.hasFocus){
41070 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
41071 this.list.setWidth(lw);
41074 this.restrictHeight();
41076 Roo.get(document).on('mousedown', this.collapseIf, this);
41077 Roo.get(document).on('mousewheel', this.collapseIf, this);
41079 this.fireEvent('expand', this);
41082 restrictHeight : function()
41084 this.list.alignTo(this.inputEl(), this.listAlign);
41085 this.list.alignTo(this.inputEl(), this.listAlign);
41088 onViewOver : function(e, t)
41090 if(this.inKeyMode){
41093 var item = this.view.findItemFromChild(t);
41096 var index = this.view.indexOf(item);
41097 this.select(index, false);
41102 onViewClick : function(view, doFocus, el, e)
41104 var index = this.view.getSelectedIndexes()[0];
41106 var r = this.store.getAt(index);
41109 this.onSelect(r, index);
41111 if(doFocus !== false && !this.blockFocus){
41112 this.inputEl().focus();
41116 onViewMove : function(e, t)
41118 this.inKeyMode = false;
41121 select : function(index, scrollIntoView)
41123 this.selectedIndex = index;
41124 this.view.select(index);
41125 if(scrollIntoView !== false){
41126 var el = this.view.getNode(index);
41128 this.list.scrollChildIntoView(el, false);
41133 createList : function()
41135 this.list = Roo.get(document.body).createChild({
41137 cls: 'typeahead typeahead-long dropdown-menu tel-list',
41138 style: 'display:none'
41141 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
41144 collapseIf : function(e)
41146 var in_combo = e.within(this.el);
41147 var in_list = e.within(this.list);
41148 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
41150 if (in_combo || in_list || is_list) {
41156 onSelect : function(record, index)
41158 if(this.fireEvent('beforeselect', this, record, index) !== false){
41160 this.setFlagClass(record.data.iso2);
41161 this.setDialCode(record.data.dialCode);
41162 this.hasFocus = false;
41164 this.fireEvent('select', this, record, index);
41168 flagEl : function()
41170 var flag = this.el.select('div.flag',true).first();
41177 dialCodeHolderEl : function()
41179 var d = this.el.select('input.dial-code-holder',true).first();
41186 setDialCode : function(v)
41188 this.dialCodeHolder.dom.value = '+'+v;
41191 setFlagClass : function(n)
41193 this.flag.dom.className = 'flag '+n;
41196 getValue : function()
41198 var v = this.inputEl().getValue();
41199 if(this.dialCodeHolder) {
41200 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
41205 setValue : function(v)
41207 var d = this.getDialCode(v);
41209 //invalid dial code
41210 if(v.length == 0 || !d || d.length == 0) {
41212 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41213 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41219 this.setFlagClass(this.dialCodeMapping[d].iso2);
41220 this.setDialCode(d);
41221 this.inputEl().dom.value = v.replace('+'+d,'');
41222 this.hiddenEl().dom.value = this.getValue();
41227 getDialCode : function(v)
41231 if (v.length == 0) {
41232 return this.dialCodeHolder.dom.value;
41236 if (v.charAt(0) != "+") {
41239 var numericChars = "";
41240 for (var i = 1; i < v.length; i++) {
41241 var c = v.charAt(i);
41244 if (this.dialCodeMapping[numericChars]) {
41245 dialCode = v.substr(1, i);
41247 if (numericChars.length == 4) {
41257 this.setValue(this.defaultDialCode);
41261 hiddenEl : function()
41263 return this.el.select('input.hidden-tel-input',true).first();
41266 // after setting val
41267 onKeyUp : function(e){
41268 this.setValue(this.getValue());
41271 onKeyPress : function(e){
41272 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
41279 * @class Roo.bootstrap.MoneyField
41280 * @extends Roo.bootstrap.ComboBox
41281 * Bootstrap MoneyField class
41284 * Create a new MoneyField.
41285 * @param {Object} config Configuration options
41288 Roo.bootstrap.MoneyField = function(config) {
41290 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
41294 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
41297 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41299 allowDecimals : true,
41301 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41303 decimalSeparator : ".",
41305 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41307 decimalPrecision : 0,
41309 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41311 allowNegative : true,
41313 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41317 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41319 minValue : Number.NEGATIVE_INFINITY,
41321 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41323 maxValue : Number.MAX_VALUE,
41325 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41327 minText : "The minimum value for this field is {0}",
41329 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41331 maxText : "The maximum value for this field is {0}",
41333 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
41334 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41336 nanText : "{0} is not a valid number",
41338 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41342 * @cfg {String} defaults currency of the MoneyField
41343 * value should be in lkey
41345 defaultCurrency : false,
41347 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41349 thousandsDelimiter : false,
41351 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41362 getAutoCreate : function()
41364 var align = this.labelAlign || this.parentLabelAlign();
41376 cls : 'form-control roo-money-amount-input',
41377 autocomplete: 'new-password'
41380 var hiddenInput = {
41384 cls: 'hidden-number-input'
41387 if(this.max_length) {
41388 input.maxlength = this.max_length;
41392 hiddenInput.name = this.name;
41395 if (this.disabled) {
41396 input.disabled = true;
41399 var clg = 12 - this.inputlg;
41400 var cmd = 12 - this.inputmd;
41401 var csm = 12 - this.inputsm;
41402 var cxs = 12 - this.inputxs;
41406 cls : 'row roo-money-field',
41410 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41414 cls: 'roo-select2-container input-group',
41418 cls : 'form-control roo-money-currency-input',
41419 autocomplete: 'new-password',
41421 name : this.currencyName
41425 cls : 'input-group-addon',
41439 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41443 cls: this.hasFeedback ? 'has-feedback' : '',
41454 if (this.fieldLabel.length) {
41457 tooltip: 'This field is required'
41463 cls: 'control-label',
41469 html: this.fieldLabel
41472 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41478 if(this.indicatorpos == 'right') {
41479 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41486 if(align == 'left') {
41494 if(this.labelWidth > 12){
41495 label.style = "width: " + this.labelWidth + 'px';
41497 if(this.labelWidth < 13 && this.labelmd == 0){
41498 this.labelmd = this.labelWidth;
41500 if(this.labellg > 0){
41501 label.cls += ' col-lg-' + this.labellg;
41502 input.cls += ' col-lg-' + (12 - this.labellg);
41504 if(this.labelmd > 0){
41505 label.cls += ' col-md-' + this.labelmd;
41506 container.cls += ' col-md-' + (12 - this.labelmd);
41508 if(this.labelsm > 0){
41509 label.cls += ' col-sm-' + this.labelsm;
41510 container.cls += ' col-sm-' + (12 - this.labelsm);
41512 if(this.labelxs > 0){
41513 label.cls += ' col-xs-' + this.labelxs;
41514 container.cls += ' col-xs-' + (12 - this.labelxs);
41525 var settings = this;
41527 ['xs','sm','md','lg'].map(function(size){
41528 if (settings[size]) {
41529 cfg.cls += ' col-' + size + '-' + settings[size];
41536 initEvents : function()
41538 this.indicator = this.indicatorEl();
41540 this.initCurrencyEvent();
41542 this.initNumberEvent();
41545 initCurrencyEvent : function()
41548 throw "can not find store for combo";
41551 this.store = Roo.factory(this.store, Roo.data);
41552 this.store.parent = this;
41556 this.triggerEl = this.el.select('.input-group-addon', true).first();
41558 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41563 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41564 _this.list.setWidth(lw);
41567 this.list.on('mouseover', this.onViewOver, this);
41568 this.list.on('mousemove', this.onViewMove, this);
41569 this.list.on('scroll', this.onViewScroll, this);
41572 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41575 this.view = new Roo.View(this.list, this.tpl, {
41576 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41579 this.view.on('click', this.onViewClick, this);
41581 this.store.on('beforeload', this.onBeforeLoad, this);
41582 this.store.on('load', this.onLoad, this);
41583 this.store.on('loadexception', this.onLoadException, this);
41585 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41586 "up" : function(e){
41587 this.inKeyMode = true;
41591 "down" : function(e){
41592 if(!this.isExpanded()){
41593 this.onTriggerClick();
41595 this.inKeyMode = true;
41600 "enter" : function(e){
41603 if(this.fireEvent("specialkey", this, e)){
41604 this.onViewClick(false);
41610 "esc" : function(e){
41614 "tab" : function(e){
41617 if(this.fireEvent("specialkey", this, e)){
41618 this.onViewClick(false);
41626 doRelay : function(foo, bar, hname){
41627 if(hname == 'down' || this.scope.isExpanded()){
41628 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41636 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41640 initNumberEvent : function(e)
41642 this.inputEl().on("keydown" , this.fireKey, this);
41643 this.inputEl().on("focus", this.onFocus, this);
41644 this.inputEl().on("blur", this.onBlur, this);
41646 this.inputEl().relayEvent('keyup', this);
41648 if(this.indicator){
41649 this.indicator.addClass('invisible');
41652 this.originalValue = this.getValue();
41654 if(this.validationEvent == 'keyup'){
41655 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41656 this.inputEl().on('keyup', this.filterValidation, this);
41658 else if(this.validationEvent !== false){
41659 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41662 if(this.selectOnFocus){
41663 this.on("focus", this.preFocus, this);
41666 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41667 this.inputEl().on("keypress", this.filterKeys, this);
41669 this.inputEl().relayEvent('keypress', this);
41672 var allowed = "0123456789";
41674 if(this.allowDecimals){
41675 allowed += this.decimalSeparator;
41678 if(this.allowNegative){
41682 if(this.thousandsDelimiter) {
41686 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41688 var keyPress = function(e){
41690 var k = e.getKey();
41692 var c = e.getCharCode();
41695 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41696 allowed.indexOf(String.fromCharCode(c)) === -1
41702 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41706 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41711 this.inputEl().on("keypress", keyPress, this);
41715 onTriggerClick : function(e)
41722 this.loadNext = false;
41724 if(this.isExpanded()){
41729 this.hasFocus = true;
41731 if(this.triggerAction == 'all') {
41732 this.doQuery(this.allQuery, true);
41736 this.doQuery(this.getRawValue());
41739 getCurrency : function()
41741 var v = this.currencyEl().getValue();
41746 restrictHeight : function()
41748 this.list.alignTo(this.currencyEl(), this.listAlign);
41749 this.list.alignTo(this.currencyEl(), this.listAlign);
41752 onViewClick : function(view, doFocus, el, e)
41754 var index = this.view.getSelectedIndexes()[0];
41756 var r = this.store.getAt(index);
41759 this.onSelect(r, index);
41763 onSelect : function(record, index){
41765 if(this.fireEvent('beforeselect', this, record, index) !== false){
41767 this.setFromCurrencyData(index > -1 ? record.data : false);
41771 this.fireEvent('select', this, record, index);
41775 setFromCurrencyData : function(o)
41779 this.lastCurrency = o;
41781 if (this.currencyField) {
41782 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41784 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41787 this.lastSelectionText = currency;
41789 //setting default currency
41790 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41791 this.setCurrency(this.defaultCurrency);
41795 this.setCurrency(currency);
41798 setFromData : function(o)
41802 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41804 this.setFromCurrencyData(c);
41809 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41811 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41814 this.setValue(value);
41818 setCurrency : function(v)
41820 this.currencyValue = v;
41823 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41828 setValue : function(v)
41830 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41836 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41838 this.inputEl().dom.value = (v == '') ? '' :
41839 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41841 if(!this.allowZero && v === '0') {
41842 this.hiddenEl().dom.value = '';
41843 this.inputEl().dom.value = '';
41850 getRawValue : function()
41852 var v = this.inputEl().getValue();
41857 getValue : function()
41859 return this.fixPrecision(this.parseValue(this.getRawValue()));
41862 parseValue : function(value)
41864 if(this.thousandsDelimiter) {
41866 r = new RegExp(",", "g");
41867 value = value.replace(r, "");
41870 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41871 return isNaN(value) ? '' : value;
41875 fixPrecision : function(value)
41877 if(this.thousandsDelimiter) {
41879 r = new RegExp(",", "g");
41880 value = value.replace(r, "");
41883 var nan = isNaN(value);
41885 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41886 return nan ? '' : value;
41888 return parseFloat(value).toFixed(this.decimalPrecision);
41891 decimalPrecisionFcn : function(v)
41893 return Math.floor(v);
41896 validateValue : function(value)
41898 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41902 var num = this.parseValue(value);
41905 this.markInvalid(String.format(this.nanText, value));
41909 if(num < this.minValue){
41910 this.markInvalid(String.format(this.minText, this.minValue));
41914 if(num > this.maxValue){
41915 this.markInvalid(String.format(this.maxText, this.maxValue));
41922 validate : function()
41924 if(this.disabled || this.allowBlank){
41929 var currency = this.getCurrency();
41931 if(this.validateValue(this.getRawValue()) && currency.length){
41936 this.markInvalid();
41940 getName: function()
41945 beforeBlur : function()
41951 var v = this.parseValue(this.getRawValue());
41958 onBlur : function()
41962 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41963 //this.el.removeClass(this.focusClass);
41966 this.hasFocus = false;
41968 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41972 var v = this.getValue();
41974 if(String(v) !== String(this.startValue)){
41975 this.fireEvent('change', this, v, this.startValue);
41978 this.fireEvent("blur", this);
41981 inputEl : function()
41983 return this.el.select('.roo-money-amount-input', true).first();
41986 currencyEl : function()
41988 return this.el.select('.roo-money-currency-input', true).first();
41991 hiddenEl : function()
41993 return this.el.select('input.hidden-number-input',true).first();
41997 * @class Roo.bootstrap.BezierSignature
41998 * @extends Roo.bootstrap.Component
41999 * Bootstrap BezierSignature class
42000 * This script refer to:
42001 * Title: Signature Pad
42003 * Availability: https://github.com/szimek/signature_pad
42006 * Create a new BezierSignature
42007 * @param {Object} config The config object
42010 Roo.bootstrap.BezierSignature = function(config){
42011 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
42017 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
42024 mouse_btn_down: true,
42027 * @cfg {int} canvas height
42029 canvas_height: '200px',
42032 * @cfg {float|function} Radius of a single dot.
42037 * @cfg {float} Minimum width of a line. Defaults to 0.5.
42042 * @cfg {float} Maximum width of a line. Defaults to 2.5.
42047 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
42052 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
42057 * @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.
42059 bg_color: 'rgba(0, 0, 0, 0)',
42062 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
42064 dot_color: 'black',
42067 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
42069 velocity_filter_weight: 0.7,
42072 * @cfg {function} Callback when stroke begin.
42077 * @cfg {function} Callback when stroke end.
42081 getAutoCreate : function()
42083 var cls = 'roo-signature column';
42086 cls += ' ' + this.cls;
42096 for(var i = 0; i < col_sizes.length; i++) {
42097 if(this[col_sizes[i]]) {
42098 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
42108 cls: 'roo-signature-body',
42112 cls: 'roo-signature-body-canvas',
42113 height: this.canvas_height,
42114 width: this.canvas_width
42121 style: 'display: none'
42129 initEvents: function()
42131 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
42133 var canvas = this.canvasEl();
42135 // mouse && touch event swapping...
42136 canvas.dom.style.touchAction = 'none';
42137 canvas.dom.style.msTouchAction = 'none';
42139 this.mouse_btn_down = false;
42140 canvas.on('mousedown', this._handleMouseDown, this);
42141 canvas.on('mousemove', this._handleMouseMove, this);
42142 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
42144 if (window.PointerEvent) {
42145 canvas.on('pointerdown', this._handleMouseDown, this);
42146 canvas.on('pointermove', this._handleMouseMove, this);
42147 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
42150 if ('ontouchstart' in window) {
42151 canvas.on('touchstart', this._handleTouchStart, this);
42152 canvas.on('touchmove', this._handleTouchMove, this);
42153 canvas.on('touchend', this._handleTouchEnd, this);
42156 Roo.EventManager.onWindowResize(this.resize, this, true);
42158 // file input event
42159 this.fileEl().on('change', this.uploadImage, this);
42166 resize: function(){
42168 var canvas = this.canvasEl().dom;
42169 var ctx = this.canvasElCtx();
42170 var img_data = false;
42172 if(canvas.width > 0) {
42173 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
42175 // setting canvas width will clean img data
42178 var style = window.getComputedStyle ?
42179 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
42181 var padding_left = parseInt(style.paddingLeft) || 0;
42182 var padding_right = parseInt(style.paddingRight) || 0;
42184 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
42187 ctx.putImageData(img_data, 0, 0);
42191 _handleMouseDown: function(e)
42193 if (e.browserEvent.which === 1) {
42194 this.mouse_btn_down = true;
42195 this.strokeBegin(e);
42199 _handleMouseMove: function (e)
42201 if (this.mouse_btn_down) {
42202 this.strokeMoveUpdate(e);
42206 _handleMouseUp: function (e)
42208 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
42209 this.mouse_btn_down = false;
42214 _handleTouchStart: function (e) {
42216 e.preventDefault();
42217 if (e.browserEvent.targetTouches.length === 1) {
42218 // var touch = e.browserEvent.changedTouches[0];
42219 // this.strokeBegin(touch);
42221 this.strokeBegin(e); // assume e catching the correct xy...
42225 _handleTouchMove: function (e) {
42226 e.preventDefault();
42227 // var touch = event.targetTouches[0];
42228 // _this._strokeMoveUpdate(touch);
42229 this.strokeMoveUpdate(e);
42232 _handleTouchEnd: function (e) {
42233 var wasCanvasTouched = e.target === this.canvasEl().dom;
42234 if (wasCanvasTouched) {
42235 e.preventDefault();
42236 // var touch = event.changedTouches[0];
42237 // _this._strokeEnd(touch);
42242 reset: function () {
42243 this._lastPoints = [];
42244 this._lastVelocity = 0;
42245 this._lastWidth = (this.min_width + this.max_width) / 2;
42246 this.canvasElCtx().fillStyle = this.dot_color;
42249 strokeMoveUpdate: function(e)
42251 this.strokeUpdate(e);
42253 if (this.throttle) {
42254 this.throttleStroke(this.strokeUpdate, this.throttle);
42257 this.strokeUpdate(e);
42261 strokeBegin: function(e)
42263 var newPointGroup = {
42264 color: this.dot_color,
42268 if (typeof this.onBegin === 'function') {
42272 this.curve_data.push(newPointGroup);
42274 this.strokeUpdate(e);
42277 strokeUpdate: function(e)
42279 var rect = this.canvasEl().dom.getBoundingClientRect();
42280 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
42281 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
42282 var lastPoints = lastPointGroup.points;
42283 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
42284 var isLastPointTooClose = lastPoint
42285 ? point.distanceTo(lastPoint) <= this.min_distance
42287 var color = lastPointGroup.color;
42288 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
42289 var curve = this.addPoint(point);
42291 this.drawDot({color: color, point: point});
42294 this.drawCurve({color: color, curve: curve});
42304 strokeEnd: function(e)
42306 this.strokeUpdate(e);
42307 if (typeof this.onEnd === 'function') {
42312 addPoint: function (point) {
42313 var _lastPoints = this._lastPoints;
42314 _lastPoints.push(point);
42315 if (_lastPoints.length > 2) {
42316 if (_lastPoints.length === 3) {
42317 _lastPoints.unshift(_lastPoints[0]);
42319 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
42320 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
42321 _lastPoints.shift();
42327 calculateCurveWidths: function (startPoint, endPoint) {
42328 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42329 (1 - this.velocity_filter_weight) * this._lastVelocity;
42331 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42334 start: this._lastWidth
42337 this._lastVelocity = velocity;
42338 this._lastWidth = newWidth;
42342 drawDot: function (_a) {
42343 var color = _a.color, point = _a.point;
42344 var ctx = this.canvasElCtx();
42345 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42347 this.drawCurveSegment(point.x, point.y, width);
42349 ctx.fillStyle = color;
42353 drawCurve: function (_a) {
42354 var color = _a.color, curve = _a.curve;
42355 var ctx = this.canvasElCtx();
42356 var widthDelta = curve.endWidth - curve.startWidth;
42357 var drawSteps = Math.floor(curve.length()) * 2;
42359 ctx.fillStyle = color;
42360 for (var i = 0; i < drawSteps; i += 1) {
42361 var t = i / drawSteps;
42367 var x = uuu * curve.startPoint.x;
42368 x += 3 * uu * t * curve.control1.x;
42369 x += 3 * u * tt * curve.control2.x;
42370 x += ttt * curve.endPoint.x;
42371 var y = uuu * curve.startPoint.y;
42372 y += 3 * uu * t * curve.control1.y;
42373 y += 3 * u * tt * curve.control2.y;
42374 y += ttt * curve.endPoint.y;
42375 var width = curve.startWidth + ttt * widthDelta;
42376 this.drawCurveSegment(x, y, width);
42382 drawCurveSegment: function (x, y, width) {
42383 var ctx = this.canvasElCtx();
42385 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42386 this.is_empty = false;
42391 var ctx = this.canvasElCtx();
42392 var canvas = this.canvasEl().dom;
42393 ctx.fillStyle = this.bg_color;
42394 ctx.clearRect(0, 0, canvas.width, canvas.height);
42395 ctx.fillRect(0, 0, canvas.width, canvas.height);
42396 this.curve_data = [];
42398 this.is_empty = true;
42403 return this.el.select('input',true).first();
42406 canvasEl: function()
42408 return this.el.select('canvas',true).first();
42411 canvasElCtx: function()
42413 return this.el.select('canvas',true).first().dom.getContext('2d');
42416 getImage: function(type)
42418 if(this.is_empty) {
42423 return this.canvasEl().dom.toDataURL('image/'+type, 1);
42426 drawFromImage: function(img_src)
42428 var img = new Image();
42430 img.onload = function(){
42431 this.canvasElCtx().drawImage(img, 0, 0);
42436 this.is_empty = false;
42439 selectImage: function()
42441 this.fileEl().dom.click();
42444 uploadImage: function(e)
42446 var reader = new FileReader();
42448 reader.onload = function(e){
42449 var img = new Image();
42450 img.onload = function(){
42452 this.canvasElCtx().drawImage(img, 0, 0);
42454 img.src = e.target.result;
42457 reader.readAsDataURL(e.target.files[0]);
42460 // Bezier Point Constructor
42461 Point: (function () {
42462 function Point(x, y, time) {
42465 this.time = time || Date.now();
42467 Point.prototype.distanceTo = function (start) {
42468 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42470 Point.prototype.equals = function (other) {
42471 return this.x === other.x && this.y === other.y && this.time === other.time;
42473 Point.prototype.velocityFrom = function (start) {
42474 return this.time !== start.time
42475 ? this.distanceTo(start) / (this.time - start.time)
42482 // Bezier Constructor
42483 Bezier: (function () {
42484 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42485 this.startPoint = startPoint;
42486 this.control2 = control2;
42487 this.control1 = control1;
42488 this.endPoint = endPoint;
42489 this.startWidth = startWidth;
42490 this.endWidth = endWidth;
42492 Bezier.fromPoints = function (points, widths, scope) {
42493 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42494 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42495 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42497 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42498 var dx1 = s1.x - s2.x;
42499 var dy1 = s1.y - s2.y;
42500 var dx2 = s2.x - s3.x;
42501 var dy2 = s2.y - s3.y;
42502 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42503 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42504 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42505 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42506 var dxm = m1.x - m2.x;
42507 var dym = m1.y - m2.y;
42508 var k = l2 / (l1 + l2);
42509 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42510 var tx = s2.x - cm.x;
42511 var ty = s2.y - cm.y;
42513 c1: new scope.Point(m1.x + tx, m1.y + ty),
42514 c2: new scope.Point(m2.x + tx, m2.y + ty)
42517 Bezier.prototype.length = function () {
42522 for (var i = 0; i <= steps; i += 1) {
42524 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42525 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42527 var xdiff = cx - px;
42528 var ydiff = cy - py;
42529 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42536 Bezier.prototype.point = function (t, start, c1, c2, end) {
42537 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42538 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42539 + (3.0 * c2 * (1.0 - t) * t * t)
42540 + (end * t * t * t);
42545 throttleStroke: function(fn, wait) {
42546 if (wait === void 0) { wait = 250; }
42548 var timeout = null;
42552 var later = function () {
42553 previous = Date.now();
42555 result = fn.apply(storedContext, storedArgs);
42557 storedContext = null;
42561 return function wrapper() {
42563 for (var _i = 0; _i < arguments.length; _i++) {
42564 args[_i] = arguments[_i];
42566 var now = Date.now();
42567 var remaining = wait - (now - previous);
42568 storedContext = this;
42570 if (remaining <= 0 || remaining > wait) {
42572 clearTimeout(timeout);
42576 result = fn.apply(storedContext, storedArgs);
42578 storedContext = null;
42582 else if (!timeout) {
42583 timeout = window.setTimeout(later, remaining);