2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = (
9 Roo.each(document.styleSheets, function(s) {
10 if ( s.href && s.href.match(/css-bootstrap4/)) {
18 * base class for bootstrap elements.
22 Roo.bootstrap = Roo.bootstrap || {};
24 * @class Roo.bootstrap.Component
25 * @extends Roo.Component
26 * Bootstrap Component base class
27 * @cfg {String} cls css class
28 * @cfg {String} style any extra css
29 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
31 * @cfg {string} dataId cutomer id
32 * @cfg {string} name Specifies name attribute
33 * @cfg {string} tooltip Text for the tooltip
34 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
35 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
38 * Do not use directly - it does not do anything..
39 * @param {Object} config The config object
44 Roo.bootstrap.Component = function(config){
45 Roo.bootstrap.Component.superclass.constructor.call(this, config);
49 * @event childrenrendered
50 * Fires when the children have been rendered..
51 * @param {Roo.bootstrap.Component} this
53 "childrenrendered" : true
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
65 allowDomMove : false, // to stop relocations in parent onRender...
75 * Initialize Events for the element
77 initEvents : function() { },
83 can_build_overlaid : true,
85 container_method : false,
92 // returns the parent component..
93 return Roo.ComponentMgr.get(this.parentId)
99 onRender : function(ct, position)
101 // Roo.log("Call onRender: " + this.xtype);
103 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
106 if (this.el.attr('xtype')) {
107 this.el.attr('xtypex', this.el.attr('xtype'));
108 this.el.dom.removeAttribute('xtype');
118 var cfg = Roo.apply({}, this.getAutoCreate());
120 cfg.id = this.id || Roo.id();
122 // fill in the extra attributes
123 if (this.xattr && typeof(this.xattr) =='object') {
124 for (var i in this.xattr) {
125 cfg[i] = this.xattr[i];
130 cfg.dataId = this.dataId;
134 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
137 if (this.style) { // fixme needs to support more complex style data.
138 cfg.style = this.style;
142 cfg.name = this.name;
145 this.el = ct.createChild(cfg, position);
148 this.tooltipEl().attr('tooltip', this.tooltip);
151 if(this.tabIndex !== undefined){
152 this.el.dom.setAttribute('tabIndex', this.tabIndex);
159 * Fetch the element to add children to
160 * @return {Roo.Element} defaults to this.el
162 getChildContainer : function()
167 * Fetch the element to display the tooltip on.
168 * @return {Roo.Element} defaults to this.el
170 tooltipEl : function()
175 addxtype : function(tree,cntr)
179 cn = Roo.factory(tree);
180 //Roo.log(['addxtype', cn]);
182 cn.parentType = this.xtype; //??
183 cn.parentId = this.id;
185 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186 if (typeof(cn.container_method) == 'string') {
187 cntr = cn.container_method;
191 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
193 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
195 var build_from_html = Roo.XComponent.build_from_html;
197 var is_body = (tree.xtype == 'Body') ;
199 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
201 var self_cntr_el = Roo.get(this[cntr](false));
203 // do not try and build conditional elements
204 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
208 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210 return this.addxtypeChild(tree,cntr, is_body);
213 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
216 return this.addxtypeChild(Roo.apply({}, tree),cntr);
219 Roo.log('skipping render');
225 if (!build_from_html) {
229 // this i think handles overlaying multiple children of the same type
230 // with the sam eelement.. - which might be buggy..
232 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
238 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
242 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
249 addxtypeChild : function (tree, cntr, is_body)
251 Roo.debug && Roo.log('addxtypeChild:' + cntr);
253 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
256 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257 (typeof(tree['flexy:foreach']) != 'undefined');
261 skip_children = false;
262 // render the element if it's not BODY.
265 // if parent was disabled, then do not try and create the children..
266 if(!this[cntr](true)){
271 cn = Roo.factory(tree);
273 cn.parentType = this.xtype; //??
274 cn.parentId = this.id;
276 var build_from_html = Roo.XComponent.build_from_html;
279 // does the container contain child eleemnts with 'xtype' attributes.
280 // that match this xtype..
281 // note - when we render we create these as well..
282 // so we should check to see if body has xtype set.
283 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
285 var self_cntr_el = Roo.get(this[cntr](false));
286 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
288 //Roo.log(Roo.XComponent.build_from_html);
289 //Roo.log("got echild:");
292 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293 // and are not displayed -this causes this to use up the wrong element when matching.
294 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
297 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
304 //echild.dom.removeAttribute('xtype');
306 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307 Roo.debug && Roo.log(self_cntr_el);
308 Roo.debug && Roo.log(echild);
309 Roo.debug && Roo.log(cn);
315 // if object has flexy:if - then it may or may not be rendered.
316 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
317 // skip a flexy if element.
318 Roo.debug && Roo.log('skipping render');
319 Roo.debug && Roo.log(tree);
321 Roo.debug && Roo.log('skipping all children');
322 skip_children = true;
327 // actually if flexy:foreach is found, we really want to create
328 // multiple copies here...
330 //Roo.log(this[cntr]());
331 // some elements do not have render methods.. like the layouts...
333 if(this[cntr](true) === false){
338 cn.render && cn.render(this[cntr](true));
341 // then add the element..
348 if (typeof (tree.menu) != 'undefined') {
349 tree.menu.parentType = cn.xtype;
350 tree.menu.triggerEl = cn.el;
351 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
355 if (!tree.items || !tree.items.length) {
357 //Roo.log(["no children", this]);
362 var items = tree.items;
365 //Roo.log(items.length);
367 if (!skip_children) {
368 for(var i =0;i < items.length;i++) {
369 // Roo.log(['add child', items[i]]);
370 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
376 //Roo.log("fire childrenrendered");
378 cn.fireEvent('childrenrendered', this);
384 * Set the element that will be used to show or hide
386 setVisibilityEl : function(el)
388 this.visibilityEl = el;
392 * Get the element that will be used to show or hide
394 getVisibilityEl : function()
396 if (typeof(this.visibilityEl) == 'object') {
397 return this.visibilityEl;
400 if (typeof(this.visibilityEl) == 'string') {
401 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
408 * Show a component - removes 'hidden' class
412 if(!this.getVisibilityEl()){
416 this.getVisibilityEl().removeClass(['hidden','d-none']);
418 this.fireEvent('show', this);
423 * Hide a component - adds 'hidden' class
427 if(!this.getVisibilityEl()){
431 this.getVisibilityEl().addClass(['hidden','d-none']);
433 this.fireEvent('hide', this);
446 * @class Roo.bootstrap.Body
447 * @extends Roo.bootstrap.Component
448 * Bootstrap Body class
452 * @param {Object} config The config object
455 Roo.bootstrap.Body = function(config){
457 config = config || {};
459 Roo.bootstrap.Body.superclass.constructor.call(this, config);
460 this.el = Roo.get(config.el ? config.el : document.body );
461 if (this.cls && this.cls.length) {
462 Roo.get(document.body).addClass(this.cls);
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
468 is_body : true,// just to make sure it's constructed?
473 onRender : function(ct, position)
475 /* Roo.log("Roo.bootstrap.Body - onRender");
476 if (this.cls && this.cls.length) {
477 Roo.get(document.body).addClass(this.cls);
496 * @class Roo.bootstrap.ButtonGroup
497 * @extends Roo.bootstrap.Component
498 * Bootstrap ButtonGroup class
499 * @cfg {String} size lg | sm | xs (default empty normal)
500 * @cfg {String} align vertical | justified (default none)
501 * @cfg {String} direction up | down (default down)
502 * @cfg {Boolean} toolbar false | true
503 * @cfg {Boolean} btn true | false
508 * @param {Object} config The config object
511 Roo.bootstrap.ButtonGroup = function(config){
512 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
523 getAutoCreate : function(){
529 cfg.html = this.html || cfg.html;
540 if (['vertical','justified'].indexOf(this.align)!==-1) {
541 cfg.cls = 'btn-group-' + this.align;
543 if (this.align == 'justified') {
544 console.log(this.items);
548 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549 cfg.cls += ' btn-group-' + this.size;
552 if (this.direction == 'up') {
553 cfg.cls += ' dropup' ;
559 * Add a button to the group (similar to NavItem API.)
561 addItem : function(cfg)
563 var cn = new Roo.bootstrap.Button(cfg);
565 cn.parentId = this.id;
566 cn.onRender(this.el, null);
580 * @class Roo.bootstrap.Button
581 * @extends Roo.bootstrap.Component
582 * Bootstrap Button class
583 * @cfg {String} html The button content
584 * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585 * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587 * @cfg {String} size ( lg | sm | xs)
588 * @cfg {String} tag ( a | input | submit)
589 * @cfg {String} href empty or href
590 * @cfg {Boolean} disabled default false;
591 * @cfg {Boolean} isClose default false;
592 * @cfg {String} glyphicon depricated - use fa
593 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
594 * @cfg {String} badge text for badge
595 * @cfg {String} theme (default|glow)
596 * @cfg {Boolean} inverse dark themed version
597 * @cfg {Boolean} toggle is it a slidy toggle button
598 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
599 * @cfg {String} ontext text for on slidy toggle state
600 * @cfg {String} offtext text for off slidy toggle state
601 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
602 * @cfg {Boolean} removeClass remove the standard class..
603 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
606 * Create a new button
607 * @param {Object} config The config object
611 Roo.bootstrap.Button = function(config){
612 Roo.bootstrap.Button.superclass.constructor.call(this, config);
613 this.weightClass = ["btn-default btn-outline-secondary",
625 * When a butotn is pressed
626 * @param {Roo.bootstrap.Button} btn
627 * @param {Roo.EventObject} e
632 * After the button has been toggles
633 * @param {Roo.bootstrap.Button} btn
634 * @param {Roo.EventObject} e
635 * @param {boolean} pressed (also available as button.pressed)
641 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
662 preventDefault: true,
670 getAutoCreate : function(){
678 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
679 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
684 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
686 if (this.toggle == true) {
689 cls: 'slider-frame roo-button',
694 'data-off-text':'OFF',
695 cls: 'slider-button',
701 if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
702 cfg.cls += ' '+this.weight;
711 cfg["aria-hidden"] = true;
713 cfg.html = "×";
719 if (this.theme==='default') {
720 cfg.cls = 'btn roo-button';
722 //if (this.parentType != 'Navbar') {
723 this.weight = this.weight.length ? this.weight : 'default';
725 if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
727 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
728 var weight = this.weight == 'default' ? 'secondary' : this.weight;
729 cfg.cls += ' btn-' + outline + weight;
730 if (this.weight == 'default') {
732 cfg.cls += ' btn-' + this.weight;
735 } else if (this.theme==='glow') {
738 cfg.cls = 'btn-glow roo-button';
740 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
742 cfg.cls += ' ' + this.weight;
748 this.cls += ' inverse';
752 if (this.active || this.pressed === true) {
753 cfg.cls += ' active';
757 cfg.disabled = 'disabled';
761 Roo.log('changing to ul' );
763 this.glyphicon = 'caret';
764 if (Roo.bootstrap.version == 4) {
765 this.fa = 'caret-down';
770 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
772 //gsRoo.log(this.parentType);
773 if (this.parentType === 'Navbar' && !this.parent().bar) {
774 Roo.log('changing to li?');
783 href : this.href || '#'
786 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
787 cfg.cls += ' dropdown';
794 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
796 if (this.glyphicon) {
797 cfg.html = ' ' + cfg.html;
802 cls: 'glyphicon glyphicon-' + this.glyphicon
807 cfg.html = ' ' + cfg.html;
812 cls: 'fa fas fa-' + this.fa
822 // cfg.cls='btn roo-button';
826 var value = cfg.html;
831 cls: 'glyphicon glyphicon-' + this.glyphicon,
838 cls: 'fa fas fa-' + this.fa,
843 var bw = this.badge_weight.length ? this.badge_weight :
844 (this.weight.length ? this.weight : 'secondary');
845 bw = bw == 'default' ? 'secondary' : bw;
851 cls: 'badge badge-' + bw,
860 cfg.cls += ' dropdown';
861 cfg.html = typeof(cfg.html) != 'undefined' ?
862 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
865 if (cfg.tag !== 'a' && this.href !== '') {
866 throw "Tag must be a to set href.";
867 } else if (this.href.length > 0) {
868 cfg.href = this.href;
871 if(this.removeClass){
876 cfg.target = this.target;
881 initEvents: function() {
882 // Roo.log('init events?');
883 // Roo.log(this.el.dom);
886 if (typeof (this.menu) != 'undefined') {
887 this.menu.parentType = this.xtype;
888 this.menu.triggerEl = this.el;
889 this.addxtype(Roo.apply({}, this.menu));
893 if (this.el.hasClass('roo-button')) {
894 this.el.on('click', this.onClick, this);
896 this.el.select('.roo-button').on('click', this.onClick, this);
899 if(this.removeClass){
900 this.el.on('click', this.onClick, this);
903 this.el.enableDisplayMode();
906 onClick : function(e)
912 Roo.log('button on click ');
913 if(this.preventDefault){
917 if (this.pressed === true || this.pressed === false) {
918 this.toggleActive(e);
922 this.fireEvent('click', this, e);
926 * Enables this button
930 this.disabled = false;
931 this.el.removeClass('disabled');
935 * Disable this button
939 this.disabled = true;
940 this.el.addClass('disabled');
943 * sets the active state on/off,
944 * @param {Boolean} state (optional) Force a particular state
946 setActive : function(v) {
948 this.el[v ? 'addClass' : 'removeClass']('active');
952 * toggles the current active state
954 toggleActive : function(e)
956 this.setActive(!this.pressed);
957 this.fireEvent('toggle', this, e, !this.pressed);
960 * get the current active state
961 * @return {boolean} true if it's active
963 isActive : function()
965 return this.el.hasClass('active');
968 * set the text of the first selected button
970 setText : function(str)
972 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
975 * get the text of the first selected button
979 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
982 setWeight : function(str)
984 this.el.removeClass(this.weightClass);
986 var outline = this.outline ? 'outline-' : '';
987 if (str == 'default') {
988 this.el.addClass('btn-default btn-outline-secondary');
991 this.el.addClass('btn-' + outline + str);
1005 * @class Roo.bootstrap.Column
1006 * @extends Roo.bootstrap.Component
1007 * Bootstrap Column class
1008 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1009 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1010 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1011 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1012 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1013 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1014 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1015 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1018 * @cfg {Boolean} hidden (true|false) hide the element
1019 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1020 * @cfg {String} fa (ban|check|...) font awesome icon
1021 * @cfg {Number} fasize (1|2|....) font awsome size
1023 * @cfg {String} icon (info-sign|check|...) glyphicon name
1025 * @cfg {String} html content of column.
1028 * Create a new Column
1029 * @param {Object} config The config object
1032 Roo.bootstrap.Column = function(config){
1033 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1036 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1054 getAutoCreate : function(){
1055 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1063 var sizes = ['xs','sm','md','lg'];
1064 sizes.map(function(size ,ix){
1065 //Roo.log( size + ':' + settings[size]);
1067 if (settings[size+'off'] !== false) {
1068 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1071 if (settings[size] === false) {
1075 if (!settings[size]) { // 0 = hidden
1076 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1078 for (var i = ix; i > -1; i--) {
1079 cfg.cls += ' d-' + sizes[i] + '-none';
1085 cfg.cls += ' col-' + size + '-' + settings[size] + (
1086 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1092 cfg.cls += ' hidden';
1095 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1096 cfg.cls +=' alert alert-' + this.alert;
1100 if (this.html.length) {
1101 cfg.html = this.html;
1105 if (this.fasize > 1) {
1106 fasize = ' fa-' + this.fasize + 'x';
1108 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1113 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1132 * @class Roo.bootstrap.Container
1133 * @extends Roo.bootstrap.Component
1134 * Bootstrap Container class
1135 * @cfg {Boolean} jumbotron is it a jumbotron element
1136 * @cfg {String} html content of element
1137 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1138 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1139 * @cfg {String} header content of header (for panel)
1140 * @cfg {String} footer content of footer (for panel)
1141 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1142 * @cfg {String} tag (header|aside|section) type of HTML tag.
1143 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1144 * @cfg {String} fa font awesome icon
1145 * @cfg {String} icon (info-sign|check|...) glyphicon name
1146 * @cfg {Boolean} hidden (true|false) hide the element
1147 * @cfg {Boolean} expandable (true|false) default false
1148 * @cfg {Boolean} expanded (true|false) default true
1149 * @cfg {String} rheader contet on the right of header
1150 * @cfg {Boolean} clickable (true|false) default false
1154 * Create a new Container
1155 * @param {Object} config The config object
1158 Roo.bootstrap.Container = function(config){
1159 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1165 * After the panel has been expand
1167 * @param {Roo.bootstrap.Container} this
1172 * After the panel has been collapsed
1174 * @param {Roo.bootstrap.Container} this
1179 * When a element is chick
1180 * @param {Roo.bootstrap.Container} this
1181 * @param {Roo.EventObject} e
1187 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1205 getChildContainer : function() {
1211 if (this.panel.length) {
1212 return this.el.select('.panel-body',true).first();
1219 getAutoCreate : function(){
1222 tag : this.tag || 'div',
1226 if (this.jumbotron) {
1227 cfg.cls = 'jumbotron';
1232 // - this is applied by the parent..
1234 // cfg.cls = this.cls + '';
1237 if (this.sticky.length) {
1239 var bd = Roo.get(document.body);
1240 if (!bd.hasClass('bootstrap-sticky')) {
1241 bd.addClass('bootstrap-sticky');
1242 Roo.select('html',true).setStyle('height', '100%');
1245 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1249 if (this.well.length) {
1250 switch (this.well) {
1253 cfg.cls +=' well well-' +this.well;
1262 cfg.cls += ' hidden';
1266 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1267 cfg.cls +=' alert alert-' + this.alert;
1272 if (this.panel.length) {
1273 cfg.cls += ' panel panel-' + this.panel;
1275 if (this.header.length) {
1279 if(this.expandable){
1281 cfg.cls = cfg.cls + ' expandable';
1285 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1293 cls : 'panel-title',
1294 html : (this.expandable ? ' ' : '') + this.header
1298 cls: 'panel-header-right',
1304 cls : 'panel-heading',
1305 style : this.expandable ? 'cursor: pointer' : '',
1313 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1318 if (this.footer.length) {
1320 cls : 'panel-footer',
1329 body.html = this.html || cfg.html;
1330 // prefix with the icons..
1332 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1335 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1340 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1341 cfg.cls = 'container';
1347 initEvents: function()
1349 if(this.expandable){
1350 var headerEl = this.headerEl();
1353 headerEl.on('click', this.onToggleClick, this);
1358 this.el.on('click', this.onClick, this);
1363 onToggleClick : function()
1365 var headerEl = this.headerEl();
1381 if(this.fireEvent('expand', this)) {
1383 this.expanded = true;
1385 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1387 this.el.select('.panel-body',true).first().removeClass('hide');
1389 var toggleEl = this.toggleEl();
1395 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1400 collapse : function()
1402 if(this.fireEvent('collapse', this)) {
1404 this.expanded = false;
1406 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1407 this.el.select('.panel-body',true).first().addClass('hide');
1409 var toggleEl = this.toggleEl();
1415 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1419 toggleEl : function()
1421 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1425 return this.el.select('.panel-heading .fa',true).first();
1428 headerEl : function()
1430 if(!this.el || !this.panel.length || !this.header.length){
1434 return this.el.select('.panel-heading',true).first()
1439 if(!this.el || !this.panel.length){
1443 return this.el.select('.panel-body',true).first()
1446 titleEl : function()
1448 if(!this.el || !this.panel.length || !this.header.length){
1452 return this.el.select('.panel-title',true).first();
1455 setTitle : function(v)
1457 var titleEl = this.titleEl();
1463 titleEl.dom.innerHTML = v;
1466 getTitle : function()
1469 var titleEl = this.titleEl();
1475 return titleEl.dom.innerHTML;
1478 setRightTitle : function(v)
1480 var t = this.el.select('.panel-header-right',true).first();
1486 t.dom.innerHTML = v;
1489 onClick : function(e)
1493 this.fireEvent('click', this, e);
1500 * This is BS4's Card element.. - similar to our containers probably..
1504 * @class Roo.bootstrap.Card
1505 * @extends Roo.bootstrap.Component
1506 * Bootstrap Card class
1509 * possible... may not be implemented..
1510 * @cfg {String} header_image src url of image.
1511 * @cfg {String} header
1512 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1514 * @cfg {String} title
1515 * @cfg {String} subtitle
1516 * @cfg {String} html -- html contents - or just use children..
1517 * @cfg {String} footer
1519 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1521 * @cfg {String} margin (0|1|2|3|4|5|auto)
1522 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1523 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1524 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1525 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1526 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1527 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1529 * @cfg {String} padding (0|1|2|3|4|5)
1530 * @cfg {String} padding_top (0|1|2|3|4|5)
1531 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1532 * @cfg {String} padding_left (0|1|2|3|4|5)
1533 * @cfg {String} padding_right (0|1|2|3|4|5)
1534 * @cfg {String} padding_x (0|1|2|3|4|5)
1535 * @cfg {String} padding_y (0|1|2|3|4|5)
1537 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1538 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1539 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1540 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1541 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1543 * @config {Boolean} dragable if this card can be dragged.
1544 * @config {Boolean} drag_group group for drag
1548 * Create a new Container
1549 * @param {Object} config The config object
1552 Roo.bootstrap.Card = function(config){
1553 Roo.bootstrap.Card.superclass.constructor.call(this, config);
1561 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
1566 margin: '', /// may be better in component?
1599 childContainer : false,
1601 layoutCls : function()
1605 Roo.log(this.margin_bottom.length);
1606 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
1607 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
1609 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
1610 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
1612 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
1613 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
1617 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
1618 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
1619 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
1623 // more generic support?
1631 // Roo.log("Call onRender: " + this.xtype);
1632 /* We are looking at something like this.
1634 <img src="..." class="card-img-top" alt="...">
1635 <div class="card-body">
1636 <h5 class="card-title">Card title</h5>
1637 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
1639 >> this bit is really the body...
1640 <div> << we will ad dthis in hopefully it will not break shit.
1642 ** card text does not actually have any styling...
1644 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
1647 <a href="#" class="card-link">Card link</a>
1650 <div class="card-footer">
1651 <small class="text-muted">Last updated 3 mins ago</small>
1655 getAutoCreate : function(){
1663 if (this.weight.length && this.weight != 'light') {
1664 cfg.cls += ' text-white';
1666 cfg.cls += ' text-dark'; // need as it's nested..
1668 if (this.weight.length) {
1669 cfg.cls += ' bg-' + this.weight;
1672 cfg.cls += this.layoutCls();
1674 if (this.header.length) {
1676 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
1677 cls : 'card-header',
1678 html : this.header // escape?
1681 if (this.header_image.length) {
1684 cls : 'card-img-top',
1685 src: this.header_image // escape?
1696 if (this.title.length) {
1700 src: this.title // escape?
1704 if (this.subtitle.length) {
1708 src: this.subtitle // escape?
1714 cls : 'roo-card-body-ctr'
1717 if (this.html.length) {
1723 // fixme ? handle objects?
1724 if (this.footer.length) {
1727 cls : 'card-footer',
1728 html: this.footer // escape?
1737 getChildContainer : function()
1743 return this.el.select('.roo-card-body-ctr',true).first();
1746 initEvents: function()
1749 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
1750 containerScroll: true,
1751 ddGroup: this.drag_group || 'default_card_drag_group'
1753 this.dragZone.getDragData = this.getDragData.createDelegate(this);
1759 getDragData : function(e) {
1760 var target = this.getEl();
1762 //this.handleSelection(e);
1767 nodes: this.getEl(),
1772 dragData.ddel = target.dom ; // the div element
1773 Roo.log(target.getWidth( ));
1774 dragData.ddel.style.width = target.getWidth() + 'px';
1792 * @class Roo.bootstrap.Img
1793 * @extends Roo.bootstrap.Component
1794 * Bootstrap Img class
1795 * @cfg {Boolean} imgResponsive false | true
1796 * @cfg {String} border rounded | circle | thumbnail
1797 * @cfg {String} src image source
1798 * @cfg {String} alt image alternative text
1799 * @cfg {String} href a tag href
1800 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1801 * @cfg {String} xsUrl xs image source
1802 * @cfg {String} smUrl sm image source
1803 * @cfg {String} mdUrl md image source
1804 * @cfg {String} lgUrl lg image source
1807 * Create a new Input
1808 * @param {Object} config The config object
1811 Roo.bootstrap.Img = function(config){
1812 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1818 * The img click event for the img.
1819 * @param {Roo.EventObject} e
1825 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1827 imgResponsive: true,
1837 getAutoCreate : function()
1839 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1840 return this.createSingleImg();
1845 cls: 'roo-image-responsive-group',
1850 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1852 if(!_this[size + 'Url']){
1858 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1859 html: _this.html || cfg.html,
1860 src: _this[size + 'Url']
1863 img.cls += ' roo-image-responsive-' + size;
1865 var s = ['xs', 'sm', 'md', 'lg'];
1867 s.splice(s.indexOf(size), 1);
1869 Roo.each(s, function(ss){
1870 img.cls += ' hidden-' + ss;
1873 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1874 cfg.cls += ' img-' + _this.border;
1878 cfg.alt = _this.alt;
1891 a.target = _this.target;
1895 cfg.cn.push((_this.href) ? a : img);
1902 createSingleImg : function()
1906 cls: (this.imgResponsive) ? 'img-responsive' : '',
1908 src : 'about:blank' // just incase src get's set to undefined?!?
1911 cfg.html = this.html || cfg.html;
1913 cfg.src = this.src || cfg.src;
1915 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1916 cfg.cls += ' img-' + this.border;
1933 a.target = this.target;
1938 return (this.href) ? a : cfg;
1941 initEvents: function()
1944 this.el.on('click', this.onClick, this);
1949 onClick : function(e)
1951 Roo.log('img onclick');
1952 this.fireEvent('click', this, e);
1955 * Sets the url of the image - used to update it
1956 * @param {String} url the url of the image
1959 setSrc : function(url)
1963 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1964 this.el.dom.src = url;
1968 this.el.select('img', true).first().dom.src = url;
1984 * @class Roo.bootstrap.Link
1985 * @extends Roo.bootstrap.Component
1986 * Bootstrap Link Class
1987 * @cfg {String} alt image alternative text
1988 * @cfg {String} href a tag href
1989 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1990 * @cfg {String} html the content of the link.
1991 * @cfg {String} anchor name for the anchor link
1992 * @cfg {String} fa - favicon
1994 * @cfg {Boolean} preventDefault (true | false) default false
1998 * Create a new Input
1999 * @param {Object} config The config object
2002 Roo.bootstrap.Link = function(config){
2003 Roo.bootstrap.Link.superclass.constructor.call(this, config);
2009 * The img click event for the img.
2010 * @param {Roo.EventObject} e
2016 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
2020 preventDefault: false,
2026 getAutoCreate : function()
2028 var html = this.html || '';
2030 if (this.fa !== false) {
2031 html = '<i class="fa fa-' + this.fa + '"></i>';
2036 // anchor's do not require html/href...
2037 if (this.anchor === false) {
2039 cfg.href = this.href || '#';
2041 cfg.name = this.anchor;
2042 if (this.html !== false || this.fa !== false) {
2045 if (this.href !== false) {
2046 cfg.href = this.href;
2050 if(this.alt !== false){
2055 if(this.target !== false) {
2056 cfg.target = this.target;
2062 initEvents: function() {
2064 if(!this.href || this.preventDefault){
2065 this.el.on('click', this.onClick, this);
2069 onClick : function(e)
2071 if(this.preventDefault){
2074 //Roo.log('img onclick');
2075 this.fireEvent('click', this, e);
2088 * @class Roo.bootstrap.Header
2089 * @extends Roo.bootstrap.Component
2090 * Bootstrap Header class
2091 * @cfg {String} html content of header
2092 * @cfg {Number} level (1|2|3|4|5|6) default 1
2095 * Create a new Header
2096 * @param {Object} config The config object
2100 Roo.bootstrap.Header = function(config){
2101 Roo.bootstrap.Header.superclass.constructor.call(this, config);
2104 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
2112 getAutoCreate : function(){
2117 tag: 'h' + (1 *this.level),
2118 html: this.html || ''
2130 * Ext JS Library 1.1.1
2131 * Copyright(c) 2006-2007, Ext JS, LLC.
2133 * Originally Released Under LGPL - original licence link has changed is not relivant.
2136 * <script type="text/javascript">
2140 * @class Roo.bootstrap.MenuMgr
2141 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
2144 Roo.bootstrap.MenuMgr = function(){
2145 var menus, active, groups = {}, attached = false, lastShow = new Date();
2147 // private - called when first menu is created
2150 active = new Roo.util.MixedCollection();
2151 Roo.get(document).addKeyListener(27, function(){
2152 if(active.length > 0){
2160 if(active && active.length > 0){
2161 var c = active.clone();
2171 if(active.length < 1){
2172 Roo.get(document).un("mouseup", onMouseDown);
2180 var last = active.last();
2181 lastShow = new Date();
2184 Roo.get(document).on("mouseup", onMouseDown);
2189 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
2190 m.parentMenu.activeChild = m;
2191 }else if(last && last.isVisible()){
2192 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
2197 function onBeforeHide(m){
2199 m.activeChild.hide();
2201 if(m.autoHideTimer){
2202 clearTimeout(m.autoHideTimer);
2203 delete m.autoHideTimer;
2208 function onBeforeShow(m){
2209 var pm = m.parentMenu;
2210 if(!pm && !m.allowOtherMenus){
2212 }else if(pm && pm.activeChild && active != m){
2213 pm.activeChild.hide();
2217 // private this should really trigger on mouseup..
2218 function onMouseDown(e){
2219 Roo.log("on Mouse Up");
2221 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
2222 Roo.log("MenuManager hideAll");
2231 function onBeforeCheck(mi, state){
2233 var g = groups[mi.group];
2234 for(var i = 0, l = g.length; i < l; i++){
2236 g[i].setChecked(false);
2245 * Hides all menus that are currently visible
2247 hideAll : function(){
2252 register : function(menu){
2256 menus[menu.id] = menu;
2257 menu.on("beforehide", onBeforeHide);
2258 menu.on("hide", onHide);
2259 menu.on("beforeshow", onBeforeShow);
2260 menu.on("show", onShow);
2262 if(g && menu.events["checkchange"]){
2266 groups[g].push(menu);
2267 menu.on("checkchange", onCheck);
2272 * Returns a {@link Roo.menu.Menu} object
2273 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
2274 * be used to generate and return a new Menu instance.
2276 get : function(menu){
2277 if(typeof menu == "string"){ // menu id
2279 }else if(menu.events){ // menu instance
2282 /*else if(typeof menu.length == 'number'){ // array of menu items?
2283 return new Roo.bootstrap.Menu({items:menu});
2284 }else{ // otherwise, must be a config
2285 return new Roo.bootstrap.Menu(menu);
2292 unregister : function(menu){
2293 delete menus[menu.id];
2294 menu.un("beforehide", onBeforeHide);
2295 menu.un("hide", onHide);
2296 menu.un("beforeshow", onBeforeShow);
2297 menu.un("show", onShow);
2299 if(g && menu.events["checkchange"]){
2300 groups[g].remove(menu);
2301 menu.un("checkchange", onCheck);
2306 registerCheckable : function(menuItem){
2307 var g = menuItem.group;
2312 groups[g].push(menuItem);
2313 menuItem.on("beforecheckchange", onBeforeCheck);
2318 unregisterCheckable : function(menuItem){
2319 var g = menuItem.group;
2321 groups[g].remove(menuItem);
2322 menuItem.un("beforecheckchange", onBeforeCheck);
2334 * @class Roo.bootstrap.Menu
2335 * @extends Roo.bootstrap.Component
2336 * Bootstrap Menu class - container for MenuItems
2337 * @cfg {String} type (dropdown|treeview|submenu) type of menu
2338 * @cfg {bool} hidden if the menu should be hidden when rendered.
2339 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
2340 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
2344 * @param {Object} config The config object
2348 Roo.bootstrap.Menu = function(config){
2349 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2350 if (this.registerMenu && this.type != 'treeview') {
2351 Roo.bootstrap.MenuMgr.register(this);
2358 * Fires before this menu is displayed (return false to block)
2359 * @param {Roo.menu.Menu} this
2364 * Fires before this menu is hidden (return false to block)
2365 * @param {Roo.menu.Menu} this
2370 * Fires after this menu is displayed
2371 * @param {Roo.menu.Menu} this
2376 * Fires after this menu is hidden
2377 * @param {Roo.menu.Menu} this
2382 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2383 * @param {Roo.menu.Menu} this
2384 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2385 * @param {Roo.EventObject} e
2390 * Fires when the mouse is hovering over this menu
2391 * @param {Roo.menu.Menu} this
2392 * @param {Roo.EventObject} e
2393 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2398 * Fires when the mouse exits this menu
2399 * @param {Roo.menu.Menu} this
2400 * @param {Roo.EventObject} e
2401 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2406 * Fires when a menu item contained in this menu is clicked
2407 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2408 * @param {Roo.EventObject} e
2412 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2415 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2419 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2422 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2424 registerMenu : true,
2426 menuItems :false, // stores the menu items..
2436 getChildContainer : function() {
2440 getAutoCreate : function(){
2442 //if (['right'].indexOf(this.align)!==-1) {
2443 // cfg.cn[1].cls += ' pull-right'
2449 cls : 'dropdown-menu' ,
2450 style : 'z-index:1000'
2454 if (this.type === 'submenu') {
2455 cfg.cls = 'submenu active';
2457 if (this.type === 'treeview') {
2458 cfg.cls = 'treeview-menu';
2463 initEvents : function() {
2465 // Roo.log("ADD event");
2466 // Roo.log(this.triggerEl.dom);
2468 this.triggerEl.on('click', this.onTriggerClick, this);
2470 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2473 if (this.triggerEl.hasClass('nav-item')) {
2474 // dropdown toggle on the 'a' in BS4?
2475 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2477 this.triggerEl.addClass('dropdown-toggle');
2480 this.el.on('touchstart' , this.onTouch, this);
2482 this.el.on('click' , this.onClick, this);
2484 this.el.on("mouseover", this.onMouseOver, this);
2485 this.el.on("mouseout", this.onMouseOut, this);
2489 findTargetItem : function(e)
2491 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2495 //Roo.log(t); Roo.log(t.id);
2497 //Roo.log(this.menuitems);
2498 return this.menuitems.get(t.id);
2500 //return this.items.get(t.menuItemId);
2506 onTouch : function(e)
2508 Roo.log("menu.onTouch");
2509 //e.stopEvent(); this make the user popdown broken
2513 onClick : function(e)
2515 Roo.log("menu.onClick");
2517 var t = this.findTargetItem(e);
2518 if(!t || t.isContainer){
2523 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2524 if(t == this.activeItem && t.shouldDeactivate(e)){
2525 this.activeItem.deactivate();
2526 delete this.activeItem;
2530 this.setActiveItem(t, true);
2538 Roo.log('pass click event');
2542 this.fireEvent("click", this, t, e);
2546 if(!t.href.length || t.href == '#'){
2547 (function() { _this.hide(); }).defer(100);
2552 onMouseOver : function(e){
2553 var t = this.findTargetItem(e);
2556 // if(t.canActivate && !t.disabled){
2557 // this.setActiveItem(t, true);
2561 this.fireEvent("mouseover", this, e, t);
2563 isVisible : function(){
2564 return !this.hidden;
2566 onMouseOut : function(e){
2567 var t = this.findTargetItem(e);
2570 // if(t == this.activeItem && t.shouldDeactivate(e)){
2571 // this.activeItem.deactivate();
2572 // delete this.activeItem;
2575 this.fireEvent("mouseout", this, e, t);
2580 * Displays this menu relative to another element
2581 * @param {String/HTMLElement/Roo.Element} element The element to align to
2582 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2583 * the element (defaults to this.defaultAlign)
2584 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2586 show : function(el, pos, parentMenu)
2588 if (false === this.fireEvent("beforeshow", this)) {
2589 Roo.log("show canceled");
2592 this.parentMenu = parentMenu;
2597 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2600 * Displays this menu at a specific xy position
2601 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2602 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2604 showAt : function(xy, parentMenu, /* private: */_e){
2605 this.parentMenu = parentMenu;
2610 this.fireEvent("beforeshow", this);
2611 //xy = this.el.adjustForConstraints(xy);
2615 this.hideMenuItems();
2616 this.hidden = false;
2617 this.triggerEl.addClass('open');
2618 this.el.addClass('show');
2620 // reassign x when hitting right
2621 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2622 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2625 // reassign y when hitting bottom
2626 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2627 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2630 // but the list may align on trigger left or trigger top... should it be a properity?
2632 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2637 this.fireEvent("show", this);
2643 this.doFocus.defer(50, this);
2647 doFocus : function(){
2649 this.focusEl.focus();
2654 * Hides this menu and optionally all parent menus
2655 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2657 hide : function(deep)
2659 if (false === this.fireEvent("beforehide", this)) {
2660 Roo.log("hide canceled");
2663 this.hideMenuItems();
2664 if(this.el && this.isVisible()){
2666 if(this.activeItem){
2667 this.activeItem.deactivate();
2668 this.activeItem = null;
2670 this.triggerEl.removeClass('open');;
2671 this.el.removeClass('show');
2673 this.fireEvent("hide", this);
2675 if(deep === true && this.parentMenu){
2676 this.parentMenu.hide(true);
2680 onTriggerClick : function(e)
2682 Roo.log('trigger click');
2684 var target = e.getTarget();
2686 Roo.log(target.nodeName.toLowerCase());
2688 if(target.nodeName.toLowerCase() === 'i'){
2694 onTriggerPress : function(e)
2696 Roo.log('trigger press');
2697 //Roo.log(e.getTarget());
2698 // Roo.log(this.triggerEl.dom);
2700 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2701 var pel = Roo.get(e.getTarget());
2702 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2703 Roo.log('is treeview or dropdown?');
2707 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2711 if (this.isVisible()) {
2716 this.show(this.triggerEl, '?', false);
2719 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2726 hideMenuItems : function()
2728 Roo.log("hide Menu Items");
2733 this.el.select('.open',true).each(function(aa) {
2735 aa.removeClass('open');
2739 addxtypeChild : function (tree, cntr) {
2740 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2742 this.menuitems.add(comp);
2754 this.getEl().dom.innerHTML = '';
2755 this.menuitems.clear();
2769 * @class Roo.bootstrap.MenuItem
2770 * @extends Roo.bootstrap.Component
2771 * Bootstrap MenuItem class
2772 * @cfg {String} html the menu label
2773 * @cfg {String} href the link
2774 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2775 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2776 * @cfg {Boolean} active used on sidebars to highlight active itesm
2777 * @cfg {String} fa favicon to show on left of menu item.
2778 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2782 * Create a new MenuItem
2783 * @param {Object} config The config object
2787 Roo.bootstrap.MenuItem = function(config){
2788 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2793 * The raw click event for the entire grid.
2794 * @param {Roo.bootstrap.MenuItem} this
2795 * @param {Roo.EventObject} e
2801 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2805 preventDefault: false,
2806 isContainer : false,
2810 getAutoCreate : function(){
2812 if(this.isContainer){
2815 cls: 'dropdown-menu-item '
2825 cls : 'dropdown-item',
2830 if (this.fa !== false) {
2833 cls : 'fa fa-' + this.fa
2842 cls: 'dropdown-menu-item',
2845 if (this.parent().type == 'treeview') {
2846 cfg.cls = 'treeview-menu';
2849 cfg.cls += ' active';
2854 anc.href = this.href || cfg.cn[0].href ;
2855 ctag.html = this.html || cfg.cn[0].html ;
2859 initEvents: function()
2861 if (this.parent().type == 'treeview') {
2862 this.el.select('a').on('click', this.onClick, this);
2866 this.menu.parentType = this.xtype;
2867 this.menu.triggerEl = this.el;
2868 this.menu = this.addxtype(Roo.apply({}, this.menu));
2872 onClick : function(e)
2874 Roo.log('item on click ');
2876 if(this.preventDefault){
2879 //this.parent().hideMenuItems();
2881 this.fireEvent('click', this, e);
2900 * @class Roo.bootstrap.MenuSeparator
2901 * @extends Roo.bootstrap.Component
2902 * Bootstrap MenuSeparator class
2905 * Create a new MenuItem
2906 * @param {Object} config The config object
2910 Roo.bootstrap.MenuSeparator = function(config){
2911 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2914 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2916 getAutoCreate : function(){
2935 * @class Roo.bootstrap.Modal
2936 * @extends Roo.bootstrap.Component
2937 * Bootstrap Modal class
2938 * @cfg {String} title Title of dialog
2939 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2940 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2941 * @cfg {Boolean} specificTitle default false
2942 * @cfg {Array} buttons Array of buttons or standard button set..
2943 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2944 * @cfg {Boolean} animate default true
2945 * @cfg {Boolean} allow_close default true
2946 * @cfg {Boolean} fitwindow default false
2947 * @cfg {Number} width fixed width - usefull for chrome extension only really.
2948 * @cfg {Number} height fixed height - usefull for chrome extension only really.
2949 * @cfg {String} size (sm|lg) default empty
2950 * @cfg {Number} max_width set the max width of modal
2954 * Create a new Modal Dialog
2955 * @param {Object} config The config object
2958 Roo.bootstrap.Modal = function(config){
2959 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2964 * The raw btnclick event for the button
2965 * @param {Roo.EventObject} e
2970 * Fire when dialog resize
2971 * @param {Roo.bootstrap.Modal} this
2972 * @param {Roo.EventObject} e
2976 this.buttons = this.buttons || [];
2979 this.tmpl = Roo.factory(this.tmpl);
2984 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2986 title : 'test dialog',
2996 specificTitle: false,
2998 buttonPosition: 'right',
3021 onRender : function(ct, position)
3023 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3026 var cfg = Roo.apply({}, this.getAutoCreate());
3029 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3031 //if (!cfg.name.length) {
3035 cfg.cls += ' ' + this.cls;
3038 cfg.style = this.style;
3040 this.el = Roo.get(document.body).createChild(cfg, position);
3042 //var type = this.el.dom.type;
3045 if(this.tabIndex !== undefined){
3046 this.el.dom.setAttribute('tabIndex', this.tabIndex);
3049 this.dialogEl = this.el.select('.modal-dialog',true).first();
3050 this.bodyEl = this.el.select('.modal-body',true).first();
3051 this.closeEl = this.el.select('.modal-header .close', true).first();
3052 this.headerEl = this.el.select('.modal-header',true).first();
3053 this.titleEl = this.el.select('.modal-title',true).first();
3054 this.footerEl = this.el.select('.modal-footer',true).first();
3056 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3058 //this.el.addClass("x-dlg-modal");
3060 if (this.buttons.length) {
3061 Roo.each(this.buttons, function(bb) {
3062 var b = Roo.apply({}, bb);
3063 b.xns = b.xns || Roo.bootstrap;
3064 b.xtype = b.xtype || 'Button';
3065 if (typeof(b.listeners) == 'undefined') {
3066 b.listeners = { click : this.onButtonClick.createDelegate(this) };
3069 var btn = Roo.factory(b);
3071 btn.render(this.getButtonContainer());
3075 // render the children.
3078 if(typeof(this.items) != 'undefined'){
3079 var items = this.items;
3082 for(var i =0;i < items.length;i++) {
3083 nitems.push(this.addxtype(Roo.apply({}, items[i])));
3087 this.items = nitems;
3089 // where are these used - they used to be body/close/footer
3093 //this.el.addClass([this.fieldClass, this.cls]);
3097 getAutoCreate : function()
3099 // we will default to modal-body-overflow - might need to remove or make optional later.
3101 cls : 'modal-body enable-modal-body-overflow ',
3102 html : this.html || ''
3107 cls : 'modal-title',
3111 if(this.specificTitle){
3117 if (this.allow_close && Roo.bootstrap.version == 3) {
3127 if (this.allow_close && Roo.bootstrap.version == 4) {
3137 if(this.size.length){
3138 size = 'modal-' + this.size;
3141 var footer = Roo.bootstrap.version == 3 ?
3143 cls : 'modal-footer',
3147 cls: 'btn-' + this.buttonPosition
3152 { // BS4 uses mr-auto on left buttons....
3153 cls : 'modal-footer'
3164 cls: "modal-dialog " + size,
3167 cls : "modal-content",
3170 cls : 'modal-header',
3185 modal.cls += ' fade';
3191 getChildContainer : function() {
3196 getButtonContainer : function() {
3198 return Roo.bootstrap.version == 4 ?
3199 this.el.select('.modal-footer',true).first()
3200 : this.el.select('.modal-footer div',true).first();
3203 initEvents : function()
3205 if (this.allow_close) {
3206 this.closeEl.on('click', this.hide, this);
3208 Roo.EventManager.onWindowResize(this.resize, this, true);
3216 this.maskEl.setSize(
3217 Roo.lib.Dom.getViewWidth(true),
3218 Roo.lib.Dom.getViewHeight(true)
3221 if (this.fitwindow) {
3225 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
3226 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
3231 if(this.max_width !== 0) {
3233 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
3236 this.setSize(w, this.height);
3240 if(this.max_height) {
3241 this.setSize(w,Math.min(
3243 Roo.lib.Dom.getViewportHeight(true) - 60
3249 if(!this.fit_content) {
3250 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
3254 this.setSize(w, Math.min(
3256 this.headerEl.getHeight() +
3257 this.footerEl.getHeight() +
3258 this.getChildHeight(this.bodyEl.dom.childNodes),
3259 Roo.lib.Dom.getViewportHeight(true) - 60)
3265 setSize : function(w,h)
3276 if (!this.rendered) {
3280 //this.el.setStyle('display', 'block');
3281 this.el.removeClass('hideing');
3282 this.el.dom.style.display='block';
3284 Roo.get(document.body).addClass('modal-open');
3286 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
3289 this.el.addClass('show');
3290 this.el.addClass('in');
3293 this.el.addClass('show');
3294 this.el.addClass('in');
3297 // not sure how we can show data in here..
3299 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3302 Roo.get(document.body).addClass("x-body-masked");
3304 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3305 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3306 this.maskEl.dom.style.display = 'block';
3307 this.maskEl.addClass('show');
3312 this.fireEvent('show', this);
3314 // set zindex here - otherwise it appears to be ignored...
3315 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3318 this.items.forEach( function(e) {
3319 e.layout ? e.layout() : false;
3327 if(this.fireEvent("beforehide", this) !== false){
3329 this.maskEl.removeClass('show');
3331 this.maskEl.dom.style.display = '';
3332 Roo.get(document.body).removeClass("x-body-masked");
3333 this.el.removeClass('in');
3334 this.el.select('.modal-dialog', true).first().setStyle('transform','');
3336 if(this.animate){ // why
3337 this.el.addClass('hideing');
3338 this.el.removeClass('show');
3340 if (!this.el.hasClass('hideing')) {
3341 return; // it's been shown again...
3344 this.el.dom.style.display='';
3346 Roo.get(document.body).removeClass('modal-open');
3347 this.el.removeClass('hideing');
3351 this.el.removeClass('show');
3352 this.el.dom.style.display='';
3353 Roo.get(document.body).removeClass('modal-open');
3356 this.fireEvent('hide', this);
3359 isVisible : function()
3362 return this.el.hasClass('show') && !this.el.hasClass('hideing');
3366 addButton : function(str, cb)
3370 var b = Roo.apply({}, { html : str } );
3371 b.xns = b.xns || Roo.bootstrap;
3372 b.xtype = b.xtype || 'Button';
3373 if (typeof(b.listeners) == 'undefined') {
3374 b.listeners = { click : cb.createDelegate(this) };
3377 var btn = Roo.factory(b);
3379 btn.render(this.getButtonContainer());
3385 setDefaultButton : function(btn)
3387 //this.el.select('.modal-footer').()
3390 resizeTo: function(w,h)
3392 this.dialogEl.setWidth(w);
3394 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
3396 this.bodyEl.setHeight(h - diff);
3398 this.fireEvent('resize', this);
3401 setContentSize : function(w, h)
3405 onButtonClick: function(btn,e)
3408 this.fireEvent('btnclick', btn.name, e);
3411 * Set the title of the Dialog
3412 * @param {String} str new Title
3414 setTitle: function(str) {
3415 this.titleEl.dom.innerHTML = str;
3418 * Set the body of the Dialog
3419 * @param {String} str new Title
3421 setBody: function(str) {
3422 this.bodyEl.dom.innerHTML = str;
3425 * Set the body of the Dialog using the template
3426 * @param {Obj} data - apply this data to the template and replace the body contents.
3428 applyBody: function(obj)
3431 Roo.log("Error - using apply Body without a template");
3434 this.tmpl.overwrite(this.bodyEl, obj);
3437 getChildHeight : function(child_nodes)
3441 child_nodes.length == 0
3446 var child_height = 0;
3448 for(var i = 0; i < child_nodes.length; i++) {
3451 * for modal with tabs...
3452 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3454 var layout_childs = child_nodes[i].childNodes;
3456 for(var j = 0; j < layout_childs.length; j++) {
3458 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3460 var layout_body_childs = layout_childs[j].childNodes;
3462 for(var k = 0; k < layout_body_childs.length; k++) {
3464 if(layout_body_childs[k].classList.contains('navbar')) {
3465 child_height += layout_body_childs[k].offsetHeight;
3469 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3471 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3473 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3475 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3476 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3491 child_height += child_nodes[i].offsetHeight;
3492 // Roo.log(child_nodes[i].offsetHeight);
3495 return child_height;
3501 Roo.apply(Roo.bootstrap.Modal, {
3503 * Button config that displays a single OK button
3512 * Button config that displays Yes and No buttons
3528 * Button config that displays OK and Cancel buttons
3543 * Button config that displays Yes, No and Cancel buttons
3567 * messagebox - can be used as a replace
3571 * @class Roo.MessageBox
3572 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3576 Roo.Msg.alert('Status', 'Changes saved successfully.');
3578 // Prompt for user data:
3579 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3581 // process text value...
3585 // Show a dialog using config options:
3587 title:'Save Changes?',
3588 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3589 buttons: Roo.Msg.YESNOCANCEL,
3596 Roo.bootstrap.MessageBox = function(){
3597 var dlg, opt, mask, waitTimer;
3598 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3599 var buttons, activeTextEl, bwidth;
3603 var handleButton = function(button){
3605 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3609 var handleHide = function(){
3611 dlg.el.removeClass(opt.cls);
3614 // Roo.TaskMgr.stop(waitTimer);
3615 // waitTimer = null;
3620 var updateButtons = function(b){
3623 buttons["ok"].hide();
3624 buttons["cancel"].hide();
3625 buttons["yes"].hide();
3626 buttons["no"].hide();
3627 dlg.footerEl.hide();
3631 dlg.footerEl.show();
3632 for(var k in buttons){
3633 if(typeof buttons[k] != "function"){
3636 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3637 width += buttons[k].el.getWidth()+15;
3647 var handleEsc = function(d, k, e){
3648 if(opt && opt.closable !== false){
3658 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3659 * @return {Roo.BasicDialog} The BasicDialog element
3661 getDialog : function(){
3663 dlg = new Roo.bootstrap.Modal( {
3666 //constraintoviewport:false,
3668 //collapsible : false,
3673 //buttonAlign:"center",
3674 closeClick : function(){
3675 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3678 handleButton("cancel");
3683 dlg.on("hide", handleHide);
3685 //dlg.addKeyListener(27, handleEsc);
3687 this.buttons = buttons;
3688 var bt = this.buttonText;
3689 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3690 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3691 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3692 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3694 bodyEl = dlg.bodyEl.createChild({
3696 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3697 '<textarea class="roo-mb-textarea"></textarea>' +
3698 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3700 msgEl = bodyEl.dom.firstChild;
3701 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3702 textboxEl.enableDisplayMode();
3703 textboxEl.addKeyListener([10,13], function(){
3704 if(dlg.isVisible() && opt && opt.buttons){
3707 }else if(opt.buttons.yes){
3708 handleButton("yes");
3712 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3713 textareaEl.enableDisplayMode();
3714 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3715 progressEl.enableDisplayMode();
3717 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3718 var pf = progressEl.dom.firstChild;
3720 pp = Roo.get(pf.firstChild);
3721 pp.setHeight(pf.offsetHeight);
3729 * Updates the message box body text
3730 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3731 * the XHTML-compliant non-breaking space character '&#160;')
3732 * @return {Roo.MessageBox} This message box
3734 updateText : function(text)
3736 if(!dlg.isVisible() && !opt.width){
3737 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3738 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3740 msgEl.innerHTML = text || ' ';
3742 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3743 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3745 Math.min(opt.width || cw , this.maxWidth),
3746 Math.max(opt.minWidth || this.minWidth, bwidth)
3749 activeTextEl.setWidth(w);
3751 if(dlg.isVisible()){
3752 dlg.fixedcenter = false;
3754 // to big, make it scroll. = But as usual stupid IE does not support
3757 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3758 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3759 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3761 bodyEl.dom.style.height = '';
3762 bodyEl.dom.style.overflowY = '';
3765 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3767 bodyEl.dom.style.overflowX = '';
3770 dlg.setContentSize(w, bodyEl.getHeight());
3771 if(dlg.isVisible()){
3772 dlg.fixedcenter = true;
3778 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3779 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3780 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3781 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3782 * @return {Roo.MessageBox} This message box
3784 updateProgress : function(value, text){
3786 this.updateText(text);
3789 if (pp) { // weird bug on my firefox - for some reason this is not defined
3790 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3791 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3797 * Returns true if the message box is currently displayed
3798 * @return {Boolean} True if the message box is visible, else false
3800 isVisible : function(){
3801 return dlg && dlg.isVisible();
3805 * Hides the message box if it is displayed
3808 if(this.isVisible()){
3814 * Displays a new message box, or reinitializes an existing message box, based on the config options
3815 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3816 * The following config object properties are supported:
3818 Property Type Description
3819 ---------- --------------- ------------------------------------------------------------------------------------
3820 animEl String/Element An id or Element from which the message box should animate as it opens and
3821 closes (defaults to undefined)
3822 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3823 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3824 closable Boolean False to hide the top-right close button (defaults to true). Note that
3825 progress and wait dialogs will ignore this property and always hide the
3826 close button as they can only be closed programmatically.
3827 cls String A custom CSS class to apply to the message box element
3828 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3829 displayed (defaults to 75)
3830 fn Function A callback function to execute after closing the dialog. The arguments to the
3831 function will be btn (the name of the button that was clicked, if applicable,
3832 e.g. "ok"), and text (the value of the active text field, if applicable).
3833 Progress and wait dialogs will ignore this option since they do not respond to
3834 user actions and can only be closed programmatically, so any required function
3835 should be called by the same code after it closes the dialog.
3836 icon String A CSS class that provides a background image to be used as an icon for
3837 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3838 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3839 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3840 modal Boolean False to allow user interaction with the page while the message box is
3841 displayed (defaults to true)
3842 msg String A string that will replace the existing message box body text (defaults
3843 to the XHTML-compliant non-breaking space character ' ')
3844 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3845 progress Boolean True to display a progress bar (defaults to false)
3846 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3847 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3848 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3849 title String The title text
3850 value String The string value to set into the active textbox element if displayed
3851 wait Boolean True to display a progress bar (defaults to false)
3852 width Number The width of the dialog in pixels
3859 msg: 'Please enter your address:',
3861 buttons: Roo.MessageBox.OKCANCEL,
3864 animEl: 'addAddressBtn'
3867 * @param {Object} config Configuration options
3868 * @return {Roo.MessageBox} This message box
3870 show : function(options)
3873 // this causes nightmares if you show one dialog after another
3874 // especially on callbacks..
3876 if(this.isVisible()){
3879 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3880 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3881 Roo.log("New Dialog Message:" + options.msg )
3882 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3883 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3886 var d = this.getDialog();
3888 d.setTitle(opt.title || " ");
3889 d.closeEl.setDisplayed(opt.closable !== false);
3890 activeTextEl = textboxEl;
3891 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3896 textareaEl.setHeight(typeof opt.multiline == "number" ?
3897 opt.multiline : this.defaultTextHeight);
3898 activeTextEl = textareaEl;
3907 progressEl.setDisplayed(opt.progress === true);
3909 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3911 this.updateProgress(0);
3912 activeTextEl.dom.value = opt.value || "";
3914 dlg.setDefaultButton(activeTextEl);
3916 var bs = opt.buttons;
3920 }else if(bs && bs.yes){
3921 db = buttons["yes"];
3923 dlg.setDefaultButton(db);
3925 bwidth = updateButtons(opt.buttons);
3926 this.updateText(opt.msg);
3928 d.el.addClass(opt.cls);
3930 d.proxyDrag = opt.proxyDrag === true;
3931 d.modal = opt.modal !== false;
3932 d.mask = opt.modal !== false ? mask : false;
3934 // force it to the end of the z-index stack so it gets a cursor in FF
3935 document.body.appendChild(dlg.el.dom);
3936 d.animateTarget = null;
3937 d.show(options.animEl);
3943 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3944 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3945 * and closing the message box when the process is complete.
3946 * @param {String} title The title bar text
3947 * @param {String} msg The message box body text
3948 * @return {Roo.MessageBox} This message box
3950 progress : function(title, msg){
3957 minWidth: this.minProgressWidth,
3964 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3965 * If a callback function is passed it will be called after the user clicks the button, and the
3966 * id of the button that was clicked will be passed as the only parameter to the callback
3967 * (could also be the top-right close button).
3968 * @param {String} title The title bar text
3969 * @param {String} msg The message box body text
3970 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3971 * @param {Object} scope (optional) The scope of the callback function
3972 * @return {Roo.MessageBox} This message box
3974 alert : function(title, msg, fn, scope)
3989 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3990 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3991 * You are responsible for closing the message box when the process is complete.
3992 * @param {String} msg The message box body text
3993 * @param {String} title (optional) The title bar text
3994 * @return {Roo.MessageBox} This message box
3996 wait : function(msg, title){
4007 waitTimer = Roo.TaskMgr.start({
4009 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
4017 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
4018 * If a callback function is passed it will be called after the user clicks either button, and the id of the
4019 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
4020 * @param {String} title The title bar text
4021 * @param {String} msg The message box body text
4022 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4023 * @param {Object} scope (optional) The scope of the callback function
4024 * @return {Roo.MessageBox} This message box
4026 confirm : function(title, msg, fn, scope){
4030 buttons: this.YESNO,
4039 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
4040 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
4041 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
4042 * (could also be the top-right close button) and the text that was entered will be passed as the two
4043 * parameters to the callback.
4044 * @param {String} title The title bar text
4045 * @param {String} msg The message box body text
4046 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4047 * @param {Object} scope (optional) The scope of the callback function
4048 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
4049 * property, or the height in pixels to create the textbox (defaults to false / single-line)
4050 * @return {Roo.MessageBox} This message box
4052 prompt : function(title, msg, fn, scope, multiline){
4056 buttons: this.OKCANCEL,
4061 multiline: multiline,
4068 * Button config that displays a single OK button
4073 * Button config that displays Yes and No buttons
4076 YESNO : {yes:true, no:true},
4078 * Button config that displays OK and Cancel buttons
4081 OKCANCEL : {ok:true, cancel:true},
4083 * Button config that displays Yes, No and Cancel buttons
4086 YESNOCANCEL : {yes:true, no:true, cancel:true},
4089 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
4092 defaultTextHeight : 75,
4094 * The maximum width in pixels of the message box (defaults to 600)
4099 * The minimum width in pixels of the message box (defaults to 100)
4104 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
4105 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
4108 minProgressWidth : 250,
4110 * An object containing the default button text strings that can be overriden for localized language support.
4111 * Supported properties are: ok, cancel, yes and no.
4112 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
4125 * Shorthand for {@link Roo.MessageBox}
4127 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
4128 Roo.Msg = Roo.Msg || Roo.MessageBox;
4137 * @class Roo.bootstrap.Navbar
4138 * @extends Roo.bootstrap.Component
4139 * Bootstrap Navbar class
4142 * Create a new Navbar
4143 * @param {Object} config The config object
4147 Roo.bootstrap.Navbar = function(config){
4148 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
4152 * @event beforetoggle
4153 * Fire before toggle the menu
4154 * @param {Roo.EventObject} e
4156 "beforetoggle" : true
4160 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
4169 getAutoCreate : function(){
4172 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
4176 initEvents :function ()
4178 //Roo.log(this.el.select('.navbar-toggle',true));
4179 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
4186 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
4188 var size = this.el.getSize();
4189 this.maskEl.setSize(size.width, size.height);
4190 this.maskEl.enableDisplayMode("block");
4199 getChildContainer : function()
4201 if (this.el && this.el.select('.collapse').getCount()) {
4202 return this.el.select('.collapse',true).first();
4217 onToggle : function()
4220 if(this.fireEvent('beforetoggle', this) === false){
4223 var ce = this.el.select('.navbar-collapse',true).first();
4225 if (!ce.hasClass('show')) {
4235 * Expand the navbar pulldown
4237 expand : function ()
4240 var ce = this.el.select('.navbar-collapse',true).first();
4241 if (ce.hasClass('collapsing')) {
4244 ce.dom.style.height = '';
4246 ce.addClass('in'); // old...
4247 ce.removeClass('collapse');
4248 ce.addClass('show');
4249 var h = ce.getHeight();
4251 ce.removeClass('show');
4252 // at this point we should be able to see it..
4253 ce.addClass('collapsing');
4255 ce.setHeight(0); // resize it ...
4256 ce.on('transitionend', function() {
4257 //Roo.log('done transition');
4258 ce.removeClass('collapsing');
4259 ce.addClass('show');
4260 ce.removeClass('collapse');
4262 ce.dom.style.height = '';
4263 }, this, { single: true} );
4265 ce.dom.scrollTop = 0;
4268 * Collapse the navbar pulldown
4270 collapse : function()
4272 var ce = this.el.select('.navbar-collapse',true).first();
4274 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
4275 // it's collapsed or collapsing..
4278 ce.removeClass('in'); // old...
4279 ce.setHeight(ce.getHeight());
4280 ce.removeClass('show');
4281 ce.addClass('collapsing');
4283 ce.on('transitionend', function() {
4284 ce.dom.style.height = '';
4285 ce.removeClass('collapsing');
4286 ce.addClass('collapse');
4287 }, this, { single: true} );
4307 * @class Roo.bootstrap.NavSimplebar
4308 * @extends Roo.bootstrap.Navbar
4309 * Bootstrap Sidebar class
4311 * @cfg {Boolean} inverse is inverted color
4313 * @cfg {String} type (nav | pills | tabs)
4314 * @cfg {Boolean} arrangement stacked | justified
4315 * @cfg {String} align (left | right) alignment
4317 * @cfg {Boolean} main (true|false) main nav bar? default false
4318 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4320 * @cfg {String} tag (header|footer|nav|div) default is nav
4322 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4326 * Create a new Sidebar
4327 * @param {Object} config The config object
4331 Roo.bootstrap.NavSimplebar = function(config){
4332 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4335 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
4351 getAutoCreate : function(){
4355 tag : this.tag || 'div',
4356 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
4358 if (['light','white'].indexOf(this.weight) > -1) {
4359 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4361 cfg.cls += ' bg-' + this.weight;
4364 cfg.cls += ' navbar-inverse';
4368 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4370 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4379 cls: 'nav nav-' + this.xtype,
4385 this.type = this.type || 'nav';
4386 if (['tabs','pills'].indexOf(this.type) != -1) {
4387 cfg.cn[0].cls += ' nav-' + this.type
4391 if (this.type!=='nav') {
4392 Roo.log('nav type must be nav/tabs/pills')
4394 cfg.cn[0].cls += ' navbar-nav'
4400 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4401 cfg.cn[0].cls += ' nav-' + this.arrangement;
4405 if (this.align === 'right') {
4406 cfg.cn[0].cls += ' navbar-right';
4431 * navbar-expand-md fixed-top
4435 * @class Roo.bootstrap.NavHeaderbar
4436 * @extends Roo.bootstrap.NavSimplebar
4437 * Bootstrap Sidebar class
4439 * @cfg {String} brand what is brand
4440 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4441 * @cfg {String} brand_href href of the brand
4442 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4443 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4444 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4445 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4448 * Create a new Sidebar
4449 * @param {Object} config The config object
4453 Roo.bootstrap.NavHeaderbar = function(config){
4454 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4458 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4465 desktopCenter : false,
4468 getAutoCreate : function(){
4471 tag: this.nav || 'nav',
4472 cls: 'navbar navbar-expand-md',
4478 if (this.desktopCenter) {
4479 cn.push({cls : 'container', cn : []});
4487 cls: 'navbar-toggle navbar-toggler',
4488 'data-toggle': 'collapse',
4493 html: 'Toggle navigation'
4497 cls: 'icon-bar navbar-toggler-icon'
4510 cn.push( Roo.bootstrap.version == 4 ? btn : {
4512 cls: 'navbar-header',
4521 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
4525 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4527 if (['light','white'].indexOf(this.weight) > -1) {
4528 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4530 cfg.cls += ' bg-' + this.weight;
4533 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4534 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4536 // tag can override this..
4538 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4541 if (this.brand !== '') {
4542 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4543 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4545 href: this.brand_href ? this.brand_href : '#',
4546 cls: 'navbar-brand',
4554 cfg.cls += ' main-nav';
4562 getHeaderChildContainer : function()
4564 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4565 return this.el.select('.navbar-header',true).first();
4568 return this.getChildContainer();
4571 getChildContainer : function()
4574 return this.el.select('.roo-navbar-collapse',true).first();
4579 initEvents : function()
4581 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4583 if (this.autohide) {
4588 Roo.get(document).on('scroll',function(e) {
4589 var ns = Roo.get(document).getScroll().top;
4590 var os = prevScroll;
4594 ft.removeClass('slideDown');
4595 ft.addClass('slideUp');
4598 ft.removeClass('slideUp');
4599 ft.addClass('slideDown');
4620 * @class Roo.bootstrap.NavSidebar
4621 * @extends Roo.bootstrap.Navbar
4622 * Bootstrap Sidebar class
4625 * Create a new Sidebar
4626 * @param {Object} config The config object
4630 Roo.bootstrap.NavSidebar = function(config){
4631 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4634 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4636 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4638 getAutoCreate : function(){
4643 cls: 'sidebar sidebar-nav'
4665 * @class Roo.bootstrap.NavGroup
4666 * @extends Roo.bootstrap.Component
4667 * Bootstrap NavGroup class
4668 * @cfg {String} align (left|right)
4669 * @cfg {Boolean} inverse
4670 * @cfg {String} type (nav|pills|tab) default nav
4671 * @cfg {String} navId - reference Id for navbar.
4675 * Create a new nav group
4676 * @param {Object} config The config object
4679 Roo.bootstrap.NavGroup = function(config){
4680 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4683 Roo.bootstrap.NavGroup.register(this);
4687 * Fires when the active item changes
4688 * @param {Roo.bootstrap.NavGroup} this
4689 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4690 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4697 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4708 getAutoCreate : function()
4710 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4716 if (Roo.bootstrap.version == 4) {
4717 if (['tabs','pills'].indexOf(this.type) != -1) {
4718 cfg.cls += ' nav-' + this.type;
4720 // trying to remove so header bar can right align top?
4721 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
4722 // do not use on header bar...
4723 cfg.cls += ' navbar-nav';
4728 if (['tabs','pills'].indexOf(this.type) != -1) {
4729 cfg.cls += ' nav-' + this.type
4731 if (this.type !== 'nav') {
4732 Roo.log('nav type must be nav/tabs/pills')
4734 cfg.cls += ' navbar-nav'
4738 if (this.parent() && this.parent().sidebar) {
4741 cls: 'dashboard-menu sidebar-menu'
4747 if (this.form === true) {
4750 cls: 'navbar-form form-inline'
4752 //nav navbar-right ml-md-auto
4753 if (this.align === 'right') {
4754 cfg.cls += ' navbar-right ml-md-auto';
4756 cfg.cls += ' navbar-left';
4760 if (this.align === 'right') {
4761 cfg.cls += ' navbar-right ml-md-auto';
4763 cfg.cls += ' mr-auto';
4767 cfg.cls += ' navbar-inverse';
4775 * sets the active Navigation item
4776 * @param {Roo.bootstrap.NavItem} the new current navitem
4778 setActiveItem : function(item)
4781 Roo.each(this.navItems, function(v){
4786 v.setActive(false, true);
4793 item.setActive(true, true);
4794 this.fireEvent('changed', this, item, prev);
4799 * gets the active Navigation item
4800 * @return {Roo.bootstrap.NavItem} the current navitem
4802 getActive : function()
4806 Roo.each(this.navItems, function(v){
4817 indexOfNav : function()
4821 Roo.each(this.navItems, function(v,i){
4832 * adds a Navigation item
4833 * @param {Roo.bootstrap.NavItem} the navitem to add
4835 addItem : function(cfg)
4837 if (this.form && Roo.bootstrap.version == 4) {
4840 var cn = new Roo.bootstrap.NavItem(cfg);
4842 cn.parentId = this.id;
4843 cn.onRender(this.el, null);
4847 * register a Navigation item
4848 * @param {Roo.bootstrap.NavItem} the navitem to add
4850 register : function(item)
4852 this.navItems.push( item);
4853 item.navId = this.navId;
4858 * clear all the Navigation item
4861 clearAll : function()
4864 this.el.dom.innerHTML = '';
4867 getNavItem: function(tabId)
4870 Roo.each(this.navItems, function(e) {
4871 if (e.tabId == tabId) {
4881 setActiveNext : function()
4883 var i = this.indexOfNav(this.getActive());
4884 if (i > this.navItems.length) {
4887 this.setActiveItem(this.navItems[i+1]);
4889 setActivePrev : function()
4891 var i = this.indexOfNav(this.getActive());
4895 this.setActiveItem(this.navItems[i-1]);
4897 clearWasActive : function(except) {
4898 Roo.each(this.navItems, function(e) {
4899 if (e.tabId != except.tabId && e.was_active) {
4900 e.was_active = false;
4907 getWasActive : function ()
4910 Roo.each(this.navItems, function(e) {
4925 Roo.apply(Roo.bootstrap.NavGroup, {
4929 * register a Navigation Group
4930 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4932 register : function(navgrp)
4934 this.groups[navgrp.navId] = navgrp;
4938 * fetch a Navigation Group based on the navigation ID
4939 * @param {string} the navgroup to add
4940 * @returns {Roo.bootstrap.NavGroup} the navgroup
4942 get: function(navId) {
4943 if (typeof(this.groups[navId]) == 'undefined') {
4945 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4947 return this.groups[navId] ;
4962 * @class Roo.bootstrap.NavItem
4963 * @extends Roo.bootstrap.Component
4964 * Bootstrap Navbar.NavItem class
4965 * @cfg {String} href link to
4966 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4968 * @cfg {String} html content of button
4969 * @cfg {String} badge text inside badge
4970 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4971 * @cfg {String} glyphicon DEPRICATED - use fa
4972 * @cfg {String} icon DEPRICATED - use fa
4973 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4974 * @cfg {Boolean} active Is item active
4975 * @cfg {Boolean} disabled Is item disabled
4977 * @cfg {Boolean} preventDefault (true | false) default false
4978 * @cfg {String} tabId the tab that this item activates.
4979 * @cfg {String} tagtype (a|span) render as a href or span?
4980 * @cfg {Boolean} animateRef (true|false) link to element default false
4983 * Create a new Navbar Item
4984 * @param {Object} config The config object
4986 Roo.bootstrap.NavItem = function(config){
4987 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4992 * The raw click event for the entire grid.
4993 * @param {Roo.EventObject} e
4998 * Fires when the active item active state changes
4999 * @param {Roo.bootstrap.NavItem} this
5000 * @param {boolean} state the new state
5006 * Fires when scroll to element
5007 * @param {Roo.bootstrap.NavItem} this
5008 * @param {Object} options
5009 * @param {Roo.EventObject} e
5017 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
5026 preventDefault : false,
5034 button_outline : false,
5038 getAutoCreate : function(){
5046 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
5048 if (this.disabled) {
5049 cfg.cls += ' disabled';
5053 if (this.button_weight.length) {
5054 cfg.tag = this.href ? 'a' : 'button';
5055 cfg.html = this.html || '';
5056 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
5058 cfg.href = this.href;
5061 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
5064 // menu .. should add dropdown-menu class - so no need for carat..
5066 if (this.badge !== '') {
5068 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5073 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
5077 href : this.href || "#",
5078 html: this.html || ''
5081 if (this.tagtype == 'a') {
5082 cfg.cn[0].cls = 'nav-link';
5085 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
5088 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
5090 if(this.glyphicon) {
5091 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
5096 cfg.cn[0].html += " <span class='caret'></span>";
5100 if (this.badge !== '') {
5102 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5110 onRender : function(ct, position)
5112 // Roo.log("Call onRender: " + this.xtype);
5113 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
5117 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
5118 this.navLink = this.el.select('.nav-link',true).first();
5123 initEvents: function()
5125 if (typeof (this.menu) != 'undefined') {
5126 this.menu.parentType = this.xtype;
5127 this.menu.triggerEl = this.el;
5128 this.menu = this.addxtype(Roo.apply({}, this.menu));
5131 this.el.select('a',true).on('click', this.onClick, this);
5133 if(this.tagtype == 'span'){
5134 this.el.select('span',true).on('click', this.onClick, this);
5137 // at this point parent should be available..
5138 this.parent().register(this);
5141 onClick : function(e)
5143 if (e.getTarget('.dropdown-menu-item')) {
5144 // did you click on a menu itemm.... - then don't trigger onclick..
5149 this.preventDefault ||
5152 Roo.log("NavItem - prevent Default?");
5156 if (this.disabled) {
5160 var tg = Roo.bootstrap.TabGroup.get(this.navId);
5161 if (tg && tg.transition) {
5162 Roo.log("waiting for the transitionend");
5168 //Roo.log("fire event clicked");
5169 if(this.fireEvent('click', this, e) === false){
5173 if(this.tagtype == 'span'){
5177 //Roo.log(this.href);
5178 var ael = this.el.select('a',true).first();
5181 if(ael && this.animateRef && this.href.indexOf('#') > -1){
5182 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
5183 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
5184 return; // ignore... - it's a 'hash' to another page.
5186 Roo.log("NavItem - prevent Default?");
5188 this.scrollToElement(e);
5192 var p = this.parent();
5194 if (['tabs','pills'].indexOf(p.type)!==-1) {
5195 if (typeof(p.setActiveItem) !== 'undefined') {
5196 p.setActiveItem(this);
5200 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
5201 if (p.parentType == 'NavHeaderbar' && !this.menu) {
5202 // remove the collapsed menu expand...
5203 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
5207 isActive: function () {
5210 setActive : function(state, fire, is_was_active)
5212 if (this.active && !state && this.navId) {
5213 this.was_active = true;
5214 var nv = Roo.bootstrap.NavGroup.get(this.navId);
5216 nv.clearWasActive(this);
5220 this.active = state;
5223 this.el.removeClass('active');
5224 this.navLink ? this.navLink.removeClass('active') : false;
5225 } else if (!this.el.hasClass('active')) {
5227 this.el.addClass('active');
5228 if (Roo.bootstrap.version == 4 && this.navLink ) {
5229 this.navLink.addClass('active');
5234 this.fireEvent('changed', this, state);
5237 // show a panel if it's registered and related..
5239 if (!this.navId || !this.tabId || !state || is_was_active) {
5243 var tg = Roo.bootstrap.TabGroup.get(this.navId);
5247 var pan = tg.getPanelByName(this.tabId);
5251 // if we can not flip to new panel - go back to old nav highlight..
5252 if (false == tg.showPanel(pan)) {
5253 var nv = Roo.bootstrap.NavGroup.get(this.navId);
5255 var onav = nv.getWasActive();
5257 onav.setActive(true, false, true);
5266 // this should not be here...
5267 setDisabled : function(state)
5269 this.disabled = state;
5271 this.el.removeClass('disabled');
5272 } else if (!this.el.hasClass('disabled')) {
5273 this.el.addClass('disabled');
5279 * Fetch the element to display the tooltip on.
5280 * @return {Roo.Element} defaults to this.el
5282 tooltipEl : function()
5284 return this.el.select('' + this.tagtype + '', true).first();
5287 scrollToElement : function(e)
5289 var c = document.body;
5292 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
5294 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
5295 c = document.documentElement;
5298 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5304 var o = target.calcOffsetsTo(c);
5311 this.fireEvent('scrollto', this, options, e);
5313 Roo.get(c).scrollTo('top', options.value, true);
5326 * <span> icon </span>
5327 * <span> text </span>
5328 * <span>badge </span>
5332 * @class Roo.bootstrap.NavSidebarItem
5333 * @extends Roo.bootstrap.NavItem
5334 * Bootstrap Navbar.NavSidebarItem class
5335 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5336 * {Boolean} open is the menu open
5337 * {Boolean} buttonView use button as the tigger el rather that a (default false)
5338 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5339 * {String} buttonSize (sm|md|lg)the extra classes for the button
5340 * {Boolean} showArrow show arrow next to the text (default true)
5342 * Create a new Navbar Button
5343 * @param {Object} config The config object
5345 Roo.bootstrap.NavSidebarItem = function(config){
5346 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5351 * The raw click event for the entire grid.
5352 * @param {Roo.EventObject} e
5357 * Fires when the active item active state changes
5358 * @param {Roo.bootstrap.NavSidebarItem} this
5359 * @param {boolean} state the new state
5367 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
5369 badgeWeight : 'default',
5375 buttonWeight : 'default',
5381 getAutoCreate : function(){
5386 href : this.href || '#',
5392 if(this.buttonView){
5395 href : this.href || '#',
5396 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5409 cfg.cls += ' active';
5412 if (this.disabled) {
5413 cfg.cls += ' disabled';
5416 cfg.cls += ' open x-open';
5419 if (this.glyphicon || this.icon) {
5420 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
5421 a.cn.push({ tag : 'i', cls : c }) ;
5424 if(!this.buttonView){
5427 html : this.html || ''
5434 if (this.badge !== '') {
5435 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
5441 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5444 a.cls += ' dropdown-toggle treeview' ;
5450 initEvents : function()
5452 if (typeof (this.menu) != 'undefined') {
5453 this.menu.parentType = this.xtype;
5454 this.menu.triggerEl = this.el;
5455 this.menu = this.addxtype(Roo.apply({}, this.menu));
5458 this.el.on('click', this.onClick, this);
5460 if(this.badge !== ''){
5461 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5466 onClick : function(e)
5473 if(this.preventDefault){
5477 this.fireEvent('click', this, e);
5480 disable : function()
5482 this.setDisabled(true);
5487 this.setDisabled(false);
5490 setDisabled : function(state)
5492 if(this.disabled == state){
5496 this.disabled = state;
5499 this.el.addClass('disabled');
5503 this.el.removeClass('disabled');
5508 setActive : function(state)
5510 if(this.active == state){
5514 this.active = state;
5517 this.el.addClass('active');
5521 this.el.removeClass('active');
5526 isActive: function ()
5531 setBadge : function(str)
5537 this.badgeEl.dom.innerHTML = str;
5554 * @class Roo.bootstrap.Row
5555 * @extends Roo.bootstrap.Component
5556 * Bootstrap Row class (contains columns...)
5560 * @param {Object} config The config object
5563 Roo.bootstrap.Row = function(config){
5564 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5567 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5569 getAutoCreate : function(){
5588 * @class Roo.bootstrap.Element
5589 * @extends Roo.bootstrap.Component
5590 * Bootstrap Element class
5591 * @cfg {String} html contents of the element
5592 * @cfg {String} tag tag of the element
5593 * @cfg {String} cls class of the element
5594 * @cfg {Boolean} preventDefault (true|false) default false
5595 * @cfg {Boolean} clickable (true|false) default false
5598 * Create a new Element
5599 * @param {Object} config The config object
5602 Roo.bootstrap.Element = function(config){
5603 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5609 * When a element is chick
5610 * @param {Roo.bootstrap.Element} this
5611 * @param {Roo.EventObject} e
5617 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5622 preventDefault: false,
5625 getAutoCreate : function(){
5629 // cls: this.cls, double assign in parent class Component.js :: onRender
5636 initEvents: function()
5638 Roo.bootstrap.Element.superclass.initEvents.call(this);
5641 this.el.on('click', this.onClick, this);
5646 onClick : function(e)
5648 if(this.preventDefault){
5652 this.fireEvent('click', this, e);
5655 getValue : function()
5657 return this.el.dom.innerHTML;
5660 setValue : function(value)
5662 this.el.dom.innerHTML = value;
5677 * @class Roo.bootstrap.Pagination
5678 * @extends Roo.bootstrap.Component
5679 * Bootstrap Pagination class
5680 * @cfg {String} size xs | sm | md | lg
5681 * @cfg {Boolean} inverse false | true
5684 * Create a new Pagination
5685 * @param {Object} config The config object
5688 Roo.bootstrap.Pagination = function(config){
5689 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5692 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5698 getAutoCreate : function(){
5704 cfg.cls += ' inverse';
5710 cfg.cls += " " + this.cls;
5728 * @class Roo.bootstrap.PaginationItem
5729 * @extends Roo.bootstrap.Component
5730 * Bootstrap PaginationItem class
5731 * @cfg {String} html text
5732 * @cfg {String} href the link
5733 * @cfg {Boolean} preventDefault (true | false) default true
5734 * @cfg {Boolean} active (true | false) default false
5735 * @cfg {Boolean} disabled default false
5739 * Create a new PaginationItem
5740 * @param {Object} config The config object
5744 Roo.bootstrap.PaginationItem = function(config){
5745 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5750 * The raw click event for the entire grid.
5751 * @param {Roo.EventObject} e
5757 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5761 preventDefault: true,
5766 getAutoCreate : function(){
5772 href : this.href ? this.href : '#',
5773 html : this.html ? this.html : ''
5783 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5787 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5793 initEvents: function() {
5795 this.el.on('click', this.onClick, this);
5798 onClick : function(e)
5800 Roo.log('PaginationItem on click ');
5801 if(this.preventDefault){
5809 this.fireEvent('click', this, e);
5825 * @class Roo.bootstrap.Slider
5826 * @extends Roo.bootstrap.Component
5827 * Bootstrap Slider class
5830 * Create a new Slider
5831 * @param {Object} config The config object
5834 Roo.bootstrap.Slider = function(config){
5835 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5838 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5840 getAutoCreate : function(){
5844 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5848 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5860 * Ext JS Library 1.1.1
5861 * Copyright(c) 2006-2007, Ext JS, LLC.
5863 * Originally Released Under LGPL - original licence link has changed is not relivant.
5866 * <script type="text/javascript">
5871 * @class Roo.grid.ColumnModel
5872 * @extends Roo.util.Observable
5873 * This is the default implementation of a ColumnModel used by the Grid. It defines
5874 * the columns in the grid.
5877 var colModel = new Roo.grid.ColumnModel([
5878 {header: "Ticker", width: 60, sortable: true, locked: true},
5879 {header: "Company Name", width: 150, sortable: true},
5880 {header: "Market Cap.", width: 100, sortable: true},
5881 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5882 {header: "Employees", width: 100, sortable: true, resizable: false}
5887 * The config options listed for this class are options which may appear in each
5888 * individual column definition.
5889 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5891 * @param {Object} config An Array of column config objects. See this class's
5892 * config objects for details.
5894 Roo.grid.ColumnModel = function(config){
5896 * The config passed into the constructor
5898 this.config = config;
5901 // if no id, create one
5902 // if the column does not have a dataIndex mapping,
5903 // map it to the order it is in the config
5904 for(var i = 0, len = config.length; i < len; i++){
5906 if(typeof c.dataIndex == "undefined"){
5909 if(typeof c.renderer == "string"){
5910 c.renderer = Roo.util.Format[c.renderer];
5912 if(typeof c.id == "undefined"){
5915 if(c.editor && c.editor.xtype){
5916 c.editor = Roo.factory(c.editor, Roo.grid);
5918 if(c.editor && c.editor.isFormField){
5919 c.editor = new Roo.grid.GridEditor(c.editor);
5921 this.lookup[c.id] = c;
5925 * The width of columns which have no width specified (defaults to 100)
5928 this.defaultWidth = 100;
5931 * Default sortable of columns which have no sortable specified (defaults to false)
5934 this.defaultSortable = false;
5938 * @event widthchange
5939 * Fires when the width of a column changes.
5940 * @param {ColumnModel} this
5941 * @param {Number} columnIndex The column index
5942 * @param {Number} newWidth The new width
5944 "widthchange": true,
5946 * @event headerchange
5947 * Fires when the text of a header changes.
5948 * @param {ColumnModel} this
5949 * @param {Number} columnIndex The column index
5950 * @param {Number} newText The new header text
5952 "headerchange": true,
5954 * @event hiddenchange
5955 * Fires when a column is hidden or "unhidden".
5956 * @param {ColumnModel} this
5957 * @param {Number} columnIndex The column index
5958 * @param {Boolean} hidden true if hidden, false otherwise
5960 "hiddenchange": true,
5962 * @event columnmoved
5963 * Fires when a column is moved.
5964 * @param {ColumnModel} this
5965 * @param {Number} oldIndex
5966 * @param {Number} newIndex
5968 "columnmoved" : true,
5970 * @event columlockchange
5971 * Fires when a column's locked state is changed
5972 * @param {ColumnModel} this
5973 * @param {Number} colIndex
5974 * @param {Boolean} locked true if locked
5976 "columnlockchange" : true
5978 Roo.grid.ColumnModel.superclass.constructor.call(this);
5980 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5982 * @cfg {String} header The header text to display in the Grid view.
5985 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5986 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5987 * specified, the column's index is used as an index into the Record's data Array.
5990 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5991 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5994 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5995 * Defaults to the value of the {@link #defaultSortable} property.
5996 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5999 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
6002 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
6005 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6008 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6011 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6012 * given the cell's data value. See {@link #setRenderer}. If not specified, the
6013 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6014 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6017 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
6020 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
6023 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
6026 * @cfg {String} cursor (Optional)
6029 * @cfg {String} tooltip (Optional)
6032 * @cfg {Number} xs (Optional)
6035 * @cfg {Number} sm (Optional)
6038 * @cfg {Number} md (Optional)
6041 * @cfg {Number} lg (Optional)
6044 * Returns the id of the column at the specified index.
6045 * @param {Number} index The column index
6046 * @return {String} the id
6048 getColumnId : function(index){
6049 return this.config[index].id;
6053 * Returns the column for a specified id.
6054 * @param {String} id The column id
6055 * @return {Object} the column
6057 getColumnById : function(id){
6058 return this.lookup[id];
6063 * Returns the column for a specified dataIndex.
6064 * @param {String} dataIndex The column dataIndex
6065 * @return {Object|Boolean} the column or false if not found
6067 getColumnByDataIndex: function(dataIndex){
6068 var index = this.findColumnIndex(dataIndex);
6069 return index > -1 ? this.config[index] : false;
6073 * Returns the index for a specified column id.
6074 * @param {String} id The column id
6075 * @return {Number} the index, or -1 if not found
6077 getIndexById : function(id){
6078 for(var i = 0, len = this.config.length; i < len; i++){
6079 if(this.config[i].id == id){
6087 * Returns the index for a specified column dataIndex.
6088 * @param {String} dataIndex The column dataIndex
6089 * @return {Number} the index, or -1 if not found
6092 findColumnIndex : function(dataIndex){
6093 for(var i = 0, len = this.config.length; i < len; i++){
6094 if(this.config[i].dataIndex == dataIndex){
6102 moveColumn : function(oldIndex, newIndex){
6103 var c = this.config[oldIndex];
6104 this.config.splice(oldIndex, 1);
6105 this.config.splice(newIndex, 0, c);
6106 this.dataMap = null;
6107 this.fireEvent("columnmoved", this, oldIndex, newIndex);
6110 isLocked : function(colIndex){
6111 return this.config[colIndex].locked === true;
6114 setLocked : function(colIndex, value, suppressEvent){
6115 if(this.isLocked(colIndex) == value){
6118 this.config[colIndex].locked = value;
6120 this.fireEvent("columnlockchange", this, colIndex, value);
6124 getTotalLockedWidth : function(){
6126 for(var i = 0; i < this.config.length; i++){
6127 if(this.isLocked(i) && !this.isHidden(i)){
6128 this.totalWidth += this.getColumnWidth(i);
6134 getLockedCount : function(){
6135 for(var i = 0, len = this.config.length; i < len; i++){
6136 if(!this.isLocked(i)){
6141 return this.config.length;
6145 * Returns the number of columns.
6148 getColumnCount : function(visibleOnly){
6149 if(visibleOnly === true){
6151 for(var i = 0, len = this.config.length; i < len; i++){
6152 if(!this.isHidden(i)){
6158 return this.config.length;
6162 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
6163 * @param {Function} fn
6164 * @param {Object} scope (optional)
6165 * @return {Array} result
6167 getColumnsBy : function(fn, scope){
6169 for(var i = 0, len = this.config.length; i < len; i++){
6170 var c = this.config[i];
6171 if(fn.call(scope||this, c, i) === true){
6179 * Returns true if the specified column is sortable.
6180 * @param {Number} col The column index
6183 isSortable : function(col){
6184 if(typeof this.config[col].sortable == "undefined"){
6185 return this.defaultSortable;
6187 return this.config[col].sortable;
6191 * Returns the rendering (formatting) function defined for the column.
6192 * @param {Number} col The column index.
6193 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
6195 getRenderer : function(col){
6196 if(!this.config[col].renderer){
6197 return Roo.grid.ColumnModel.defaultRenderer;
6199 return this.config[col].renderer;
6203 * Sets the rendering (formatting) function for a column.
6204 * @param {Number} col The column index
6205 * @param {Function} fn The function to use to process the cell's raw data
6206 * to return HTML markup for the grid view. The render function is called with
6207 * the following parameters:<ul>
6208 * <li>Data value.</li>
6209 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
6210 * <li>css A CSS style string to apply to the table cell.</li>
6211 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
6212 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
6213 * <li>Row index</li>
6214 * <li>Column index</li>
6215 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
6217 setRenderer : function(col, fn){
6218 this.config[col].renderer = fn;
6222 * Returns the width for the specified column.
6223 * @param {Number} col The column index
6226 getColumnWidth : function(col){
6227 return this.config[col].width * 1 || this.defaultWidth;
6231 * Sets the width for a column.
6232 * @param {Number} col The column index
6233 * @param {Number} width The new width
6235 setColumnWidth : function(col, width, suppressEvent){
6236 this.config[col].width = width;
6237 this.totalWidth = null;
6239 this.fireEvent("widthchange", this, col, width);
6244 * Returns the total width of all columns.
6245 * @param {Boolean} includeHidden True to include hidden column widths
6248 getTotalWidth : function(includeHidden){
6249 if(!this.totalWidth){
6250 this.totalWidth = 0;
6251 for(var i = 0, len = this.config.length; i < len; i++){
6252 if(includeHidden || !this.isHidden(i)){
6253 this.totalWidth += this.getColumnWidth(i);
6257 return this.totalWidth;
6261 * Returns the header for the specified column.
6262 * @param {Number} col The column index
6265 getColumnHeader : function(col){
6266 return this.config[col].header;
6270 * Sets the header for a column.
6271 * @param {Number} col The column index
6272 * @param {String} header The new header
6274 setColumnHeader : function(col, header){
6275 this.config[col].header = header;
6276 this.fireEvent("headerchange", this, col, header);
6280 * Returns the tooltip for the specified column.
6281 * @param {Number} col The column index
6284 getColumnTooltip : function(col){
6285 return this.config[col].tooltip;
6288 * Sets the tooltip for a column.
6289 * @param {Number} col The column index
6290 * @param {String} tooltip The new tooltip
6292 setColumnTooltip : function(col, tooltip){
6293 this.config[col].tooltip = tooltip;
6297 * Returns the dataIndex for the specified column.
6298 * @param {Number} col The column index
6301 getDataIndex : function(col){
6302 return this.config[col].dataIndex;
6306 * Sets the dataIndex for a column.
6307 * @param {Number} col The column index
6308 * @param {Number} dataIndex The new dataIndex
6310 setDataIndex : function(col, dataIndex){
6311 this.config[col].dataIndex = dataIndex;
6317 * Returns true if the cell is editable.
6318 * @param {Number} colIndex The column index
6319 * @param {Number} rowIndex The row index - this is nto actually used..?
6322 isCellEditable : function(colIndex, rowIndex){
6323 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6327 * Returns the editor defined for the cell/column.
6328 * return false or null to disable editing.
6329 * @param {Number} colIndex The column index
6330 * @param {Number} rowIndex The row index
6333 getCellEditor : function(colIndex, rowIndex){
6334 return this.config[colIndex].editor;
6338 * Sets if a column is editable.
6339 * @param {Number} col The column index
6340 * @param {Boolean} editable True if the column is editable
6342 setEditable : function(col, editable){
6343 this.config[col].editable = editable;
6348 * Returns true if the column is hidden.
6349 * @param {Number} colIndex The column index
6352 isHidden : function(colIndex){
6353 return this.config[colIndex].hidden;
6358 * Returns true if the column width cannot be changed
6360 isFixed : function(colIndex){
6361 return this.config[colIndex].fixed;
6365 * Returns true if the column can be resized
6368 isResizable : function(colIndex){
6369 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6372 * Sets if a column is hidden.
6373 * @param {Number} colIndex The column index
6374 * @param {Boolean} hidden True if the column is hidden
6376 setHidden : function(colIndex, hidden){
6377 this.config[colIndex].hidden = hidden;
6378 this.totalWidth = null;
6379 this.fireEvent("hiddenchange", this, colIndex, hidden);
6383 * Sets the editor for a column.
6384 * @param {Number} col The column index
6385 * @param {Object} editor The editor object
6387 setEditor : function(col, editor){
6388 this.config[col].editor = editor;
6392 Roo.grid.ColumnModel.defaultRenderer = function(value)
6394 if(typeof value == "object") {
6397 if(typeof value == "string" && value.length < 1){
6401 return String.format("{0}", value);
6404 // Alias for backwards compatibility
6405 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6408 * Ext JS Library 1.1.1
6409 * Copyright(c) 2006-2007, Ext JS, LLC.
6411 * Originally Released Under LGPL - original licence link has changed is not relivant.
6414 * <script type="text/javascript">
6418 * @class Roo.LoadMask
6419 * A simple utility class for generically masking elements while loading data. If the element being masked has
6420 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6421 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
6422 * element's UpdateManager load indicator and will be destroyed after the initial load.
6424 * Create a new LoadMask
6425 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6426 * @param {Object} config The config object
6428 Roo.LoadMask = function(el, config){
6429 this.el = Roo.get(el);
6430 Roo.apply(this, config);
6432 this.store.on('beforeload', this.onBeforeLoad, this);
6433 this.store.on('load', this.onLoad, this);
6434 this.store.on('loadexception', this.onLoadException, this);
6435 this.removeMask = false;
6437 var um = this.el.getUpdateManager();
6438 um.showLoadIndicator = false; // disable the default indicator
6439 um.on('beforeupdate', this.onBeforeLoad, this);
6440 um.on('update', this.onLoad, this);
6441 um.on('failure', this.onLoad, this);
6442 this.removeMask = true;
6446 Roo.LoadMask.prototype = {
6448 * @cfg {Boolean} removeMask
6449 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6450 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
6454 * The text to display in a centered loading message box (defaults to 'Loading...')
6458 * @cfg {String} msgCls
6459 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6461 msgCls : 'x-mask-loading',
6464 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6470 * Disables the mask to prevent it from being displayed
6472 disable : function(){
6473 this.disabled = true;
6477 * Enables the mask so that it can be displayed
6479 enable : function(){
6480 this.disabled = false;
6483 onLoadException : function()
6487 if (typeof(arguments[3]) != 'undefined') {
6488 Roo.MessageBox.alert("Error loading",arguments[3]);
6492 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6493 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6500 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6505 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6509 onBeforeLoad : function(){
6511 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6516 destroy : function(){
6518 this.store.un('beforeload', this.onBeforeLoad, this);
6519 this.store.un('load', this.onLoad, this);
6520 this.store.un('loadexception', this.onLoadException, this);
6522 var um = this.el.getUpdateManager();
6523 um.un('beforeupdate', this.onBeforeLoad, this);
6524 um.un('update', this.onLoad, this);
6525 um.un('failure', this.onLoad, this);
6536 * @class Roo.bootstrap.Table
6537 * @extends Roo.bootstrap.Component
6538 * Bootstrap Table class
6539 * @cfg {String} cls table class
6540 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6541 * @cfg {String} bgcolor Specifies the background color for a table
6542 * @cfg {Number} border Specifies whether the table cells should have borders or not
6543 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6544 * @cfg {Number} cellspacing Specifies the space between cells
6545 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6546 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6547 * @cfg {String} sortable Specifies that the table should be sortable
6548 * @cfg {String} summary Specifies a summary of the content of a table
6549 * @cfg {Number} width Specifies the width of a table
6550 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6552 * @cfg {boolean} striped Should the rows be alternative striped
6553 * @cfg {boolean} bordered Add borders to the table
6554 * @cfg {boolean} hover Add hover highlighting
6555 * @cfg {boolean} condensed Format condensed
6556 * @cfg {boolean} responsive Format condensed
6557 * @cfg {Boolean} loadMask (true|false) default false
6558 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6559 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6560 * @cfg {Boolean} rowSelection (true|false) default false
6561 * @cfg {Boolean} cellSelection (true|false) default false
6562 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6563 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6564 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6565 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6569 * Create a new Table
6570 * @param {Object} config The config object
6573 Roo.bootstrap.Table = function(config){
6574 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6579 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6580 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6581 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6582 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6584 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6586 this.sm.grid = this;
6587 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6588 this.sm = this.selModel;
6589 this.sm.xmodule = this.xmodule || false;
6592 if (this.cm && typeof(this.cm.config) == 'undefined') {
6593 this.colModel = new Roo.grid.ColumnModel(this.cm);
6594 this.cm = this.colModel;
6595 this.cm.xmodule = this.xmodule || false;
6598 this.store= Roo.factory(this.store, Roo.data);
6599 this.ds = this.store;
6600 this.ds.xmodule = this.xmodule || false;
6603 if (this.footer && this.store) {
6604 this.footer.dataSource = this.ds;
6605 this.footer = Roo.factory(this.footer);
6612 * Fires when a cell is clicked
6613 * @param {Roo.bootstrap.Table} this
6614 * @param {Roo.Element} el
6615 * @param {Number} rowIndex
6616 * @param {Number} columnIndex
6617 * @param {Roo.EventObject} e
6621 * @event celldblclick
6622 * Fires when a cell is double clicked
6623 * @param {Roo.bootstrap.Table} this
6624 * @param {Roo.Element} el
6625 * @param {Number} rowIndex
6626 * @param {Number} columnIndex
6627 * @param {Roo.EventObject} e
6629 "celldblclick" : true,
6632 * Fires when a row is clicked
6633 * @param {Roo.bootstrap.Table} this
6634 * @param {Roo.Element} el
6635 * @param {Number} rowIndex
6636 * @param {Roo.EventObject} e
6640 * @event rowdblclick
6641 * Fires when a row is double clicked
6642 * @param {Roo.bootstrap.Table} this
6643 * @param {Roo.Element} el
6644 * @param {Number} rowIndex
6645 * @param {Roo.EventObject} e
6647 "rowdblclick" : true,
6650 * Fires when a mouseover occur
6651 * @param {Roo.bootstrap.Table} this
6652 * @param {Roo.Element} el
6653 * @param {Number} rowIndex
6654 * @param {Number} columnIndex
6655 * @param {Roo.EventObject} e
6660 * Fires when a mouseout occur
6661 * @param {Roo.bootstrap.Table} this
6662 * @param {Roo.Element} el
6663 * @param {Number} rowIndex
6664 * @param {Number} columnIndex
6665 * @param {Roo.EventObject} e
6670 * Fires when a row is rendered, so you can change add a style to it.
6671 * @param {Roo.bootstrap.Table} this
6672 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6676 * @event rowsrendered
6677 * Fires when all the rows have been rendered
6678 * @param {Roo.bootstrap.Table} this
6680 'rowsrendered' : true,
6682 * @event contextmenu
6683 * The raw contextmenu event for the entire grid.
6684 * @param {Roo.EventObject} e
6686 "contextmenu" : true,
6688 * @event rowcontextmenu
6689 * Fires when a row is right clicked
6690 * @param {Roo.bootstrap.Table} this
6691 * @param {Number} rowIndex
6692 * @param {Roo.EventObject} e
6694 "rowcontextmenu" : true,
6696 * @event cellcontextmenu
6697 * Fires when a cell is right clicked
6698 * @param {Roo.bootstrap.Table} this
6699 * @param {Number} rowIndex
6700 * @param {Number} cellIndex
6701 * @param {Roo.EventObject} e
6703 "cellcontextmenu" : true,
6705 * @event headercontextmenu
6706 * Fires when a header is right clicked
6707 * @param {Roo.bootstrap.Table} this
6708 * @param {Number} columnIndex
6709 * @param {Roo.EventObject} e
6711 "headercontextmenu" : true
6715 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6741 rowSelection : false,
6742 cellSelection : false,
6745 // Roo.Element - the tbody
6747 // Roo.Element - thead element
6750 container: false, // used by gridpanel...
6756 auto_hide_footer : false,
6758 getAutoCreate : function()
6760 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6767 if (this.scrollBody) {
6768 cfg.cls += ' table-body-fixed';
6771 cfg.cls += ' table-striped';
6775 cfg.cls += ' table-hover';
6777 if (this.bordered) {
6778 cfg.cls += ' table-bordered';
6780 if (this.condensed) {
6781 cfg.cls += ' table-condensed';
6783 if (this.responsive) {
6784 cfg.cls += ' table-responsive';
6788 cfg.cls+= ' ' +this.cls;
6791 // this lot should be simplifed...
6804 ].forEach(function(k) {
6812 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6815 if(this.store || this.cm){
6816 if(this.headerShow){
6817 cfg.cn.push(this.renderHeader());
6820 cfg.cn.push(this.renderBody());
6822 if(this.footerShow){
6823 cfg.cn.push(this.renderFooter());
6825 // where does this come from?
6826 //cfg.cls+= ' TableGrid';
6829 return { cn : [ cfg ] };
6832 initEvents : function()
6834 if(!this.store || !this.cm){
6837 if (this.selModel) {
6838 this.selModel.initEvents();
6842 //Roo.log('initEvents with ds!!!!');
6844 this.mainBody = this.el.select('tbody', true).first();
6845 this.mainHead = this.el.select('thead', true).first();
6846 this.mainFoot = this.el.select('tfoot', true).first();
6852 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6853 e.on('click', _this.sort, _this);
6856 this.mainBody.on("click", this.onClick, this);
6857 this.mainBody.on("dblclick", this.onDblClick, this);
6859 // why is this done????? = it breaks dialogs??
6860 //this.parent().el.setStyle('position', 'relative');
6864 this.footer.parentId = this.id;
6865 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6868 this.el.select('tfoot tr td').first().addClass('hide');
6873 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6876 this.store.on('load', this.onLoad, this);
6877 this.store.on('beforeload', this.onBeforeLoad, this);
6878 this.store.on('update', this.onUpdate, this);
6879 this.store.on('add', this.onAdd, this);
6880 this.store.on("clear", this.clear, this);
6882 this.el.on("contextmenu", this.onContextMenu, this);
6884 this.mainBody.on('scroll', this.onBodyScroll, this);
6886 this.cm.on("headerchange", this.onHeaderChange, this);
6888 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6892 onContextMenu : function(e, t)
6894 this.processEvent("contextmenu", e);
6897 processEvent : function(name, e)
6899 if (name != 'touchstart' ) {
6900 this.fireEvent(name, e);
6903 var t = e.getTarget();
6905 var cell = Roo.get(t);
6911 if(cell.findParent('tfoot', false, true)){
6915 if(cell.findParent('thead', false, true)){
6917 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6918 cell = Roo.get(t).findParent('th', false, true);
6920 Roo.log("failed to find th in thead?");
6921 Roo.log(e.getTarget());
6926 var cellIndex = cell.dom.cellIndex;
6928 var ename = name == 'touchstart' ? 'click' : name;
6929 this.fireEvent("header" + ename, this, cellIndex, e);
6934 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6935 cell = Roo.get(t).findParent('td', false, true);
6937 Roo.log("failed to find th in tbody?");
6938 Roo.log(e.getTarget());
6943 var row = cell.findParent('tr', false, true);
6944 var cellIndex = cell.dom.cellIndex;
6945 var rowIndex = row.dom.rowIndex - 1;
6949 this.fireEvent("row" + name, this, rowIndex, e);
6953 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6959 onMouseover : function(e, el)
6961 var cell = Roo.get(el);
6967 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6968 cell = cell.findParent('td', false, true);
6971 var row = cell.findParent('tr', false, true);
6972 var cellIndex = cell.dom.cellIndex;
6973 var rowIndex = row.dom.rowIndex - 1; // start from 0
6975 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6979 onMouseout : function(e, el)
6981 var cell = Roo.get(el);
6987 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6988 cell = cell.findParent('td', false, true);
6991 var row = cell.findParent('tr', false, true);
6992 var cellIndex = cell.dom.cellIndex;
6993 var rowIndex = row.dom.rowIndex - 1; // start from 0
6995 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6999 onClick : function(e, el)
7001 var cell = Roo.get(el);
7003 if(!cell || (!this.cellSelection && !this.rowSelection)){
7007 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7008 cell = cell.findParent('td', false, true);
7011 if(!cell || typeof(cell) == 'undefined'){
7015 var row = cell.findParent('tr', false, true);
7017 if(!row || typeof(row) == 'undefined'){
7021 var cellIndex = cell.dom.cellIndex;
7022 var rowIndex = this.getRowIndex(row);
7024 // why??? - should these not be based on SelectionModel?
7025 if(this.cellSelection){
7026 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7029 if(this.rowSelection){
7030 this.fireEvent('rowclick', this, row, rowIndex, e);
7036 onDblClick : function(e,el)
7038 var cell = Roo.get(el);
7040 if(!cell || (!this.cellSelection && !this.rowSelection)){
7044 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7045 cell = cell.findParent('td', false, true);
7048 if(!cell || typeof(cell) == 'undefined'){
7052 var row = cell.findParent('tr', false, true);
7054 if(!row || typeof(row) == 'undefined'){
7058 var cellIndex = cell.dom.cellIndex;
7059 var rowIndex = this.getRowIndex(row);
7061 if(this.cellSelection){
7062 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7065 if(this.rowSelection){
7066 this.fireEvent('rowdblclick', this, row, rowIndex, e);
7070 sort : function(e,el)
7072 var col = Roo.get(el);
7074 if(!col.hasClass('sortable')){
7078 var sort = col.attr('sort');
7081 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7085 this.store.sortInfo = {field : sort, direction : dir};
7088 Roo.log("calling footer first");
7089 this.footer.onClick('first');
7092 this.store.load({ params : { start : 0 } });
7096 renderHeader : function()
7104 this.totalWidth = 0;
7106 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7108 var config = cm.config[i];
7112 cls : 'x-hcol-' + i,
7114 html: cm.getColumnHeader(i)
7119 if(typeof(config.sortable) != 'undefined' && config.sortable){
7121 c.html = '<i class="glyphicon"></i>' + c.html;
7124 // could use BS4 hidden-..-down
7126 if(typeof(config.lgHeader) != 'undefined'){
7127 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
7130 if(typeof(config.mdHeader) != 'undefined'){
7131 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
7134 if(typeof(config.smHeader) != 'undefined'){
7135 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
7138 if(typeof(config.xsHeader) != 'undefined'){
7139 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
7146 if(typeof(config.tooltip) != 'undefined'){
7147 c.tooltip = config.tooltip;
7150 if(typeof(config.colspan) != 'undefined'){
7151 c.colspan = config.colspan;
7154 if(typeof(config.hidden) != 'undefined' && config.hidden){
7155 c.style += ' display:none;';
7158 if(typeof(config.dataIndex) != 'undefined'){
7159 c.sort = config.dataIndex;
7164 if(typeof(config.align) != 'undefined' && config.align.length){
7165 c.style += ' text-align:' + config.align + ';';
7168 if(typeof(config.width) != 'undefined'){
7169 c.style += ' width:' + config.width + 'px;';
7170 this.totalWidth += config.width;
7172 this.totalWidth += 100; // assume minimum of 100 per column?
7175 if(typeof(config.cls) != 'undefined'){
7176 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
7179 ['xs','sm','md','lg'].map(function(size){
7181 if(typeof(config[size]) == 'undefined'){
7185 if (!config[size]) { // 0 = hidden
7186 // BS 4 '0' is treated as hide that column and below.
7187 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
7191 c.cls += ' col-' + size + '-' + config[size] + (
7192 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7204 renderBody : function()
7214 colspan : this.cm.getColumnCount()
7224 renderFooter : function()
7234 colspan : this.cm.getColumnCount()
7248 // Roo.log('ds onload');
7253 var ds = this.store;
7255 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7256 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
7257 if (_this.store.sortInfo) {
7259 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
7260 e.select('i', true).addClass(['glyphicon-arrow-up']);
7263 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
7264 e.select('i', true).addClass(['glyphicon-arrow-down']);
7269 var tbody = this.mainBody;
7271 if(ds.getCount() > 0){
7272 ds.data.each(function(d,rowIndex){
7273 var row = this.renderRow(cm, ds, rowIndex);
7275 tbody.createChild(row);
7279 if(row.cellObjects.length){
7280 Roo.each(row.cellObjects, function(r){
7281 _this.renderCellObject(r);
7288 var tfoot = this.el.select('tfoot', true).first();
7290 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7292 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7294 var total = this.ds.getTotalCount();
7296 if(this.footer.pageSize < total){
7297 this.mainFoot.show();
7301 Roo.each(this.el.select('tbody td', true).elements, function(e){
7302 e.on('mouseover', _this.onMouseover, _this);
7305 Roo.each(this.el.select('tbody td', true).elements, function(e){
7306 e.on('mouseout', _this.onMouseout, _this);
7308 this.fireEvent('rowsrendered', this);
7314 onUpdate : function(ds,record)
7316 this.refreshRow(record);
7320 onRemove : function(ds, record, index, isUpdate){
7321 if(isUpdate !== true){
7322 this.fireEvent("beforerowremoved", this, index, record);
7324 var bt = this.mainBody.dom;
7326 var rows = this.el.select('tbody > tr', true).elements;
7328 if(typeof(rows[index]) != 'undefined'){
7329 bt.removeChild(rows[index].dom);
7332 // if(bt.rows[index]){
7333 // bt.removeChild(bt.rows[index]);
7336 if(isUpdate !== true){
7337 //this.stripeRows(index);
7338 //this.syncRowHeights(index, index);
7340 this.fireEvent("rowremoved", this, index, record);
7344 onAdd : function(ds, records, rowIndex)
7346 //Roo.log('on Add called');
7347 // - note this does not handle multiple adding very well..
7348 var bt = this.mainBody.dom;
7349 for (var i =0 ; i < records.length;i++) {
7350 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7351 //Roo.log(records[i]);
7352 //Roo.log(this.store.getAt(rowIndex+i));
7353 this.insertRow(this.store, rowIndex + i, false);
7360 refreshRow : function(record){
7361 var ds = this.store, index;
7362 if(typeof record == 'number'){
7364 record = ds.getAt(index);
7366 index = ds.indexOf(record);
7368 this.insertRow(ds, index, true);
7370 this.onRemove(ds, record, index+1, true);
7372 //this.syncRowHeights(index, index);
7374 this.fireEvent("rowupdated", this, index, record);
7377 insertRow : function(dm, rowIndex, isUpdate){
7380 this.fireEvent("beforerowsinserted", this, rowIndex);
7382 //var s = this.getScrollState();
7383 var row = this.renderRow(this.cm, this.store, rowIndex);
7384 // insert before rowIndex..
7385 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7389 if(row.cellObjects.length){
7390 Roo.each(row.cellObjects, function(r){
7391 _this.renderCellObject(r);
7396 this.fireEvent("rowsinserted", this, rowIndex);
7397 //this.syncRowHeights(firstRow, lastRow);
7398 //this.stripeRows(firstRow);
7405 getRowDom : function(rowIndex)
7407 var rows = this.el.select('tbody > tr', true).elements;
7409 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7412 // returns the object tree for a tr..
7415 renderRow : function(cm, ds, rowIndex)
7417 var d = ds.getAt(rowIndex);
7421 cls : 'x-row-' + rowIndex,
7425 var cellObjects = [];
7427 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7428 var config = cm.config[i];
7430 var renderer = cm.getRenderer(i);
7434 if(typeof(renderer) !== 'undefined'){
7435 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7437 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7438 // and are rendered into the cells after the row is rendered - using the id for the element.
7440 if(typeof(value) === 'object'){
7450 rowIndex : rowIndex,
7455 this.fireEvent('rowclass', this, rowcfg);
7459 cls : rowcfg.rowClass + ' x-col-' + i,
7461 html: (typeof(value) === 'object') ? '' : value
7468 if(typeof(config.colspan) != 'undefined'){
7469 td.colspan = config.colspan;
7472 if(typeof(config.hidden) != 'undefined' && config.hidden){
7473 td.style += ' display:none;';
7476 if(typeof(config.align) != 'undefined' && config.align.length){
7477 td.style += ' text-align:' + config.align + ';';
7479 if(typeof(config.valign) != 'undefined' && config.valign.length){
7480 td.style += ' vertical-align:' + config.valign + ';';
7483 if(typeof(config.width) != 'undefined'){
7484 td.style += ' width:' + config.width + 'px;';
7487 if(typeof(config.cursor) != 'undefined'){
7488 td.style += ' cursor:' + config.cursor + ';';
7491 if(typeof(config.cls) != 'undefined'){
7492 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7495 ['xs','sm','md','lg'].map(function(size){
7497 if(typeof(config[size]) == 'undefined'){
7503 if (!config[size]) { // 0 = hidden
7504 // BS 4 '0' is treated as hide that column and below.
7505 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7509 td.cls += ' col-' + size + '-' + config[size] + (
7510 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7520 row.cellObjects = cellObjects;
7528 onBeforeLoad : function()
7537 this.el.select('tbody', true).first().dom.innerHTML = '';
7540 * Show or hide a row.
7541 * @param {Number} rowIndex to show or hide
7542 * @param {Boolean} state hide
7544 setRowVisibility : function(rowIndex, state)
7546 var bt = this.mainBody.dom;
7548 var rows = this.el.select('tbody > tr', true).elements;
7550 if(typeof(rows[rowIndex]) == 'undefined'){
7553 rows[rowIndex].dom.style.display = state ? '' : 'none';
7557 getSelectionModel : function(){
7559 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7561 return this.selModel;
7564 * Render the Roo.bootstrap object from renderder
7566 renderCellObject : function(r)
7570 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7572 var t = r.cfg.render(r.container);
7575 Roo.each(r.cfg.cn, function(c){
7577 container: t.getChildContainer(),
7580 _this.renderCellObject(child);
7585 getRowIndex : function(row)
7589 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7600 * Returns the grid's underlying element = used by panel.Grid
7601 * @return {Element} The element
7603 getGridEl : function(){
7607 * Forces a resize - used by panel.Grid
7608 * @return {Element} The element
7610 autoSize : function()
7612 //var ctr = Roo.get(this.container.dom.parentElement);
7613 var ctr = Roo.get(this.el.dom);
7615 var thd = this.getGridEl().select('thead',true).first();
7616 var tbd = this.getGridEl().select('tbody', true).first();
7617 var tfd = this.getGridEl().select('tfoot', true).first();
7619 var cw = ctr.getWidth();
7623 tbd.setWidth(ctr.getWidth());
7624 // if the body has a max height - and then scrolls - we should perhaps set up the height here
7625 // this needs fixing for various usage - currently only hydra job advers I think..
7627 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7629 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7632 cw = Math.max(cw, this.totalWidth);
7633 this.getGridEl().select('tr',true).setWidth(cw);
7634 // resize 'expandable coloumn?
7636 return; // we doe not have a view in this design..
7639 onBodyScroll: function()
7641 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7643 this.mainHead.setStyle({
7644 'position' : 'relative',
7645 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7651 var scrollHeight = this.mainBody.dom.scrollHeight;
7653 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7655 var height = this.mainBody.getHeight();
7657 if(scrollHeight - height == scrollTop) {
7659 var total = this.ds.getTotalCount();
7661 if(this.footer.cursor + this.footer.pageSize < total){
7663 this.footer.ds.load({
7665 start : this.footer.cursor + this.footer.pageSize,
7666 limit : this.footer.pageSize
7676 onHeaderChange : function()
7678 var header = this.renderHeader();
7679 var table = this.el.select('table', true).first();
7681 this.mainHead.remove();
7682 this.mainHead = table.createChild(header, this.mainBody, false);
7685 onHiddenChange : function(colModel, colIndex, hidden)
7687 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7688 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7690 this.CSS.updateRule(thSelector, "display", "");
7691 this.CSS.updateRule(tdSelector, "display", "");
7694 this.CSS.updateRule(thSelector, "display", "none");
7695 this.CSS.updateRule(tdSelector, "display", "none");
7698 this.onHeaderChange();
7702 setColumnWidth: function(col_index, width)
7704 // width = "md-2 xs-2..."
7705 if(!this.colModel.config[col_index]) {
7709 var w = width.split(" ");
7711 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7713 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7716 for(var j = 0; j < w.length; j++) {
7722 var size_cls = w[j].split("-");
7724 if(!Number.isInteger(size_cls[1] * 1)) {
7728 if(!this.colModel.config[col_index][size_cls[0]]) {
7732 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7736 h_row[0].classList.replace(
7737 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7738 "col-"+size_cls[0]+"-"+size_cls[1]
7741 for(var i = 0; i < rows.length; i++) {
7743 var size_cls = w[j].split("-");
7745 if(!Number.isInteger(size_cls[1] * 1)) {
7749 if(!this.colModel.config[col_index][size_cls[0]]) {
7753 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7757 rows[i].classList.replace(
7758 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7759 "col-"+size_cls[0]+"-"+size_cls[1]
7763 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7778 * @class Roo.bootstrap.TableCell
7779 * @extends Roo.bootstrap.Component
7780 * Bootstrap TableCell class
7781 * @cfg {String} html cell contain text
7782 * @cfg {String} cls cell class
7783 * @cfg {String} tag cell tag (td|th) default td
7784 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7785 * @cfg {String} align Aligns the content in a cell
7786 * @cfg {String} axis Categorizes cells
7787 * @cfg {String} bgcolor Specifies the background color of a cell
7788 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7789 * @cfg {Number} colspan Specifies the number of columns a cell should span
7790 * @cfg {String} headers Specifies one or more header cells a cell is related to
7791 * @cfg {Number} height Sets the height of a cell
7792 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7793 * @cfg {Number} rowspan Sets the number of rows a cell should span
7794 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7795 * @cfg {String} valign Vertical aligns the content in a cell
7796 * @cfg {Number} width Specifies the width of a cell
7799 * Create a new TableCell
7800 * @param {Object} config The config object
7803 Roo.bootstrap.TableCell = function(config){
7804 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7807 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7827 getAutoCreate : function(){
7828 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7848 cfg.align=this.align
7854 cfg.bgcolor=this.bgcolor
7857 cfg.charoff=this.charoff
7860 cfg.colspan=this.colspan
7863 cfg.headers=this.headers
7866 cfg.height=this.height
7869 cfg.nowrap=this.nowrap
7872 cfg.rowspan=this.rowspan
7875 cfg.scope=this.scope
7878 cfg.valign=this.valign
7881 cfg.width=this.width
7900 * @class Roo.bootstrap.TableRow
7901 * @extends Roo.bootstrap.Component
7902 * Bootstrap TableRow class
7903 * @cfg {String} cls row class
7904 * @cfg {String} align Aligns the content in a table row
7905 * @cfg {String} bgcolor Specifies a background color for a table row
7906 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7907 * @cfg {String} valign Vertical aligns the content in a table row
7910 * Create a new TableRow
7911 * @param {Object} config The config object
7914 Roo.bootstrap.TableRow = function(config){
7915 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7918 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7926 getAutoCreate : function(){
7927 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7937 cfg.align = this.align;
7940 cfg.bgcolor = this.bgcolor;
7943 cfg.charoff = this.charoff;
7946 cfg.valign = this.valign;
7964 * @class Roo.bootstrap.TableBody
7965 * @extends Roo.bootstrap.Component
7966 * Bootstrap TableBody class
7967 * @cfg {String} cls element class
7968 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7969 * @cfg {String} align Aligns the content inside the element
7970 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7971 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7974 * Create a new TableBody
7975 * @param {Object} config The config object
7978 Roo.bootstrap.TableBody = function(config){
7979 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7982 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7990 getAutoCreate : function(){
7991 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8005 cfg.align = this.align;
8008 cfg.charoff = this.charoff;
8011 cfg.valign = this.valign;
8018 // initEvents : function()
8025 // this.store = Roo.factory(this.store, Roo.data);
8026 // this.store.on('load', this.onLoad, this);
8028 // this.store.load();
8032 // onLoad: function ()
8034 // this.fireEvent('load', this);
8044 * Ext JS Library 1.1.1
8045 * Copyright(c) 2006-2007, Ext JS, LLC.
8047 * Originally Released Under LGPL - original licence link has changed is not relivant.
8050 * <script type="text/javascript">
8053 // as we use this in bootstrap.
8054 Roo.namespace('Roo.form');
8056 * @class Roo.form.Action
8057 * Internal Class used to handle form actions
8059 * @param {Roo.form.BasicForm} el The form element or its id
8060 * @param {Object} config Configuration options
8065 // define the action interface
8066 Roo.form.Action = function(form, options){
8068 this.options = options || {};
8071 * Client Validation Failed
8074 Roo.form.Action.CLIENT_INVALID = 'client';
8076 * Server Validation Failed
8079 Roo.form.Action.SERVER_INVALID = 'server';
8081 * Connect to Server Failed
8084 Roo.form.Action.CONNECT_FAILURE = 'connect';
8086 * Reading Data from Server Failed
8089 Roo.form.Action.LOAD_FAILURE = 'load';
8091 Roo.form.Action.prototype = {
8093 failureType : undefined,
8094 response : undefined,
8098 run : function(options){
8103 success : function(response){
8108 handleResponse : function(response){
8112 // default connection failure
8113 failure : function(response){
8115 this.response = response;
8116 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8117 this.form.afterAction(this, false);
8120 processResponse : function(response){
8121 this.response = response;
8122 if(!response.responseText){
8125 this.result = this.handleResponse(response);
8129 // utility functions used internally
8130 getUrl : function(appendParams){
8131 var url = this.options.url || this.form.url || this.form.el.dom.action;
8133 var p = this.getParams();
8135 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
8141 getMethod : function(){
8142 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
8145 getParams : function(){
8146 var bp = this.form.baseParams;
8147 var p = this.options.params;
8149 if(typeof p == "object"){
8150 p = Roo.urlEncode(Roo.applyIf(p, bp));
8151 }else if(typeof p == 'string' && bp){
8152 p += '&' + Roo.urlEncode(bp);
8155 p = Roo.urlEncode(bp);
8160 createCallback : function(){
8162 success: this.success,
8163 failure: this.failure,
8165 timeout: (this.form.timeout*1000),
8166 upload: this.form.fileUpload ? this.success : undefined
8171 Roo.form.Action.Submit = function(form, options){
8172 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
8175 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
8178 haveProgress : false,
8179 uploadComplete : false,
8181 // uploadProgress indicator.
8182 uploadProgress : function()
8184 if (!this.form.progressUrl) {
8188 if (!this.haveProgress) {
8189 Roo.MessageBox.progress("Uploading", "Uploading");
8191 if (this.uploadComplete) {
8192 Roo.MessageBox.hide();
8196 this.haveProgress = true;
8198 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
8200 var c = new Roo.data.Connection();
8202 url : this.form.progressUrl,
8207 success : function(req){
8208 //console.log(data);
8212 rdata = Roo.decode(req.responseText)
8214 Roo.log("Invalid data from server..");
8218 if (!rdata || !rdata.success) {
8220 Roo.MessageBox.alert(Roo.encode(rdata));
8223 var data = rdata.data;
8225 if (this.uploadComplete) {
8226 Roo.MessageBox.hide();
8231 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
8232 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
8235 this.uploadProgress.defer(2000,this);
8238 failure: function(data) {
8239 Roo.log('progress url failed ');
8250 // run get Values on the form, so it syncs any secondary forms.
8251 this.form.getValues();
8253 var o = this.options;
8254 var method = this.getMethod();
8255 var isPost = method == 'POST';
8256 if(o.clientValidation === false || this.form.isValid()){
8258 if (this.form.progressUrl) {
8259 this.form.findField('UPLOAD_IDENTIFIER').setValue(
8260 (new Date() * 1) + '' + Math.random());
8265 Roo.Ajax.request(Roo.apply(this.createCallback(), {
8266 form:this.form.el.dom,
8267 url:this.getUrl(!isPost),
8269 params:isPost ? this.getParams() : null,
8270 isUpload: this.form.fileUpload,
8271 formData : this.form.formData
8274 this.uploadProgress();
8276 }else if (o.clientValidation !== false){ // client validation failed
8277 this.failureType = Roo.form.Action.CLIENT_INVALID;
8278 this.form.afterAction(this, false);
8282 success : function(response)
8284 this.uploadComplete= true;
8285 if (this.haveProgress) {
8286 Roo.MessageBox.hide();
8290 var result = this.processResponse(response);
8291 if(result === true || result.success){
8292 this.form.afterAction(this, true);
8296 this.form.markInvalid(result.errors);
8297 this.failureType = Roo.form.Action.SERVER_INVALID;
8299 this.form.afterAction(this, false);
8301 failure : function(response)
8303 this.uploadComplete= true;
8304 if (this.haveProgress) {
8305 Roo.MessageBox.hide();
8308 this.response = response;
8309 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8310 this.form.afterAction(this, false);
8313 handleResponse : function(response){
8314 if(this.form.errorReader){
8315 var rs = this.form.errorReader.read(response);
8318 for(var i = 0, len = rs.records.length; i < len; i++) {
8319 var r = rs.records[i];
8323 if(errors.length < 1){
8327 success : rs.success,
8333 ret = Roo.decode(response.responseText);
8337 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8347 Roo.form.Action.Load = function(form, options){
8348 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8349 this.reader = this.form.reader;
8352 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8357 Roo.Ajax.request(Roo.apply(
8358 this.createCallback(), {
8359 method:this.getMethod(),
8360 url:this.getUrl(false),
8361 params:this.getParams()
8365 success : function(response){
8367 var result = this.processResponse(response);
8368 if(result === true || !result.success || !result.data){
8369 this.failureType = Roo.form.Action.LOAD_FAILURE;
8370 this.form.afterAction(this, false);
8373 this.form.clearInvalid();
8374 this.form.setValues(result.data);
8375 this.form.afterAction(this, true);
8378 handleResponse : function(response){
8379 if(this.form.reader){
8380 var rs = this.form.reader.read(response);
8381 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8383 success : rs.success,
8387 return Roo.decode(response.responseText);
8391 Roo.form.Action.ACTION_TYPES = {
8392 'load' : Roo.form.Action.Load,
8393 'submit' : Roo.form.Action.Submit
8402 * @class Roo.bootstrap.Form
8403 * @extends Roo.bootstrap.Component
8404 * Bootstrap Form class
8405 * @cfg {String} method GET | POST (default POST)
8406 * @cfg {String} labelAlign top | left (default top)
8407 * @cfg {String} align left | right - for navbars
8408 * @cfg {Boolean} loadMask load mask when submit (default true)
8413 * @param {Object} config The config object
8417 Roo.bootstrap.Form = function(config){
8419 Roo.bootstrap.Form.superclass.constructor.call(this, config);
8421 Roo.bootstrap.Form.popover.apply();
8425 * @event clientvalidation
8426 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8427 * @param {Form} this
8428 * @param {Boolean} valid true if the form has passed client-side validation
8430 clientvalidation: true,
8432 * @event beforeaction
8433 * Fires before any action is performed. Return false to cancel the action.
8434 * @param {Form} this
8435 * @param {Action} action The action to be performed
8439 * @event actionfailed
8440 * Fires when an action fails.
8441 * @param {Form} this
8442 * @param {Action} action The action that failed
8444 actionfailed : true,
8446 * @event actioncomplete
8447 * Fires when an action is completed.
8448 * @param {Form} this
8449 * @param {Action} action The action that completed
8451 actioncomplete : true
8455 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
8458 * @cfg {String} method
8459 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8464 * The URL to use for form actions if one isn't supplied in the action options.
8467 * @cfg {Boolean} fileUpload
8468 * Set to true if this form is a file upload.
8472 * @cfg {Object} baseParams
8473 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8477 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8481 * @cfg {Sting} align (left|right) for navbar forms
8486 activeAction : null,
8489 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8490 * element by passing it or its id or mask the form itself by passing in true.
8493 waitMsgTarget : false,
8498 * @cfg {Boolean} errorMask (true|false) default false
8503 * @cfg {Number} maskOffset Default 100
8508 * @cfg {Boolean} maskBody
8512 getAutoCreate : function(){
8516 method : this.method || 'POST',
8517 id : this.id || Roo.id(),
8520 if (this.parent().xtype.match(/^Nav/)) {
8521 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8525 if (this.labelAlign == 'left' ) {
8526 cfg.cls += ' form-horizontal';
8532 initEvents : function()
8534 this.el.on('submit', this.onSubmit, this);
8535 // this was added as random key presses on the form where triggering form submit.
8536 this.el.on('keypress', function(e) {
8537 if (e.getCharCode() != 13) {
8540 // we might need to allow it for textareas.. and some other items.
8541 // check e.getTarget().
8543 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8547 Roo.log("keypress blocked");
8555 onSubmit : function(e){
8560 * Returns true if client-side validation on the form is successful.
8563 isValid : function(){
8564 var items = this.getItems();
8568 items.each(function(f){
8574 Roo.log('invalid field: ' + f.name);
8578 if(!target && f.el.isVisible(true)){
8584 if(this.errorMask && !valid){
8585 Roo.bootstrap.Form.popover.mask(this, target);
8592 * Returns true if any fields in this form have changed since their original load.
8595 isDirty : function(){
8597 var items = this.getItems();
8598 items.each(function(f){
8608 * Performs a predefined action (submit or load) or custom actions you define on this form.
8609 * @param {String} actionName The name of the action type
8610 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8611 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8612 * accept other config options):
8614 Property Type Description
8615 ---------------- --------------- ----------------------------------------------------------------------------------
8616 url String The url for the action (defaults to the form's url)
8617 method String The form method to use (defaults to the form's method, or POST if not defined)
8618 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8619 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8620 validate the form on the client (defaults to false)
8622 * @return {BasicForm} this
8624 doAction : function(action, options){
8625 if(typeof action == 'string'){
8626 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8628 if(this.fireEvent('beforeaction', this, action) !== false){
8629 this.beforeAction(action);
8630 action.run.defer(100, action);
8636 beforeAction : function(action){
8637 var o = action.options;
8642 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8644 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8647 // not really supported yet.. ??
8649 //if(this.waitMsgTarget === true){
8650 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8651 //}else if(this.waitMsgTarget){
8652 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8653 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8655 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8661 afterAction : function(action, success){
8662 this.activeAction = null;
8663 var o = action.options;
8668 Roo.get(document.body).unmask();
8674 //if(this.waitMsgTarget === true){
8675 // this.el.unmask();
8676 //}else if(this.waitMsgTarget){
8677 // this.waitMsgTarget.unmask();
8679 // Roo.MessageBox.updateProgress(1);
8680 // Roo.MessageBox.hide();
8687 Roo.callback(o.success, o.scope, [this, action]);
8688 this.fireEvent('actioncomplete', this, action);
8692 // failure condition..
8693 // we have a scenario where updates need confirming.
8694 // eg. if a locking scenario exists..
8695 // we look for { errors : { needs_confirm : true }} in the response.
8697 (typeof(action.result) != 'undefined') &&
8698 (typeof(action.result.errors) != 'undefined') &&
8699 (typeof(action.result.errors.needs_confirm) != 'undefined')
8702 Roo.log("not supported yet");
8705 Roo.MessageBox.confirm(
8706 "Change requires confirmation",
8707 action.result.errorMsg,
8712 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8722 Roo.callback(o.failure, o.scope, [this, action]);
8723 // show an error message if no failed handler is set..
8724 if (!this.hasListener('actionfailed')) {
8725 Roo.log("need to add dialog support");
8727 Roo.MessageBox.alert("Error",
8728 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8729 action.result.errorMsg :
8730 "Saving Failed, please check your entries or try again"
8735 this.fireEvent('actionfailed', this, action);
8740 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8741 * @param {String} id The value to search for
8744 findField : function(id){
8745 var items = this.getItems();
8746 var field = items.get(id);
8748 items.each(function(f){
8749 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8756 return field || null;
8759 * Mark fields in this form invalid in bulk.
8760 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8761 * @return {BasicForm} this
8763 markInvalid : function(errors){
8764 if(errors instanceof Array){
8765 for(var i = 0, len = errors.length; i < len; i++){
8766 var fieldError = errors[i];
8767 var f = this.findField(fieldError.id);
8769 f.markInvalid(fieldError.msg);
8775 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8776 field.markInvalid(errors[id]);
8780 //Roo.each(this.childForms || [], function (f) {
8781 // f.markInvalid(errors);
8788 * Set values for fields in this form in bulk.
8789 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8790 * @return {BasicForm} this
8792 setValues : function(values){
8793 if(values instanceof Array){ // array of objects
8794 for(var i = 0, len = values.length; i < len; i++){
8796 var f = this.findField(v.id);
8798 f.setValue(v.value);
8799 if(this.trackResetOnLoad){
8800 f.originalValue = f.getValue();
8804 }else{ // object hash
8807 if(typeof values[id] != 'function' && (field = this.findField(id))){
8809 if (field.setFromData &&
8811 field.displayField &&
8812 // combos' with local stores can
8813 // be queried via setValue()
8814 // to set their value..
8815 (field.store && !field.store.isLocal)
8819 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8820 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8821 field.setFromData(sd);
8823 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8825 field.setFromData(values);
8828 field.setValue(values[id]);
8832 if(this.trackResetOnLoad){
8833 field.originalValue = field.getValue();
8839 //Roo.each(this.childForms || [], function (f) {
8840 // f.setValues(values);
8847 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8848 * they are returned as an array.
8849 * @param {Boolean} asString
8852 getValues : function(asString){
8853 //if (this.childForms) {
8854 // copy values from the child forms
8855 // Roo.each(this.childForms, function (f) {
8856 // this.setValues(f.getValues());
8862 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8863 if(asString === true){
8866 return Roo.urlDecode(fs);
8870 * Returns the fields in this form as an object with key/value pairs.
8871 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8874 getFieldValues : function(with_hidden)
8876 var items = this.getItems();
8878 items.each(function(f){
8884 var v = f.getValue();
8886 if (f.inputType =='radio') {
8887 if (typeof(ret[f.getName()]) == 'undefined') {
8888 ret[f.getName()] = ''; // empty..
8891 if (!f.el.dom.checked) {
8899 if(f.xtype == 'MoneyField'){
8900 ret[f.currencyName] = f.getCurrency();
8903 // not sure if this supported any more..
8904 if ((typeof(v) == 'object') && f.getRawValue) {
8905 v = f.getRawValue() ; // dates..
8907 // combo boxes where name != hiddenName...
8908 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8909 ret[f.name] = f.getRawValue();
8911 ret[f.getName()] = v;
8918 * Clears all invalid messages in this form.
8919 * @return {BasicForm} this
8921 clearInvalid : function(){
8922 var items = this.getItems();
8924 items.each(function(f){
8933 * @return {BasicForm} this
8936 var items = this.getItems();
8937 items.each(function(f){
8941 Roo.each(this.childForms || [], function (f) {
8949 getItems : function()
8951 var r=new Roo.util.MixedCollection(false, function(o){
8952 return o.id || (o.id = Roo.id());
8954 var iter = function(el) {
8961 Roo.each(el.items,function(e) {
8970 hideFields : function(items)
8972 Roo.each(items, function(i){
8974 var f = this.findField(i);
8985 showFields : function(items)
8987 Roo.each(items, function(i){
8989 var f = this.findField(i);
9002 Roo.apply(Roo.bootstrap.Form, {
9029 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9030 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9031 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9032 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9035 this.maskEl.top.enableDisplayMode("block");
9036 this.maskEl.left.enableDisplayMode("block");
9037 this.maskEl.bottom.enableDisplayMode("block");
9038 this.maskEl.right.enableDisplayMode("block");
9040 this.toolTip = new Roo.bootstrap.Tooltip({
9041 cls : 'roo-form-error-popover',
9043 'left' : ['r-l', [-2,0], 'right'],
9044 'right' : ['l-r', [2,0], 'left'],
9045 'bottom' : ['tl-bl', [0,2], 'top'],
9046 'top' : [ 'bl-tl', [0,-2], 'bottom']
9050 this.toolTip.render(Roo.get(document.body));
9052 this.toolTip.el.enableDisplayMode("block");
9054 Roo.get(document.body).on('click', function(){
9058 Roo.get(document.body).on('touchstart', function(){
9062 this.isApplied = true
9065 mask : function(form, target)
9069 this.target = target;
9071 if(!this.form.errorMask || !target.el){
9075 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9077 Roo.log(scrollable);
9079 var ot = this.target.el.calcOffsetsTo(scrollable);
9081 var scrollTo = ot[1] - this.form.maskOffset;
9083 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9085 scrollable.scrollTo('top', scrollTo);
9087 var box = this.target.el.getBox();
9089 var zIndex = Roo.bootstrap.Modal.zIndex++;
9092 this.maskEl.top.setStyle('position', 'absolute');
9093 this.maskEl.top.setStyle('z-index', zIndex);
9094 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9095 this.maskEl.top.setLeft(0);
9096 this.maskEl.top.setTop(0);
9097 this.maskEl.top.show();
9099 this.maskEl.left.setStyle('position', 'absolute');
9100 this.maskEl.left.setStyle('z-index', zIndex);
9101 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9102 this.maskEl.left.setLeft(0);
9103 this.maskEl.left.setTop(box.y - this.padding);
9104 this.maskEl.left.show();
9106 this.maskEl.bottom.setStyle('position', 'absolute');
9107 this.maskEl.bottom.setStyle('z-index', zIndex);
9108 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9109 this.maskEl.bottom.setLeft(0);
9110 this.maskEl.bottom.setTop(box.bottom + this.padding);
9111 this.maskEl.bottom.show();
9113 this.maskEl.right.setStyle('position', 'absolute');
9114 this.maskEl.right.setStyle('z-index', zIndex);
9115 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9116 this.maskEl.right.setLeft(box.right + this.padding);
9117 this.maskEl.right.setTop(box.y - this.padding);
9118 this.maskEl.right.show();
9120 this.toolTip.bindEl = this.target.el;
9122 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9124 var tip = this.target.blankText;
9126 if(this.target.getValue() !== '' ) {
9128 if (this.target.invalidText.length) {
9129 tip = this.target.invalidText;
9130 } else if (this.target.regexText.length){
9131 tip = this.target.regexText;
9135 this.toolTip.show(tip);
9137 this.intervalID = window.setInterval(function() {
9138 Roo.bootstrap.Form.popover.unmask();
9141 window.onwheel = function(){ return false;};
9143 (function(){ this.isMasked = true; }).defer(500, this);
9149 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
9153 this.maskEl.top.setStyle('position', 'absolute');
9154 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
9155 this.maskEl.top.hide();
9157 this.maskEl.left.setStyle('position', 'absolute');
9158 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
9159 this.maskEl.left.hide();
9161 this.maskEl.bottom.setStyle('position', 'absolute');
9162 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
9163 this.maskEl.bottom.hide();
9165 this.maskEl.right.setStyle('position', 'absolute');
9166 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
9167 this.maskEl.right.hide();
9169 this.toolTip.hide();
9171 this.toolTip.el.hide();
9173 window.onwheel = function(){ return true;};
9175 if(this.intervalID){
9176 window.clearInterval(this.intervalID);
9177 this.intervalID = false;
9180 this.isMasked = false;
9190 * Ext JS Library 1.1.1
9191 * Copyright(c) 2006-2007, Ext JS, LLC.
9193 * Originally Released Under LGPL - original licence link has changed is not relivant.
9196 * <script type="text/javascript">
9199 * @class Roo.form.VTypes
9200 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
9203 Roo.form.VTypes = function(){
9204 // closure these in so they are only created once.
9205 var alpha = /^[a-zA-Z_]+$/;
9206 var alphanum = /^[a-zA-Z0-9_]+$/;
9207 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
9208 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
9210 // All these messages and functions are configurable
9213 * The function used to validate email addresses
9214 * @param {String} value The email address
9216 'email' : function(v){
9217 return email.test(v);
9220 * The error text to display when the email validation function returns false
9223 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
9225 * The keystroke filter mask to be applied on email input
9228 'emailMask' : /[a-z0-9_\.\-@]/i,
9231 * The function used to validate URLs
9232 * @param {String} value The URL
9234 'url' : function(v){
9238 * The error text to display when the url validation function returns false
9241 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
9244 * The function used to validate alpha values
9245 * @param {String} value The value
9247 'alpha' : function(v){
9248 return alpha.test(v);
9251 * The error text to display when the alpha validation function returns false
9254 'alphaText' : 'This field should only contain letters and _',
9256 * The keystroke filter mask to be applied on alpha input
9259 'alphaMask' : /[a-z_]/i,
9262 * The function used to validate alphanumeric values
9263 * @param {String} value The value
9265 'alphanum' : function(v){
9266 return alphanum.test(v);
9269 * The error text to display when the alphanumeric validation function returns false
9272 'alphanumText' : 'This field should only contain letters, numbers and _',
9274 * The keystroke filter mask to be applied on alphanumeric input
9277 'alphanumMask' : /[a-z0-9_]/i
9287 * @class Roo.bootstrap.Input
9288 * @extends Roo.bootstrap.Component
9289 * Bootstrap Input class
9290 * @cfg {Boolean} disabled is it disabled
9291 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9292 * @cfg {String} name name of the input
9293 * @cfg {string} fieldLabel - the label associated
9294 * @cfg {string} placeholder - placeholder to put in text.
9295 * @cfg {string} before - input group add on before
9296 * @cfg {string} after - input group add on after
9297 * @cfg {string} size - (lg|sm) or leave empty..
9298 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9299 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9300 * @cfg {Number} md colspan out of 12 for computer-sized screens
9301 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9302 * @cfg {string} value default value of the input
9303 * @cfg {Number} labelWidth set the width of label
9304 * @cfg {Number} labellg set the width of label (1-12)
9305 * @cfg {Number} labelmd set the width of label (1-12)
9306 * @cfg {Number} labelsm set the width of label (1-12)
9307 * @cfg {Number} labelxs set the width of label (1-12)
9308 * @cfg {String} labelAlign (top|left)
9309 * @cfg {Boolean} readOnly Specifies that the field should be read-only
9310 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9311 * @cfg {String} indicatorpos (left|right) default left
9312 * @cfg {String} capture (user|camera) use for file input only. (default empty)
9313 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9315 * @cfg {String} align (left|center|right) Default left
9316 * @cfg {Boolean} forceFeedback (true|false) Default false
9319 * Create a new Input
9320 * @param {Object} config The config object
9323 Roo.bootstrap.Input = function(config){
9325 Roo.bootstrap.Input.superclass.constructor.call(this, config);
9330 * Fires when this field receives input focus.
9331 * @param {Roo.form.Field} this
9336 * Fires when this field loses input focus.
9337 * @param {Roo.form.Field} this
9342 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
9343 * {@link Roo.EventObject#getKey} to determine which key was pressed.
9344 * @param {Roo.form.Field} this
9345 * @param {Roo.EventObject} e The event object
9350 * Fires just before the field blurs if the field value has changed.
9351 * @param {Roo.form.Field} this
9352 * @param {Mixed} newValue The new value
9353 * @param {Mixed} oldValue The original value
9358 * Fires after the field has been marked as invalid.
9359 * @param {Roo.form.Field} this
9360 * @param {String} msg The validation message
9365 * Fires after the field has been validated with no errors.
9366 * @param {Roo.form.Field} this
9371 * Fires after the key up
9372 * @param {Roo.form.Field} this
9373 * @param {Roo.EventObject} e The event Object
9379 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
9381 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9382 automatic validation (defaults to "keyup").
9384 validationEvent : "keyup",
9386 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9388 validateOnBlur : true,
9390 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9392 validationDelay : 250,
9394 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9396 focusClass : "x-form-focus", // not needed???
9400 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9402 invalidClass : "has-warning",
9405 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9407 validClass : "has-success",
9410 * @cfg {Boolean} hasFeedback (true|false) default true
9415 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9417 invalidFeedbackClass : "glyphicon-warning-sign",
9420 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9422 validFeedbackClass : "glyphicon-ok",
9425 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9427 selectOnFocus : false,
9430 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9434 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9439 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9441 disableKeyFilter : false,
9444 * @cfg {Boolean} disabled True to disable the field (defaults to false).
9448 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9452 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9454 blankText : "Please complete this mandatory field",
9457 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9461 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9463 maxLength : Number.MAX_VALUE,
9465 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9467 minLengthText : "The minimum length for this field is {0}",
9469 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9471 maxLengthText : "The maximum length for this field is {0}",
9475 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9476 * If available, this function will be called only after the basic validators all return true, and will be passed the
9477 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9481 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9482 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9483 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9487 * @cfg {String} regexText -- Depricated - use Invalid Text
9492 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9498 autocomplete: false,
9517 formatedValue : false,
9518 forceFeedback : false,
9520 indicatorpos : 'left',
9530 parentLabelAlign : function()
9533 while (parent.parent()) {
9534 parent = parent.parent();
9535 if (typeof(parent.labelAlign) !='undefined') {
9536 return parent.labelAlign;
9543 getAutoCreate : function()
9545 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9551 if(this.inputType != 'hidden'){
9552 cfg.cls = 'form-group' //input-group
9558 type : this.inputType,
9560 cls : 'form-control',
9561 placeholder : this.placeholder || '',
9562 autocomplete : this.autocomplete || 'new-password'
9565 if(this.capture.length){
9566 input.capture = this.capture;
9569 if(this.accept.length){
9570 input.accept = this.accept + "/*";
9574 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9577 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9578 input.maxLength = this.maxLength;
9581 if (this.disabled) {
9582 input.disabled=true;
9585 if (this.readOnly) {
9586 input.readonly=true;
9590 input.name = this.name;
9594 input.cls += ' input-' + this.size;
9598 ['xs','sm','md','lg'].map(function(size){
9599 if (settings[size]) {
9600 cfg.cls += ' col-' + size + '-' + settings[size];
9604 var inputblock = input;
9608 cls: 'glyphicon form-control-feedback'
9611 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9614 cls : 'has-feedback',
9622 if (this.before || this.after) {
9625 cls : 'input-group',
9629 if (this.before && typeof(this.before) == 'string') {
9631 inputblock.cn.push({
9633 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9637 if (this.before && typeof(this.before) == 'object') {
9638 this.before = Roo.factory(this.before);
9640 inputblock.cn.push({
9642 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9643 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9647 inputblock.cn.push(input);
9649 if (this.after && typeof(this.after) == 'string') {
9650 inputblock.cn.push({
9652 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9656 if (this.after && typeof(this.after) == 'object') {
9657 this.after = Roo.factory(this.after);
9659 inputblock.cn.push({
9661 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9662 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9666 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9667 inputblock.cls += ' has-feedback';
9668 inputblock.cn.push(feedback);
9673 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9674 tooltip : 'This field is required'
9676 if (Roo.bootstrap.version == 4) {
9679 style : 'display-none'
9682 if (align ==='left' && this.fieldLabel.length) {
9684 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
9691 cls : 'control-label col-form-label',
9692 html : this.fieldLabel
9703 var labelCfg = cfg.cn[1];
9704 var contentCfg = cfg.cn[2];
9706 if(this.indicatorpos == 'right'){
9711 cls : 'control-label col-form-label',
9715 html : this.fieldLabel
9729 labelCfg = cfg.cn[0];
9730 contentCfg = cfg.cn[1];
9734 if(this.labelWidth > 12){
9735 labelCfg.style = "width: " + this.labelWidth + 'px';
9738 if(this.labelWidth < 13 && this.labelmd == 0){
9739 this.labelmd = this.labelWidth;
9742 if(this.labellg > 0){
9743 labelCfg.cls += ' col-lg-' + this.labellg;
9744 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9747 if(this.labelmd > 0){
9748 labelCfg.cls += ' col-md-' + this.labelmd;
9749 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9752 if(this.labelsm > 0){
9753 labelCfg.cls += ' col-sm-' + this.labelsm;
9754 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9757 if(this.labelxs > 0){
9758 labelCfg.cls += ' col-xs-' + this.labelxs;
9759 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9763 } else if ( this.fieldLabel.length) {
9768 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9769 tooltip : 'This field is required'
9773 //cls : 'input-group-addon',
9774 html : this.fieldLabel
9782 if(this.indicatorpos == 'right'){
9787 //cls : 'input-group-addon',
9788 html : this.fieldLabel
9793 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9794 tooltip : 'This field is required'
9814 if (this.parentType === 'Navbar' && this.parent().bar) {
9815 cfg.cls += ' navbar-form';
9818 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9819 // on BS4 we do this only if not form
9820 cfg.cls += ' navbar-form';
9828 * return the real input element.
9830 inputEl: function ()
9832 return this.el.select('input.form-control',true).first();
9835 tooltipEl : function()
9837 return this.inputEl();
9840 indicatorEl : function()
9842 if (Roo.bootstrap.version == 4) {
9843 return false; // not enabled in v4 yet.
9846 var indicator = this.el.select('i.roo-required-indicator',true).first();
9856 setDisabled : function(v)
9858 var i = this.inputEl().dom;
9860 i.removeAttribute('disabled');
9864 i.setAttribute('disabled','true');
9866 initEvents : function()
9869 this.inputEl().on("keydown" , this.fireKey, this);
9870 this.inputEl().on("focus", this.onFocus, this);
9871 this.inputEl().on("blur", this.onBlur, this);
9873 this.inputEl().relayEvent('keyup', this);
9875 this.indicator = this.indicatorEl();
9878 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9881 // reference to original value for reset
9882 this.originalValue = this.getValue();
9883 //Roo.form.TextField.superclass.initEvents.call(this);
9884 if(this.validationEvent == 'keyup'){
9885 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9886 this.inputEl().on('keyup', this.filterValidation, this);
9888 else if(this.validationEvent !== false){
9889 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9892 if(this.selectOnFocus){
9893 this.on("focus", this.preFocus, this);
9896 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9897 this.inputEl().on("keypress", this.filterKeys, this);
9899 this.inputEl().relayEvent('keypress', this);
9902 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9903 this.el.on("click", this.autoSize, this);
9906 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9907 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9910 if (typeof(this.before) == 'object') {
9911 this.before.render(this.el.select('.roo-input-before',true).first());
9913 if (typeof(this.after) == 'object') {
9914 this.after.render(this.el.select('.roo-input-after',true).first());
9917 this.inputEl().on('change', this.onChange, this);
9920 filterValidation : function(e){
9921 if(!e.isNavKeyPress()){
9922 this.validationTask.delay(this.validationDelay);
9926 * Validates the field value
9927 * @return {Boolean} True if the value is valid, else false
9929 validate : function(){
9930 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9931 if(this.disabled || this.validateValue(this.getRawValue())){
9942 * Validates a value according to the field's validation rules and marks the field as invalid
9943 * if the validation fails
9944 * @param {Mixed} value The value to validate
9945 * @return {Boolean} True if the value is valid, else false
9947 validateValue : function(value)
9949 if(this.getVisibilityEl().hasClass('hidden')){
9953 if(value.length < 1) { // if it's blank
9954 if(this.allowBlank){
9960 if(value.length < this.minLength){
9963 if(value.length > this.maxLength){
9967 var vt = Roo.form.VTypes;
9968 if(!vt[this.vtype](value, this)){
9972 if(typeof this.validator == "function"){
9973 var msg = this.validator(value);
9977 if (typeof(msg) == 'string') {
9978 this.invalidText = msg;
9982 if(this.regex && !this.regex.test(value)){
9990 fireKey : function(e){
9991 //Roo.log('field ' + e.getKey());
9992 if(e.isNavKeyPress()){
9993 this.fireEvent("specialkey", this, e);
9996 focus : function (selectText){
9998 this.inputEl().focus();
9999 if(selectText === true){
10000 this.inputEl().dom.select();
10006 onFocus : function(){
10007 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10008 // this.el.addClass(this.focusClass);
10010 if(!this.hasFocus){
10011 this.hasFocus = true;
10012 this.startValue = this.getValue();
10013 this.fireEvent("focus", this);
10017 beforeBlur : Roo.emptyFn,
10021 onBlur : function(){
10023 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10024 //this.el.removeClass(this.focusClass);
10026 this.hasFocus = false;
10027 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10030 var v = this.getValue();
10031 if(String(v) !== String(this.startValue)){
10032 this.fireEvent('change', this, v, this.startValue);
10034 this.fireEvent("blur", this);
10037 onChange : function(e)
10039 var v = this.getValue();
10040 if(String(v) !== String(this.startValue)){
10041 this.fireEvent('change', this, v, this.startValue);
10047 * Resets the current field value to the originally loaded value and clears any validation messages
10049 reset : function(){
10050 this.setValue(this.originalValue);
10054 * Returns the name of the field
10055 * @return {Mixed} name The name field
10057 getName: function(){
10061 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
10062 * @return {Mixed} value The field value
10064 getValue : function(){
10066 var v = this.inputEl().getValue();
10071 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
10072 * @return {Mixed} value The field value
10074 getRawValue : function(){
10075 var v = this.inputEl().getValue();
10081 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
10082 * @param {Mixed} value The value to set
10084 setRawValue : function(v){
10085 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10088 selectText : function(start, end){
10089 var v = this.getRawValue();
10091 start = start === undefined ? 0 : start;
10092 end = end === undefined ? v.length : end;
10093 var d = this.inputEl().dom;
10094 if(d.setSelectionRange){
10095 d.setSelectionRange(start, end);
10096 }else if(d.createTextRange){
10097 var range = d.createTextRange();
10098 range.moveStart("character", start);
10099 range.moveEnd("character", v.length-end);
10106 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
10107 * @param {Mixed} value The value to set
10109 setValue : function(v){
10112 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10118 processValue : function(value){
10119 if(this.stripCharsRe){
10120 var newValue = value.replace(this.stripCharsRe, '');
10121 if(newValue !== value){
10122 this.setRawValue(newValue);
10129 preFocus : function(){
10131 if(this.selectOnFocus){
10132 this.inputEl().dom.select();
10135 filterKeys : function(e){
10136 var k = e.getKey();
10137 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
10140 var c = e.getCharCode(), cc = String.fromCharCode(c);
10141 if(Roo.isIE && (e.isSpecialKey() || !cc)){
10144 if(!this.maskRe.test(cc)){
10149 * Clear any invalid styles/messages for this field
10151 clearInvalid : function(){
10153 if(!this.el || this.preventMark){ // not rendered
10158 this.el.removeClass([this.invalidClass, 'is-invalid']);
10160 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10162 var feedback = this.el.select('.form-control-feedback', true).first();
10165 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10170 if(this.indicator){
10171 this.indicator.removeClass('visible');
10172 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10175 this.fireEvent('valid', this);
10179 * Mark this field as valid
10181 markValid : function()
10183 if(!this.el || this.preventMark){ // not rendered...
10187 this.el.removeClass([this.invalidClass, this.validClass]);
10188 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10190 var feedback = this.el.select('.form-control-feedback', true).first();
10193 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10196 if(this.indicator){
10197 this.indicator.removeClass('visible');
10198 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10205 if(this.allowBlank && !this.getRawValue().length){
10208 if (Roo.bootstrap.version == 3) {
10209 this.el.addClass(this.validClass);
10211 this.inputEl().addClass('is-valid');
10214 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10216 var feedback = this.el.select('.form-control-feedback', true).first();
10219 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10220 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10225 this.fireEvent('valid', this);
10229 * Mark this field as invalid
10230 * @param {String} msg The validation message
10232 markInvalid : function(msg)
10234 if(!this.el || this.preventMark){ // not rendered
10238 this.el.removeClass([this.invalidClass, this.validClass]);
10239 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10241 var feedback = this.el.select('.form-control-feedback', true).first();
10244 this.el.select('.form-control-feedback', true).first().removeClass(
10245 [this.invalidFeedbackClass, this.validFeedbackClass]);
10252 if(this.allowBlank && !this.getRawValue().length){
10256 if(this.indicator){
10257 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10258 this.indicator.addClass('visible');
10260 if (Roo.bootstrap.version == 3) {
10261 this.el.addClass(this.invalidClass);
10263 this.inputEl().addClass('is-invalid');
10268 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10270 var feedback = this.el.select('.form-control-feedback', true).first();
10273 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10275 if(this.getValue().length || this.forceFeedback){
10276 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10283 this.fireEvent('invalid', this, msg);
10286 SafariOnKeyDown : function(event)
10288 // this is a workaround for a password hang bug on chrome/ webkit.
10289 if (this.inputEl().dom.type != 'password') {
10293 var isSelectAll = false;
10295 if(this.inputEl().dom.selectionEnd > 0){
10296 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10298 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10299 event.preventDefault();
10304 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10306 event.preventDefault();
10307 // this is very hacky as keydown always get's upper case.
10309 var cc = String.fromCharCode(event.getCharCode());
10310 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
10314 adjustWidth : function(tag, w){
10315 tag = tag.toLowerCase();
10316 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10317 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10318 if(tag == 'input'){
10321 if(tag == 'textarea'){
10324 }else if(Roo.isOpera){
10325 if(tag == 'input'){
10328 if(tag == 'textarea'){
10336 setFieldLabel : function(v)
10338 if(!this.rendered){
10342 if(this.indicatorEl()){
10343 var ar = this.el.select('label > span',true);
10345 if (ar.elements.length) {
10346 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10347 this.fieldLabel = v;
10351 var br = this.el.select('label',true);
10353 if(br.elements.length) {
10354 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10355 this.fieldLabel = v;
10359 Roo.log('Cannot Found any of label > span || label in input');
10363 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10364 this.fieldLabel = v;
10379 * @class Roo.bootstrap.TextArea
10380 * @extends Roo.bootstrap.Input
10381 * Bootstrap TextArea class
10382 * @cfg {Number} cols Specifies the visible width of a text area
10383 * @cfg {Number} rows Specifies the visible number of lines in a text area
10384 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10385 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10386 * @cfg {string} html text
10389 * Create a new TextArea
10390 * @param {Object} config The config object
10393 Roo.bootstrap.TextArea = function(config){
10394 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10398 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
10408 getAutoCreate : function(){
10410 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10416 if(this.inputType != 'hidden'){
10417 cfg.cls = 'form-group' //input-group
10425 value : this.value || '',
10426 html: this.html || '',
10427 cls : 'form-control',
10428 placeholder : this.placeholder || ''
10432 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10433 input.maxLength = this.maxLength;
10437 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10441 input.cols = this.cols;
10444 if (this.readOnly) {
10445 input.readonly = true;
10449 input.name = this.name;
10453 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10457 ['xs','sm','md','lg'].map(function(size){
10458 if (settings[size]) {
10459 cfg.cls += ' col-' + size + '-' + settings[size];
10463 var inputblock = input;
10465 if(this.hasFeedback && !this.allowBlank){
10469 cls: 'glyphicon form-control-feedback'
10473 cls : 'has-feedback',
10482 if (this.before || this.after) {
10485 cls : 'input-group',
10489 inputblock.cn.push({
10491 cls : 'input-group-addon',
10496 inputblock.cn.push(input);
10498 if(this.hasFeedback && !this.allowBlank){
10499 inputblock.cls += ' has-feedback';
10500 inputblock.cn.push(feedback);
10504 inputblock.cn.push({
10506 cls : 'input-group-addon',
10513 if (align ==='left' && this.fieldLabel.length) {
10518 cls : 'control-label',
10519 html : this.fieldLabel
10530 if(this.labelWidth > 12){
10531 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10534 if(this.labelWidth < 13 && this.labelmd == 0){
10535 this.labelmd = this.labelWidth;
10538 if(this.labellg > 0){
10539 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10540 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10543 if(this.labelmd > 0){
10544 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10545 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10548 if(this.labelsm > 0){
10549 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10550 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10553 if(this.labelxs > 0){
10554 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10555 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10558 } else if ( this.fieldLabel.length) {
10563 //cls : 'input-group-addon',
10564 html : this.fieldLabel
10582 if (this.disabled) {
10583 input.disabled=true;
10590 * return the real textarea element.
10592 inputEl: function ()
10594 return this.el.select('textarea.form-control',true).first();
10598 * Clear any invalid styles/messages for this field
10600 clearInvalid : function()
10603 if(!this.el || this.preventMark){ // not rendered
10607 var label = this.el.select('label', true).first();
10608 var icon = this.el.select('i.fa-star', true).first();
10613 this.el.removeClass( this.validClass);
10614 this.inputEl().removeClass('is-invalid');
10616 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10618 var feedback = this.el.select('.form-control-feedback', true).first();
10621 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10626 this.fireEvent('valid', this);
10630 * Mark this field as valid
10632 markValid : function()
10634 if(!this.el || this.preventMark){ // not rendered
10638 this.el.removeClass([this.invalidClass, this.validClass]);
10639 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10641 var feedback = this.el.select('.form-control-feedback', true).first();
10644 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10647 if(this.disabled || this.allowBlank){
10651 var label = this.el.select('label', true).first();
10652 var icon = this.el.select('i.fa-star', true).first();
10657 if (Roo.bootstrap.version == 3) {
10658 this.el.addClass(this.validClass);
10660 this.inputEl().addClass('is-valid');
10664 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10666 var feedback = this.el.select('.form-control-feedback', true).first();
10669 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10670 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10675 this.fireEvent('valid', this);
10679 * Mark this field as invalid
10680 * @param {String} msg The validation message
10682 markInvalid : function(msg)
10684 if(!this.el || this.preventMark){ // not rendered
10688 this.el.removeClass([this.invalidClass, this.validClass]);
10689 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10691 var feedback = this.el.select('.form-control-feedback', true).first();
10694 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10697 if(this.disabled || this.allowBlank){
10701 var label = this.el.select('label', true).first();
10702 var icon = this.el.select('i.fa-star', true).first();
10704 if(!this.getValue().length && label && !icon){
10705 this.el.createChild({
10707 cls : 'text-danger fa fa-lg fa-star',
10708 tooltip : 'This field is required',
10709 style : 'margin-right:5px;'
10713 if (Roo.bootstrap.version == 3) {
10714 this.el.addClass(this.invalidClass);
10716 this.inputEl().addClass('is-invalid');
10719 // fixme ... this may be depricated need to test..
10720 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10722 var feedback = this.el.select('.form-control-feedback', true).first();
10725 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10727 if(this.getValue().length || this.forceFeedback){
10728 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10735 this.fireEvent('invalid', this, msg);
10743 * trigger field - base class for combo..
10748 * @class Roo.bootstrap.TriggerField
10749 * @extends Roo.bootstrap.Input
10750 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10751 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10752 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10753 * for which you can provide a custom implementation. For example:
10755 var trigger = new Roo.bootstrap.TriggerField();
10756 trigger.onTriggerClick = myTriggerFn;
10757 trigger.applyTo('my-field');
10760 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10761 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10762 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10763 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10764 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10767 * Create a new TriggerField.
10768 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10769 * to the base TextField)
10771 Roo.bootstrap.TriggerField = function(config){
10772 this.mimicing = false;
10773 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10776 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10778 * @cfg {String} triggerClass A CSS class to apply to the trigger
10781 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10786 * @cfg {Boolean} removable (true|false) special filter default false
10790 /** @cfg {Boolean} grow @hide */
10791 /** @cfg {Number} growMin @hide */
10792 /** @cfg {Number} growMax @hide */
10798 autoSize: Roo.emptyFn,
10802 deferHeight : true,
10805 actionMode : 'wrap',
10810 getAutoCreate : function(){
10812 var align = this.labelAlign || this.parentLabelAlign();
10817 cls: 'form-group' //input-group
10824 type : this.inputType,
10825 cls : 'form-control',
10826 autocomplete: 'new-password',
10827 placeholder : this.placeholder || ''
10831 input.name = this.name;
10834 input.cls += ' input-' + this.size;
10837 if (this.disabled) {
10838 input.disabled=true;
10841 var inputblock = input;
10843 if(this.hasFeedback && !this.allowBlank){
10847 cls: 'glyphicon form-control-feedback'
10850 if(this.removable && !this.editable && !this.tickable){
10852 cls : 'has-feedback',
10858 cls : 'roo-combo-removable-btn close'
10865 cls : 'has-feedback',
10874 if(this.removable && !this.editable && !this.tickable){
10876 cls : 'roo-removable',
10882 cls : 'roo-combo-removable-btn close'
10889 if (this.before || this.after) {
10892 cls : 'input-group',
10896 inputblock.cn.push({
10898 cls : 'input-group-addon input-group-prepend input-group-text',
10903 inputblock.cn.push(input);
10905 if(this.hasFeedback && !this.allowBlank){
10906 inputblock.cls += ' has-feedback';
10907 inputblock.cn.push(feedback);
10911 inputblock.cn.push({
10913 cls : 'input-group-addon input-group-append input-group-text',
10922 var ibwrap = inputblock;
10927 cls: 'roo-select2-choices',
10931 cls: 'roo-select2-search-field',
10943 cls: 'roo-select2-container input-group',
10948 cls: 'form-hidden-field'
10954 if(!this.multiple && this.showToggleBtn){
10960 if (this.caret != false) {
10963 cls: 'fa fa-' + this.caret
10970 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10972 Roo.bootstrap.version == 3 ? caret : '',
10975 cls: 'combobox-clear',
10989 combobox.cls += ' roo-select2-container-multi';
10993 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10994 tooltip : 'This field is required'
10996 if (Roo.bootstrap.version == 4) {
10999 style : 'display:none'
11004 if (align ==='left' && this.fieldLabel.length) {
11006 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11013 cls : 'control-label',
11014 html : this.fieldLabel
11026 var labelCfg = cfg.cn[1];
11027 var contentCfg = cfg.cn[2];
11029 if(this.indicatorpos == 'right'){
11034 cls : 'control-label',
11038 html : this.fieldLabel
11052 labelCfg = cfg.cn[0];
11053 contentCfg = cfg.cn[1];
11056 if(this.labelWidth > 12){
11057 labelCfg.style = "width: " + this.labelWidth + 'px';
11060 if(this.labelWidth < 13 && this.labelmd == 0){
11061 this.labelmd = this.labelWidth;
11064 if(this.labellg > 0){
11065 labelCfg.cls += ' col-lg-' + this.labellg;
11066 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11069 if(this.labelmd > 0){
11070 labelCfg.cls += ' col-md-' + this.labelmd;
11071 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11074 if(this.labelsm > 0){
11075 labelCfg.cls += ' col-sm-' + this.labelsm;
11076 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11079 if(this.labelxs > 0){
11080 labelCfg.cls += ' col-xs-' + this.labelxs;
11081 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11084 } else if ( this.fieldLabel.length) {
11085 // Roo.log(" label");
11090 //cls : 'input-group-addon',
11091 html : this.fieldLabel
11099 if(this.indicatorpos == 'right'){
11107 html : this.fieldLabel
11121 // Roo.log(" no label && no align");
11128 ['xs','sm','md','lg'].map(function(size){
11129 if (settings[size]) {
11130 cfg.cls += ' col-' + size + '-' + settings[size];
11141 onResize : function(w, h){
11142 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
11143 // if(typeof w == 'number'){
11144 // var x = w - this.trigger.getWidth();
11145 // this.inputEl().setWidth(this.adjustWidth('input', x));
11146 // this.trigger.setStyle('left', x+'px');
11151 adjustSize : Roo.BoxComponent.prototype.adjustSize,
11154 getResizeEl : function(){
11155 return this.inputEl();
11159 getPositionEl : function(){
11160 return this.inputEl();
11164 alignErrorIcon : function(){
11165 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
11169 initEvents : function(){
11173 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
11174 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
11175 if(!this.multiple && this.showToggleBtn){
11176 this.trigger = this.el.select('span.dropdown-toggle',true).first();
11177 if(this.hideTrigger){
11178 this.trigger.setDisplayed(false);
11180 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
11184 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
11187 if(this.removable && !this.editable && !this.tickable){
11188 var close = this.closeTriggerEl();
11191 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
11192 close.on('click', this.removeBtnClick, this, close);
11196 //this.trigger.addClassOnOver('x-form-trigger-over');
11197 //this.trigger.addClassOnClick('x-form-trigger-click');
11200 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
11204 closeTriggerEl : function()
11206 var close = this.el.select('.roo-combo-removable-btn', true).first();
11207 return close ? close : false;
11210 removeBtnClick : function(e, h, el)
11212 e.preventDefault();
11214 if(this.fireEvent("remove", this) !== false){
11216 this.fireEvent("afterremove", this)
11220 createList : function()
11222 this.list = Roo.get(document.body).createChild({
11223 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
11224 cls: 'typeahead typeahead-long dropdown-menu',
11225 style: 'display:none'
11228 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
11233 initTrigger : function(){
11238 onDestroy : function(){
11240 this.trigger.removeAllListeners();
11241 // this.trigger.remove();
11244 // this.wrap.remove();
11246 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
11250 onFocus : function(){
11251 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
11253 if(!this.mimicing){
11254 this.wrap.addClass('x-trigger-wrap-focus');
11255 this.mimicing = true;
11256 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
11257 if(this.monitorTab){
11258 this.el.on("keydown", this.checkTab, this);
11265 checkTab : function(e){
11266 if(e.getKey() == e.TAB){
11267 this.triggerBlur();
11272 onBlur : function(){
11277 mimicBlur : function(e, t){
11279 if(!this.wrap.contains(t) && this.validateBlur()){
11280 this.triggerBlur();
11286 triggerBlur : function(){
11287 this.mimicing = false;
11288 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
11289 if(this.monitorTab){
11290 this.el.un("keydown", this.checkTab, this);
11292 //this.wrap.removeClass('x-trigger-wrap-focus');
11293 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11297 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11298 validateBlur : function(e, t){
11303 onDisable : function(){
11304 this.inputEl().dom.disabled = true;
11305 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11307 // this.wrap.addClass('x-item-disabled');
11312 onEnable : function(){
11313 this.inputEl().dom.disabled = false;
11314 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11316 // this.el.removeClass('x-item-disabled');
11321 onShow : function(){
11322 var ae = this.getActionEl();
11325 ae.dom.style.display = '';
11326 ae.dom.style.visibility = 'visible';
11332 onHide : function(){
11333 var ae = this.getActionEl();
11334 ae.dom.style.display = 'none';
11338 * The function that should handle the trigger's click event. This method does nothing by default until overridden
11339 * by an implementing function.
11341 * @param {EventObject} e
11343 onTriggerClick : Roo.emptyFn
11347 * Ext JS Library 1.1.1
11348 * Copyright(c) 2006-2007, Ext JS, LLC.
11350 * Originally Released Under LGPL - original licence link has changed is not relivant.
11353 * <script type="text/javascript">
11358 * @class Roo.data.SortTypes
11360 * Defines the default sorting (casting?) comparison functions used when sorting data.
11362 Roo.data.SortTypes = {
11364 * Default sort that does nothing
11365 * @param {Mixed} s The value being converted
11366 * @return {Mixed} The comparison value
11368 none : function(s){
11373 * The regular expression used to strip tags
11377 stripTagsRE : /<\/?[^>]+>/gi,
11380 * Strips all HTML tags to sort on text only
11381 * @param {Mixed} s The value being converted
11382 * @return {String} The comparison value
11384 asText : function(s){
11385 return String(s).replace(this.stripTagsRE, "");
11389 * Strips all HTML tags to sort on text only - Case insensitive
11390 * @param {Mixed} s The value being converted
11391 * @return {String} The comparison value
11393 asUCText : function(s){
11394 return String(s).toUpperCase().replace(this.stripTagsRE, "");
11398 * Case insensitive string
11399 * @param {Mixed} s The value being converted
11400 * @return {String} The comparison value
11402 asUCString : function(s) {
11403 return String(s).toUpperCase();
11408 * @param {Mixed} s The value being converted
11409 * @return {Number} The comparison value
11411 asDate : function(s) {
11415 if(s instanceof Date){
11416 return s.getTime();
11418 return Date.parse(String(s));
11423 * @param {Mixed} s The value being converted
11424 * @return {Float} The comparison value
11426 asFloat : function(s) {
11427 var val = parseFloat(String(s).replace(/,/g, ""));
11436 * @param {Mixed} s The value being converted
11437 * @return {Number} The comparison value
11439 asInt : function(s) {
11440 var val = parseInt(String(s).replace(/,/g, ""));
11448 * Ext JS Library 1.1.1
11449 * Copyright(c) 2006-2007, Ext JS, LLC.
11451 * Originally Released Under LGPL - original licence link has changed is not relivant.
11454 * <script type="text/javascript">
11458 * @class Roo.data.Record
11459 * Instances of this class encapsulate both record <em>definition</em> information, and record
11460 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11461 * to access Records cached in an {@link Roo.data.Store} object.<br>
11463 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11464 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11467 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11469 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11470 * {@link #create}. The parameters are the same.
11471 * @param {Array} data An associative Array of data values keyed by the field name.
11472 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11473 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11474 * not specified an integer id is generated.
11476 Roo.data.Record = function(data, id){
11477 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11482 * Generate a constructor for a specific record layout.
11483 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11484 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11485 * Each field definition object may contain the following properties: <ul>
11486 * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
11487 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11488 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11489 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11490 * is being used, then this is a string containing the javascript expression to reference the data relative to
11491 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11492 * to the data item relative to the record element. If the mapping expression is the same as the field name,
11493 * this may be omitted.</p></li>
11494 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11495 * <ul><li>auto (Default, implies no conversion)</li>
11500 * <li>date</li></ul></p></li>
11501 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11502 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11503 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11504 * by the Reader into an object that will be stored in the Record. It is passed the
11505 * following parameters:<ul>
11506 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11508 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11510 * <br>usage:<br><pre><code>
11511 var TopicRecord = Roo.data.Record.create(
11512 {name: 'title', mapping: 'topic_title'},
11513 {name: 'author', mapping: 'username'},
11514 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11515 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11516 {name: 'lastPoster', mapping: 'user2'},
11517 {name: 'excerpt', mapping: 'post_text'}
11520 var myNewRecord = new TopicRecord({
11521 title: 'Do my job please',
11524 lastPost: new Date(),
11525 lastPoster: 'Animal',
11526 excerpt: 'No way dude!'
11528 myStore.add(myNewRecord);
11533 Roo.data.Record.create = function(o){
11534 var f = function(){
11535 f.superclass.constructor.apply(this, arguments);
11537 Roo.extend(f, Roo.data.Record);
11538 var p = f.prototype;
11539 p.fields = new Roo.util.MixedCollection(false, function(field){
11542 for(var i = 0, len = o.length; i < len; i++){
11543 p.fields.add(new Roo.data.Field(o[i]));
11545 f.getField = function(name){
11546 return p.fields.get(name);
11551 Roo.data.Record.AUTO_ID = 1000;
11552 Roo.data.Record.EDIT = 'edit';
11553 Roo.data.Record.REJECT = 'reject';
11554 Roo.data.Record.COMMIT = 'commit';
11556 Roo.data.Record.prototype = {
11558 * Readonly flag - true if this record has been modified.
11567 join : function(store){
11568 this.store = store;
11572 * Set the named field to the specified value.
11573 * @param {String} name The name of the field to set.
11574 * @param {Object} value The value to set the field to.
11576 set : function(name, value){
11577 if(this.data[name] == value){
11581 if(!this.modified){
11582 this.modified = {};
11584 if(typeof this.modified[name] == 'undefined'){
11585 this.modified[name] = this.data[name];
11587 this.data[name] = value;
11588 if(!this.editing && this.store){
11589 this.store.afterEdit(this);
11594 * Get the value of the named field.
11595 * @param {String} name The name of the field to get the value of.
11596 * @return {Object} The value of the field.
11598 get : function(name){
11599 return this.data[name];
11603 beginEdit : function(){
11604 this.editing = true;
11605 this.modified = {};
11609 cancelEdit : function(){
11610 this.editing = false;
11611 delete this.modified;
11615 endEdit : function(){
11616 this.editing = false;
11617 if(this.dirty && this.store){
11618 this.store.afterEdit(this);
11623 * Usually called by the {@link Roo.data.Store} which owns the Record.
11624 * Rejects all changes made to the Record since either creation, or the last commit operation.
11625 * Modified fields are reverted to their original values.
11627 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11628 * of reject operations.
11630 reject : function(){
11631 var m = this.modified;
11633 if(typeof m[n] != "function"){
11634 this.data[n] = m[n];
11637 this.dirty = false;
11638 delete this.modified;
11639 this.editing = false;
11641 this.store.afterReject(this);
11646 * Usually called by the {@link Roo.data.Store} which owns the Record.
11647 * Commits all changes made to the Record since either creation, or the last commit operation.
11649 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11650 * of commit operations.
11652 commit : function(){
11653 this.dirty = false;
11654 delete this.modified;
11655 this.editing = false;
11657 this.store.afterCommit(this);
11662 hasError : function(){
11663 return this.error != null;
11667 clearError : function(){
11672 * Creates a copy of this record.
11673 * @param {String} id (optional) A new record id if you don't want to use this record's id
11676 copy : function(newId) {
11677 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11681 * Ext JS Library 1.1.1
11682 * Copyright(c) 2006-2007, Ext JS, LLC.
11684 * Originally Released Under LGPL - original licence link has changed is not relivant.
11687 * <script type="text/javascript">
11693 * @class Roo.data.Store
11694 * @extends Roo.util.Observable
11695 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11696 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11698 * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
11699 * has no knowledge of the format of the data returned by the Proxy.<br>
11701 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11702 * instances from the data object. These records are cached and made available through accessor functions.
11704 * Creates a new Store.
11705 * @param {Object} config A config object containing the objects needed for the Store to access data,
11706 * and read the data into Records.
11708 Roo.data.Store = function(config){
11709 this.data = new Roo.util.MixedCollection(false);
11710 this.data.getKey = function(o){
11713 this.baseParams = {};
11715 this.paramNames = {
11720 "multisort" : "_multisort"
11723 if(config && config.data){
11724 this.inlineData = config.data;
11725 delete config.data;
11728 Roo.apply(this, config);
11730 if(this.reader){ // reader passed
11731 this.reader = Roo.factory(this.reader, Roo.data);
11732 this.reader.xmodule = this.xmodule || false;
11733 if(!this.recordType){
11734 this.recordType = this.reader.recordType;
11736 if(this.reader.onMetaChange){
11737 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11741 if(this.recordType){
11742 this.fields = this.recordType.prototype.fields;
11744 this.modified = [];
11748 * @event datachanged
11749 * Fires when the data cache has changed, and a widget which is using this Store
11750 * as a Record cache should refresh its view.
11751 * @param {Store} this
11753 datachanged : true,
11755 * @event metachange
11756 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11757 * @param {Store} this
11758 * @param {Object} meta The JSON metadata
11763 * Fires when Records have been added to the Store
11764 * @param {Store} this
11765 * @param {Roo.data.Record[]} records The array of Records added
11766 * @param {Number} index The index at which the record(s) were added
11771 * Fires when a Record has been removed from the Store
11772 * @param {Store} this
11773 * @param {Roo.data.Record} record The Record that was removed
11774 * @param {Number} index The index at which the record was removed
11779 * Fires when a Record has been updated
11780 * @param {Store} this
11781 * @param {Roo.data.Record} record The Record that was updated
11782 * @param {String} operation The update operation being performed. Value may be one of:
11784 Roo.data.Record.EDIT
11785 Roo.data.Record.REJECT
11786 Roo.data.Record.COMMIT
11792 * Fires when the data cache has been cleared.
11793 * @param {Store} this
11797 * @event beforeload
11798 * Fires before a request is made for a new data object. If the beforeload handler returns false
11799 * the load action will be canceled.
11800 * @param {Store} this
11801 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11805 * @event beforeloadadd
11806 * Fires after a new set of Records has been loaded.
11807 * @param {Store} this
11808 * @param {Roo.data.Record[]} records The Records that were loaded
11809 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11811 beforeloadadd : true,
11814 * Fires after a new set of Records has been loaded, before they are added to the store.
11815 * @param {Store} this
11816 * @param {Roo.data.Record[]} records The Records that were loaded
11817 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11818 * @params {Object} return from reader
11822 * @event loadexception
11823 * Fires if an exception occurs in the Proxy during loading.
11824 * Called with the signature of the Proxy's "loadexception" event.
11825 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11828 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11829 * @param {Object} load options
11830 * @param {Object} jsonData from your request (normally this contains the Exception)
11832 loadexception : true
11836 this.proxy = Roo.factory(this.proxy, Roo.data);
11837 this.proxy.xmodule = this.xmodule || false;
11838 this.relayEvents(this.proxy, ["loadexception"]);
11840 this.sortToggle = {};
11841 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11843 Roo.data.Store.superclass.constructor.call(this);
11845 if(this.inlineData){
11846 this.loadData(this.inlineData);
11847 delete this.inlineData;
11851 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11853 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11854 * without a remote query - used by combo/forms at present.
11858 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11861 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11864 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11865 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11868 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11869 * on any HTTP request
11872 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11875 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11879 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11880 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11882 remoteSort : false,
11885 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11886 * loaded or when a record is removed. (defaults to false).
11888 pruneModifiedRecords : false,
11891 lastOptions : null,
11894 * Add Records to the Store and fires the add event.
11895 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11897 add : function(records){
11898 records = [].concat(records);
11899 for(var i = 0, len = records.length; i < len; i++){
11900 records[i].join(this);
11902 var index = this.data.length;
11903 this.data.addAll(records);
11904 this.fireEvent("add", this, records, index);
11908 * Remove a Record from the Store and fires the remove event.
11909 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11911 remove : function(record){
11912 var index = this.data.indexOf(record);
11913 this.data.removeAt(index);
11915 if(this.pruneModifiedRecords){
11916 this.modified.remove(record);
11918 this.fireEvent("remove", this, record, index);
11922 * Remove all Records from the Store and fires the clear event.
11924 removeAll : function(){
11926 if(this.pruneModifiedRecords){
11927 this.modified = [];
11929 this.fireEvent("clear", this);
11933 * Inserts Records to the Store at the given index and fires the add event.
11934 * @param {Number} index The start index at which to insert the passed Records.
11935 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11937 insert : function(index, records){
11938 records = [].concat(records);
11939 for(var i = 0, len = records.length; i < len; i++){
11940 this.data.insert(index, records[i]);
11941 records[i].join(this);
11943 this.fireEvent("add", this, records, index);
11947 * Get the index within the cache of the passed Record.
11948 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11949 * @return {Number} The index of the passed Record. Returns -1 if not found.
11951 indexOf : function(record){
11952 return this.data.indexOf(record);
11956 * Get the index within the cache of the Record with the passed id.
11957 * @param {String} id The id of the Record to find.
11958 * @return {Number} The index of the Record. Returns -1 if not found.
11960 indexOfId : function(id){
11961 return this.data.indexOfKey(id);
11965 * Get the Record with the specified id.
11966 * @param {String} id The id of the Record to find.
11967 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11969 getById : function(id){
11970 return this.data.key(id);
11974 * Get the Record at the specified index.
11975 * @param {Number} index The index of the Record to find.
11976 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11978 getAt : function(index){
11979 return this.data.itemAt(index);
11983 * Returns a range of Records between specified indices.
11984 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11985 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11986 * @return {Roo.data.Record[]} An array of Records
11988 getRange : function(start, end){
11989 return this.data.getRange(start, end);
11993 storeOptions : function(o){
11994 o = Roo.apply({}, o);
11997 this.lastOptions = o;
12001 * Loads the Record cache from the configured Proxy using the configured Reader.
12003 * If using remote paging, then the first load call must specify the <em>start</em>
12004 * and <em>limit</em> properties in the options.params property to establish the initial
12005 * position within the dataset, and the number of Records to cache on each read from the Proxy.
12007 * <strong>It is important to note that for remote data sources, loading is asynchronous,
12008 * and this call will return before the new data has been loaded. Perform any post-processing
12009 * in a callback function, or in a "load" event handler.</strong>
12011 * @param {Object} options An object containing properties which control loading options:<ul>
12012 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
12013 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
12014 * passed the following arguments:<ul>
12015 * <li>r : Roo.data.Record[]</li>
12016 * <li>options: Options object from the load call</li>
12017 * <li>success: Boolean success indicator</li></ul></li>
12018 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
12019 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
12022 load : function(options){
12023 options = options || {};
12024 if(this.fireEvent("beforeload", this, options) !== false){
12025 this.storeOptions(options);
12026 var p = Roo.apply(options.params || {}, this.baseParams);
12027 // if meta was not loaded from remote source.. try requesting it.
12028 if (!this.reader.metaFromRemote) {
12029 p._requestMeta = 1;
12031 if(this.sortInfo && this.remoteSort){
12032 var pn = this.paramNames;
12033 p[pn["sort"]] = this.sortInfo.field;
12034 p[pn["dir"]] = this.sortInfo.direction;
12036 if (this.multiSort) {
12037 var pn = this.paramNames;
12038 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
12041 this.proxy.load(p, this.reader, this.loadRecords, this, options);
12046 * Reloads the Record cache from the configured Proxy using the configured Reader and
12047 * the options from the last load operation performed.
12048 * @param {Object} options (optional) An object containing properties which may override the options
12049 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
12050 * the most recently used options are reused).
12052 reload : function(options){
12053 this.load(Roo.applyIf(options||{}, this.lastOptions));
12057 // Called as a callback by the Reader during a load operation.
12058 loadRecords : function(o, options, success){
12059 if(!o || success === false){
12060 if(success !== false){
12061 this.fireEvent("load", this, [], options, o);
12063 if(options.callback){
12064 options.callback.call(options.scope || this, [], options, false);
12068 // if data returned failure - throw an exception.
12069 if (o.success === false) {
12070 // show a message if no listener is registered.
12071 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
12072 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
12074 // loadmask wil be hooked into this..
12075 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
12078 var r = o.records, t = o.totalRecords || r.length;
12080 this.fireEvent("beforeloadadd", this, r, options, o);
12082 if(!options || options.add !== true){
12083 if(this.pruneModifiedRecords){
12084 this.modified = [];
12086 for(var i = 0, len = r.length; i < len; i++){
12090 this.data = this.snapshot;
12091 delete this.snapshot;
12094 this.data.addAll(r);
12095 this.totalLength = t;
12097 this.fireEvent("datachanged", this);
12099 this.totalLength = Math.max(t, this.data.length+r.length);
12103 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
12105 var e = new Roo.data.Record({});
12107 e.set(this.parent.displayField, this.parent.emptyTitle);
12108 e.set(this.parent.valueField, '');
12113 this.fireEvent("load", this, r, options, o);
12114 if(options.callback){
12115 options.callback.call(options.scope || this, r, options, true);
12121 * Loads data from a passed data block. A Reader which understands the format of the data
12122 * must have been configured in the constructor.
12123 * @param {Object} data The data block from which to read the Records. The format of the data expected
12124 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
12125 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
12127 loadData : function(o, append){
12128 var r = this.reader.readRecords(o);
12129 this.loadRecords(r, {add: append}, true);
12133 * using 'cn' the nested child reader read the child array into it's child stores.
12134 * @param {Object} rec The record with a 'children array
12136 loadDataFromChildren : function(rec)
12138 this.loadData(this.reader.toLoadData(rec));
12143 * Gets the number of cached records.
12145 * <em>If using paging, this may not be the total size of the dataset. If the data object
12146 * used by the Reader contains the dataset size, then the getTotalCount() function returns
12147 * the data set size</em>
12149 getCount : function(){
12150 return this.data.length || 0;
12154 * Gets the total number of records in the dataset as returned by the server.
12156 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
12157 * the dataset size</em>
12159 getTotalCount : function(){
12160 return this.totalLength || 0;
12164 * Returns the sort state of the Store as an object with two properties:
12166 field {String} The name of the field by which the Records are sorted
12167 direction {String} The sort order, "ASC" or "DESC"
12170 getSortState : function(){
12171 return this.sortInfo;
12175 applySort : function(){
12176 if(this.sortInfo && !this.remoteSort){
12177 var s = this.sortInfo, f = s.field;
12178 var st = this.fields.get(f).sortType;
12179 var fn = function(r1, r2){
12180 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
12181 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12183 this.data.sort(s.direction, fn);
12184 if(this.snapshot && this.snapshot != this.data){
12185 this.snapshot.sort(s.direction, fn);
12191 * Sets the default sort column and order to be used by the next load operation.
12192 * @param {String} fieldName The name of the field to sort by.
12193 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12195 setDefaultSort : function(field, dir){
12196 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
12200 * Sort the Records.
12201 * If remote sorting is used, the sort is performed on the server, and the cache is
12202 * reloaded. If local sorting is used, the cache is sorted internally.
12203 * @param {String} fieldName The name of the field to sort by.
12204 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12206 sort : function(fieldName, dir){
12207 var f = this.fields.get(fieldName);
12209 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
12211 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
12212 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
12217 this.sortToggle[f.name] = dir;
12218 this.sortInfo = {field: f.name, direction: dir};
12219 if(!this.remoteSort){
12221 this.fireEvent("datachanged", this);
12223 this.load(this.lastOptions);
12228 * Calls the specified function for each of the Records in the cache.
12229 * @param {Function} fn The function to call. The Record is passed as the first parameter.
12230 * Returning <em>false</em> aborts and exits the iteration.
12231 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
12233 each : function(fn, scope){
12234 this.data.each(fn, scope);
12238 * Gets all records modified since the last commit. Modified records are persisted across load operations
12239 * (e.g., during paging).
12240 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
12242 getModifiedRecords : function(){
12243 return this.modified;
12247 createFilterFn : function(property, value, anyMatch){
12248 if(!value.exec){ // not a regex
12249 value = String(value);
12250 if(value.length == 0){
12253 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
12255 return function(r){
12256 return value.test(r.data[property]);
12261 * Sums the value of <i>property</i> for each record between start and end and returns the result.
12262 * @param {String} property A field on your records
12263 * @param {Number} start The record index to start at (defaults to 0)
12264 * @param {Number} end The last record index to include (defaults to length - 1)
12265 * @return {Number} The sum
12267 sum : function(property, start, end){
12268 var rs = this.data.items, v = 0;
12269 start = start || 0;
12270 end = (end || end === 0) ? end : rs.length-1;
12272 for(var i = start; i <= end; i++){
12273 v += (rs[i].data[property] || 0);
12279 * Filter the records by a specified property.
12280 * @param {String} field A field on your records
12281 * @param {String/RegExp} value Either a string that the field
12282 * should start with or a RegExp to test against the field
12283 * @param {Boolean} anyMatch True to match any part not just the beginning
12285 filter : function(property, value, anyMatch){
12286 var fn = this.createFilterFn(property, value, anyMatch);
12287 return fn ? this.filterBy(fn) : this.clearFilter();
12291 * Filter by a function. The specified function will be called with each
12292 * record in this data source. If the function returns true the record is included,
12293 * otherwise it is filtered.
12294 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12295 * @param {Object} scope (optional) The scope of the function (defaults to this)
12297 filterBy : function(fn, scope){
12298 this.snapshot = this.snapshot || this.data;
12299 this.data = this.queryBy(fn, scope||this);
12300 this.fireEvent("datachanged", this);
12304 * Query the records by a specified property.
12305 * @param {String} field A field on your records
12306 * @param {String/RegExp} value Either a string that the field
12307 * should start with or a RegExp to test against the field
12308 * @param {Boolean} anyMatch True to match any part not just the beginning
12309 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12311 query : function(property, value, anyMatch){
12312 var fn = this.createFilterFn(property, value, anyMatch);
12313 return fn ? this.queryBy(fn) : this.data.clone();
12317 * Query by a function. The specified function will be called with each
12318 * record in this data source. If the function returns true the record is included
12320 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12321 * @param {Object} scope (optional) The scope of the function (defaults to this)
12322 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12324 queryBy : function(fn, scope){
12325 var data = this.snapshot || this.data;
12326 return data.filterBy(fn, scope||this);
12330 * Collects unique values for a particular dataIndex from this store.
12331 * @param {String} dataIndex The property to collect
12332 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12333 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12334 * @return {Array} An array of the unique values
12336 collect : function(dataIndex, allowNull, bypassFilter){
12337 var d = (bypassFilter === true && this.snapshot) ?
12338 this.snapshot.items : this.data.items;
12339 var v, sv, r = [], l = {};
12340 for(var i = 0, len = d.length; i < len; i++){
12341 v = d[i].data[dataIndex];
12343 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12352 * Revert to a view of the Record cache with no filtering applied.
12353 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12355 clearFilter : function(suppressEvent){
12356 if(this.snapshot && this.snapshot != this.data){
12357 this.data = this.snapshot;
12358 delete this.snapshot;
12359 if(suppressEvent !== true){
12360 this.fireEvent("datachanged", this);
12366 afterEdit : function(record){
12367 if(this.modified.indexOf(record) == -1){
12368 this.modified.push(record);
12370 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12374 afterReject : function(record){
12375 this.modified.remove(record);
12376 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12380 afterCommit : function(record){
12381 this.modified.remove(record);
12382 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12386 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12387 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12389 commitChanges : function(){
12390 var m = this.modified.slice(0);
12391 this.modified = [];
12392 for(var i = 0, len = m.length; i < len; i++){
12398 * Cancel outstanding changes on all changed records.
12400 rejectChanges : function(){
12401 var m = this.modified.slice(0);
12402 this.modified = [];
12403 for(var i = 0, len = m.length; i < len; i++){
12408 onMetaChange : function(meta, rtype, o){
12409 this.recordType = rtype;
12410 this.fields = rtype.prototype.fields;
12411 delete this.snapshot;
12412 this.sortInfo = meta.sortInfo || this.sortInfo;
12413 this.modified = [];
12414 this.fireEvent('metachange', this, this.reader.meta);
12417 moveIndex : function(data, type)
12419 var index = this.indexOf(data);
12421 var newIndex = index + type;
12425 this.insert(newIndex, data);
12430 * Ext JS Library 1.1.1
12431 * Copyright(c) 2006-2007, Ext JS, LLC.
12433 * Originally Released Under LGPL - original licence link has changed is not relivant.
12436 * <script type="text/javascript">
12440 * @class Roo.data.SimpleStore
12441 * @extends Roo.data.Store
12442 * Small helper class to make creating Stores from Array data easier.
12443 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12444 * @cfg {Array} fields An array of field definition objects, or field name strings.
12445 * @cfg {Object} an existing reader (eg. copied from another store)
12446 * @cfg {Array} data The multi-dimensional array of data
12448 * @param {Object} config
12450 Roo.data.SimpleStore = function(config)
12452 Roo.data.SimpleStore.superclass.constructor.call(this, {
12454 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
12457 Roo.data.Record.create(config.fields)
12459 proxy : new Roo.data.MemoryProxy(config.data)
12463 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12465 * Ext JS Library 1.1.1
12466 * Copyright(c) 2006-2007, Ext JS, LLC.
12468 * Originally Released Under LGPL - original licence link has changed is not relivant.
12471 * <script type="text/javascript">
12476 * @extends Roo.data.Store
12477 * @class Roo.data.JsonStore
12478 * Small helper class to make creating Stores for JSON data easier. <br/>
12480 var store = new Roo.data.JsonStore({
12481 url: 'get-images.php',
12483 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12486 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12487 * JsonReader and HttpProxy (unless inline data is provided).</b>
12488 * @cfg {Array} fields An array of field definition objects, or field name strings.
12490 * @param {Object} config
12492 Roo.data.JsonStore = function(c){
12493 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12494 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12495 reader: new Roo.data.JsonReader(c, c.fields)
12498 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12500 * Ext JS Library 1.1.1
12501 * Copyright(c) 2006-2007, Ext JS, LLC.
12503 * Originally Released Under LGPL - original licence link has changed is not relivant.
12506 * <script type="text/javascript">
12510 Roo.data.Field = function(config){
12511 if(typeof config == "string"){
12512 config = {name: config};
12514 Roo.apply(this, config);
12517 this.type = "auto";
12520 var st = Roo.data.SortTypes;
12521 // named sortTypes are supported, here we look them up
12522 if(typeof this.sortType == "string"){
12523 this.sortType = st[this.sortType];
12526 // set default sortType for strings and dates
12527 if(!this.sortType){
12530 this.sortType = st.asUCString;
12533 this.sortType = st.asDate;
12536 this.sortType = st.none;
12541 var stripRe = /[\$,%]/g;
12543 // prebuilt conversion function for this field, instead of
12544 // switching every time we're reading a value
12546 var cv, dateFormat = this.dateFormat;
12551 cv = function(v){ return v; };
12554 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12558 return v !== undefined && v !== null && v !== '' ?
12559 parseInt(String(v).replace(stripRe, ""), 10) : '';
12564 return v !== undefined && v !== null && v !== '' ?
12565 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12570 cv = function(v){ return v === true || v === "true" || v == 1; };
12577 if(v instanceof Date){
12581 if(dateFormat == "timestamp"){
12582 return new Date(v*1000);
12584 return Date.parseDate(v, dateFormat);
12586 var parsed = Date.parse(v);
12587 return parsed ? new Date(parsed) : null;
12596 Roo.data.Field.prototype = {
12604 * Ext JS Library 1.1.1
12605 * Copyright(c) 2006-2007, Ext JS, LLC.
12607 * Originally Released Under LGPL - original licence link has changed is not relivant.
12610 * <script type="text/javascript">
12613 // Base class for reading structured data from a data source. This class is intended to be
12614 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12617 * @class Roo.data.DataReader
12618 * Base class for reading structured data from a data source. This class is intended to be
12619 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12622 Roo.data.DataReader = function(meta, recordType){
12626 this.recordType = recordType instanceof Array ?
12627 Roo.data.Record.create(recordType) : recordType;
12630 Roo.data.DataReader.prototype = {
12633 readerType : 'Data',
12635 * Create an empty record
12636 * @param {Object} data (optional) - overlay some values
12637 * @return {Roo.data.Record} record created.
12639 newRow : function(d) {
12641 this.recordType.prototype.fields.each(function(c) {
12643 case 'int' : da[c.name] = 0; break;
12644 case 'date' : da[c.name] = new Date(); break;
12645 case 'float' : da[c.name] = 0.0; break;
12646 case 'boolean' : da[c.name] = false; break;
12647 default : da[c.name] = ""; break;
12651 return new this.recordType(Roo.apply(da, d));
12657 * Ext JS Library 1.1.1
12658 * Copyright(c) 2006-2007, Ext JS, LLC.
12660 * Originally Released Under LGPL - original licence link has changed is not relivant.
12663 * <script type="text/javascript">
12667 * @class Roo.data.DataProxy
12668 * @extends Roo.data.Observable
12669 * This class is an abstract base class for implementations which provide retrieval of
12670 * unformatted data objects.<br>
12672 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12673 * (of the appropriate type which knows how to parse the data object) to provide a block of
12674 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12676 * Custom implementations must implement the load method as described in
12677 * {@link Roo.data.HttpProxy#load}.
12679 Roo.data.DataProxy = function(){
12682 * @event beforeload
12683 * Fires before a network request is made to retrieve a data object.
12684 * @param {Object} This DataProxy object.
12685 * @param {Object} params The params parameter to the load function.
12690 * Fires before the load method's callback is called.
12691 * @param {Object} This DataProxy object.
12692 * @param {Object} o The data object.
12693 * @param {Object} arg The callback argument object passed to the load function.
12697 * @event loadexception
12698 * Fires if an Exception occurs during data retrieval.
12699 * @param {Object} This DataProxy object.
12700 * @param {Object} o The data object.
12701 * @param {Object} arg The callback argument object passed to the load function.
12702 * @param {Object} e The Exception.
12704 loadexception : true
12706 Roo.data.DataProxy.superclass.constructor.call(this);
12709 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12712 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12716 * Ext JS Library 1.1.1
12717 * Copyright(c) 2006-2007, Ext JS, LLC.
12719 * Originally Released Under LGPL - original licence link has changed is not relivant.
12722 * <script type="text/javascript">
12725 * @class Roo.data.MemoryProxy
12726 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12727 * to the Reader when its load method is called.
12729 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12731 Roo.data.MemoryProxy = function(data){
12735 Roo.data.MemoryProxy.superclass.constructor.call(this);
12739 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12742 * Load data from the requested source (in this case an in-memory
12743 * data object passed to the constructor), read the data object into
12744 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12745 * process that block using the passed callback.
12746 * @param {Object} params This parameter is not used by the MemoryProxy class.
12747 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12748 * object into a block of Roo.data.Records.
12749 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12750 * The function must be passed <ul>
12751 * <li>The Record block object</li>
12752 * <li>The "arg" argument from the load function</li>
12753 * <li>A boolean success indicator</li>
12755 * @param {Object} scope The scope in which to call the callback
12756 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12758 load : function(params, reader, callback, scope, arg){
12759 params = params || {};
12762 result = reader.readRecords(params.data ? params.data :this.data);
12764 this.fireEvent("loadexception", this, arg, null, e);
12765 callback.call(scope, null, arg, false);
12768 callback.call(scope, result, arg, true);
12772 update : function(params, records){
12777 * Ext JS Library 1.1.1
12778 * Copyright(c) 2006-2007, Ext JS, LLC.
12780 * Originally Released Under LGPL - original licence link has changed is not relivant.
12783 * <script type="text/javascript">
12786 * @class Roo.data.HttpProxy
12787 * @extends Roo.data.DataProxy
12788 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12789 * configured to reference a certain URL.<br><br>
12791 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12792 * from which the running page was served.<br><br>
12794 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12796 * Be aware that to enable the browser to parse an XML document, the server must set
12797 * the Content-Type header in the HTTP response to "text/xml".
12799 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12800 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12801 * will be used to make the request.
12803 Roo.data.HttpProxy = function(conn){
12804 Roo.data.HttpProxy.superclass.constructor.call(this);
12805 // is conn a conn config or a real conn?
12807 this.useAjax = !conn || !conn.events;
12811 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12812 // thse are take from connection...
12815 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12818 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12819 * extra parameters to each request made by this object. (defaults to undefined)
12822 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12823 * to each request made by this object. (defaults to undefined)
12826 * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
12829 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12832 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12838 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12842 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12843 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12844 * a finer-grained basis than the DataProxy events.
12846 getConnection : function(){
12847 return this.useAjax ? Roo.Ajax : this.conn;
12851 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12852 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12853 * process that block using the passed callback.
12854 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12855 * for the request to the remote server.
12856 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12857 * object into a block of Roo.data.Records.
12858 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12859 * The function must be passed <ul>
12860 * <li>The Record block object</li>
12861 * <li>The "arg" argument from the load function</li>
12862 * <li>A boolean success indicator</li>
12864 * @param {Object} scope The scope in which to call the callback
12865 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12867 load : function(params, reader, callback, scope, arg){
12868 if(this.fireEvent("beforeload", this, params) !== false){
12870 params : params || {},
12872 callback : callback,
12877 callback : this.loadResponse,
12881 Roo.applyIf(o, this.conn);
12882 if(this.activeRequest){
12883 Roo.Ajax.abort(this.activeRequest);
12885 this.activeRequest = Roo.Ajax.request(o);
12887 this.conn.request(o);
12890 callback.call(scope||this, null, arg, false);
12895 loadResponse : function(o, success, response){
12896 delete this.activeRequest;
12898 this.fireEvent("loadexception", this, o, response);
12899 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12904 result = o.reader.read(response);
12906 this.fireEvent("loadexception", this, o, response, e);
12907 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12911 this.fireEvent("load", this, o, o.request.arg);
12912 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12916 update : function(dataSet){
12921 updateResponse : function(dataSet){
12926 * Ext JS Library 1.1.1
12927 * Copyright(c) 2006-2007, Ext JS, LLC.
12929 * Originally Released Under LGPL - original licence link has changed is not relivant.
12932 * <script type="text/javascript">
12936 * @class Roo.data.ScriptTagProxy
12937 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12938 * other than the originating domain of the running page.<br><br>
12940 * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
12941 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12943 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12944 * source code that is used as the source inside a <script> tag.<br><br>
12946 * In order for the browser to process the returned data, the server must wrap the data object
12947 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12948 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12949 * depending on whether the callback name was passed:
12952 boolean scriptTag = false;
12953 String cb = request.getParameter("callback");
12956 response.setContentType("text/javascript");
12958 response.setContentType("application/x-json");
12960 Writer out = response.getWriter();
12962 out.write(cb + "(");
12964 out.print(dataBlock.toJsonString());
12971 * @param {Object} config A configuration object.
12973 Roo.data.ScriptTagProxy = function(config){
12974 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12975 Roo.apply(this, config);
12976 this.head = document.getElementsByTagName("head")[0];
12979 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12981 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12983 * @cfg {String} url The URL from which to request the data object.
12986 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12990 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12991 * the server the name of the callback function set up by the load call to process the returned data object.
12992 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12993 * javascript output which calls this named function passing the data object as its only parameter.
12995 callbackParam : "callback",
12997 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12998 * name to the request.
13003 * Load data from the configured URL, read the data object into
13004 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13005 * process that block using the passed callback.
13006 * @param {Object} params An object containing properties which are to be used as HTTP parameters
13007 * for the request to the remote server.
13008 * @param {Roo.data.DataReader} reader The Reader object which converts the data
13009 * object into a block of Roo.data.Records.
13010 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
13011 * The function must be passed <ul>
13012 * <li>The Record block object</li>
13013 * <li>The "arg" argument from the load function</li>
13014 * <li>A boolean success indicator</li>
13016 * @param {Object} scope The scope in which to call the callback
13017 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13019 load : function(params, reader, callback, scope, arg){
13020 if(this.fireEvent("beforeload", this, params) !== false){
13022 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
13024 var url = this.url;
13025 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
13027 url += "&_dc=" + (new Date().getTime());
13029 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
13032 cb : "stcCallback"+transId,
13033 scriptId : "stcScript"+transId,
13037 callback : callback,
13043 window[trans.cb] = function(o){
13044 conn.handleResponse(o, trans);
13047 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
13049 if(this.autoAbort !== false){
13053 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
13055 var script = document.createElement("script");
13056 script.setAttribute("src", url);
13057 script.setAttribute("type", "text/javascript");
13058 script.setAttribute("id", trans.scriptId);
13059 this.head.appendChild(script);
13061 this.trans = trans;
13063 callback.call(scope||this, null, arg, false);
13068 isLoading : function(){
13069 return this.trans ? true : false;
13073 * Abort the current server request.
13075 abort : function(){
13076 if(this.isLoading()){
13077 this.destroyTrans(this.trans);
13082 destroyTrans : function(trans, isLoaded){
13083 this.head.removeChild(document.getElementById(trans.scriptId));
13084 clearTimeout(trans.timeoutId);
13086 window[trans.cb] = undefined;
13088 delete window[trans.cb];
13091 // if hasn't been loaded, wait for load to remove it to prevent script error
13092 window[trans.cb] = function(){
13093 window[trans.cb] = undefined;
13095 delete window[trans.cb];
13102 handleResponse : function(o, trans){
13103 this.trans = false;
13104 this.destroyTrans(trans, true);
13107 result = trans.reader.readRecords(o);
13109 this.fireEvent("loadexception", this, o, trans.arg, e);
13110 trans.callback.call(trans.scope||window, null, trans.arg, false);
13113 this.fireEvent("load", this, o, trans.arg);
13114 trans.callback.call(trans.scope||window, result, trans.arg, true);
13118 handleFailure : function(trans){
13119 this.trans = false;
13120 this.destroyTrans(trans, false);
13121 this.fireEvent("loadexception", this, null, trans.arg);
13122 trans.callback.call(trans.scope||window, null, trans.arg, false);
13126 * Ext JS Library 1.1.1
13127 * Copyright(c) 2006-2007, Ext JS, LLC.
13129 * Originally Released Under LGPL - original licence link has changed is not relivant.
13132 * <script type="text/javascript">
13136 * @class Roo.data.JsonReader
13137 * @extends Roo.data.DataReader
13138 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
13139 * based on mappings in a provided Roo.data.Record constructor.
13141 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
13142 * in the reply previously.
13147 var RecordDef = Roo.data.Record.create([
13148 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
13149 {name: 'occupation'} // This field will use "occupation" as the mapping.
13151 var myReader = new Roo.data.JsonReader({
13152 totalProperty: "results", // The property which contains the total dataset size (optional)
13153 root: "rows", // The property which contains an Array of row objects
13154 id: "id" // The property within each row object that provides an ID for the record (optional)
13158 * This would consume a JSON file like this:
13160 { 'results': 2, 'rows': [
13161 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
13162 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
13165 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
13166 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
13167 * paged from the remote server.
13168 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
13169 * @cfg {String} root name of the property which contains the Array of row objects.
13170 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13171 * @cfg {Array} fields Array of field definition objects
13173 * Create a new JsonReader
13174 * @param {Object} meta Metadata configuration options
13175 * @param {Object} recordType Either an Array of field definition objects,
13176 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
13178 Roo.data.JsonReader = function(meta, recordType){
13181 // set some defaults:
13182 Roo.applyIf(meta, {
13183 totalProperty: 'total',
13184 successProperty : 'success',
13189 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13191 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
13193 readerType : 'Json',
13196 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
13197 * Used by Store query builder to append _requestMeta to params.
13200 metaFromRemote : false,
13202 * This method is only used by a DataProxy which has retrieved data from a remote server.
13203 * @param {Object} response The XHR object which contains the JSON data in its responseText.
13204 * @return {Object} data A data block which is used by an Roo.data.Store object as
13205 * a cache of Roo.data.Records.
13207 read : function(response){
13208 var json = response.responseText;
13210 var o = /* eval:var:o */ eval("("+json+")");
13212 throw {message: "JsonReader.read: Json object not found"};
13218 this.metaFromRemote = true;
13219 this.meta = o.metaData;
13220 this.recordType = Roo.data.Record.create(o.metaData.fields);
13221 this.onMetaChange(this.meta, this.recordType, o);
13223 return this.readRecords(o);
13226 // private function a store will implement
13227 onMetaChange : function(meta, recordType, o){
13234 simpleAccess: function(obj, subsc) {
13241 getJsonAccessor: function(){
13243 return function(expr) {
13245 return(re.test(expr))
13246 ? new Function("obj", "return obj." + expr)
13251 return Roo.emptyFn;
13256 * Create a data block containing Roo.data.Records from an XML document.
13257 * @param {Object} o An object which contains an Array of row objects in the property specified
13258 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
13259 * which contains the total size of the dataset.
13260 * @return {Object} data A data block which is used by an Roo.data.Store object as
13261 * a cache of Roo.data.Records.
13263 readRecords : function(o){
13265 * After any data loads, the raw JSON data is available for further custom processing.
13269 var s = this.meta, Record = this.recordType,
13270 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
13272 // Generate extraction functions for the totalProperty, the root, the id, and for each field
13274 if(s.totalProperty) {
13275 this.getTotal = this.getJsonAccessor(s.totalProperty);
13277 if(s.successProperty) {
13278 this.getSuccess = this.getJsonAccessor(s.successProperty);
13280 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
13282 var g = this.getJsonAccessor(s.id);
13283 this.getId = function(rec) {
13285 return (r === undefined || r === "") ? null : r;
13288 this.getId = function(){return null;};
13291 for(var jj = 0; jj < fl; jj++){
13293 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
13294 this.ef[jj] = this.getJsonAccessor(map);
13298 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
13299 if(s.totalProperty){
13300 var vt = parseInt(this.getTotal(o), 10);
13305 if(s.successProperty){
13306 var vs = this.getSuccess(o);
13307 if(vs === false || vs === 'false'){
13312 for(var i = 0; i < c; i++){
13315 var id = this.getId(n);
13316 for(var j = 0; j < fl; j++){
13318 var v = this.ef[j](n);
13320 Roo.log('missing convert for ' + f.name);
13324 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13326 var record = new Record(values, id);
13328 records[i] = record;
13334 totalRecords : totalRecords
13337 // used when loading children.. @see loadDataFromChildren
13338 toLoadData: function(rec)
13340 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13341 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13342 return { data : data, total : data.length };
13347 * Ext JS Library 1.1.1
13348 * Copyright(c) 2006-2007, Ext JS, LLC.
13350 * Originally Released Under LGPL - original licence link has changed is not relivant.
13353 * <script type="text/javascript">
13357 * @class Roo.data.ArrayReader
13358 * @extends Roo.data.DataReader
13359 * Data reader class to create an Array of Roo.data.Record objects from an Array.
13360 * Each element of that Array represents a row of data fields. The
13361 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13362 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13366 var RecordDef = Roo.data.Record.create([
13367 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
13368 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
13370 var myReader = new Roo.data.ArrayReader({
13371 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
13375 * This would consume an Array like this:
13377 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13381 * Create a new JsonReader
13382 * @param {Object} meta Metadata configuration options.
13383 * @param {Object|Array} recordType Either an Array of field definition objects
13385 * @cfg {Array} fields Array of field definition objects
13386 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13387 * as specified to {@link Roo.data.Record#create},
13388 * or an {@link Roo.data.Record} object
13391 * created using {@link Roo.data.Record#create}.
13393 Roo.data.ArrayReader = function(meta, recordType)
13395 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13398 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13401 * Create a data block containing Roo.data.Records from an XML document.
13402 * @param {Object} o An Array of row objects which represents the dataset.
13403 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13404 * a cache of Roo.data.Records.
13406 readRecords : function(o)
13408 var sid = this.meta ? this.meta.id : null;
13409 var recordType = this.recordType, fields = recordType.prototype.fields;
13412 for(var i = 0; i < root.length; i++){
13415 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13416 for(var j = 0, jlen = fields.length; j < jlen; j++){
13417 var f = fields.items[j];
13418 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13419 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13421 values[f.name] = v;
13423 var record = new recordType(values, id);
13425 records[records.length] = record;
13429 totalRecords : records.length
13432 // used when loading children.. @see loadDataFromChildren
13433 toLoadData: function(rec)
13435 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13436 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13447 * @class Roo.bootstrap.ComboBox
13448 * @extends Roo.bootstrap.TriggerField
13449 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13450 * @cfg {Boolean} append (true|false) default false
13451 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13452 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13453 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13454 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13455 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13456 * @cfg {Boolean} animate default true
13457 * @cfg {Boolean} emptyResultText only for touch device
13458 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13459 * @cfg {String} emptyTitle default ''
13461 * Create a new ComboBox.
13462 * @param {Object} config Configuration options
13464 Roo.bootstrap.ComboBox = function(config){
13465 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13469 * Fires when the dropdown list is expanded
13470 * @param {Roo.bootstrap.ComboBox} combo This combo box
13475 * Fires when the dropdown list is collapsed
13476 * @param {Roo.bootstrap.ComboBox} combo This combo box
13480 * @event beforeselect
13481 * Fires before a list item is selected. Return false to cancel the selection.
13482 * @param {Roo.bootstrap.ComboBox} combo This combo box
13483 * @param {Roo.data.Record} record The data record returned from the underlying store
13484 * @param {Number} index The index of the selected item in the dropdown list
13486 'beforeselect' : true,
13489 * Fires when a list item is selected
13490 * @param {Roo.bootstrap.ComboBox} combo This combo box
13491 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13492 * @param {Number} index The index of the selected item in the dropdown list
13496 * @event beforequery
13497 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13498 * The event object passed has these properties:
13499 * @param {Roo.bootstrap.ComboBox} combo This combo box
13500 * @param {String} query The query
13501 * @param {Boolean} forceAll true to force "all" query
13502 * @param {Boolean} cancel true to cancel the query
13503 * @param {Object} e The query event object
13505 'beforequery': true,
13508 * Fires when the 'add' icon is pressed (add a listener to enable add button)
13509 * @param {Roo.bootstrap.ComboBox} combo This combo box
13514 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13515 * @param {Roo.bootstrap.ComboBox} combo This combo box
13516 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13521 * Fires when the remove value from the combobox array
13522 * @param {Roo.bootstrap.ComboBox} combo This combo box
13526 * @event afterremove
13527 * Fires when the remove value from the combobox array
13528 * @param {Roo.bootstrap.ComboBox} combo This combo box
13530 'afterremove' : true,
13532 * @event specialfilter
13533 * Fires when specialfilter
13534 * @param {Roo.bootstrap.ComboBox} combo This combo box
13536 'specialfilter' : true,
13539 * Fires when tick the element
13540 * @param {Roo.bootstrap.ComboBox} combo This combo box
13544 * @event touchviewdisplay
13545 * Fires when touch view require special display (default is using displayField)
13546 * @param {Roo.bootstrap.ComboBox} combo This combo box
13547 * @param {Object} cfg set html .
13549 'touchviewdisplay' : true
13554 this.tickItems = [];
13556 this.selectedIndex = -1;
13557 if(this.mode == 'local'){
13558 if(config.queryDelay === undefined){
13559 this.queryDelay = 10;
13561 if(config.minChars === undefined){
13567 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13570 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13571 * rendering into an Roo.Editor, defaults to false)
13574 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13575 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13578 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13581 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13582 * the dropdown list (defaults to undefined, with no header element)
13586 * @cfg {String/Roo.Template} tpl The template to use to render the output
13590 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13592 listWidth: undefined,
13594 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13595 * mode = 'remote' or 'text' if mode = 'local')
13597 displayField: undefined,
13600 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13601 * mode = 'remote' or 'value' if mode = 'local').
13602 * Note: use of a valueField requires the user make a selection
13603 * in order for a value to be mapped.
13605 valueField: undefined,
13607 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13612 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13613 * field's data value (defaults to the underlying DOM element's name)
13615 hiddenName: undefined,
13617 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13621 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13623 selectedClass: 'active',
13626 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13630 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13631 * anchor positions (defaults to 'tl-bl')
13633 listAlign: 'tl-bl?',
13635 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13639 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13640 * query specified by the allQuery config option (defaults to 'query')
13642 triggerAction: 'query',
13644 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13645 * (defaults to 4, does not apply if editable = false)
13649 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13650 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13654 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13655 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13659 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13660 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13664 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13665 * when editable = true (defaults to false)
13667 selectOnFocus:false,
13669 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13671 queryParam: 'query',
13673 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13674 * when mode = 'remote' (defaults to 'Loading...')
13676 loadingText: 'Loading...',
13678 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13682 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13686 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13687 * traditional select (defaults to true)
13691 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13695 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13699 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13700 * listWidth has a higher value)
13704 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13705 * allow the user to set arbitrary text into the field (defaults to false)
13707 forceSelection:false,
13709 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13710 * if typeAhead = true (defaults to 250)
13712 typeAheadDelay : 250,
13714 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13715 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13717 valueNotFoundText : undefined,
13719 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13721 blockFocus : false,
13724 * @cfg {Boolean} disableClear Disable showing of clear button.
13726 disableClear : false,
13728 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13730 alwaysQuery : false,
13733 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13738 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13740 invalidClass : "has-warning",
13743 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13745 validClass : "has-success",
13748 * @cfg {Boolean} specialFilter (true|false) special filter default false
13750 specialFilter : false,
13753 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13755 mobileTouchView : true,
13758 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13760 useNativeIOS : false,
13763 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13765 mobile_restrict_height : false,
13767 ios_options : false,
13779 btnPosition : 'right',
13780 triggerList : true,
13781 showToggleBtn : true,
13783 emptyResultText: 'Empty',
13784 triggerText : 'Select',
13787 // element that contains real text value.. (when hidden is used..)
13789 getAutoCreate : function()
13794 * Render classic select for iso
13797 if(Roo.isIOS && this.useNativeIOS){
13798 cfg = this.getAutoCreateNativeIOS();
13806 if(Roo.isTouch && this.mobileTouchView){
13807 cfg = this.getAutoCreateTouchView();
13814 if(!this.tickable){
13815 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13820 * ComboBox with tickable selections
13823 var align = this.labelAlign || this.parentLabelAlign();
13826 cls : 'form-group roo-combobox-tickable' //input-group
13829 var btn_text_select = '';
13830 var btn_text_done = '';
13831 var btn_text_cancel = '';
13833 if (this.btn_text_show) {
13834 btn_text_select = 'Select';
13835 btn_text_done = 'Done';
13836 btn_text_cancel = 'Cancel';
13841 cls : 'tickable-buttons',
13846 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13847 //html : this.triggerText
13848 html: btn_text_select
13854 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13856 html: btn_text_done
13862 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13864 html: btn_text_cancel
13870 buttons.cn.unshift({
13872 cls: 'roo-select2-search-field-input'
13878 Roo.each(buttons.cn, function(c){
13880 c.cls += ' btn-' + _this.size;
13883 if (_this.disabled) {
13890 style : 'display: contents',
13895 cls: 'form-hidden-field'
13899 cls: 'roo-select2-choices',
13903 cls: 'roo-select2-search-field',
13914 cls: 'roo-select2-container input-group roo-select2-container-multi',
13920 // cls: 'typeahead typeahead-long dropdown-menu',
13921 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13926 if(this.hasFeedback && !this.allowBlank){
13930 cls: 'glyphicon form-control-feedback'
13933 combobox.cn.push(feedback);
13938 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13939 tooltip : 'This field is required'
13941 if (Roo.bootstrap.version == 4) {
13944 style : 'display:none'
13947 if (align ==='left' && this.fieldLabel.length) {
13949 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13956 cls : 'control-label col-form-label',
13957 html : this.fieldLabel
13969 var labelCfg = cfg.cn[1];
13970 var contentCfg = cfg.cn[2];
13973 if(this.indicatorpos == 'right'){
13979 cls : 'control-label col-form-label',
13983 html : this.fieldLabel
13999 labelCfg = cfg.cn[0];
14000 contentCfg = cfg.cn[1];
14004 if(this.labelWidth > 12){
14005 labelCfg.style = "width: " + this.labelWidth + 'px';
14008 if(this.labelWidth < 13 && this.labelmd == 0){
14009 this.labelmd = this.labelWidth;
14012 if(this.labellg > 0){
14013 labelCfg.cls += ' col-lg-' + this.labellg;
14014 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14017 if(this.labelmd > 0){
14018 labelCfg.cls += ' col-md-' + this.labelmd;
14019 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14022 if(this.labelsm > 0){
14023 labelCfg.cls += ' col-sm-' + this.labelsm;
14024 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14027 if(this.labelxs > 0){
14028 labelCfg.cls += ' col-xs-' + this.labelxs;
14029 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14033 } else if ( this.fieldLabel.length) {
14034 // Roo.log(" label");
14039 //cls : 'input-group-addon',
14040 html : this.fieldLabel
14045 if(this.indicatorpos == 'right'){
14049 //cls : 'input-group-addon',
14050 html : this.fieldLabel
14060 // Roo.log(" no label && no align");
14067 ['xs','sm','md','lg'].map(function(size){
14068 if (settings[size]) {
14069 cfg.cls += ' col-' + size + '-' + settings[size];
14077 _initEventsCalled : false,
14080 initEvents: function()
14082 if (this._initEventsCalled) { // as we call render... prevent looping...
14085 this._initEventsCalled = true;
14088 throw "can not find store for combo";
14091 this.indicator = this.indicatorEl();
14093 this.store = Roo.factory(this.store, Roo.data);
14094 this.store.parent = this;
14096 // if we are building from html. then this element is so complex, that we can not really
14097 // use the rendered HTML.
14098 // so we have to trash and replace the previous code.
14099 if (Roo.XComponent.build_from_html) {
14100 // remove this element....
14101 var e = this.el.dom, k=0;
14102 while (e ) { e = e.previousSibling; ++k;}
14107 this.rendered = false;
14109 this.render(this.parent().getChildContainer(true), k);
14112 if(Roo.isIOS && this.useNativeIOS){
14113 this.initIOSView();
14121 if(Roo.isTouch && this.mobileTouchView){
14122 this.initTouchView();
14127 this.initTickableEvents();
14131 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
14133 if(this.hiddenName){
14135 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14137 this.hiddenField.dom.value =
14138 this.hiddenValue !== undefined ? this.hiddenValue :
14139 this.value !== undefined ? this.value : '';
14141 // prevent input submission
14142 this.el.dom.removeAttribute('name');
14143 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14148 // this.el.dom.setAttribute('autocomplete', 'off');
14151 var cls = 'x-combo-list';
14153 //this.list = new Roo.Layer({
14154 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
14160 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14161 _this.list.setWidth(lw);
14164 this.list.on('mouseover', this.onViewOver, this);
14165 this.list.on('mousemove', this.onViewMove, this);
14166 this.list.on('scroll', this.onViewScroll, this);
14169 this.list.swallowEvent('mousewheel');
14170 this.assetHeight = 0;
14173 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
14174 this.assetHeight += this.header.getHeight();
14177 this.innerList = this.list.createChild({cls:cls+'-inner'});
14178 this.innerList.on('mouseover', this.onViewOver, this);
14179 this.innerList.on('mousemove', this.onViewMove, this);
14180 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14182 if(this.allowBlank && !this.pageSize && !this.disableClear){
14183 this.footer = this.list.createChild({cls:cls+'-ft'});
14184 this.pageTb = new Roo.Toolbar(this.footer);
14188 this.footer = this.list.createChild({cls:cls+'-ft'});
14189 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
14190 {pageSize: this.pageSize});
14194 if (this.pageTb && this.allowBlank && !this.disableClear) {
14196 this.pageTb.add(new Roo.Toolbar.Fill(), {
14197 cls: 'x-btn-icon x-btn-clear',
14199 handler: function()
14202 _this.clearValue();
14203 _this.onSelect(false, -1);
14208 this.assetHeight += this.footer.getHeight();
14213 this.tpl = Roo.bootstrap.version == 4 ?
14214 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
14215 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
14218 this.view = new Roo.View(this.list, this.tpl, {
14219 singleSelect:true, store: this.store, selectedClass: this.selectedClass
14221 //this.view.wrapEl.setDisplayed(false);
14222 this.view.on('click', this.onViewClick, this);
14225 this.store.on('beforeload', this.onBeforeLoad, this);
14226 this.store.on('load', this.onLoad, this);
14227 this.store.on('loadexception', this.onLoadException, this);
14229 if(this.resizable){
14230 this.resizer = new Roo.Resizable(this.list, {
14231 pinned:true, handles:'se'
14233 this.resizer.on('resize', function(r, w, h){
14234 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
14235 this.listWidth = w;
14236 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
14237 this.restrictHeight();
14239 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
14242 if(!this.editable){
14243 this.editable = true;
14244 this.setEditable(false);
14249 if (typeof(this.events.add.listeners) != 'undefined') {
14251 this.addicon = this.wrap.createChild(
14252 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
14254 this.addicon.on('click', function(e) {
14255 this.fireEvent('add', this);
14258 if (typeof(this.events.edit.listeners) != 'undefined') {
14260 this.editicon = this.wrap.createChild(
14261 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
14262 if (this.addicon) {
14263 this.editicon.setStyle('margin-left', '40px');
14265 this.editicon.on('click', function(e) {
14267 // we fire even if inothing is selected..
14268 this.fireEvent('edit', this, this.lastData );
14274 this.keyNav = new Roo.KeyNav(this.inputEl(), {
14275 "up" : function(e){
14276 this.inKeyMode = true;
14280 "down" : function(e){
14281 if(!this.isExpanded()){
14282 this.onTriggerClick();
14284 this.inKeyMode = true;
14289 "enter" : function(e){
14290 // this.onViewClick();
14294 if(this.fireEvent("specialkey", this, e)){
14295 this.onViewClick(false);
14301 "esc" : function(e){
14305 "tab" : function(e){
14308 if(this.fireEvent("specialkey", this, e)){
14309 this.onViewClick(false);
14317 doRelay : function(foo, bar, hname){
14318 if(hname == 'down' || this.scope.isExpanded()){
14319 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14328 this.queryDelay = Math.max(this.queryDelay || 10,
14329 this.mode == 'local' ? 10 : 250);
14332 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14334 if(this.typeAhead){
14335 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14337 if(this.editable !== false){
14338 this.inputEl().on("keyup", this.onKeyUp, this);
14340 if(this.forceSelection){
14341 this.inputEl().on('blur', this.doForce, this);
14345 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14346 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14350 initTickableEvents: function()
14354 if(this.hiddenName){
14356 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14358 this.hiddenField.dom.value =
14359 this.hiddenValue !== undefined ? this.hiddenValue :
14360 this.value !== undefined ? this.value : '';
14362 // prevent input submission
14363 this.el.dom.removeAttribute('name');
14364 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14369 // this.list = this.el.select('ul.dropdown-menu',true).first();
14371 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14372 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14373 if(this.triggerList){
14374 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14377 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14378 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14380 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14381 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14383 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14384 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14386 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14387 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14388 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14391 this.cancelBtn.hide();
14396 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14397 _this.list.setWidth(lw);
14400 this.list.on('mouseover', this.onViewOver, this);
14401 this.list.on('mousemove', this.onViewMove, this);
14403 this.list.on('scroll', this.onViewScroll, this);
14406 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
14407 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14410 this.view = new Roo.View(this.list, this.tpl, {
14415 selectedClass: this.selectedClass
14418 //this.view.wrapEl.setDisplayed(false);
14419 this.view.on('click', this.onViewClick, this);
14423 this.store.on('beforeload', this.onBeforeLoad, this);
14424 this.store.on('load', this.onLoad, this);
14425 this.store.on('loadexception', this.onLoadException, this);
14428 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14429 "up" : function(e){
14430 this.inKeyMode = true;
14434 "down" : function(e){
14435 this.inKeyMode = true;
14439 "enter" : function(e){
14440 if(this.fireEvent("specialkey", this, e)){
14441 this.onViewClick(false);
14447 "esc" : function(e){
14448 this.onTickableFooterButtonClick(e, false, false);
14451 "tab" : function(e){
14452 this.fireEvent("specialkey", this, e);
14454 this.onTickableFooterButtonClick(e, false, false);
14461 doRelay : function(e, fn, key){
14462 if(this.scope.isExpanded()){
14463 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14472 this.queryDelay = Math.max(this.queryDelay || 10,
14473 this.mode == 'local' ? 10 : 250);
14476 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14478 if(this.typeAhead){
14479 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14482 if(this.editable !== false){
14483 this.tickableInputEl().on("keyup", this.onKeyUp, this);
14486 this.indicator = this.indicatorEl();
14488 if(this.indicator){
14489 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14490 this.indicator.hide();
14495 onDestroy : function(){
14497 this.view.setStore(null);
14498 this.view.el.removeAllListeners();
14499 this.view.el.remove();
14500 this.view.purgeListeners();
14503 this.list.dom.innerHTML = '';
14507 this.store.un('beforeload', this.onBeforeLoad, this);
14508 this.store.un('load', this.onLoad, this);
14509 this.store.un('loadexception', this.onLoadException, this);
14511 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14515 fireKey : function(e){
14516 if(e.isNavKeyPress() && !this.list.isVisible()){
14517 this.fireEvent("specialkey", this, e);
14522 onResize: function(w, h){
14523 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14525 // if(typeof w != 'number'){
14526 // // we do not handle it!?!?
14529 // var tw = this.trigger.getWidth();
14530 // // tw += this.addicon ? this.addicon.getWidth() : 0;
14531 // // tw += this.editicon ? this.editicon.getWidth() : 0;
14533 // this.inputEl().setWidth( this.adjustWidth('input', x));
14535 // //this.trigger.setStyle('left', x+'px');
14537 // if(this.list && this.listWidth === undefined){
14538 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14539 // this.list.setWidth(lw);
14540 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14548 * Allow or prevent the user from directly editing the field text. If false is passed,
14549 * the user will only be able to select from the items defined in the dropdown list. This method
14550 * is the runtime equivalent of setting the 'editable' config option at config time.
14551 * @param {Boolean} value True to allow the user to directly edit the field text
14553 setEditable : function(value){
14554 if(value == this.editable){
14557 this.editable = value;
14559 this.inputEl().dom.setAttribute('readOnly', true);
14560 this.inputEl().on('mousedown', this.onTriggerClick, this);
14561 this.inputEl().addClass('x-combo-noedit');
14563 this.inputEl().dom.setAttribute('readOnly', false);
14564 this.inputEl().un('mousedown', this.onTriggerClick, this);
14565 this.inputEl().removeClass('x-combo-noedit');
14571 onBeforeLoad : function(combo,opts){
14572 if(!this.hasFocus){
14576 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14578 this.restrictHeight();
14579 this.selectedIndex = -1;
14583 onLoad : function(){
14585 this.hasQuery = false;
14587 if(!this.hasFocus){
14591 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14592 this.loading.hide();
14595 if(this.store.getCount() > 0){
14598 this.restrictHeight();
14599 if(this.lastQuery == this.allQuery){
14600 if(this.editable && !this.tickable){
14601 this.inputEl().dom.select();
14605 !this.selectByValue(this.value, true) &&
14608 !this.store.lastOptions ||
14609 typeof(this.store.lastOptions.add) == 'undefined' ||
14610 this.store.lastOptions.add != true
14613 this.select(0, true);
14616 if(this.autoFocus){
14619 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14620 this.taTask.delay(this.typeAheadDelay);
14624 this.onEmptyResults();
14630 onLoadException : function()
14632 this.hasQuery = false;
14634 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14635 this.loading.hide();
14638 if(this.tickable && this.editable){
14643 // only causes errors at present
14644 //Roo.log(this.store.reader.jsonData);
14645 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14647 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14653 onTypeAhead : function(){
14654 if(this.store.getCount() > 0){
14655 var r = this.store.getAt(0);
14656 var newValue = r.data[this.displayField];
14657 var len = newValue.length;
14658 var selStart = this.getRawValue().length;
14660 if(selStart != len){
14661 this.setRawValue(newValue);
14662 this.selectText(selStart, newValue.length);
14668 onSelect : function(record, index){
14670 if(this.fireEvent('beforeselect', this, record, index) !== false){
14672 this.setFromData(index > -1 ? record.data : false);
14675 this.fireEvent('select', this, record, index);
14680 * Returns the currently selected field value or empty string if no value is set.
14681 * @return {String} value The selected value
14683 getValue : function()
14685 if(Roo.isIOS && this.useNativeIOS){
14686 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14690 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14693 if(this.valueField){
14694 return typeof this.value != 'undefined' ? this.value : '';
14696 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14700 getRawValue : function()
14702 if(Roo.isIOS && this.useNativeIOS){
14703 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14706 var v = this.inputEl().getValue();
14712 * Clears any text/value currently set in the field
14714 clearValue : function(){
14716 if(this.hiddenField){
14717 this.hiddenField.dom.value = '';
14720 this.setRawValue('');
14721 this.lastSelectionText = '';
14722 this.lastData = false;
14724 var close = this.closeTriggerEl();
14735 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14736 * will be displayed in the field. If the value does not match the data value of an existing item,
14737 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14738 * Otherwise the field will be blank (although the value will still be set).
14739 * @param {String} value The value to match
14741 setValue : function(v)
14743 if(Roo.isIOS && this.useNativeIOS){
14744 this.setIOSValue(v);
14754 if(this.valueField){
14755 var r = this.findRecord(this.valueField, v);
14757 text = r.data[this.displayField];
14758 }else if(this.valueNotFoundText !== undefined){
14759 text = this.valueNotFoundText;
14762 this.lastSelectionText = text;
14763 if(this.hiddenField){
14764 this.hiddenField.dom.value = v;
14766 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14769 var close = this.closeTriggerEl();
14772 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14778 * @property {Object} the last set data for the element
14783 * Sets the value of the field based on a object which is related to the record format for the store.
14784 * @param {Object} value the value to set as. or false on reset?
14786 setFromData : function(o){
14793 var dv = ''; // display value
14794 var vv = ''; // value value..
14796 if (this.displayField) {
14797 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14799 // this is an error condition!!!
14800 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14803 if(this.valueField){
14804 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14807 var close = this.closeTriggerEl();
14810 if(dv.length || vv * 1 > 0){
14812 this.blockFocus=true;
14818 if(this.hiddenField){
14819 this.hiddenField.dom.value = vv;
14821 this.lastSelectionText = dv;
14822 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14826 // no hidden field.. - we store the value in 'value', but still display
14827 // display field!!!!
14828 this.lastSelectionText = dv;
14829 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14836 reset : function(){
14837 // overridden so that last data is reset..
14844 this.setValue(this.originalValue);
14845 //this.clearInvalid();
14846 this.lastData = false;
14848 this.view.clearSelections();
14854 findRecord : function(prop, value){
14856 if(this.store.getCount() > 0){
14857 this.store.each(function(r){
14858 if(r.data[prop] == value){
14868 getName: function()
14870 // returns hidden if it's set..
14871 if (!this.rendered) {return ''};
14872 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14876 onViewMove : function(e, t){
14877 this.inKeyMode = false;
14881 onViewOver : function(e, t){
14882 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14885 var item = this.view.findItemFromChild(t);
14888 var index = this.view.indexOf(item);
14889 this.select(index, false);
14894 onViewClick : function(view, doFocus, el, e)
14896 var index = this.view.getSelectedIndexes()[0];
14898 var r = this.store.getAt(index);
14902 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14909 Roo.each(this.tickItems, function(v,k){
14911 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14913 _this.tickItems.splice(k, 1);
14915 if(typeof(e) == 'undefined' && view == false){
14916 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14928 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14929 this.tickItems.push(r.data);
14932 if(typeof(e) == 'undefined' && view == false){
14933 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14940 this.onSelect(r, index);
14942 if(doFocus !== false && !this.blockFocus){
14943 this.inputEl().focus();
14948 restrictHeight : function(){
14949 //this.innerList.dom.style.height = '';
14950 //var inner = this.innerList.dom;
14951 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14952 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14953 //this.list.beginUpdate();
14954 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14955 this.list.alignTo(this.inputEl(), this.listAlign);
14956 this.list.alignTo(this.inputEl(), this.listAlign);
14957 //this.list.endUpdate();
14961 onEmptyResults : function(){
14963 if(this.tickable && this.editable){
14964 this.hasFocus = false;
14965 this.restrictHeight();
14973 * Returns true if the dropdown list is expanded, else false.
14975 isExpanded : function(){
14976 return this.list.isVisible();
14980 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14981 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14982 * @param {String} value The data value of the item to select
14983 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14984 * selected item if it is not currently in view (defaults to true)
14985 * @return {Boolean} True if the value matched an item in the list, else false
14987 selectByValue : function(v, scrollIntoView){
14988 if(v !== undefined && v !== null){
14989 var r = this.findRecord(this.valueField || this.displayField, v);
14991 this.select(this.store.indexOf(r), scrollIntoView);
14999 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
15000 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15001 * @param {Number} index The zero-based index of the list item to select
15002 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15003 * selected item if it is not currently in view (defaults to true)
15005 select : function(index, scrollIntoView){
15006 this.selectedIndex = index;
15007 this.view.select(index);
15008 if(scrollIntoView !== false){
15009 var el = this.view.getNode(index);
15011 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
15014 this.list.scrollChildIntoView(el, false);
15020 selectNext : function(){
15021 var ct = this.store.getCount();
15023 if(this.selectedIndex == -1){
15025 }else if(this.selectedIndex < ct-1){
15026 this.select(this.selectedIndex+1);
15032 selectPrev : function(){
15033 var ct = this.store.getCount();
15035 if(this.selectedIndex == -1){
15037 }else if(this.selectedIndex != 0){
15038 this.select(this.selectedIndex-1);
15044 onKeyUp : function(e){
15045 if(this.editable !== false && !e.isSpecialKey()){
15046 this.lastKey = e.getKey();
15047 this.dqTask.delay(this.queryDelay);
15052 validateBlur : function(){
15053 return !this.list || !this.list.isVisible();
15057 initQuery : function(){
15059 var v = this.getRawValue();
15061 if(this.tickable && this.editable){
15062 v = this.tickableInputEl().getValue();
15069 doForce : function(){
15070 if(this.inputEl().dom.value.length > 0){
15071 this.inputEl().dom.value =
15072 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
15078 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
15079 * query allowing the query action to be canceled if needed.
15080 * @param {String} query The SQL query to execute
15081 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
15082 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
15083 * saved in the current store (defaults to false)
15085 doQuery : function(q, forceAll){
15087 if(q === undefined || q === null){
15092 forceAll: forceAll,
15096 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
15101 forceAll = qe.forceAll;
15102 if(forceAll === true || (q.length >= this.minChars)){
15104 this.hasQuery = true;
15106 if(this.lastQuery != q || this.alwaysQuery){
15107 this.lastQuery = q;
15108 if(this.mode == 'local'){
15109 this.selectedIndex = -1;
15111 this.store.clearFilter();
15114 if(this.specialFilter){
15115 this.fireEvent('specialfilter', this);
15120 this.store.filter(this.displayField, q);
15123 this.store.fireEvent("datachanged", this.store);
15130 this.store.baseParams[this.queryParam] = q;
15132 var options = {params : this.getParams(q)};
15135 options.add = true;
15136 options.params.start = this.page * this.pageSize;
15139 this.store.load(options);
15142 * this code will make the page width larger, at the beginning, the list not align correctly,
15143 * we should expand the list on onLoad
15144 * so command out it
15149 this.selectedIndex = -1;
15154 this.loadNext = false;
15158 getParams : function(q){
15160 //p[this.queryParam] = q;
15164 p.limit = this.pageSize;
15170 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
15172 collapse : function(){
15173 if(!this.isExpanded()){
15179 this.hasFocus = false;
15183 this.cancelBtn.hide();
15184 this.trigger.show();
15187 this.tickableInputEl().dom.value = '';
15188 this.tickableInputEl().blur();
15193 Roo.get(document).un('mousedown', this.collapseIf, this);
15194 Roo.get(document).un('mousewheel', this.collapseIf, this);
15195 if (!this.editable) {
15196 Roo.get(document).un('keydown', this.listKeyPress, this);
15198 this.fireEvent('collapse', this);
15204 collapseIf : function(e){
15205 var in_combo = e.within(this.el);
15206 var in_list = e.within(this.list);
15207 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
15209 if (in_combo || in_list || is_list) {
15210 //e.stopPropagation();
15215 this.onTickableFooterButtonClick(e, false, false);
15223 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
15225 expand : function(){
15227 if(this.isExpanded() || !this.hasFocus){
15231 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
15232 this.list.setWidth(lw);
15238 this.restrictHeight();
15242 this.tickItems = Roo.apply([], this.item);
15245 this.cancelBtn.show();
15246 this.trigger.hide();
15249 this.tickableInputEl().focus();
15254 Roo.get(document).on('mousedown', this.collapseIf, this);
15255 Roo.get(document).on('mousewheel', this.collapseIf, this);
15256 if (!this.editable) {
15257 Roo.get(document).on('keydown', this.listKeyPress, this);
15260 this.fireEvent('expand', this);
15264 // Implements the default empty TriggerField.onTriggerClick function
15265 onTriggerClick : function(e)
15267 Roo.log('trigger click');
15269 if(this.disabled || !this.triggerList){
15274 this.loadNext = false;
15276 if(this.isExpanded()){
15278 if (!this.blockFocus) {
15279 this.inputEl().focus();
15283 this.hasFocus = true;
15284 if(this.triggerAction == 'all') {
15285 this.doQuery(this.allQuery, true);
15287 this.doQuery(this.getRawValue());
15289 if (!this.blockFocus) {
15290 this.inputEl().focus();
15295 onTickableTriggerClick : function(e)
15302 this.loadNext = false;
15303 this.hasFocus = true;
15305 if(this.triggerAction == 'all') {
15306 this.doQuery(this.allQuery, true);
15308 this.doQuery(this.getRawValue());
15312 onSearchFieldClick : function(e)
15314 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15315 this.onTickableFooterButtonClick(e, false, false);
15319 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15324 this.loadNext = false;
15325 this.hasFocus = true;
15327 if(this.triggerAction == 'all') {
15328 this.doQuery(this.allQuery, true);
15330 this.doQuery(this.getRawValue());
15334 listKeyPress : function(e)
15336 //Roo.log('listkeypress');
15337 // scroll to first matching element based on key pres..
15338 if (e.isSpecialKey()) {
15341 var k = String.fromCharCode(e.getKey()).toUpperCase();
15344 var csel = this.view.getSelectedNodes();
15345 var cselitem = false;
15347 var ix = this.view.indexOf(csel[0]);
15348 cselitem = this.store.getAt(ix);
15349 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15355 this.store.each(function(v) {
15357 // start at existing selection.
15358 if (cselitem.id == v.id) {
15364 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15365 match = this.store.indexOf(v);
15371 if (match === false) {
15372 return true; // no more action?
15375 this.view.select(match);
15376 var sn = Roo.get(this.view.getSelectedNodes()[0]);
15377 sn.scrollIntoView(sn.dom.parentNode, false);
15380 onViewScroll : function(e, t){
15382 if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
15386 this.hasQuery = true;
15388 this.loading = this.list.select('.loading', true).first();
15390 if(this.loading === null){
15391 this.list.createChild({
15393 cls: 'loading roo-select2-more-results roo-select2-active',
15394 html: 'Loading more results...'
15397 this.loading = this.list.select('.loading', true).first();
15399 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15401 this.loading.hide();
15404 this.loading.show();
15409 this.loadNext = true;
15411 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15416 addItem : function(o)
15418 var dv = ''; // display value
15420 if (this.displayField) {
15421 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15423 // this is an error condition!!!
15424 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
15431 var choice = this.choices.createChild({
15433 cls: 'roo-select2-search-choice',
15442 cls: 'roo-select2-search-choice-close fa fa-times',
15447 }, this.searchField);
15449 var close = choice.select('a.roo-select2-search-choice-close', true).first();
15451 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15459 this.inputEl().dom.value = '';
15464 onRemoveItem : function(e, _self, o)
15466 e.preventDefault();
15468 this.lastItem = Roo.apply([], this.item);
15470 var index = this.item.indexOf(o.data) * 1;
15473 Roo.log('not this item?!');
15477 this.item.splice(index, 1);
15482 this.fireEvent('remove', this, e);
15488 syncValue : function()
15490 if(!this.item.length){
15497 Roo.each(this.item, function(i){
15498 if(_this.valueField){
15499 value.push(i[_this.valueField]);
15506 this.value = value.join(',');
15508 if(this.hiddenField){
15509 this.hiddenField.dom.value = this.value;
15512 this.store.fireEvent("datachanged", this.store);
15517 clearItem : function()
15519 if(!this.multiple){
15525 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15533 if(this.tickable && !Roo.isTouch){
15534 this.view.refresh();
15538 inputEl: function ()
15540 if(Roo.isIOS && this.useNativeIOS){
15541 return this.el.select('select.roo-ios-select', true).first();
15544 if(Roo.isTouch && this.mobileTouchView){
15545 return this.el.select('input.form-control',true).first();
15549 return this.searchField;
15552 return this.el.select('input.form-control',true).first();
15555 onTickableFooterButtonClick : function(e, btn, el)
15557 e.preventDefault();
15559 this.lastItem = Roo.apply([], this.item);
15561 if(btn && btn.name == 'cancel'){
15562 this.tickItems = Roo.apply([], this.item);
15571 Roo.each(this.tickItems, function(o){
15579 validate : function()
15581 if(this.getVisibilityEl().hasClass('hidden')){
15585 var v = this.getRawValue();
15588 v = this.getValue();
15591 if(this.disabled || this.allowBlank || v.length){
15596 this.markInvalid();
15600 tickableInputEl : function()
15602 if(!this.tickable || !this.editable){
15603 return this.inputEl();
15606 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15610 getAutoCreateTouchView : function()
15615 cls: 'form-group' //input-group
15621 type : this.inputType,
15622 cls : 'form-control x-combo-noedit',
15623 autocomplete: 'new-password',
15624 placeholder : this.placeholder || '',
15629 input.name = this.name;
15633 input.cls += ' input-' + this.size;
15636 if (this.disabled) {
15637 input.disabled = true;
15648 inputblock.cls += ' input-group';
15650 inputblock.cn.unshift({
15652 cls : 'input-group-addon input-group-prepend input-group-text',
15657 if(this.removable && !this.multiple){
15658 inputblock.cls += ' roo-removable';
15660 inputblock.cn.push({
15663 cls : 'roo-combo-removable-btn close'
15667 if(this.hasFeedback && !this.allowBlank){
15669 inputblock.cls += ' has-feedback';
15671 inputblock.cn.push({
15673 cls: 'glyphicon form-control-feedback'
15680 inputblock.cls += (this.before) ? '' : ' input-group';
15682 inputblock.cn.push({
15684 cls : 'input-group-addon input-group-append input-group-text',
15690 var ibwrap = inputblock;
15695 cls: 'roo-select2-choices',
15699 cls: 'roo-select2-search-field',
15712 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15717 cls: 'form-hidden-field'
15723 if(!this.multiple && this.showToggleBtn){
15729 if (this.caret != false) {
15732 cls: 'fa fa-' + this.caret
15739 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15741 Roo.bootstrap.version == 3 ? caret : '',
15744 cls: 'combobox-clear',
15758 combobox.cls += ' roo-select2-container-multi';
15761 var align = this.labelAlign || this.parentLabelAlign();
15763 if (align ==='left' && this.fieldLabel.length) {
15768 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15769 tooltip : 'This field is required'
15773 cls : 'control-label col-form-label',
15774 html : this.fieldLabel
15785 var labelCfg = cfg.cn[1];
15786 var contentCfg = cfg.cn[2];
15789 if(this.indicatorpos == 'right'){
15794 cls : 'control-label col-form-label',
15798 html : this.fieldLabel
15802 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15803 tooltip : 'This field is required'
15816 labelCfg = cfg.cn[0];
15817 contentCfg = cfg.cn[1];
15822 if(this.labelWidth > 12){
15823 labelCfg.style = "width: " + this.labelWidth + 'px';
15826 if(this.labelWidth < 13 && this.labelmd == 0){
15827 this.labelmd = this.labelWidth;
15830 if(this.labellg > 0){
15831 labelCfg.cls += ' col-lg-' + this.labellg;
15832 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15835 if(this.labelmd > 0){
15836 labelCfg.cls += ' col-md-' + this.labelmd;
15837 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15840 if(this.labelsm > 0){
15841 labelCfg.cls += ' col-sm-' + this.labelsm;
15842 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15845 if(this.labelxs > 0){
15846 labelCfg.cls += ' col-xs-' + this.labelxs;
15847 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15851 } else if ( this.fieldLabel.length) {
15855 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15856 tooltip : 'This field is required'
15860 cls : 'control-label',
15861 html : this.fieldLabel
15872 if(this.indicatorpos == 'right'){
15876 cls : 'control-label',
15877 html : this.fieldLabel,
15881 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15882 tooltip : 'This field is required'
15899 var settings = this;
15901 ['xs','sm','md','lg'].map(function(size){
15902 if (settings[size]) {
15903 cfg.cls += ' col-' + size + '-' + settings[size];
15910 initTouchView : function()
15912 this.renderTouchView();
15914 this.touchViewEl.on('scroll', function(){
15915 this.el.dom.scrollTop = 0;
15918 this.originalValue = this.getValue();
15920 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15922 this.inputEl().on("click", this.showTouchView, this);
15923 if (this.triggerEl) {
15924 this.triggerEl.on("click", this.showTouchView, this);
15928 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15929 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15931 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15933 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15934 this.store.on('load', this.onTouchViewLoad, this);
15935 this.store.on('loadexception', this.onTouchViewLoadException, this);
15937 if(this.hiddenName){
15939 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15941 this.hiddenField.dom.value =
15942 this.hiddenValue !== undefined ? this.hiddenValue :
15943 this.value !== undefined ? this.value : '';
15945 this.el.dom.removeAttribute('name');
15946 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15950 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15951 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15954 if(this.removable && !this.multiple){
15955 var close = this.closeTriggerEl();
15957 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15958 close.on('click', this.removeBtnClick, this, close);
15962 * fix the bug in Safari iOS8
15964 this.inputEl().on("focus", function(e){
15965 document.activeElement.blur();
15968 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15975 renderTouchView : function()
15977 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15978 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15980 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15981 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15983 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15984 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15985 this.touchViewBodyEl.setStyle('overflow', 'auto');
15987 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15988 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15990 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15991 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15995 showTouchView : function()
16001 this.touchViewHeaderEl.hide();
16003 if(this.modalTitle.length){
16004 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
16005 this.touchViewHeaderEl.show();
16008 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
16009 this.touchViewEl.show();
16011 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
16013 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
16014 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
16016 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16018 if(this.modalTitle.length){
16019 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16022 this.touchViewBodyEl.setHeight(bodyHeight);
16026 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
16028 this.touchViewEl.addClass('in');
16031 if(this._touchViewMask){
16032 Roo.get(document.body).addClass("x-body-masked");
16033 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
16034 this._touchViewMask.setStyle('z-index', 10000);
16035 this._touchViewMask.addClass('show');
16038 this.doTouchViewQuery();
16042 hideTouchView : function()
16044 this.touchViewEl.removeClass('in');
16048 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
16050 this.touchViewEl.setStyle('display', 'none');
16053 if(this._touchViewMask){
16054 this._touchViewMask.removeClass('show');
16055 Roo.get(document.body).removeClass("x-body-masked");
16059 setTouchViewValue : function()
16066 Roo.each(this.tickItems, function(o){
16071 this.hideTouchView();
16074 doTouchViewQuery : function()
16083 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
16087 if(!this.alwaysQuery || this.mode == 'local'){
16088 this.onTouchViewLoad();
16095 onTouchViewBeforeLoad : function(combo,opts)
16101 onTouchViewLoad : function()
16103 if(this.store.getCount() < 1){
16104 this.onTouchViewEmptyResults();
16108 this.clearTouchView();
16110 var rawValue = this.getRawValue();
16112 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
16114 this.tickItems = [];
16116 this.store.data.each(function(d, rowIndex){
16117 var row = this.touchViewListGroup.createChild(template);
16119 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
16120 row.addClass(d.data.cls);
16123 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16126 html : d.data[this.displayField]
16129 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
16130 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
16133 row.removeClass('selected');
16134 if(!this.multiple && this.valueField &&
16135 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
16138 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16139 row.addClass('selected');
16142 if(this.multiple && this.valueField &&
16143 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
16147 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16148 this.tickItems.push(d.data);
16151 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
16155 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
16157 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16159 if(this.modalTitle.length){
16160 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16163 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
16165 if(this.mobile_restrict_height && listHeight < bodyHeight){
16166 this.touchViewBodyEl.setHeight(listHeight);
16171 if(firstChecked && listHeight > bodyHeight){
16172 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
16177 onTouchViewLoadException : function()
16179 this.hideTouchView();
16182 onTouchViewEmptyResults : function()
16184 this.clearTouchView();
16186 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
16188 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
16192 clearTouchView : function()
16194 this.touchViewListGroup.dom.innerHTML = '';
16197 onTouchViewClick : function(e, el, o)
16199 e.preventDefault();
16202 var rowIndex = o.rowIndex;
16204 var r = this.store.getAt(rowIndex);
16206 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
16208 if(!this.multiple){
16209 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
16210 c.dom.removeAttribute('checked');
16213 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16215 this.setFromData(r.data);
16217 var close = this.closeTriggerEl();
16223 this.hideTouchView();
16225 this.fireEvent('select', this, r, rowIndex);
16230 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
16231 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
16232 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
16236 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16237 this.addItem(r.data);
16238 this.tickItems.push(r.data);
16242 getAutoCreateNativeIOS : function()
16245 cls: 'form-group' //input-group,
16250 cls : 'roo-ios-select'
16254 combobox.name = this.name;
16257 if (this.disabled) {
16258 combobox.disabled = true;
16261 var settings = this;
16263 ['xs','sm','md','lg'].map(function(size){
16264 if (settings[size]) {
16265 cfg.cls += ' col-' + size + '-' + settings[size];
16275 initIOSView : function()
16277 this.store.on('load', this.onIOSViewLoad, this);
16282 onIOSViewLoad : function()
16284 if(this.store.getCount() < 1){
16288 this.clearIOSView();
16290 if(this.allowBlank) {
16292 var default_text = '-- SELECT --';
16294 if(this.placeholder.length){
16295 default_text = this.placeholder;
16298 if(this.emptyTitle.length){
16299 default_text += ' - ' + this.emptyTitle + ' -';
16302 var opt = this.inputEl().createChild({
16305 html : default_text
16309 o[this.valueField] = 0;
16310 o[this.displayField] = default_text;
16312 this.ios_options.push({
16319 this.store.data.each(function(d, rowIndex){
16323 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16324 html = d.data[this.displayField];
16329 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16330 value = d.data[this.valueField];
16339 if(this.value == d.data[this.valueField]){
16340 option['selected'] = true;
16343 var opt = this.inputEl().createChild(option);
16345 this.ios_options.push({
16352 this.inputEl().on('change', function(){
16353 this.fireEvent('select', this);
16358 clearIOSView: function()
16360 this.inputEl().dom.innerHTML = '';
16362 this.ios_options = [];
16365 setIOSValue: function(v)
16369 if(!this.ios_options){
16373 Roo.each(this.ios_options, function(opts){
16375 opts.el.dom.removeAttribute('selected');
16377 if(opts.data[this.valueField] != v){
16381 opts.el.dom.setAttribute('selected', true);
16387 * @cfg {Boolean} grow
16391 * @cfg {Number} growMin
16395 * @cfg {Number} growMax
16404 Roo.apply(Roo.bootstrap.ComboBox, {
16408 cls: 'modal-header',
16430 cls: 'list-group-item',
16434 cls: 'roo-combobox-list-group-item-value'
16438 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16452 listItemCheckbox : {
16454 cls: 'list-group-item',
16458 cls: 'roo-combobox-list-group-item-value'
16462 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16478 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16483 cls: 'modal-footer',
16491 cls: 'col-xs-6 text-left',
16494 cls: 'btn btn-danger roo-touch-view-cancel',
16500 cls: 'col-xs-6 text-right',
16503 cls: 'btn btn-success roo-touch-view-ok',
16514 Roo.apply(Roo.bootstrap.ComboBox, {
16516 touchViewTemplate : {
16518 cls: 'modal fade roo-combobox-touch-view',
16522 cls: 'modal-dialog',
16523 style : 'position:fixed', // we have to fix position....
16527 cls: 'modal-content',
16529 Roo.bootstrap.ComboBox.header,
16530 Roo.bootstrap.ComboBox.body,
16531 Roo.bootstrap.ComboBox.footer
16540 * Ext JS Library 1.1.1
16541 * Copyright(c) 2006-2007, Ext JS, LLC.
16543 * Originally Released Under LGPL - original licence link has changed is not relivant.
16546 * <script type="text/javascript">
16551 * @extends Roo.util.Observable
16552 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16553 * This class also supports single and multi selection modes. <br>
16554 * Create a data model bound view:
16556 var store = new Roo.data.Store(...);
16558 var view = new Roo.View({
16560 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16562 singleSelect: true,
16563 selectedClass: "ydataview-selected",
16567 // listen for node click?
16568 view.on("click", function(vw, index, node, e){
16569 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16573 dataModel.load("foobar.xml");
16575 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16577 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16578 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16580 * Note: old style constructor is still suported (container, template, config)
16583 * Create a new View
16584 * @param {Object} config The config object
16587 Roo.View = function(config, depreciated_tpl, depreciated_config){
16589 this.parent = false;
16591 if (typeof(depreciated_tpl) == 'undefined') {
16592 // new way.. - universal constructor.
16593 Roo.apply(this, config);
16594 this.el = Roo.get(this.el);
16597 this.el = Roo.get(config);
16598 this.tpl = depreciated_tpl;
16599 Roo.apply(this, depreciated_config);
16601 this.wrapEl = this.el.wrap().wrap();
16602 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16605 if(typeof(this.tpl) == "string"){
16606 this.tpl = new Roo.Template(this.tpl);
16608 // support xtype ctors..
16609 this.tpl = new Roo.factory(this.tpl, Roo);
16613 this.tpl.compile();
16618 * @event beforeclick
16619 * Fires before a click is processed. Returns false to cancel the default action.
16620 * @param {Roo.View} this
16621 * @param {Number} index The index of the target node
16622 * @param {HTMLElement} node The target node
16623 * @param {Roo.EventObject} e The raw event object
16625 "beforeclick" : true,
16628 * Fires when a template node is clicked.
16629 * @param {Roo.View} this
16630 * @param {Number} index The index of the target node
16631 * @param {HTMLElement} node The target node
16632 * @param {Roo.EventObject} e The raw event object
16637 * Fires when a template node is double clicked.
16638 * @param {Roo.View} this
16639 * @param {Number} index The index of the target node
16640 * @param {HTMLElement} node The target node
16641 * @param {Roo.EventObject} e The raw event object
16645 * @event contextmenu
16646 * Fires when a template node is right clicked.
16647 * @param {Roo.View} this
16648 * @param {Number} index The index of the target node
16649 * @param {HTMLElement} node The target node
16650 * @param {Roo.EventObject} e The raw event object
16652 "contextmenu" : true,
16654 * @event selectionchange
16655 * Fires when the selected nodes change.
16656 * @param {Roo.View} this
16657 * @param {Array} selections Array of the selected nodes
16659 "selectionchange" : true,
16662 * @event beforeselect
16663 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16664 * @param {Roo.View} this
16665 * @param {HTMLElement} node The node to be selected
16666 * @param {Array} selections Array of currently selected nodes
16668 "beforeselect" : true,
16670 * @event preparedata
16671 * Fires on every row to render, to allow you to change the data.
16672 * @param {Roo.View} this
16673 * @param {Object} data to be rendered (change this)
16675 "preparedata" : true
16683 "click": this.onClick,
16684 "dblclick": this.onDblClick,
16685 "contextmenu": this.onContextMenu,
16689 this.selections = [];
16691 this.cmp = new Roo.CompositeElementLite([]);
16693 this.store = Roo.factory(this.store, Roo.data);
16694 this.setStore(this.store, true);
16697 if ( this.footer && this.footer.xtype) {
16699 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16701 this.footer.dataSource = this.store;
16702 this.footer.container = fctr;
16703 this.footer = Roo.factory(this.footer, Roo);
16704 fctr.insertFirst(this.el);
16706 // this is a bit insane - as the paging toolbar seems to detach the el..
16707 // dom.parentNode.parentNode.parentNode
16708 // they get detached?
16712 Roo.View.superclass.constructor.call(this);
16717 Roo.extend(Roo.View, Roo.util.Observable, {
16720 * @cfg {Roo.data.Store} store Data store to load data from.
16725 * @cfg {String|Roo.Element} el The container element.
16730 * @cfg {String|Roo.Template} tpl The template used by this View
16734 * @cfg {String} dataName the named area of the template to use as the data area
16735 * Works with domtemplates roo-name="name"
16739 * @cfg {String} selectedClass The css class to add to selected nodes
16741 selectedClass : "x-view-selected",
16743 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16748 * @cfg {String} text to display on mask (default Loading)
16752 * @cfg {Boolean} multiSelect Allow multiple selection
16754 multiSelect : false,
16756 * @cfg {Boolean} singleSelect Allow single selection
16758 singleSelect: false,
16761 * @cfg {Boolean} toggleSelect - selecting
16763 toggleSelect : false,
16766 * @cfg {Boolean} tickable - selecting
16771 * Returns the element this view is bound to.
16772 * @return {Roo.Element}
16774 getEl : function(){
16775 return this.wrapEl;
16781 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16783 refresh : function(){
16784 //Roo.log('refresh');
16787 // if we are using something like 'domtemplate', then
16788 // the what gets used is:
16789 // t.applySubtemplate(NAME, data, wrapping data..)
16790 // the outer template then get' applied with
16791 // the store 'extra data'
16792 // and the body get's added to the
16793 // roo-name="data" node?
16794 // <span class='roo-tpl-{name}'></span> ?????
16798 this.clearSelections();
16799 this.el.update("");
16801 var records = this.store.getRange();
16802 if(records.length < 1) {
16804 // is this valid?? = should it render a template??
16806 this.el.update(this.emptyText);
16810 if (this.dataName) {
16811 this.el.update(t.apply(this.store.meta)); //????
16812 el = this.el.child('.roo-tpl-' + this.dataName);
16815 for(var i = 0, len = records.length; i < len; i++){
16816 var data = this.prepareData(records[i].data, i, records[i]);
16817 this.fireEvent("preparedata", this, data, i, records[i]);
16819 var d = Roo.apply({}, data);
16822 Roo.apply(d, {'roo-id' : Roo.id()});
16826 Roo.each(this.parent.item, function(item){
16827 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16830 Roo.apply(d, {'roo-data-checked' : 'checked'});
16834 html[html.length] = Roo.util.Format.trim(
16836 t.applySubtemplate(this.dataName, d, this.store.meta) :
16843 el.update(html.join(""));
16844 this.nodes = el.dom.childNodes;
16845 this.updateIndexes(0);
16850 * Function to override to reformat the data that is sent to
16851 * the template for each node.
16852 * DEPRICATED - use the preparedata event handler.
16853 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16854 * a JSON object for an UpdateManager bound view).
16856 prepareData : function(data, index, record)
16858 this.fireEvent("preparedata", this, data, index, record);
16862 onUpdate : function(ds, record){
16863 // Roo.log('on update');
16864 this.clearSelections();
16865 var index = this.store.indexOf(record);
16866 var n = this.nodes[index];
16867 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16868 n.parentNode.removeChild(n);
16869 this.updateIndexes(index, index);
16875 onAdd : function(ds, records, index)
16877 //Roo.log(['on Add', ds, records, index] );
16878 this.clearSelections();
16879 if(this.nodes.length == 0){
16883 var n = this.nodes[index];
16884 for(var i = 0, len = records.length; i < len; i++){
16885 var d = this.prepareData(records[i].data, i, records[i]);
16887 this.tpl.insertBefore(n, d);
16890 this.tpl.append(this.el, d);
16893 this.updateIndexes(index);
16896 onRemove : function(ds, record, index){
16897 // Roo.log('onRemove');
16898 this.clearSelections();
16899 var el = this.dataName ?
16900 this.el.child('.roo-tpl-' + this.dataName) :
16903 el.dom.removeChild(this.nodes[index]);
16904 this.updateIndexes(index);
16908 * Refresh an individual node.
16909 * @param {Number} index
16911 refreshNode : function(index){
16912 this.onUpdate(this.store, this.store.getAt(index));
16915 updateIndexes : function(startIndex, endIndex){
16916 var ns = this.nodes;
16917 startIndex = startIndex || 0;
16918 endIndex = endIndex || ns.length - 1;
16919 for(var i = startIndex; i <= endIndex; i++){
16920 ns[i].nodeIndex = i;
16925 * Changes the data store this view uses and refresh the view.
16926 * @param {Store} store
16928 setStore : function(store, initial){
16929 if(!initial && this.store){
16930 this.store.un("datachanged", this.refresh);
16931 this.store.un("add", this.onAdd);
16932 this.store.un("remove", this.onRemove);
16933 this.store.un("update", this.onUpdate);
16934 this.store.un("clear", this.refresh);
16935 this.store.un("beforeload", this.onBeforeLoad);
16936 this.store.un("load", this.onLoad);
16937 this.store.un("loadexception", this.onLoad);
16941 store.on("datachanged", this.refresh, this);
16942 store.on("add", this.onAdd, this);
16943 store.on("remove", this.onRemove, this);
16944 store.on("update", this.onUpdate, this);
16945 store.on("clear", this.refresh, this);
16946 store.on("beforeload", this.onBeforeLoad, this);
16947 store.on("load", this.onLoad, this);
16948 store.on("loadexception", this.onLoad, this);
16956 * onbeforeLoad - masks the loading area.
16959 onBeforeLoad : function(store,opts)
16961 //Roo.log('onBeforeLoad');
16963 this.el.update("");
16965 this.el.mask(this.mask ? this.mask : "Loading" );
16967 onLoad : function ()
16974 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16975 * @param {HTMLElement} node
16976 * @return {HTMLElement} The template node
16978 findItemFromChild : function(node){
16979 var el = this.dataName ?
16980 this.el.child('.roo-tpl-' + this.dataName,true) :
16983 if(!node || node.parentNode == el){
16986 var p = node.parentNode;
16987 while(p && p != el){
16988 if(p.parentNode == el){
16997 onClick : function(e){
16998 var item = this.findItemFromChild(e.getTarget());
17000 var index = this.indexOf(item);
17001 if(this.onItemClick(item, index, e) !== false){
17002 this.fireEvent("click", this, index, item, e);
17005 this.clearSelections();
17010 onContextMenu : function(e){
17011 var item = this.findItemFromChild(e.getTarget());
17013 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
17018 onDblClick : function(e){
17019 var item = this.findItemFromChild(e.getTarget());
17021 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
17025 onItemClick : function(item, index, e)
17027 if(this.fireEvent("beforeclick", this, index, item, e) === false){
17030 if (this.toggleSelect) {
17031 var m = this.isSelected(item) ? 'unselect' : 'select';
17034 _t[m](item, true, false);
17037 if(this.multiSelect || this.singleSelect){
17038 if(this.multiSelect && e.shiftKey && this.lastSelection){
17039 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
17041 this.select(item, this.multiSelect && e.ctrlKey);
17042 this.lastSelection = item;
17045 if(!this.tickable){
17046 e.preventDefault();
17054 * Get the number of selected nodes.
17057 getSelectionCount : function(){
17058 return this.selections.length;
17062 * Get the currently selected nodes.
17063 * @return {Array} An array of HTMLElements
17065 getSelectedNodes : function(){
17066 return this.selections;
17070 * Get the indexes of the selected nodes.
17073 getSelectedIndexes : function(){
17074 var indexes = [], s = this.selections;
17075 for(var i = 0, len = s.length; i < len; i++){
17076 indexes.push(s[i].nodeIndex);
17082 * Clear all selections
17083 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
17085 clearSelections : function(suppressEvent){
17086 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
17087 this.cmp.elements = this.selections;
17088 this.cmp.removeClass(this.selectedClass);
17089 this.selections = [];
17090 if(!suppressEvent){
17091 this.fireEvent("selectionchange", this, this.selections);
17097 * Returns true if the passed node is selected
17098 * @param {HTMLElement/Number} node The node or node index
17099 * @return {Boolean}
17101 isSelected : function(node){
17102 var s = this.selections;
17106 node = this.getNode(node);
17107 return s.indexOf(node) !== -1;
17112 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
17113 * @param {Boolean} keepExisting (optional) true to keep existing selections
17114 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17116 select : function(nodeInfo, keepExisting, suppressEvent){
17117 if(nodeInfo instanceof Array){
17119 this.clearSelections(true);
17121 for(var i = 0, len = nodeInfo.length; i < len; i++){
17122 this.select(nodeInfo[i], true, true);
17126 var node = this.getNode(nodeInfo);
17127 if(!node || this.isSelected(node)){
17128 return; // already selected.
17131 this.clearSelections(true);
17134 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
17135 Roo.fly(node).addClass(this.selectedClass);
17136 this.selections.push(node);
17137 if(!suppressEvent){
17138 this.fireEvent("selectionchange", this, this.selections);
17146 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
17147 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
17148 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17150 unselect : function(nodeInfo, keepExisting, suppressEvent)
17152 if(nodeInfo instanceof Array){
17153 Roo.each(this.selections, function(s) {
17154 this.unselect(s, nodeInfo);
17158 var node = this.getNode(nodeInfo);
17159 if(!node || !this.isSelected(node)){
17160 //Roo.log("not selected");
17161 return; // not selected.
17165 Roo.each(this.selections, function(s) {
17167 Roo.fly(node).removeClass(this.selectedClass);
17174 this.selections= ns;
17175 this.fireEvent("selectionchange", this, this.selections);
17179 * Gets a template node.
17180 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17181 * @return {HTMLElement} The node or null if it wasn't found
17183 getNode : function(nodeInfo){
17184 if(typeof nodeInfo == "string"){
17185 return document.getElementById(nodeInfo);
17186 }else if(typeof nodeInfo == "number"){
17187 return this.nodes[nodeInfo];
17193 * Gets a range template nodes.
17194 * @param {Number} startIndex
17195 * @param {Number} endIndex
17196 * @return {Array} An array of nodes
17198 getNodes : function(start, end){
17199 var ns = this.nodes;
17200 start = start || 0;
17201 end = typeof end == "undefined" ? ns.length - 1 : end;
17204 for(var i = start; i <= end; i++){
17208 for(var i = start; i >= end; i--){
17216 * Finds the index of the passed node
17217 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17218 * @return {Number} The index of the node or -1
17220 indexOf : function(node){
17221 node = this.getNode(node);
17222 if(typeof node.nodeIndex == "number"){
17223 return node.nodeIndex;
17225 var ns = this.nodes;
17226 for(var i = 0, len = ns.length; i < len; i++){
17237 * based on jquery fullcalendar
17241 Roo.bootstrap = Roo.bootstrap || {};
17243 * @class Roo.bootstrap.Calendar
17244 * @extends Roo.bootstrap.Component
17245 * Bootstrap Calendar class
17246 * @cfg {Boolean} loadMask (true|false) default false
17247 * @cfg {Object} header generate the user specific header of the calendar, default false
17250 * Create a new Container
17251 * @param {Object} config The config object
17256 Roo.bootstrap.Calendar = function(config){
17257 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
17261 * Fires when a date is selected
17262 * @param {DatePicker} this
17263 * @param {Date} date The selected date
17267 * @event monthchange
17268 * Fires when the displayed month changes
17269 * @param {DatePicker} this
17270 * @param {Date} date The selected month
17272 'monthchange': true,
17274 * @event evententer
17275 * Fires when mouse over an event
17276 * @param {Calendar} this
17277 * @param {event} Event
17279 'evententer': true,
17281 * @event eventleave
17282 * Fires when the mouse leaves an
17283 * @param {Calendar} this
17286 'eventleave': true,
17288 * @event eventclick
17289 * Fires when the mouse click an
17290 * @param {Calendar} this
17299 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
17302 * @cfg {Number} startDay
17303 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17311 getAutoCreate : function(){
17314 var fc_button = function(name, corner, style, content ) {
17315 return Roo.apply({},{
17317 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
17319 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17322 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17333 style : 'width:100%',
17340 cls : 'fc-header-left',
17342 fc_button('prev', 'left', 'arrow', '‹' ),
17343 fc_button('next', 'right', 'arrow', '›' ),
17344 { tag: 'span', cls: 'fc-header-space' },
17345 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
17353 cls : 'fc-header-center',
17357 cls: 'fc-header-title',
17360 html : 'month / year'
17368 cls : 'fc-header-right',
17370 /* fc_button('month', 'left', '', 'month' ),
17371 fc_button('week', '', '', 'week' ),
17372 fc_button('day', 'right', '', 'day' )
17384 header = this.header;
17387 var cal_heads = function() {
17389 // fixme - handle this.
17391 for (var i =0; i < Date.dayNames.length; i++) {
17392 var d = Date.dayNames[i];
17395 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17396 html : d.substring(0,3)
17400 ret[0].cls += ' fc-first';
17401 ret[6].cls += ' fc-last';
17404 var cal_cell = function(n) {
17407 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17412 cls: 'fc-day-number',
17416 cls: 'fc-day-content',
17420 style: 'position: relative;' // height: 17px;
17432 var cal_rows = function() {
17435 for (var r = 0; r < 6; r++) {
17442 for (var i =0; i < Date.dayNames.length; i++) {
17443 var d = Date.dayNames[i];
17444 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17447 row.cn[0].cls+=' fc-first';
17448 row.cn[0].cn[0].style = 'min-height:90px';
17449 row.cn[6].cls+=' fc-last';
17453 ret[0].cls += ' fc-first';
17454 ret[4].cls += ' fc-prev-last';
17455 ret[5].cls += ' fc-last';
17462 cls: 'fc-border-separate',
17463 style : 'width:100%',
17471 cls : 'fc-first fc-last',
17489 cls : 'fc-content',
17490 style : "position: relative;",
17493 cls : 'fc-view fc-view-month fc-grid',
17494 style : 'position: relative',
17495 unselectable : 'on',
17498 cls : 'fc-event-container',
17499 style : 'position:absolute;z-index:8;top:0;left:0;'
17517 initEvents : function()
17520 throw "can not find store for calendar";
17526 style: "text-align:center",
17530 style: "background-color:white;width:50%;margin:250 auto",
17534 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17545 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17547 var size = this.el.select('.fc-content', true).first().getSize();
17548 this.maskEl.setSize(size.width, size.height);
17549 this.maskEl.enableDisplayMode("block");
17550 if(!this.loadMask){
17551 this.maskEl.hide();
17554 this.store = Roo.factory(this.store, Roo.data);
17555 this.store.on('load', this.onLoad, this);
17556 this.store.on('beforeload', this.onBeforeLoad, this);
17560 this.cells = this.el.select('.fc-day',true);
17561 //Roo.log(this.cells);
17562 this.textNodes = this.el.query('.fc-day-number');
17563 this.cells.addClassOnOver('fc-state-hover');
17565 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17566 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17567 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17568 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17570 this.on('monthchange', this.onMonthChange, this);
17572 this.update(new Date().clearTime());
17575 resize : function() {
17576 var sz = this.el.getSize();
17578 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17579 this.el.select('.fc-day-content div',true).setHeight(34);
17584 showPrevMonth : function(e){
17585 this.update(this.activeDate.add("mo", -1));
17587 showToday : function(e){
17588 this.update(new Date().clearTime());
17591 showNextMonth : function(e){
17592 this.update(this.activeDate.add("mo", 1));
17596 showPrevYear : function(){
17597 this.update(this.activeDate.add("y", -1));
17601 showNextYear : function(){
17602 this.update(this.activeDate.add("y", 1));
17607 update : function(date)
17609 var vd = this.activeDate;
17610 this.activeDate = date;
17611 // if(vd && this.el){
17612 // var t = date.getTime();
17613 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17614 // Roo.log('using add remove');
17616 // this.fireEvent('monthchange', this, date);
17618 // this.cells.removeClass("fc-state-highlight");
17619 // this.cells.each(function(c){
17620 // if(c.dateValue == t){
17621 // c.addClass("fc-state-highlight");
17622 // setTimeout(function(){
17623 // try{c.dom.firstChild.focus();}catch(e){}
17633 var days = date.getDaysInMonth();
17635 var firstOfMonth = date.getFirstDateOfMonth();
17636 var startingPos = firstOfMonth.getDay()-this.startDay;
17638 if(startingPos < this.startDay){
17642 var pm = date.add(Date.MONTH, -1);
17643 var prevStart = pm.getDaysInMonth()-startingPos;
17645 this.cells = this.el.select('.fc-day',true);
17646 this.textNodes = this.el.query('.fc-day-number');
17647 this.cells.addClassOnOver('fc-state-hover');
17649 var cells = this.cells.elements;
17650 var textEls = this.textNodes;
17652 Roo.each(cells, function(cell){
17653 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17656 days += startingPos;
17658 // convert everything to numbers so it's fast
17659 var day = 86400000;
17660 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17663 //Roo.log(prevStart);
17665 var today = new Date().clearTime().getTime();
17666 var sel = date.clearTime().getTime();
17667 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17668 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17669 var ddMatch = this.disabledDatesRE;
17670 var ddText = this.disabledDatesText;
17671 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17672 var ddaysText = this.disabledDaysText;
17673 var format = this.format;
17675 var setCellClass = function(cal, cell){
17679 //Roo.log('set Cell Class');
17681 var t = d.getTime();
17685 cell.dateValue = t;
17687 cell.className += " fc-today";
17688 cell.className += " fc-state-highlight";
17689 cell.title = cal.todayText;
17692 // disable highlight in other month..
17693 //cell.className += " fc-state-highlight";
17698 cell.className = " fc-state-disabled";
17699 cell.title = cal.minText;
17703 cell.className = " fc-state-disabled";
17704 cell.title = cal.maxText;
17708 if(ddays.indexOf(d.getDay()) != -1){
17709 cell.title = ddaysText;
17710 cell.className = " fc-state-disabled";
17713 if(ddMatch && format){
17714 var fvalue = d.dateFormat(format);
17715 if(ddMatch.test(fvalue)){
17716 cell.title = ddText.replace("%0", fvalue);
17717 cell.className = " fc-state-disabled";
17721 if (!cell.initialClassName) {
17722 cell.initialClassName = cell.dom.className;
17725 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17730 for(; i < startingPos; i++) {
17731 textEls[i].innerHTML = (++prevStart);
17732 d.setDate(d.getDate()+1);
17734 cells[i].className = "fc-past fc-other-month";
17735 setCellClass(this, cells[i]);
17740 for(; i < days; i++){
17741 intDay = i - startingPos + 1;
17742 textEls[i].innerHTML = (intDay);
17743 d.setDate(d.getDate()+1);
17745 cells[i].className = ''; // "x-date-active";
17746 setCellClass(this, cells[i]);
17750 for(; i < 42; i++) {
17751 textEls[i].innerHTML = (++extraDays);
17752 d.setDate(d.getDate()+1);
17754 cells[i].className = "fc-future fc-other-month";
17755 setCellClass(this, cells[i]);
17758 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17760 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17762 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17763 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17765 if(totalRows != 6){
17766 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17767 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17770 this.fireEvent('monthchange', this, date);
17774 if(!this.internalRender){
17775 var main = this.el.dom.firstChild;
17776 var w = main.offsetWidth;
17777 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17778 Roo.fly(main).setWidth(w);
17779 this.internalRender = true;
17780 // opera does not respect the auto grow header center column
17781 // then, after it gets a width opera refuses to recalculate
17782 // without a second pass
17783 if(Roo.isOpera && !this.secondPass){
17784 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17785 this.secondPass = true;
17786 this.update.defer(10, this, [date]);
17793 findCell : function(dt) {
17794 dt = dt.clearTime().getTime();
17796 this.cells.each(function(c){
17797 //Roo.log("check " +c.dateValue + '?=' + dt);
17798 if(c.dateValue == dt){
17808 findCells : function(ev) {
17809 var s = ev.start.clone().clearTime().getTime();
17811 var e= ev.end.clone().clearTime().getTime();
17814 this.cells.each(function(c){
17815 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17817 if(c.dateValue > e){
17820 if(c.dateValue < s){
17829 // findBestRow: function(cells)
17833 // for (var i =0 ; i < cells.length;i++) {
17834 // ret = Math.max(cells[i].rows || 0,ret);
17841 addItem : function(ev)
17843 // look for vertical location slot in
17844 var cells = this.findCells(ev);
17846 // ev.row = this.findBestRow(cells);
17848 // work out the location.
17852 for(var i =0; i < cells.length; i++) {
17854 cells[i].row = cells[0].row;
17857 cells[i].row = cells[i].row + 1;
17867 if (crow.start.getY() == cells[i].getY()) {
17869 crow.end = cells[i];
17886 cells[0].events.push(ev);
17888 this.calevents.push(ev);
17891 clearEvents: function() {
17893 if(!this.calevents){
17897 Roo.each(this.cells.elements, function(c){
17903 Roo.each(this.calevents, function(e) {
17904 Roo.each(e.els, function(el) {
17905 el.un('mouseenter' ,this.onEventEnter, this);
17906 el.un('mouseleave' ,this.onEventLeave, this);
17911 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17917 renderEvents: function()
17921 this.cells.each(function(c) {
17930 if(c.row != c.events.length){
17931 r = 4 - (4 - (c.row - c.events.length));
17934 c.events = ev.slice(0, r);
17935 c.more = ev.slice(r);
17937 if(c.more.length && c.more.length == 1){
17938 c.events.push(c.more.pop());
17941 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17945 this.cells.each(function(c) {
17947 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17950 for (var e = 0; e < c.events.length; e++){
17951 var ev = c.events[e];
17952 var rows = ev.rows;
17954 for(var i = 0; i < rows.length; i++) {
17956 // how many rows should it span..
17959 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17960 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17962 unselectable : "on",
17965 cls: 'fc-event-inner',
17969 // cls: 'fc-event-time',
17970 // html : cells.length > 1 ? '' : ev.time
17974 cls: 'fc-event-title',
17975 html : String.format('{0}', ev.title)
17982 cls: 'ui-resizable-handle ui-resizable-e',
17983 html : '  '
17990 cfg.cls += ' fc-event-start';
17992 if ((i+1) == rows.length) {
17993 cfg.cls += ' fc-event-end';
17996 var ctr = _this.el.select('.fc-event-container',true).first();
17997 var cg = ctr.createChild(cfg);
17999 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
18000 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
18002 var r = (c.more.length) ? 1 : 0;
18003 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
18004 cg.setWidth(ebox.right - sbox.x -2);
18006 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
18007 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
18008 cg.on('click', _this.onEventClick, _this, ev);
18019 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
18020 style : 'position: absolute',
18021 unselectable : "on",
18024 cls: 'fc-event-inner',
18028 cls: 'fc-event-title',
18036 cls: 'ui-resizable-handle ui-resizable-e',
18037 html : '  '
18043 var ctr = _this.el.select('.fc-event-container',true).first();
18044 var cg = ctr.createChild(cfg);
18046 var sbox = c.select('.fc-day-content',true).first().getBox();
18047 var ebox = c.select('.fc-day-content',true).first().getBox();
18049 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
18050 cg.setWidth(ebox.right - sbox.x -2);
18052 cg.on('click', _this.onMoreEventClick, _this, c.more);
18062 onEventEnter: function (e, el,event,d) {
18063 this.fireEvent('evententer', this, el, event);
18066 onEventLeave: function (e, el,event,d) {
18067 this.fireEvent('eventleave', this, el, event);
18070 onEventClick: function (e, el,event,d) {
18071 this.fireEvent('eventclick', this, el, event);
18074 onMonthChange: function () {
18078 onMoreEventClick: function(e, el, more)
18082 this.calpopover.placement = 'right';
18083 this.calpopover.setTitle('More');
18085 this.calpopover.setContent('');
18087 var ctr = this.calpopover.el.select('.popover-content', true).first();
18089 Roo.each(more, function(m){
18091 cls : 'fc-event-hori fc-event-draggable',
18094 var cg = ctr.createChild(cfg);
18096 cg.on('click', _this.onEventClick, _this, m);
18099 this.calpopover.show(el);
18104 onLoad: function ()
18106 this.calevents = [];
18109 if(this.store.getCount() > 0){
18110 this.store.data.each(function(d){
18113 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
18114 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
18115 time : d.data.start_time,
18116 title : d.data.title,
18117 description : d.data.description,
18118 venue : d.data.venue
18123 this.renderEvents();
18125 if(this.calevents.length && this.loadMask){
18126 this.maskEl.hide();
18130 onBeforeLoad: function()
18132 this.clearEvents();
18134 this.maskEl.show();
18148 * @class Roo.bootstrap.Popover
18149 * @extends Roo.bootstrap.Component
18150 * Bootstrap Popover class
18151 * @cfg {String} html contents of the popover (or false to use children..)
18152 * @cfg {String} title of popover (or false to hide)
18153 * @cfg {String} placement how it is placed
18154 * @cfg {String} trigger click || hover (or false to trigger manually)
18155 * @cfg {String} over what (parent or false to trigger manually.)
18156 * @cfg {Number} delay - delay before showing
18159 * Create a new Popover
18160 * @param {Object} config The config object
18163 Roo.bootstrap.Popover = function(config){
18164 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
18170 * After the popover show
18172 * @param {Roo.bootstrap.Popover} this
18177 * After the popover hide
18179 * @param {Roo.bootstrap.Popover} this
18185 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
18187 title: 'Fill in a title',
18190 placement : 'right',
18191 trigger : 'hover', // hover
18197 can_build_overlaid : false,
18199 getChildContainer : function()
18201 return this.el.select('.popover-content',true).first();
18204 getAutoCreate : function(){
18207 cls : 'popover roo-dynamic',
18208 style: 'display:block',
18214 cls : 'popover-inner',
18218 cls: 'popover-title popover-header',
18222 cls : 'popover-content popover-body',
18233 setTitle: function(str)
18236 this.el.select('.popover-title',true).first().dom.innerHTML = str;
18238 setContent: function(str)
18241 this.el.select('.popover-content',true).first().dom.innerHTML = str;
18243 // as it get's added to the bottom of the page.
18244 onRender : function(ct, position)
18246 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18248 var cfg = Roo.apply({}, this.getAutoCreate());
18252 cfg.cls += ' ' + this.cls;
18255 cfg.style = this.style;
18257 //Roo.log("adding to ");
18258 this.el = Roo.get(document.body).createChild(cfg, position);
18259 // Roo.log(this.el);
18264 initEvents : function()
18266 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
18267 this.el.enableDisplayMode('block');
18269 if (this.over === false) {
18272 if (this.triggers === false) {
18275 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18276 var triggers = this.trigger ? this.trigger.split(' ') : [];
18277 Roo.each(triggers, function(trigger) {
18279 if (trigger == 'click') {
18280 on_el.on('click', this.toggle, this);
18281 } else if (trigger != 'manual') {
18282 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
18283 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
18285 on_el.on(eventIn ,this.enter, this);
18286 on_el.on(eventOut, this.leave, this);
18297 toggle : function () {
18298 this.hoverState == 'in' ? this.leave() : this.enter();
18301 enter : function () {
18303 clearTimeout(this.timeout);
18305 this.hoverState = 'in';
18307 if (!this.delay || !this.delay.show) {
18312 this.timeout = setTimeout(function () {
18313 if (_t.hoverState == 'in') {
18316 }, this.delay.show)
18319 leave : function() {
18320 clearTimeout(this.timeout);
18322 this.hoverState = 'out';
18324 if (!this.delay || !this.delay.hide) {
18329 this.timeout = setTimeout(function () {
18330 if (_t.hoverState == 'out') {
18333 }, this.delay.hide)
18336 show : function (on_el)
18339 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18343 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18344 if (this.html !== false) {
18345 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18347 this.el.removeClass([
18348 'fade','top','bottom', 'left', 'right','in',
18349 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18351 if (!this.title.length) {
18352 this.el.select('.popover-title',true).hide();
18355 var placement = typeof this.placement == 'function' ?
18356 this.placement.call(this, this.el, on_el) :
18359 var autoToken = /\s?auto?\s?/i;
18360 var autoPlace = autoToken.test(placement);
18362 placement = placement.replace(autoToken, '') || 'top';
18366 //this.el.setXY([0,0]);
18368 this.el.dom.style.display='block';
18369 this.el.addClass(placement);
18371 //this.el.appendTo(on_el);
18373 var p = this.getPosition();
18374 var box = this.el.getBox();
18379 var align = Roo.bootstrap.Popover.alignment[placement];
18382 this.el.alignTo(on_el, align[0],align[1]);
18383 //var arrow = this.el.select('.arrow',true).first();
18384 //arrow.set(align[2],
18386 this.el.addClass('in');
18389 if (this.el.hasClass('fade')) {
18393 this.hoverState = 'in';
18395 this.fireEvent('show', this);
18400 this.el.setXY([0,0]);
18401 this.el.removeClass('in');
18403 this.hoverState = null;
18405 this.fireEvent('hide', this);
18410 Roo.bootstrap.Popover.alignment = {
18411 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18412 'right' : ['l-r', [10,0], 'left bs-popover-left'],
18413 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18414 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18425 * @class Roo.bootstrap.Progress
18426 * @extends Roo.bootstrap.Component
18427 * Bootstrap Progress class
18428 * @cfg {Boolean} striped striped of the progress bar
18429 * @cfg {Boolean} active animated of the progress bar
18433 * Create a new Progress
18434 * @param {Object} config The config object
18437 Roo.bootstrap.Progress = function(config){
18438 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18441 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
18446 getAutoCreate : function(){
18454 cfg.cls += ' progress-striped';
18458 cfg.cls += ' active';
18477 * @class Roo.bootstrap.ProgressBar
18478 * @extends Roo.bootstrap.Component
18479 * Bootstrap ProgressBar class
18480 * @cfg {Number} aria_valuenow aria-value now
18481 * @cfg {Number} aria_valuemin aria-value min
18482 * @cfg {Number} aria_valuemax aria-value max
18483 * @cfg {String} label label for the progress bar
18484 * @cfg {String} panel (success | info | warning | danger )
18485 * @cfg {String} role role of the progress bar
18486 * @cfg {String} sr_only text
18490 * Create a new ProgressBar
18491 * @param {Object} config The config object
18494 Roo.bootstrap.ProgressBar = function(config){
18495 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18498 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
18502 aria_valuemax : 100,
18508 getAutoCreate : function()
18513 cls: 'progress-bar',
18514 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18526 cfg.role = this.role;
18529 if(this.aria_valuenow){
18530 cfg['aria-valuenow'] = this.aria_valuenow;
18533 if(this.aria_valuemin){
18534 cfg['aria-valuemin'] = this.aria_valuemin;
18537 if(this.aria_valuemax){
18538 cfg['aria-valuemax'] = this.aria_valuemax;
18541 if(this.label && !this.sr_only){
18542 cfg.html = this.label;
18546 cfg.cls += ' progress-bar-' + this.panel;
18552 update : function(aria_valuenow)
18554 this.aria_valuenow = aria_valuenow;
18556 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18571 * @class Roo.bootstrap.TabGroup
18572 * @extends Roo.bootstrap.Column
18573 * Bootstrap Column class
18574 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18575 * @cfg {Boolean} carousel true to make the group behave like a carousel
18576 * @cfg {Boolean} bullets show bullets for the panels
18577 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18578 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18579 * @cfg {Boolean} showarrow (true|false) show arrow default true
18582 * Create a new TabGroup
18583 * @param {Object} config The config object
18586 Roo.bootstrap.TabGroup = function(config){
18587 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18589 this.navId = Roo.id();
18592 Roo.bootstrap.TabGroup.register(this);
18596 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18599 transition : false,
18604 slideOnTouch : false,
18607 getAutoCreate : function()
18609 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18611 cfg.cls += ' tab-content';
18613 if (this.carousel) {
18614 cfg.cls += ' carousel slide';
18617 cls : 'carousel-inner',
18621 if(this.bullets && !Roo.isTouch){
18624 cls : 'carousel-bullets',
18628 if(this.bullets_cls){
18629 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18636 cfg.cn[0].cn.push(bullets);
18639 if(this.showarrow){
18640 cfg.cn[0].cn.push({
18642 class : 'carousel-arrow',
18646 class : 'carousel-prev',
18650 class : 'fa fa-chevron-left'
18656 class : 'carousel-next',
18660 class : 'fa fa-chevron-right'
18673 initEvents: function()
18675 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18676 // this.el.on("touchstart", this.onTouchStart, this);
18679 if(this.autoslide){
18682 this.slideFn = window.setInterval(function() {
18683 _this.showPanelNext();
18687 if(this.showarrow){
18688 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18689 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18695 // onTouchStart : function(e, el, o)
18697 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18701 // this.showPanelNext();
18705 getChildContainer : function()
18707 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18711 * register a Navigation item
18712 * @param {Roo.bootstrap.NavItem} the navitem to add
18714 register : function(item)
18716 this.tabs.push( item);
18717 item.navId = this.navId; // not really needed..
18722 getActivePanel : function()
18725 Roo.each(this.tabs, function(t) {
18735 getPanelByName : function(n)
18738 Roo.each(this.tabs, function(t) {
18739 if (t.tabId == n) {
18747 indexOfPanel : function(p)
18750 Roo.each(this.tabs, function(t,i) {
18751 if (t.tabId == p.tabId) {
18760 * show a specific panel
18761 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18762 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18764 showPanel : function (pan)
18766 if(this.transition || typeof(pan) == 'undefined'){
18767 Roo.log("waiting for the transitionend");
18771 if (typeof(pan) == 'number') {
18772 pan = this.tabs[pan];
18775 if (typeof(pan) == 'string') {
18776 pan = this.getPanelByName(pan);
18779 var cur = this.getActivePanel();
18782 Roo.log('pan or acitve pan is undefined');
18786 if (pan.tabId == this.getActivePanel().tabId) {
18790 if (false === cur.fireEvent('beforedeactivate')) {
18794 if(this.bullets > 0 && !Roo.isTouch){
18795 this.setActiveBullet(this.indexOfPanel(pan));
18798 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18800 //class="carousel-item carousel-item-next carousel-item-left"
18802 this.transition = true;
18803 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18804 var lr = dir == 'next' ? 'left' : 'right';
18805 pan.el.addClass(dir); // or prev
18806 pan.el.addClass('carousel-item-' + dir); // or prev
18807 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18808 cur.el.addClass(lr); // or right
18809 pan.el.addClass(lr);
18810 cur.el.addClass('carousel-item-' +lr); // or right
18811 pan.el.addClass('carousel-item-' +lr);
18815 cur.el.on('transitionend', function() {
18816 Roo.log("trans end?");
18818 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18819 pan.setActive(true);
18821 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18822 cur.setActive(false);
18824 _this.transition = false;
18826 }, this, { single: true } );
18831 cur.setActive(false);
18832 pan.setActive(true);
18837 showPanelNext : function()
18839 var i = this.indexOfPanel(this.getActivePanel());
18841 if (i >= this.tabs.length - 1 && !this.autoslide) {
18845 if (i >= this.tabs.length - 1 && this.autoslide) {
18849 this.showPanel(this.tabs[i+1]);
18852 showPanelPrev : function()
18854 var i = this.indexOfPanel(this.getActivePanel());
18856 if (i < 1 && !this.autoslide) {
18860 if (i < 1 && this.autoslide) {
18861 i = this.tabs.length;
18864 this.showPanel(this.tabs[i-1]);
18868 addBullet: function()
18870 if(!this.bullets || Roo.isTouch){
18873 var ctr = this.el.select('.carousel-bullets',true).first();
18874 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18875 var bullet = ctr.createChild({
18876 cls : 'bullet bullet-' + i
18877 },ctr.dom.lastChild);
18882 bullet.on('click', (function(e, el, o, ii, t){
18884 e.preventDefault();
18886 this.showPanel(ii);
18888 if(this.autoslide && this.slideFn){
18889 clearInterval(this.slideFn);
18890 this.slideFn = window.setInterval(function() {
18891 _this.showPanelNext();
18895 }).createDelegate(this, [i, bullet], true));
18900 setActiveBullet : function(i)
18906 Roo.each(this.el.select('.bullet', true).elements, function(el){
18907 el.removeClass('selected');
18910 var bullet = this.el.select('.bullet-' + i, true).first();
18916 bullet.addClass('selected');
18927 Roo.apply(Roo.bootstrap.TabGroup, {
18931 * register a Navigation Group
18932 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18934 register : function(navgrp)
18936 this.groups[navgrp.navId] = navgrp;
18940 * fetch a Navigation Group based on the navigation ID
18941 * if one does not exist , it will get created.
18942 * @param {string} the navgroup to add
18943 * @returns {Roo.bootstrap.NavGroup} the navgroup
18945 get: function(navId) {
18946 if (typeof(this.groups[navId]) == 'undefined') {
18947 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18949 return this.groups[navId] ;
18964 * @class Roo.bootstrap.TabPanel
18965 * @extends Roo.bootstrap.Component
18966 * Bootstrap TabPanel class
18967 * @cfg {Boolean} active panel active
18968 * @cfg {String} html panel content
18969 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18970 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18971 * @cfg {String} href click to link..
18975 * Create a new TabPanel
18976 * @param {Object} config The config object
18979 Roo.bootstrap.TabPanel = function(config){
18980 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18984 * Fires when the active status changes
18985 * @param {Roo.bootstrap.TabPanel} this
18986 * @param {Boolean} state the new state
18991 * @event beforedeactivate
18992 * Fires before a tab is de-activated - can be used to do validation on a form.
18993 * @param {Roo.bootstrap.TabPanel} this
18994 * @return {Boolean} false if there is an error
18997 'beforedeactivate': true
19000 this.tabId = this.tabId || Roo.id();
19004 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
19012 getAutoCreate : function(){
19017 // item is needed for carousel - not sure if it has any effect otherwise
19018 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
19019 html: this.html || ''
19023 cfg.cls += ' active';
19027 cfg.tabId = this.tabId;
19035 initEvents: function()
19037 var p = this.parent();
19039 this.navId = this.navId || p.navId;
19041 if (typeof(this.navId) != 'undefined') {
19042 // not really needed.. but just in case.. parent should be a NavGroup.
19043 var tg = Roo.bootstrap.TabGroup.get(this.navId);
19047 var i = tg.tabs.length - 1;
19049 if(this.active && tg.bullets > 0 && i < tg.bullets){
19050 tg.setActiveBullet(i);
19054 this.el.on('click', this.onClick, this);
19057 this.el.on("touchstart", this.onTouchStart, this);
19058 this.el.on("touchmove", this.onTouchMove, this);
19059 this.el.on("touchend", this.onTouchEnd, this);
19064 onRender : function(ct, position)
19066 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
19069 setActive : function(state)
19071 Roo.log("panel - set active " + this.tabId + "=" + state);
19073 this.active = state;
19075 this.el.removeClass('active');
19077 } else if (!this.el.hasClass('active')) {
19078 this.el.addClass('active');
19081 this.fireEvent('changed', this, state);
19084 onClick : function(e)
19086 e.preventDefault();
19088 if(!this.href.length){
19092 window.location.href = this.href;
19101 onTouchStart : function(e)
19103 this.swiping = false;
19105 this.startX = e.browserEvent.touches[0].clientX;
19106 this.startY = e.browserEvent.touches[0].clientY;
19109 onTouchMove : function(e)
19111 this.swiping = true;
19113 this.endX = e.browserEvent.touches[0].clientX;
19114 this.endY = e.browserEvent.touches[0].clientY;
19117 onTouchEnd : function(e)
19124 var tabGroup = this.parent();
19126 if(this.endX > this.startX){ // swiping right
19127 tabGroup.showPanelPrev();
19131 if(this.startX > this.endX){ // swiping left
19132 tabGroup.showPanelNext();
19151 * @class Roo.bootstrap.DateField
19152 * @extends Roo.bootstrap.Input
19153 * Bootstrap DateField class
19154 * @cfg {Number} weekStart default 0
19155 * @cfg {String} viewMode default empty, (months|years)
19156 * @cfg {String} minViewMode default empty, (months|years)
19157 * @cfg {Number} startDate default -Infinity
19158 * @cfg {Number} endDate default Infinity
19159 * @cfg {Boolean} todayHighlight default false
19160 * @cfg {Boolean} todayBtn default false
19161 * @cfg {Boolean} calendarWeeks default false
19162 * @cfg {Object} daysOfWeekDisabled default empty
19163 * @cfg {Boolean} singleMode default false (true | false)
19165 * @cfg {Boolean} keyboardNavigation default true
19166 * @cfg {String} language default en
19169 * Create a new DateField
19170 * @param {Object} config The config object
19173 Roo.bootstrap.DateField = function(config){
19174 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
19178 * Fires when this field show.
19179 * @param {Roo.bootstrap.DateField} this
19180 * @param {Mixed} date The date value
19185 * Fires when this field hide.
19186 * @param {Roo.bootstrap.DateField} this
19187 * @param {Mixed} date The date value
19192 * Fires when select a date.
19193 * @param {Roo.bootstrap.DateField} this
19194 * @param {Mixed} date The date value
19198 * @event beforeselect
19199 * Fires when before select a date.
19200 * @param {Roo.bootstrap.DateField} this
19201 * @param {Mixed} date The date value
19203 beforeselect : true
19207 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
19210 * @cfg {String} format
19211 * The default date format string which can be overriden for localization support. The format must be
19212 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
19216 * @cfg {String} altFormats
19217 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
19218 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
19220 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
19228 todayHighlight : false,
19234 keyboardNavigation: true,
19236 calendarWeeks: false,
19238 startDate: -Infinity,
19242 daysOfWeekDisabled: [],
19246 singleMode : false,
19248 UTCDate: function()
19250 return new Date(Date.UTC.apply(Date, arguments));
19253 UTCToday: function()
19255 var today = new Date();
19256 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
19259 getDate: function() {
19260 var d = this.getUTCDate();
19261 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
19264 getUTCDate: function() {
19268 setDate: function(d) {
19269 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
19272 setUTCDate: function(d) {
19274 this.setValue(this.formatDate(this.date));
19277 onRender: function(ct, position)
19280 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
19282 this.language = this.language || 'en';
19283 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
19284 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
19286 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
19287 this.format = this.format || 'm/d/y';
19288 this.isInline = false;
19289 this.isInput = true;
19290 this.component = this.el.select('.add-on', true).first() || false;
19291 this.component = (this.component && this.component.length === 0) ? false : this.component;
19292 this.hasInput = this.component && this.inputEl().length;
19294 if (typeof(this.minViewMode === 'string')) {
19295 switch (this.minViewMode) {
19297 this.minViewMode = 1;
19300 this.minViewMode = 2;
19303 this.minViewMode = 0;
19308 if (typeof(this.viewMode === 'string')) {
19309 switch (this.viewMode) {
19322 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19324 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19326 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19328 this.picker().on('mousedown', this.onMousedown, this);
19329 this.picker().on('click', this.onClick, this);
19331 this.picker().addClass('datepicker-dropdown');
19333 this.startViewMode = this.viewMode;
19335 if(this.singleMode){
19336 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19337 v.setVisibilityMode(Roo.Element.DISPLAY);
19341 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19342 v.setStyle('width', '189px');
19346 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19347 if(!this.calendarWeeks){
19352 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19353 v.attr('colspan', function(i, val){
19354 return parseInt(val) + 1;
19359 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19361 this.setStartDate(this.startDate);
19362 this.setEndDate(this.endDate);
19364 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19371 if(this.isInline) {
19376 picker : function()
19378 return this.pickerEl;
19379 // return this.el.select('.datepicker', true).first();
19382 fillDow: function()
19384 var dowCnt = this.weekStart;
19393 if(this.calendarWeeks){
19401 while (dowCnt < this.weekStart + 7) {
19405 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19409 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19412 fillMonths: function()
19415 var months = this.picker().select('>.datepicker-months td', true).first();
19417 months.dom.innerHTML = '';
19423 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19426 months.createChild(month);
19433 this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
19435 if (this.date < this.startDate) {
19436 this.viewDate = new Date(this.startDate);
19437 } else if (this.date > this.endDate) {
19438 this.viewDate = new Date(this.endDate);
19440 this.viewDate = new Date(this.date);
19448 var d = new Date(this.viewDate),
19449 year = d.getUTCFullYear(),
19450 month = d.getUTCMonth(),
19451 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19452 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19453 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19454 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19455 currentDate = this.date && this.date.valueOf(),
19456 today = this.UTCToday();
19458 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19460 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19462 // this.picker.select('>tfoot th.today').
19463 // .text(dates[this.language].today)
19464 // .toggle(this.todayBtn !== false);
19466 this.updateNavArrows();
19469 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19471 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19473 prevMonth.setUTCDate(day);
19475 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19477 var nextMonth = new Date(prevMonth);
19479 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19481 nextMonth = nextMonth.valueOf();
19483 var fillMonths = false;
19485 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19487 while(prevMonth.valueOf() <= nextMonth) {
19490 if (prevMonth.getUTCDay() === this.weekStart) {
19492 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19500 if(this.calendarWeeks){
19501 // ISO 8601: First week contains first thursday.
19502 // ISO also states week starts on Monday, but we can be more abstract here.
19504 // Start of current week: based on weekstart/current date
19505 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19506 // Thursday of this week
19507 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19508 // First Thursday of year, year from thursday
19509 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19510 // Calendar week: ms between thursdays, div ms per day, div 7 days
19511 calWeek = (th - yth) / 864e5 / 7 + 1;
19513 fillMonths.cn.push({
19521 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19523 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19526 if (this.todayHighlight &&
19527 prevMonth.getUTCFullYear() == today.getFullYear() &&
19528 prevMonth.getUTCMonth() == today.getMonth() &&
19529 prevMonth.getUTCDate() == today.getDate()) {
19530 clsName += ' today';
19533 if (currentDate && prevMonth.valueOf() === currentDate) {
19534 clsName += ' active';
19537 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19538 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19539 clsName += ' disabled';
19542 fillMonths.cn.push({
19544 cls: 'day ' + clsName,
19545 html: prevMonth.getDate()
19548 prevMonth.setDate(prevMonth.getDate()+1);
19551 var currentYear = this.date && this.date.getUTCFullYear();
19552 var currentMonth = this.date && this.date.getUTCMonth();
19554 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19556 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19557 v.removeClass('active');
19559 if(currentYear === year && k === currentMonth){
19560 v.addClass('active');
19563 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19564 v.addClass('disabled');
19570 year = parseInt(year/10, 10) * 10;
19572 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19574 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19577 for (var i = -1; i < 11; i++) {
19578 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19580 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19588 showMode: function(dir)
19591 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19594 Roo.each(this.picker().select('>div',true).elements, function(v){
19595 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19598 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19603 if(this.isInline) {
19607 this.picker().removeClass(['bottom', 'top']);
19609 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19611 * place to the top of element!
19615 this.picker().addClass('top');
19616 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19621 this.picker().addClass('bottom');
19623 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19626 parseDate : function(value)
19628 if(!value || value instanceof Date){
19631 var v = Date.parseDate(value, this.format);
19632 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19633 v = Date.parseDate(value, 'Y-m-d');
19635 if(!v && this.altFormats){
19636 if(!this.altFormatsArray){
19637 this.altFormatsArray = this.altFormats.split("|");
19639 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19640 v = Date.parseDate(value, this.altFormatsArray[i]);
19646 formatDate : function(date, fmt)
19648 return (!date || !(date instanceof Date)) ?
19649 date : date.dateFormat(fmt || this.format);
19652 onFocus : function()
19654 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19658 onBlur : function()
19660 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19662 var d = this.inputEl().getValue();
19669 showPopup : function()
19671 this.picker().show();
19675 this.fireEvent('showpopup', this, this.date);
19678 hidePopup : function()
19680 if(this.isInline) {
19683 this.picker().hide();
19684 this.viewMode = this.startViewMode;
19687 this.fireEvent('hidepopup', this, this.date);
19691 onMousedown: function(e)
19693 e.stopPropagation();
19694 e.preventDefault();
19699 Roo.bootstrap.DateField.superclass.keyup.call(this);
19703 setValue: function(v)
19705 if(this.fireEvent('beforeselect', this, v) !== false){
19706 var d = new Date(this.parseDate(v) ).clearTime();
19708 if(isNaN(d.getTime())){
19709 this.date = this.viewDate = '';
19710 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19714 v = this.formatDate(d);
19716 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19718 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19722 this.fireEvent('select', this, this.date);
19726 getValue: function()
19728 return this.formatDate(this.date);
19731 fireKey: function(e)
19733 if (!this.picker().isVisible()){
19734 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19740 var dateChanged = false,
19742 newDate, newViewDate;
19747 e.preventDefault();
19751 if (!this.keyboardNavigation) {
19754 dir = e.keyCode == 37 ? -1 : 1;
19757 newDate = this.moveYear(this.date, dir);
19758 newViewDate = this.moveYear(this.viewDate, dir);
19759 } else if (e.shiftKey){
19760 newDate = this.moveMonth(this.date, dir);
19761 newViewDate = this.moveMonth(this.viewDate, dir);
19763 newDate = new Date(this.date);
19764 newDate.setUTCDate(this.date.getUTCDate() + dir);
19765 newViewDate = new Date(this.viewDate);
19766 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19768 if (this.dateWithinRange(newDate)){
19769 this.date = newDate;
19770 this.viewDate = newViewDate;
19771 this.setValue(this.formatDate(this.date));
19773 e.preventDefault();
19774 dateChanged = true;
19779 if (!this.keyboardNavigation) {
19782 dir = e.keyCode == 38 ? -1 : 1;
19784 newDate = this.moveYear(this.date, dir);
19785 newViewDate = this.moveYear(this.viewDate, dir);
19786 } else if (e.shiftKey){
19787 newDate = this.moveMonth(this.date, dir);
19788 newViewDate = this.moveMonth(this.viewDate, dir);
19790 newDate = new Date(this.date);
19791 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19792 newViewDate = new Date(this.viewDate);
19793 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19795 if (this.dateWithinRange(newDate)){
19796 this.date = newDate;
19797 this.viewDate = newViewDate;
19798 this.setValue(this.formatDate(this.date));
19800 e.preventDefault();
19801 dateChanged = true;
19805 this.setValue(this.formatDate(this.date));
19807 e.preventDefault();
19810 this.setValue(this.formatDate(this.date));
19824 onClick: function(e)
19826 e.stopPropagation();
19827 e.preventDefault();
19829 var target = e.getTarget();
19831 if(target.nodeName.toLowerCase() === 'i'){
19832 target = Roo.get(target).dom.parentNode;
19835 var nodeName = target.nodeName;
19836 var className = target.className;
19837 var html = target.innerHTML;
19838 //Roo.log(nodeName);
19840 switch(nodeName.toLowerCase()) {
19842 switch(className) {
19848 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19849 switch(this.viewMode){
19851 this.viewDate = this.moveMonth(this.viewDate, dir);
19855 this.viewDate = this.moveYear(this.viewDate, dir);
19861 var date = new Date();
19862 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19864 this.setValue(this.formatDate(this.date));
19871 if (className.indexOf('disabled') < 0) {
19872 this.viewDate.setUTCDate(1);
19873 if (className.indexOf('month') > -1) {
19874 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19876 var year = parseInt(html, 10) || 0;
19877 this.viewDate.setUTCFullYear(year);
19881 if(this.singleMode){
19882 this.setValue(this.formatDate(this.viewDate));
19893 //Roo.log(className);
19894 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19895 var day = parseInt(html, 10) || 1;
19896 var year = this.viewDate.getUTCFullYear(),
19897 month = this.viewDate.getUTCMonth();
19899 if (className.indexOf('old') > -1) {
19906 } else if (className.indexOf('new') > -1) {
19914 //Roo.log([year,month,day]);
19915 this.date = this.UTCDate(year, month, day,0,0,0,0);
19916 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19918 //Roo.log(this.formatDate(this.date));
19919 this.setValue(this.formatDate(this.date));
19926 setStartDate: function(startDate)
19928 this.startDate = startDate || -Infinity;
19929 if (this.startDate !== -Infinity) {
19930 this.startDate = this.parseDate(this.startDate);
19933 this.updateNavArrows();
19936 setEndDate: function(endDate)
19938 this.endDate = endDate || Infinity;
19939 if (this.endDate !== Infinity) {
19940 this.endDate = this.parseDate(this.endDate);
19943 this.updateNavArrows();
19946 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19948 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19949 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19950 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19952 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19953 return parseInt(d, 10);
19956 this.updateNavArrows();
19959 updateNavArrows: function()
19961 if(this.singleMode){
19965 var d = new Date(this.viewDate),
19966 year = d.getUTCFullYear(),
19967 month = d.getUTCMonth();
19969 Roo.each(this.picker().select('.prev', true).elements, function(v){
19971 switch (this.viewMode) {
19974 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19980 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19987 Roo.each(this.picker().select('.next', true).elements, function(v){
19989 switch (this.viewMode) {
19992 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19998 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
20006 moveMonth: function(date, dir)
20011 var new_date = new Date(date.valueOf()),
20012 day = new_date.getUTCDate(),
20013 month = new_date.getUTCMonth(),
20014 mag = Math.abs(dir),
20016 dir = dir > 0 ? 1 : -1;
20019 // If going back one month, make sure month is not current month
20020 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
20022 return new_date.getUTCMonth() == month;
20024 // If going forward one month, make sure month is as expected
20025 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
20027 return new_date.getUTCMonth() != new_month;
20029 new_month = month + dir;
20030 new_date.setUTCMonth(new_month);
20031 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
20032 if (new_month < 0 || new_month > 11) {
20033 new_month = (new_month + 12) % 12;
20036 // For magnitudes >1, move one month at a time...
20037 for (var i=0; i<mag; i++) {
20038 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
20039 new_date = this.moveMonth(new_date, dir);
20041 // ...then reset the day, keeping it in the new month
20042 new_month = new_date.getUTCMonth();
20043 new_date.setUTCDate(day);
20045 return new_month != new_date.getUTCMonth();
20048 // Common date-resetting loop -- if date is beyond end of month, make it
20051 new_date.setUTCDate(--day);
20052 new_date.setUTCMonth(new_month);
20057 moveYear: function(date, dir)
20059 return this.moveMonth(date, dir*12);
20062 dateWithinRange: function(date)
20064 return date >= this.startDate && date <= this.endDate;
20070 this.picker().remove();
20073 validateValue : function(value)
20075 if(this.getVisibilityEl().hasClass('hidden')){
20079 if(value.length < 1) {
20080 if(this.allowBlank){
20086 if(value.length < this.minLength){
20089 if(value.length > this.maxLength){
20093 var vt = Roo.form.VTypes;
20094 if(!vt[this.vtype](value, this)){
20098 if(typeof this.validator == "function"){
20099 var msg = this.validator(value);
20105 if(this.regex && !this.regex.test(value)){
20109 if(typeof(this.parseDate(value)) == 'undefined'){
20113 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
20117 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
20127 this.date = this.viewDate = '';
20129 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20134 Roo.apply(Roo.bootstrap.DateField, {
20145 html: '<i class="fa fa-arrow-left"/>'
20155 html: '<i class="fa fa-arrow-right"/>'
20197 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
20198 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
20199 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
20200 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20201 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
20214 navFnc: 'FullYear',
20219 navFnc: 'FullYear',
20224 Roo.apply(Roo.bootstrap.DateField, {
20228 cls: 'datepicker dropdown-menu roo-dynamic',
20232 cls: 'datepicker-days',
20236 cls: 'table-condensed',
20238 Roo.bootstrap.DateField.head,
20242 Roo.bootstrap.DateField.footer
20249 cls: 'datepicker-months',
20253 cls: 'table-condensed',
20255 Roo.bootstrap.DateField.head,
20256 Roo.bootstrap.DateField.content,
20257 Roo.bootstrap.DateField.footer
20264 cls: 'datepicker-years',
20268 cls: 'table-condensed',
20270 Roo.bootstrap.DateField.head,
20271 Roo.bootstrap.DateField.content,
20272 Roo.bootstrap.DateField.footer
20291 * @class Roo.bootstrap.TimeField
20292 * @extends Roo.bootstrap.Input
20293 * Bootstrap DateField class
20297 * Create a new TimeField
20298 * @param {Object} config The config object
20301 Roo.bootstrap.TimeField = function(config){
20302 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20306 * Fires when this field show.
20307 * @param {Roo.bootstrap.DateField} thisthis
20308 * @param {Mixed} date The date value
20313 * Fires when this field hide.
20314 * @param {Roo.bootstrap.DateField} this
20315 * @param {Mixed} date The date value
20320 * Fires when select a date.
20321 * @param {Roo.bootstrap.DateField} this
20322 * @param {Mixed} date The date value
20328 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
20331 * @cfg {String} format
20332 * The default time format string which can be overriden for localization support. The format must be
20333 * valid according to {@link Date#parseDate} (defaults to 'H:i').
20337 onRender: function(ct, position)
20340 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20342 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20344 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20346 this.pop = this.picker().select('>.datepicker-time',true).first();
20347 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20349 this.picker().on('mousedown', this.onMousedown, this);
20350 this.picker().on('click', this.onClick, this);
20352 this.picker().addClass('datepicker-dropdown');
20357 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20358 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20359 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20360 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20361 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20362 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20366 fireKey: function(e){
20367 if (!this.picker().isVisible()){
20368 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20374 e.preventDefault();
20382 this.onTogglePeriod();
20385 this.onIncrementMinutes();
20388 this.onDecrementMinutes();
20397 onClick: function(e) {
20398 e.stopPropagation();
20399 e.preventDefault();
20402 picker : function()
20404 return this.el.select('.datepicker', true).first();
20407 fillTime: function()
20409 var time = this.pop.select('tbody', true).first();
20411 time.dom.innerHTML = '';
20426 cls: 'hours-up glyphicon glyphicon-chevron-up'
20446 cls: 'minutes-up glyphicon glyphicon-chevron-up'
20467 cls: 'timepicker-hour',
20482 cls: 'timepicker-minute',
20497 cls: 'btn btn-primary period',
20519 cls: 'hours-down glyphicon glyphicon-chevron-down'
20539 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20557 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20564 var hours = this.time.getHours();
20565 var minutes = this.time.getMinutes();
20578 hours = hours - 12;
20582 hours = '0' + hours;
20586 minutes = '0' + minutes;
20589 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20590 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20591 this.pop.select('button', true).first().dom.innerHTML = period;
20597 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20599 var cls = ['bottom'];
20601 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20608 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20613 this.picker().addClass(cls.join('-'));
20617 Roo.each(cls, function(c){
20619 _this.picker().setTop(_this.inputEl().getHeight());
20623 _this.picker().setTop(0 - _this.picker().getHeight());
20628 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20632 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20639 onFocus : function()
20641 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20645 onBlur : function()
20647 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20653 this.picker().show();
20658 this.fireEvent('show', this, this.date);
20663 this.picker().hide();
20666 this.fireEvent('hide', this, this.date);
20669 setTime : function()
20672 this.setValue(this.time.format(this.format));
20674 this.fireEvent('select', this, this.date);
20679 onMousedown: function(e){
20680 e.stopPropagation();
20681 e.preventDefault();
20684 onIncrementHours: function()
20686 Roo.log('onIncrementHours');
20687 this.time = this.time.add(Date.HOUR, 1);
20692 onDecrementHours: function()
20694 Roo.log('onDecrementHours');
20695 this.time = this.time.add(Date.HOUR, -1);
20699 onIncrementMinutes: function()
20701 Roo.log('onIncrementMinutes');
20702 this.time = this.time.add(Date.MINUTE, 1);
20706 onDecrementMinutes: function()
20708 Roo.log('onDecrementMinutes');
20709 this.time = this.time.add(Date.MINUTE, -1);
20713 onTogglePeriod: function()
20715 Roo.log('onTogglePeriod');
20716 this.time = this.time.add(Date.HOUR, 12);
20723 Roo.apply(Roo.bootstrap.TimeField, {
20753 cls: 'btn btn-info ok',
20765 Roo.apply(Roo.bootstrap.TimeField, {
20769 cls: 'datepicker dropdown-menu',
20773 cls: 'datepicker-time',
20777 cls: 'table-condensed',
20779 Roo.bootstrap.TimeField.content,
20780 Roo.bootstrap.TimeField.footer
20799 * @class Roo.bootstrap.MonthField
20800 * @extends Roo.bootstrap.Input
20801 * Bootstrap MonthField class
20803 * @cfg {String} language default en
20806 * Create a new MonthField
20807 * @param {Object} config The config object
20810 Roo.bootstrap.MonthField = function(config){
20811 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20816 * Fires when this field show.
20817 * @param {Roo.bootstrap.MonthField} this
20818 * @param {Mixed} date The date value
20823 * Fires when this field hide.
20824 * @param {Roo.bootstrap.MonthField} this
20825 * @param {Mixed} date The date value
20830 * Fires when select a date.
20831 * @param {Roo.bootstrap.MonthField} this
20832 * @param {String} oldvalue The old value
20833 * @param {String} newvalue The new value
20839 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20841 onRender: function(ct, position)
20844 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20846 this.language = this.language || 'en';
20847 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20848 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20850 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20851 this.isInline = false;
20852 this.isInput = true;
20853 this.component = this.el.select('.add-on', true).first() || false;
20854 this.component = (this.component && this.component.length === 0) ? false : this.component;
20855 this.hasInput = this.component && this.inputEL().length;
20857 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20859 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20861 this.picker().on('mousedown', this.onMousedown, this);
20862 this.picker().on('click', this.onClick, this);
20864 this.picker().addClass('datepicker-dropdown');
20866 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20867 v.setStyle('width', '189px');
20874 if(this.isInline) {
20880 setValue: function(v, suppressEvent)
20882 var o = this.getValue();
20884 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20888 if(suppressEvent !== true){
20889 this.fireEvent('select', this, o, v);
20894 getValue: function()
20899 onClick: function(e)
20901 e.stopPropagation();
20902 e.preventDefault();
20904 var target = e.getTarget();
20906 if(target.nodeName.toLowerCase() === 'i'){
20907 target = Roo.get(target).dom.parentNode;
20910 var nodeName = target.nodeName;
20911 var className = target.className;
20912 var html = target.innerHTML;
20914 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20918 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20920 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20926 picker : function()
20928 return this.pickerEl;
20931 fillMonths: function()
20934 var months = this.picker().select('>.datepicker-months td', true).first();
20936 months.dom.innerHTML = '';
20942 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20945 months.createChild(month);
20954 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20955 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20958 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20959 e.removeClass('active');
20961 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20962 e.addClass('active');
20969 if(this.isInline) {
20973 this.picker().removeClass(['bottom', 'top']);
20975 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20977 * place to the top of element!
20981 this.picker().addClass('top');
20982 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20987 this.picker().addClass('bottom');
20989 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20992 onFocus : function()
20994 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20998 onBlur : function()
21000 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
21002 var d = this.inputEl().getValue();
21011 this.picker().show();
21012 this.picker().select('>.datepicker-months', true).first().show();
21016 this.fireEvent('show', this, this.date);
21021 if(this.isInline) {
21024 this.picker().hide();
21025 this.fireEvent('hide', this, this.date);
21029 onMousedown: function(e)
21031 e.stopPropagation();
21032 e.preventDefault();
21037 Roo.bootstrap.MonthField.superclass.keyup.call(this);
21041 fireKey: function(e)
21043 if (!this.picker().isVisible()){
21044 if (e.keyCode == 27) {// allow escape to hide and re-show picker
21055 e.preventDefault();
21059 dir = e.keyCode == 37 ? -1 : 1;
21061 this.vIndex = this.vIndex + dir;
21063 if(this.vIndex < 0){
21067 if(this.vIndex > 11){
21071 if(isNaN(this.vIndex)){
21075 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21081 dir = e.keyCode == 38 ? -1 : 1;
21083 this.vIndex = this.vIndex + dir * 4;
21085 if(this.vIndex < 0){
21089 if(this.vIndex > 11){
21093 if(isNaN(this.vIndex)){
21097 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21102 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21103 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21107 e.preventDefault();
21110 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21111 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21127 this.picker().remove();
21132 Roo.apply(Roo.bootstrap.MonthField, {
21151 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21152 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
21157 Roo.apply(Roo.bootstrap.MonthField, {
21161 cls: 'datepicker dropdown-menu roo-dynamic',
21165 cls: 'datepicker-months',
21169 cls: 'table-condensed',
21171 Roo.bootstrap.DateField.content
21191 * @class Roo.bootstrap.CheckBox
21192 * @extends Roo.bootstrap.Input
21193 * Bootstrap CheckBox class
21195 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
21196 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
21197 * @cfg {String} boxLabel The text that appears beside the checkbox
21198 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
21199 * @cfg {Boolean} checked initnal the element
21200 * @cfg {Boolean} inline inline the element (default false)
21201 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
21202 * @cfg {String} tooltip label tooltip
21205 * Create a new CheckBox
21206 * @param {Object} config The config object
21209 Roo.bootstrap.CheckBox = function(config){
21210 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
21215 * Fires when the element is checked or unchecked.
21216 * @param {Roo.bootstrap.CheckBox} this This input
21217 * @param {Boolean} checked The new checked value
21222 * Fires when the element is click.
21223 * @param {Roo.bootstrap.CheckBox} this This input
21230 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
21232 inputType: 'checkbox',
21241 // checkbox success does not make any sense really..
21246 getAutoCreate : function()
21248 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
21254 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
21257 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
21263 type : this.inputType,
21264 value : this.inputValue,
21265 cls : 'roo-' + this.inputType, //'form-box',
21266 placeholder : this.placeholder || ''
21270 if(this.inputType != 'radio'){
21274 cls : 'roo-hidden-value',
21275 value : this.checked ? this.inputValue : this.valueOff
21280 if (this.weight) { // Validity check?
21281 cfg.cls += " " + this.inputType + "-" + this.weight;
21284 if (this.disabled) {
21285 input.disabled=true;
21289 input.checked = this.checked;
21294 input.name = this.name;
21296 if(this.inputType != 'radio'){
21297 hidden.name = this.name;
21298 input.name = '_hidden_' + this.name;
21303 input.cls += ' input-' + this.size;
21308 ['xs','sm','md','lg'].map(function(size){
21309 if (settings[size]) {
21310 cfg.cls += ' col-' + size + '-' + settings[size];
21314 var inputblock = input;
21316 if (this.before || this.after) {
21319 cls : 'input-group',
21324 inputblock.cn.push({
21326 cls : 'input-group-addon',
21331 inputblock.cn.push(input);
21333 if(this.inputType != 'radio'){
21334 inputblock.cn.push(hidden);
21338 inputblock.cn.push({
21340 cls : 'input-group-addon',
21346 var boxLabelCfg = false;
21352 //'for': id, // box label is handled by onclick - so no for...
21354 html: this.boxLabel
21357 boxLabelCfg.tooltip = this.tooltip;
21363 if (align ==='left' && this.fieldLabel.length) {
21364 // Roo.log("left and has label");
21369 cls : 'control-label',
21370 html : this.fieldLabel
21381 cfg.cn[1].cn.push(boxLabelCfg);
21384 if(this.labelWidth > 12){
21385 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21388 if(this.labelWidth < 13 && this.labelmd == 0){
21389 this.labelmd = this.labelWidth;
21392 if(this.labellg > 0){
21393 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21394 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21397 if(this.labelmd > 0){
21398 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21399 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21402 if(this.labelsm > 0){
21403 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21404 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21407 if(this.labelxs > 0){
21408 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21409 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21412 } else if ( this.fieldLabel.length) {
21413 // Roo.log(" label");
21417 tag: this.boxLabel ? 'span' : 'label',
21419 cls: 'control-label box-input-label',
21420 //cls : 'input-group-addon',
21421 html : this.fieldLabel
21428 cfg.cn.push(boxLabelCfg);
21433 // Roo.log(" no label && no align");
21434 cfg.cn = [ inputblock ] ;
21436 cfg.cn.push(boxLabelCfg);
21444 if(this.inputType != 'radio'){
21445 cfg.cn.push(hidden);
21453 * return the real input element.
21455 inputEl: function ()
21457 return this.el.select('input.roo-' + this.inputType,true).first();
21459 hiddenEl: function ()
21461 return this.el.select('input.roo-hidden-value',true).first();
21464 labelEl: function()
21466 return this.el.select('label.control-label',true).first();
21468 /* depricated... */
21472 return this.labelEl();
21475 boxLabelEl: function()
21477 return this.el.select('label.box-label',true).first();
21480 initEvents : function()
21482 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21484 this.inputEl().on('click', this.onClick, this);
21486 if (this.boxLabel) {
21487 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
21490 this.startValue = this.getValue();
21493 Roo.bootstrap.CheckBox.register(this);
21497 onClick : function(e)
21499 if(this.fireEvent('click', this, e) !== false){
21500 this.setChecked(!this.checked);
21505 setChecked : function(state,suppressEvent)
21507 this.startValue = this.getValue();
21509 if(this.inputType == 'radio'){
21511 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21512 e.dom.checked = false;
21515 this.inputEl().dom.checked = true;
21517 this.inputEl().dom.value = this.inputValue;
21519 if(suppressEvent !== true){
21520 this.fireEvent('check', this, true);
21528 this.checked = state;
21530 this.inputEl().dom.checked = state;
21533 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21535 if(suppressEvent !== true){
21536 this.fireEvent('check', this, state);
21542 getValue : function()
21544 if(this.inputType == 'radio'){
21545 return this.getGroupValue();
21548 return this.hiddenEl().dom.value;
21552 getGroupValue : function()
21554 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21558 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21561 setValue : function(v,suppressEvent)
21563 if(this.inputType == 'radio'){
21564 this.setGroupValue(v, suppressEvent);
21568 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21573 setGroupValue : function(v, suppressEvent)
21575 this.startValue = this.getValue();
21577 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21578 e.dom.checked = false;
21580 if(e.dom.value == v){
21581 e.dom.checked = true;
21585 if(suppressEvent !== true){
21586 this.fireEvent('check', this, true);
21594 validate : function()
21596 if(this.getVisibilityEl().hasClass('hidden')){
21602 (this.inputType == 'radio' && this.validateRadio()) ||
21603 (this.inputType == 'checkbox' && this.validateCheckbox())
21609 this.markInvalid();
21613 validateRadio : function()
21615 if(this.getVisibilityEl().hasClass('hidden')){
21619 if(this.allowBlank){
21625 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21626 if(!e.dom.checked){
21638 validateCheckbox : function()
21641 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21642 //return (this.getValue() == this.inputValue) ? true : false;
21645 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21653 for(var i in group){
21654 if(group[i].el.isVisible(true)){
21662 for(var i in group){
21667 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21674 * Mark this field as valid
21676 markValid : function()
21680 this.fireEvent('valid', this);
21682 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21685 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21692 if(this.inputType == 'radio'){
21693 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21694 var fg = e.findParent('.form-group', false, true);
21695 if (Roo.bootstrap.version == 3) {
21696 fg.removeClass([_this.invalidClass, _this.validClass]);
21697 fg.addClass(_this.validClass);
21699 fg.removeClass(['is-valid', 'is-invalid']);
21700 fg.addClass('is-valid');
21708 var fg = this.el.findParent('.form-group', false, true);
21709 if (Roo.bootstrap.version == 3) {
21710 fg.removeClass([this.invalidClass, this.validClass]);
21711 fg.addClass(this.validClass);
21713 fg.removeClass(['is-valid', 'is-invalid']);
21714 fg.addClass('is-valid');
21719 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21725 for(var i in group){
21726 var fg = group[i].el.findParent('.form-group', false, true);
21727 if (Roo.bootstrap.version == 3) {
21728 fg.removeClass([this.invalidClass, this.validClass]);
21729 fg.addClass(this.validClass);
21731 fg.removeClass(['is-valid', 'is-invalid']);
21732 fg.addClass('is-valid');
21738 * Mark this field as invalid
21739 * @param {String} msg The validation message
21741 markInvalid : function(msg)
21743 if(this.allowBlank){
21749 this.fireEvent('invalid', this, msg);
21751 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21754 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21758 label.markInvalid();
21761 if(this.inputType == 'radio'){
21763 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21764 var fg = e.findParent('.form-group', false, true);
21765 if (Roo.bootstrap.version == 3) {
21766 fg.removeClass([_this.invalidClass, _this.validClass]);
21767 fg.addClass(_this.invalidClass);
21769 fg.removeClass(['is-invalid', 'is-valid']);
21770 fg.addClass('is-invalid');
21778 var fg = this.el.findParent('.form-group', false, true);
21779 if (Roo.bootstrap.version == 3) {
21780 fg.removeClass([_this.invalidClass, _this.validClass]);
21781 fg.addClass(_this.invalidClass);
21783 fg.removeClass(['is-invalid', 'is-valid']);
21784 fg.addClass('is-invalid');
21789 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21795 for(var i in group){
21796 var fg = group[i].el.findParent('.form-group', false, true);
21797 if (Roo.bootstrap.version == 3) {
21798 fg.removeClass([_this.invalidClass, _this.validClass]);
21799 fg.addClass(_this.invalidClass);
21801 fg.removeClass(['is-invalid', 'is-valid']);
21802 fg.addClass('is-invalid');
21808 clearInvalid : function()
21810 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21812 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21814 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21816 if (label && label.iconEl) {
21817 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21818 label.iconEl.removeClass(['is-invalid', 'is-valid']);
21822 disable : function()
21824 if(this.inputType != 'radio'){
21825 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21832 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21833 _this.getActionEl().addClass(this.disabledClass);
21834 e.dom.disabled = true;
21838 this.disabled = true;
21839 this.fireEvent("disable", this);
21843 enable : function()
21845 if(this.inputType != 'radio'){
21846 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21853 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21854 _this.getActionEl().removeClass(this.disabledClass);
21855 e.dom.disabled = false;
21859 this.disabled = false;
21860 this.fireEvent("enable", this);
21864 setBoxLabel : function(v)
21869 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21875 Roo.apply(Roo.bootstrap.CheckBox, {
21880 * register a CheckBox Group
21881 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21883 register : function(checkbox)
21885 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21886 this.groups[checkbox.groupId] = {};
21889 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21893 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21897 * fetch a CheckBox Group based on the group ID
21898 * @param {string} the group ID
21899 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21901 get: function(groupId) {
21902 if (typeof(this.groups[groupId]) == 'undefined') {
21906 return this.groups[groupId] ;
21919 * @class Roo.bootstrap.Radio
21920 * @extends Roo.bootstrap.Component
21921 * Bootstrap Radio class
21922 * @cfg {String} boxLabel - the label associated
21923 * @cfg {String} value - the value of radio
21926 * Create a new Radio
21927 * @param {Object} config The config object
21929 Roo.bootstrap.Radio = function(config){
21930 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21934 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21940 getAutoCreate : function()
21944 cls : 'form-group radio',
21949 html : this.boxLabel
21957 initEvents : function()
21959 this.parent().register(this);
21961 this.el.on('click', this.onClick, this);
21965 onClick : function(e)
21967 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21968 this.setChecked(true);
21972 setChecked : function(state, suppressEvent)
21974 this.parent().setValue(this.value, suppressEvent);
21978 setBoxLabel : function(v)
21983 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21998 * @class Roo.bootstrap.SecurePass
21999 * @extends Roo.bootstrap.Input
22000 * Bootstrap SecurePass class
22004 * Create a new SecurePass
22005 * @param {Object} config The config object
22008 Roo.bootstrap.SecurePass = function (config) {
22009 // these go here, so the translation tool can replace them..
22011 PwdEmpty: "Please type a password, and then retype it to confirm.",
22012 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22013 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22014 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22015 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22016 FNInPwd: "Your password can't contain your first name. Please type a different password.",
22017 LNInPwd: "Your password can't contain your last name. Please type a different password.",
22018 TooWeak: "Your password is Too Weak."
22020 this.meterLabel = "Password strength:";
22021 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
22022 this.meterClass = [
22023 "roo-password-meter-tooweak",
22024 "roo-password-meter-weak",
22025 "roo-password-meter-medium",
22026 "roo-password-meter-strong",
22027 "roo-password-meter-grey"
22032 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
22035 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
22037 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
22039 * PwdEmpty: "Please type a password, and then retype it to confirm.",
22040 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22041 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22042 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22043 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22044 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
22045 * LNInPwd: "Your password can't contain your last name. Please type a different password."
22055 * @cfg {String/Object} Label for the strength meter (defaults to
22056 * 'Password strength:')
22061 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
22062 * ['Weak', 'Medium', 'Strong'])
22065 pwdStrengths: false,
22078 initEvents: function ()
22080 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
22082 if (this.el.is('input[type=password]') && Roo.isSafari) {
22083 this.el.on('keydown', this.SafariOnKeyDown, this);
22086 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
22089 onRender: function (ct, position)
22091 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
22092 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
22093 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
22095 this.trigger.createChild({
22100 cls: 'roo-password-meter-grey col-xs-12',
22103 //width: this.meterWidth + 'px'
22107 cls: 'roo-password-meter-text'
22113 if (this.hideTrigger) {
22114 this.trigger.setDisplayed(false);
22116 this.setSize(this.width || '', this.height || '');
22119 onDestroy: function ()
22121 if (this.trigger) {
22122 this.trigger.removeAllListeners();
22123 this.trigger.remove();
22126 this.wrap.remove();
22128 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
22131 checkStrength: function ()
22133 var pwd = this.inputEl().getValue();
22134 if (pwd == this._lastPwd) {
22139 if (this.ClientSideStrongPassword(pwd)) {
22141 } else if (this.ClientSideMediumPassword(pwd)) {
22143 } else if (this.ClientSideWeakPassword(pwd)) {
22149 Roo.log('strength1: ' + strength);
22151 //var pm = this.trigger.child('div/div/div').dom;
22152 var pm = this.trigger.child('div/div');
22153 pm.removeClass(this.meterClass);
22154 pm.addClass(this.meterClass[strength]);
22157 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22159 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
22161 this._lastPwd = pwd;
22165 Roo.bootstrap.SecurePass.superclass.reset.call(this);
22167 this._lastPwd = '';
22169 var pm = this.trigger.child('div/div');
22170 pm.removeClass(this.meterClass);
22171 pm.addClass('roo-password-meter-grey');
22174 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22177 this.inputEl().dom.type='password';
22180 validateValue: function (value)
22183 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
22186 if (value.length == 0) {
22187 if (this.allowBlank) {
22188 this.clearInvalid();
22192 this.markInvalid(this.errors.PwdEmpty);
22193 this.errorMsg = this.errors.PwdEmpty;
22201 if ('[\x21-\x7e]*'.match(value)) {
22202 this.markInvalid(this.errors.PwdBadChar);
22203 this.errorMsg = this.errors.PwdBadChar;
22206 if (value.length < 6) {
22207 this.markInvalid(this.errors.PwdShort);
22208 this.errorMsg = this.errors.PwdShort;
22211 if (value.length > 16) {
22212 this.markInvalid(this.errors.PwdLong);
22213 this.errorMsg = this.errors.PwdLong;
22217 if (this.ClientSideStrongPassword(value)) {
22219 } else if (this.ClientSideMediumPassword(value)) {
22221 } else if (this.ClientSideWeakPassword(value)) {
22228 if (strength < 2) {
22229 //this.markInvalid(this.errors.TooWeak);
22230 this.errorMsg = this.errors.TooWeak;
22235 console.log('strength2: ' + strength);
22237 //var pm = this.trigger.child('div/div/div').dom;
22239 var pm = this.trigger.child('div/div');
22240 pm.removeClass(this.meterClass);
22241 pm.addClass(this.meterClass[strength]);
22243 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22245 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
22247 this.errorMsg = '';
22251 CharacterSetChecks: function (type)
22254 this.fResult = false;
22257 isctype: function (character, type)
22260 case this.kCapitalLetter:
22261 if (character >= 'A' && character <= 'Z') {
22266 case this.kSmallLetter:
22267 if (character >= 'a' && character <= 'z') {
22273 if (character >= '0' && character <= '9') {
22278 case this.kPunctuation:
22279 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
22290 IsLongEnough: function (pwd, size)
22292 return !(pwd == null || isNaN(size) || pwd.length < size);
22295 SpansEnoughCharacterSets: function (word, nb)
22297 if (!this.IsLongEnough(word, nb))
22302 var characterSetChecks = new Array(
22303 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22304 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22307 for (var index = 0; index < word.length; ++index) {
22308 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22309 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22310 characterSetChecks[nCharSet].fResult = true;
22317 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22318 if (characterSetChecks[nCharSet].fResult) {
22323 if (nCharSets < nb) {
22329 ClientSideStrongPassword: function (pwd)
22331 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22334 ClientSideMediumPassword: function (pwd)
22336 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22339 ClientSideWeakPassword: function (pwd)
22341 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22344 })//<script type="text/javascript">
22347 * Based Ext JS Library 1.1.1
22348 * Copyright(c) 2006-2007, Ext JS, LLC.
22354 * @class Roo.HtmlEditorCore
22355 * @extends Roo.Component
22356 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22358 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22361 Roo.HtmlEditorCore = function(config){
22364 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22369 * @event initialize
22370 * Fires when the editor is fully initialized (including the iframe)
22371 * @param {Roo.HtmlEditorCore} this
22376 * Fires when the editor is first receives the focus. Any insertion must wait
22377 * until after this event.
22378 * @param {Roo.HtmlEditorCore} this
22382 * @event beforesync
22383 * Fires before the textarea is updated with content from the editor iframe. Return false
22384 * to cancel the sync.
22385 * @param {Roo.HtmlEditorCore} this
22386 * @param {String} html
22390 * @event beforepush
22391 * Fires before the iframe editor is updated with content from the textarea. Return false
22392 * to cancel the push.
22393 * @param {Roo.HtmlEditorCore} this
22394 * @param {String} html
22399 * Fires when the textarea is updated with content from the editor iframe.
22400 * @param {Roo.HtmlEditorCore} this
22401 * @param {String} html
22406 * Fires when the iframe editor is updated with content from the textarea.
22407 * @param {Roo.HtmlEditorCore} this
22408 * @param {String} html
22413 * @event editorevent
22414 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22415 * @param {Roo.HtmlEditorCore} this
22421 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22423 // defaults : white / black...
22424 this.applyBlacklists();
22431 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
22435 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
22441 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22446 * @cfg {Number} height (in pixels)
22450 * @cfg {Number} width (in pixels)
22455 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22458 stylesheets: false,
22463 // private properties
22464 validationEvent : false,
22466 initialized : false,
22468 sourceEditMode : false,
22469 onFocus : Roo.emptyFn,
22471 hideMode:'offsets',
22475 // blacklist + whitelisted elements..
22482 * Protected method that will not generally be called directly. It
22483 * is called when the editor initializes the iframe with HTML contents. Override this method if you
22484 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22486 getDocMarkup : function(){
22490 // inherit styels from page...??
22491 if (this.stylesheets === false) {
22493 Roo.get(document.head).select('style').each(function(node) {
22494 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22497 Roo.get(document.head).select('link').each(function(node) {
22498 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22501 } else if (!this.stylesheets.length) {
22503 st = '<style type="text/css">' +
22504 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22507 st = '<style type="text/css">' +
22512 st += '<style type="text/css">' +
22513 'IMG { cursor: pointer } ' +
22516 var cls = 'roo-htmleditor-body';
22518 if(this.bodyCls.length){
22519 cls += ' ' + this.bodyCls;
22522 return '<html><head>' + st +
22523 //<style type="text/css">' +
22524 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22526 ' </head><body class="' + cls + '"></body></html>';
22530 onRender : function(ct, position)
22533 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22534 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22537 this.el.dom.style.border = '0 none';
22538 this.el.dom.setAttribute('tabIndex', -1);
22539 this.el.addClass('x-hidden hide');
22543 if(Roo.isIE){ // fix IE 1px bogus margin
22544 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22548 this.frameId = Roo.id();
22552 var iframe = this.owner.wrap.createChild({
22554 cls: 'form-control', // bootstrap..
22556 name: this.frameId,
22557 frameBorder : 'no',
22558 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
22563 this.iframe = iframe.dom;
22565 this.assignDocWin();
22567 this.doc.designMode = 'on';
22570 this.doc.write(this.getDocMarkup());
22574 var task = { // must defer to wait for browser to be ready
22576 //console.log("run task?" + this.doc.readyState);
22577 this.assignDocWin();
22578 if(this.doc.body || this.doc.readyState == 'complete'){
22580 this.doc.designMode="on";
22584 Roo.TaskMgr.stop(task);
22585 this.initEditor.defer(10, this);
22592 Roo.TaskMgr.start(task);
22597 onResize : function(w, h)
22599 Roo.log('resize: ' +w + ',' + h );
22600 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22604 if(typeof w == 'number'){
22606 this.iframe.style.width = w + 'px';
22608 if(typeof h == 'number'){
22610 this.iframe.style.height = h + 'px';
22612 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22619 * Toggles the editor between standard and source edit mode.
22620 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22622 toggleSourceEdit : function(sourceEditMode){
22624 this.sourceEditMode = sourceEditMode === true;
22626 if(this.sourceEditMode){
22628 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22631 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22632 //this.iframe.className = '';
22635 //this.setSize(this.owner.wrap.getSize());
22636 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22643 * Protected method that will not generally be called directly. If you need/want
22644 * custom HTML cleanup, this is the method you should override.
22645 * @param {String} html The HTML to be cleaned
22646 * return {String} The cleaned HTML
22648 cleanHtml : function(html){
22649 html = String(html);
22650 if(html.length > 5){
22651 if(Roo.isSafari){ // strip safari nonsense
22652 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22655 if(html == ' '){
22662 * HTML Editor -> Textarea
22663 * Protected method that will not generally be called directly. Syncs the contents
22664 * of the editor iframe with the textarea.
22666 syncValue : function(){
22667 if(this.initialized){
22668 var bd = (this.doc.body || this.doc.documentElement);
22669 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22670 var html = bd.innerHTML;
22672 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22673 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22675 html = '<div style="'+m[0]+'">' + html + '</div>';
22678 html = this.cleanHtml(html);
22679 // fix up the special chars.. normaly like back quotes in word...
22680 // however we do not want to do this with chinese..
22681 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
22683 var cc = match.charCodeAt();
22685 // Get the character value, handling surrogate pairs
22686 if (match.length == 2) {
22687 // It's a surrogate pair, calculate the Unicode code point
22688 var high = match.charCodeAt(0) - 0xD800;
22689 var low = match.charCodeAt(1) - 0xDC00;
22690 cc = (high * 0x400) + low + 0x10000;
22692 (cc >= 0x4E00 && cc < 0xA000 ) ||
22693 (cc >= 0x3400 && cc < 0x4E00 ) ||
22694 (cc >= 0xf900 && cc < 0xfb00 )
22699 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
22700 return "&#" + cc + ";";
22707 if(this.owner.fireEvent('beforesync', this, html) !== false){
22708 this.el.dom.value = html;
22709 this.owner.fireEvent('sync', this, html);
22715 * Protected method that will not generally be called directly. Pushes the value of the textarea
22716 * into the iframe editor.
22718 pushValue : function(){
22719 if(this.initialized){
22720 var v = this.el.dom.value.trim();
22722 // if(v.length < 1){
22726 if(this.owner.fireEvent('beforepush', this, v) !== false){
22727 var d = (this.doc.body || this.doc.documentElement);
22729 this.cleanUpPaste();
22730 this.el.dom.value = d.innerHTML;
22731 this.owner.fireEvent('push', this, v);
22737 deferFocus : function(){
22738 this.focus.defer(10, this);
22742 focus : function(){
22743 if(this.win && !this.sourceEditMode){
22750 assignDocWin: function()
22752 var iframe = this.iframe;
22755 this.doc = iframe.contentWindow.document;
22756 this.win = iframe.contentWindow;
22758 // if (!Roo.get(this.frameId)) {
22761 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22762 // this.win = Roo.get(this.frameId).dom.contentWindow;
22764 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22768 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22769 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22774 initEditor : function(){
22775 //console.log("INIT EDITOR");
22776 this.assignDocWin();
22780 this.doc.designMode="on";
22782 this.doc.write(this.getDocMarkup());
22785 var dbody = (this.doc.body || this.doc.documentElement);
22786 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22787 // this copies styles from the containing element into thsi one..
22788 // not sure why we need all of this..
22789 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22791 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22792 //ss['background-attachment'] = 'fixed'; // w3c
22793 dbody.bgProperties = 'fixed'; // ie
22794 //Roo.DomHelper.applyStyles(dbody, ss);
22795 Roo.EventManager.on(this.doc, {
22796 //'mousedown': this.onEditorEvent,
22797 'mouseup': this.onEditorEvent,
22798 'dblclick': this.onEditorEvent,
22799 'click': this.onEditorEvent,
22800 'keyup': this.onEditorEvent,
22805 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22807 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22808 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22810 this.initialized = true;
22812 this.owner.fireEvent('initialize', this);
22817 onDestroy : function(){
22823 //for (var i =0; i < this.toolbars.length;i++) {
22824 // // fixme - ask toolbars for heights?
22825 // this.toolbars[i].onDestroy();
22828 //this.wrap.dom.innerHTML = '';
22829 //this.wrap.remove();
22834 onFirstFocus : function(){
22836 this.assignDocWin();
22839 this.activated = true;
22842 if(Roo.isGecko){ // prevent silly gecko errors
22844 var s = this.win.getSelection();
22845 if(!s.focusNode || s.focusNode.nodeType != 3){
22846 var r = s.getRangeAt(0);
22847 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22852 this.execCmd('useCSS', true);
22853 this.execCmd('styleWithCSS', false);
22856 this.owner.fireEvent('activate', this);
22860 adjustFont: function(btn){
22861 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22862 //if(Roo.isSafari){ // safari
22865 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22866 if(Roo.isSafari){ // safari
22867 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22868 v = (v < 10) ? 10 : v;
22869 v = (v > 48) ? 48 : v;
22870 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22875 v = Math.max(1, v+adjust);
22877 this.execCmd('FontSize', v );
22880 onEditorEvent : function(e)
22882 this.owner.fireEvent('editorevent', this, e);
22883 // this.updateToolbar();
22884 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22887 insertTag : function(tg)
22889 // could be a bit smarter... -> wrap the current selected tRoo..
22890 if (tg.toLowerCase() == 'span' ||
22891 tg.toLowerCase() == 'code' ||
22892 tg.toLowerCase() == 'sup' ||
22893 tg.toLowerCase() == 'sub'
22896 range = this.createRange(this.getSelection());
22897 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22898 wrappingNode.appendChild(range.extractContents());
22899 range.insertNode(wrappingNode);
22906 this.execCmd("formatblock", tg);
22910 insertText : function(txt)
22914 var range = this.createRange();
22915 range.deleteContents();
22916 //alert(Sender.getAttribute('label'));
22918 range.insertNode(this.doc.createTextNode(txt));
22924 * Executes a Midas editor command on the editor document and performs necessary focus and
22925 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22926 * @param {String} cmd The Midas command
22927 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22929 relayCmd : function(cmd, value){
22931 this.execCmd(cmd, value);
22932 this.owner.fireEvent('editorevent', this);
22933 //this.updateToolbar();
22934 this.owner.deferFocus();
22938 * Executes a Midas editor command directly on the editor document.
22939 * For visual commands, you should use {@link #relayCmd} instead.
22940 * <b>This should only be called after the editor is initialized.</b>
22941 * @param {String} cmd The Midas command
22942 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22944 execCmd : function(cmd, value){
22945 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22952 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22954 * @param {String} text | dom node..
22956 insertAtCursor : function(text)
22959 if(!this.activated){
22965 var r = this.doc.selection.createRange();
22976 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22980 // from jquery ui (MIT licenced)
22982 var win = this.win;
22984 if (win.getSelection && win.getSelection().getRangeAt) {
22985 range = win.getSelection().getRangeAt(0);
22986 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22987 range.insertNode(node);
22988 } else if (win.document.selection && win.document.selection.createRange) {
22989 // no firefox support
22990 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22991 win.document.selection.createRange().pasteHTML(txt);
22993 // no firefox support
22994 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22995 this.execCmd('InsertHTML', txt);
23004 mozKeyPress : function(e){
23006 var c = e.getCharCode(), cmd;
23009 c = String.fromCharCode(c).toLowerCase();
23023 this.cleanUpPaste.defer(100, this);
23031 e.preventDefault();
23039 fixKeys : function(){ // load time branching for fastest keydown performance
23041 return function(e){
23042 var k = e.getKey(), r;
23045 r = this.doc.selection.createRange();
23048 r.pasteHTML('    ');
23055 r = this.doc.selection.createRange();
23057 var target = r.parentElement();
23058 if(!target || target.tagName.toLowerCase() != 'li'){
23060 r.pasteHTML('<br />');
23066 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23067 this.cleanUpPaste.defer(100, this);
23073 }else if(Roo.isOpera){
23074 return function(e){
23075 var k = e.getKey();
23079 this.execCmd('InsertHTML','    ');
23082 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23083 this.cleanUpPaste.defer(100, this);
23088 }else if(Roo.isSafari){
23089 return function(e){
23090 var k = e.getKey();
23094 this.execCmd('InsertText','\t');
23098 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23099 this.cleanUpPaste.defer(100, this);
23107 getAllAncestors: function()
23109 var p = this.getSelectedNode();
23112 a.push(p); // push blank onto stack..
23113 p = this.getParentElement();
23117 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
23121 a.push(this.doc.body);
23125 lastSelNode : false,
23128 getSelection : function()
23130 this.assignDocWin();
23131 return Roo.isIE ? this.doc.selection : this.win.getSelection();
23134 getSelectedNode: function()
23136 // this may only work on Gecko!!!
23138 // should we cache this!!!!
23143 var range = this.createRange(this.getSelection()).cloneRange();
23146 var parent = range.parentElement();
23148 var testRange = range.duplicate();
23149 testRange.moveToElementText(parent);
23150 if (testRange.inRange(range)) {
23153 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
23156 parent = parent.parentElement;
23161 // is ancestor a text element.
23162 var ac = range.commonAncestorContainer;
23163 if (ac.nodeType == 3) {
23164 ac = ac.parentNode;
23167 var ar = ac.childNodes;
23170 var other_nodes = [];
23171 var has_other_nodes = false;
23172 for (var i=0;i<ar.length;i++) {
23173 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
23176 // fullly contained node.
23178 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
23183 // probably selected..
23184 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
23185 other_nodes.push(ar[i]);
23189 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
23194 has_other_nodes = true;
23196 if (!nodes.length && other_nodes.length) {
23197 nodes= other_nodes;
23199 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
23205 createRange: function(sel)
23207 // this has strange effects when using with
23208 // top toolbar - not sure if it's a great idea.
23209 //this.editor.contentWindow.focus();
23210 if (typeof sel != "undefined") {
23212 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
23214 return this.doc.createRange();
23217 return this.doc.createRange();
23220 getParentElement: function()
23223 this.assignDocWin();
23224 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
23226 var range = this.createRange(sel);
23229 var p = range.commonAncestorContainer;
23230 while (p.nodeType == 3) { // text node
23241 * Range intersection.. the hard stuff...
23245 * [ -- selected range --- ]
23249 * if end is before start or hits it. fail.
23250 * if start is after end or hits it fail.
23252 * if either hits (but other is outside. - then it's not
23258 // @see http://www.thismuchiknow.co.uk/?p=64.
23259 rangeIntersectsNode : function(range, node)
23261 var nodeRange = node.ownerDocument.createRange();
23263 nodeRange.selectNode(node);
23265 nodeRange.selectNodeContents(node);
23268 var rangeStartRange = range.cloneRange();
23269 rangeStartRange.collapse(true);
23271 var rangeEndRange = range.cloneRange();
23272 rangeEndRange.collapse(false);
23274 var nodeStartRange = nodeRange.cloneRange();
23275 nodeStartRange.collapse(true);
23277 var nodeEndRange = nodeRange.cloneRange();
23278 nodeEndRange.collapse(false);
23280 return rangeStartRange.compareBoundaryPoints(
23281 Range.START_TO_START, nodeEndRange) == -1 &&
23282 rangeEndRange.compareBoundaryPoints(
23283 Range.START_TO_START, nodeStartRange) == 1;
23287 rangeCompareNode : function(range, node)
23289 var nodeRange = node.ownerDocument.createRange();
23291 nodeRange.selectNode(node);
23293 nodeRange.selectNodeContents(node);
23297 range.collapse(true);
23299 nodeRange.collapse(true);
23301 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23302 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
23304 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23306 var nodeIsBefore = ss == 1;
23307 var nodeIsAfter = ee == -1;
23309 if (nodeIsBefore && nodeIsAfter) {
23312 if (!nodeIsBefore && nodeIsAfter) {
23313 return 1; //right trailed.
23316 if (nodeIsBefore && !nodeIsAfter) {
23317 return 2; // left trailed.
23323 // private? - in a new class?
23324 cleanUpPaste : function()
23326 // cleans up the whole document..
23327 Roo.log('cleanuppaste');
23329 this.cleanUpChildren(this.doc.body);
23330 var clean = this.cleanWordChars(this.doc.body.innerHTML);
23331 if (clean != this.doc.body.innerHTML) {
23332 this.doc.body.innerHTML = clean;
23337 cleanWordChars : function(input) {// change the chars to hex code
23338 var he = Roo.HtmlEditorCore;
23340 var output = input;
23341 Roo.each(he.swapCodes, function(sw) {
23342 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23344 output = output.replace(swapper, sw[1]);
23351 cleanUpChildren : function (n)
23353 if (!n.childNodes.length) {
23356 for (var i = n.childNodes.length-1; i > -1 ; i--) {
23357 this.cleanUpChild(n.childNodes[i]);
23364 cleanUpChild : function (node)
23367 //console.log(node);
23368 if (node.nodeName == "#text") {
23369 // clean up silly Windows -- stuff?
23372 if (node.nodeName == "#comment") {
23373 node.parentNode.removeChild(node);
23374 // clean up silly Windows -- stuff?
23377 var lcname = node.tagName.toLowerCase();
23378 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23379 // whitelist of tags..
23381 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23383 node.parentNode.removeChild(node);
23388 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23390 // spans with no attributes - just remove them..
23391 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
23392 remove_keep_children = true;
23395 // remove <a name=....> as rendering on yahoo mailer is borked with this.
23396 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23398 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23399 // remove_keep_children = true;
23402 if (remove_keep_children) {
23403 this.cleanUpChildren(node);
23404 // inserts everything just before this node...
23405 while (node.childNodes.length) {
23406 var cn = node.childNodes[0];
23407 node.removeChild(cn);
23408 node.parentNode.insertBefore(cn, node);
23410 node.parentNode.removeChild(node);
23414 if (!node.attributes || !node.attributes.length) {
23419 this.cleanUpChildren(node);
23423 function cleanAttr(n,v)
23426 if (v.match(/^\./) || v.match(/^\//)) {
23429 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23432 if (v.match(/^#/)) {
23435 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23436 node.removeAttribute(n);
23440 var cwhite = this.cwhite;
23441 var cblack = this.cblack;
23443 function cleanStyle(n,v)
23445 if (v.match(/expression/)) { //XSS?? should we even bother..
23446 node.removeAttribute(n);
23450 var parts = v.split(/;/);
23453 Roo.each(parts, function(p) {
23454 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23458 var l = p.split(':').shift().replace(/\s+/g,'');
23459 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23461 if ( cwhite.length && cblack.indexOf(l) > -1) {
23462 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23463 //node.removeAttribute(n);
23467 // only allow 'c whitelisted system attributes'
23468 if ( cwhite.length && cwhite.indexOf(l) < 0) {
23469 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23470 //node.removeAttribute(n);
23480 if (clean.length) {
23481 node.setAttribute(n, clean.join(';'));
23483 node.removeAttribute(n);
23489 for (var i = node.attributes.length-1; i > -1 ; i--) {
23490 var a = node.attributes[i];
23493 if (a.name.toLowerCase().substr(0,2)=='on') {
23494 node.removeAttribute(a.name);
23497 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23498 node.removeAttribute(a.name);
23501 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23502 cleanAttr(a.name,a.value); // fixme..
23505 if (a.name == 'style') {
23506 cleanStyle(a.name,a.value);
23509 /// clean up MS crap..
23510 // tecnically this should be a list of valid class'es..
23513 if (a.name == 'class') {
23514 if (a.value.match(/^Mso/)) {
23515 node.removeAttribute('class');
23518 if (a.value.match(/^body$/)) {
23519 node.removeAttribute('class');
23530 this.cleanUpChildren(node);
23536 * Clean up MS wordisms...
23538 cleanWord : function(node)
23541 this.cleanWord(this.doc.body);
23546 node.nodeName == 'SPAN' &&
23547 !node.hasAttributes() &&
23548 node.childNodes.length == 1 &&
23549 node.firstChild.nodeName == "#text"
23551 var textNode = node.firstChild;
23552 node.removeChild(textNode);
23553 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
23554 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
23556 node.parentNode.insertBefore(textNode, node);
23557 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
23558 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
23560 node.parentNode.removeChild(node);
23563 if (node.nodeName == "#text") {
23564 // clean up silly Windows -- stuff?
23567 if (node.nodeName == "#comment") {
23568 node.parentNode.removeChild(node);
23569 // clean up silly Windows -- stuff?
23573 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23574 node.parentNode.removeChild(node);
23577 //Roo.log(node.tagName);
23578 // remove - but keep children..
23579 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
23580 //Roo.log('-- removed');
23581 while (node.childNodes.length) {
23582 var cn = node.childNodes[0];
23583 node.removeChild(cn);
23584 node.parentNode.insertBefore(cn, node);
23585 // move node to parent - and clean it..
23586 this.cleanWord(cn);
23588 node.parentNode.removeChild(node);
23589 /// no need to iterate chidlren = it's got none..
23590 //this.iterateChildren(node, this.cleanWord);
23594 if (node.className.length) {
23596 var cn = node.className.split(/\W+/);
23598 Roo.each(cn, function(cls) {
23599 if (cls.match(/Mso[a-zA-Z]+/)) {
23604 node.className = cna.length ? cna.join(' ') : '';
23606 node.removeAttribute("class");
23610 if (node.hasAttribute("lang")) {
23611 node.removeAttribute("lang");
23614 if (node.hasAttribute("style")) {
23616 var styles = node.getAttribute("style").split(";");
23618 Roo.each(styles, function(s) {
23619 if (!s.match(/:/)) {
23622 var kv = s.split(":");
23623 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23626 // what ever is left... we allow.
23629 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23630 if (!nstyle.length) {
23631 node.removeAttribute('style');
23634 this.iterateChildren(node, this.cleanWord);
23640 * iterateChildren of a Node, calling fn each time, using this as the scole..
23641 * @param {DomNode} node node to iterate children of.
23642 * @param {Function} fn method of this class to call on each item.
23644 iterateChildren : function(node, fn)
23646 if (!node.childNodes.length) {
23649 for (var i = node.childNodes.length-1; i > -1 ; i--) {
23650 fn.call(this, node.childNodes[i])
23656 * cleanTableWidths.
23658 * Quite often pasting from word etc.. results in tables with column and widths.
23659 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23662 cleanTableWidths : function(node)
23667 this.cleanTableWidths(this.doc.body);
23672 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23675 Roo.log(node.tagName);
23676 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23677 this.iterateChildren(node, this.cleanTableWidths);
23680 if (node.hasAttribute('width')) {
23681 node.removeAttribute('width');
23685 if (node.hasAttribute("style")) {
23688 var styles = node.getAttribute("style").split(";");
23690 Roo.each(styles, function(s) {
23691 if (!s.match(/:/)) {
23694 var kv = s.split(":");
23695 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23698 // what ever is left... we allow.
23701 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23702 if (!nstyle.length) {
23703 node.removeAttribute('style');
23707 this.iterateChildren(node, this.cleanTableWidths);
23715 domToHTML : function(currentElement, depth, nopadtext) {
23717 depth = depth || 0;
23718 nopadtext = nopadtext || false;
23720 if (!currentElement) {
23721 return this.domToHTML(this.doc.body);
23724 //Roo.log(currentElement);
23726 var allText = false;
23727 var nodeName = currentElement.nodeName;
23728 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23730 if (nodeName == '#text') {
23732 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23737 if (nodeName != 'BODY') {
23740 // Prints the node tagName, such as <A>, <IMG>, etc
23743 for(i = 0; i < currentElement.attributes.length;i++) {
23745 var aname = currentElement.attributes.item(i).name;
23746 if (!currentElement.attributes.item(i).value.length) {
23749 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23752 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23761 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23764 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23769 // Traverse the tree
23771 var currentElementChild = currentElement.childNodes.item(i);
23772 var allText = true;
23773 var innerHTML = '';
23775 while (currentElementChild) {
23776 // Formatting code (indent the tree so it looks nice on the screen)
23777 var nopad = nopadtext;
23778 if (lastnode == 'SPAN') {
23782 if (currentElementChild.nodeName == '#text') {
23783 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23784 toadd = nopadtext ? toadd : toadd.trim();
23785 if (!nopad && toadd.length > 80) {
23786 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23788 innerHTML += toadd;
23791 currentElementChild = currentElement.childNodes.item(i);
23797 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23799 // Recursively traverse the tree structure of the child node
23800 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23801 lastnode = currentElementChild.nodeName;
23803 currentElementChild=currentElement.childNodes.item(i);
23809 // The remaining code is mostly for formatting the tree
23810 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23815 ret+= "</"+tagName+">";
23821 applyBlacklists : function()
23823 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23824 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23828 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23829 if (b.indexOf(tag) > -1) {
23832 this.white.push(tag);
23836 Roo.each(w, function(tag) {
23837 if (b.indexOf(tag) > -1) {
23840 if (this.white.indexOf(tag) > -1) {
23843 this.white.push(tag);
23848 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23849 if (w.indexOf(tag) > -1) {
23852 this.black.push(tag);
23856 Roo.each(b, function(tag) {
23857 if (w.indexOf(tag) > -1) {
23860 if (this.black.indexOf(tag) > -1) {
23863 this.black.push(tag);
23868 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23869 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23873 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23874 if (b.indexOf(tag) > -1) {
23877 this.cwhite.push(tag);
23881 Roo.each(w, function(tag) {
23882 if (b.indexOf(tag) > -1) {
23885 if (this.cwhite.indexOf(tag) > -1) {
23888 this.cwhite.push(tag);
23893 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23894 if (w.indexOf(tag) > -1) {
23897 this.cblack.push(tag);
23901 Roo.each(b, function(tag) {
23902 if (w.indexOf(tag) > -1) {
23905 if (this.cblack.indexOf(tag) > -1) {
23908 this.cblack.push(tag);
23913 setStylesheets : function(stylesheets)
23915 if(typeof(stylesheets) == 'string'){
23916 Roo.get(this.iframe.contentDocument.head).createChild({
23918 rel : 'stylesheet',
23927 Roo.each(stylesheets, function(s) {
23932 Roo.get(_this.iframe.contentDocument.head).createChild({
23934 rel : 'stylesheet',
23943 removeStylesheets : function()
23947 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23952 setStyle : function(style)
23954 Roo.get(this.iframe.contentDocument.head).createChild({
23963 // hide stuff that is not compatible
23977 * @event specialkey
23981 * @cfg {String} fieldClass @hide
23984 * @cfg {String} focusClass @hide
23987 * @cfg {String} autoCreate @hide
23990 * @cfg {String} inputType @hide
23993 * @cfg {String} invalidClass @hide
23996 * @cfg {String} invalidText @hide
23999 * @cfg {String} msgFx @hide
24002 * @cfg {String} validateOnBlur @hide
24006 Roo.HtmlEditorCore.white = [
24007 'area', 'br', 'img', 'input', 'hr', 'wbr',
24009 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
24010 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
24011 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
24012 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
24013 'table', 'ul', 'xmp',
24015 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
24018 'dir', 'menu', 'ol', 'ul', 'dl',
24024 Roo.HtmlEditorCore.black = [
24025 // 'embed', 'object', // enable - backend responsiblity to clean thiese
24027 'base', 'basefont', 'bgsound', 'blink', 'body',
24028 'frame', 'frameset', 'head', 'html', 'ilayer',
24029 'iframe', 'layer', 'link', 'meta', 'object',
24030 'script', 'style' ,'title', 'xml' // clean later..
24032 Roo.HtmlEditorCore.clean = [
24033 'script', 'style', 'title', 'xml'
24035 Roo.HtmlEditorCore.remove = [
24040 Roo.HtmlEditorCore.ablack = [
24044 Roo.HtmlEditorCore.aclean = [
24045 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24049 Roo.HtmlEditorCore.pwhite= [
24050 'http', 'https', 'mailto'
24053 // white listed style attributes.
24054 Roo.HtmlEditorCore.cwhite= [
24055 // 'text-align', /// default is to allow most things..
24061 // black listed style attributes.
24062 Roo.HtmlEditorCore.cblack= [
24063 // 'font-size' -- this can be set by the project
24067 Roo.HtmlEditorCore.swapCodes =[
24086 * @class Roo.bootstrap.HtmlEditor
24087 * @extends Roo.bootstrap.TextArea
24088 * Bootstrap HtmlEditor class
24091 * Create a new HtmlEditor
24092 * @param {Object} config The config object
24095 Roo.bootstrap.HtmlEditor = function(config){
24096 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
24097 if (!this.toolbars) {
24098 this.toolbars = [];
24101 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
24104 * @event initialize
24105 * Fires when the editor is fully initialized (including the iframe)
24106 * @param {HtmlEditor} this
24111 * Fires when the editor is first receives the focus. Any insertion must wait
24112 * until after this event.
24113 * @param {HtmlEditor} this
24117 * @event beforesync
24118 * Fires before the textarea is updated with content from the editor iframe. Return false
24119 * to cancel the sync.
24120 * @param {HtmlEditor} this
24121 * @param {String} html
24125 * @event beforepush
24126 * Fires before the iframe editor is updated with content from the textarea. Return false
24127 * to cancel the push.
24128 * @param {HtmlEditor} this
24129 * @param {String} html
24134 * Fires when the textarea is updated with content from the editor iframe.
24135 * @param {HtmlEditor} this
24136 * @param {String} html
24141 * Fires when the iframe editor is updated with content from the textarea.
24142 * @param {HtmlEditor} this
24143 * @param {String} html
24147 * @event editmodechange
24148 * Fires when the editor switches edit modes
24149 * @param {HtmlEditor} this
24150 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24152 editmodechange: true,
24154 * @event editorevent
24155 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24156 * @param {HtmlEditor} this
24160 * @event firstfocus
24161 * Fires when on first focus - needed by toolbars..
24162 * @param {HtmlEditor} this
24167 * Auto save the htmlEditor value as a file into Events
24168 * @param {HtmlEditor} this
24172 * @event savedpreview
24173 * preview the saved version of htmlEditor
24174 * @param {HtmlEditor} this
24181 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
24185 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24190 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
24195 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24200 * @cfg {Number} height (in pixels)
24204 * @cfg {Number} width (in pixels)
24209 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24212 stylesheets: false,
24217 // private properties
24218 validationEvent : false,
24220 initialized : false,
24223 onFocus : Roo.emptyFn,
24225 hideMode:'offsets',
24227 tbContainer : false,
24231 toolbarContainer :function() {
24232 return this.wrap.select('.x-html-editor-tb',true).first();
24236 * Protected method that will not generally be called directly. It
24237 * is called when the editor creates its toolbar. Override this method if you need to
24238 * add custom toolbar buttons.
24239 * @param {HtmlEditor} editor
24241 createToolbar : function(){
24242 Roo.log('renewing');
24243 Roo.log("create toolbars");
24245 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
24246 this.toolbars[0].render(this.toolbarContainer());
24250 // if (!editor.toolbars || !editor.toolbars.length) {
24251 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
24254 // for (var i =0 ; i < editor.toolbars.length;i++) {
24255 // editor.toolbars[i] = Roo.factory(
24256 // typeof(editor.toolbars[i]) == 'string' ?
24257 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
24258 // Roo.bootstrap.HtmlEditor);
24259 // editor.toolbars[i].init(editor);
24265 onRender : function(ct, position)
24267 // Roo.log("Call onRender: " + this.xtype);
24269 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
24271 this.wrap = this.inputEl().wrap({
24272 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24275 this.editorcore.onRender(ct, position);
24277 if (this.resizable) {
24278 this.resizeEl = new Roo.Resizable(this.wrap, {
24282 minHeight : this.height,
24283 height: this.height,
24284 handles : this.resizable,
24287 resize : function(r, w, h) {
24288 _t.onResize(w,h); // -something
24294 this.createToolbar(this);
24297 if(!this.width && this.resizable){
24298 this.setSize(this.wrap.getSize());
24300 if (this.resizeEl) {
24301 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24302 // should trigger onReize..
24308 onResize : function(w, h)
24310 Roo.log('resize: ' +w + ',' + h );
24311 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
24315 if(this.inputEl() ){
24316 if(typeof w == 'number'){
24317 var aw = w - this.wrap.getFrameWidth('lr');
24318 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24321 if(typeof h == 'number'){
24322 var tbh = -11; // fixme it needs to tool bar size!
24323 for (var i =0; i < this.toolbars.length;i++) {
24324 // fixme - ask toolbars for heights?
24325 tbh += this.toolbars[i].el.getHeight();
24326 //if (this.toolbars[i].footer) {
24327 // tbh += this.toolbars[i].footer.el.getHeight();
24335 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24336 ah -= 5; // knock a few pixes off for look..
24337 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24341 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24342 this.editorcore.onResize(ew,eh);
24347 * Toggles the editor between standard and source edit mode.
24348 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24350 toggleSourceEdit : function(sourceEditMode)
24352 this.editorcore.toggleSourceEdit(sourceEditMode);
24354 if(this.editorcore.sourceEditMode){
24355 Roo.log('editor - showing textarea');
24358 // Roo.log(this.syncValue());
24360 this.inputEl().removeClass(['hide', 'x-hidden']);
24361 this.inputEl().dom.removeAttribute('tabIndex');
24362 this.inputEl().focus();
24364 Roo.log('editor - hiding textarea');
24366 // Roo.log(this.pushValue());
24369 this.inputEl().addClass(['hide', 'x-hidden']);
24370 this.inputEl().dom.setAttribute('tabIndex', -1);
24371 //this.deferFocus();
24374 if(this.resizable){
24375 this.setSize(this.wrap.getSize());
24378 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24381 // private (for BoxComponent)
24382 adjustSize : Roo.BoxComponent.prototype.adjustSize,
24384 // private (for BoxComponent)
24385 getResizeEl : function(){
24389 // private (for BoxComponent)
24390 getPositionEl : function(){
24395 initEvents : function(){
24396 this.originalValue = this.getValue();
24400 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24403 // markInvalid : Roo.emptyFn,
24405 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24408 // clearInvalid : Roo.emptyFn,
24410 setValue : function(v){
24411 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24412 this.editorcore.pushValue();
24417 deferFocus : function(){
24418 this.focus.defer(10, this);
24422 focus : function(){
24423 this.editorcore.focus();
24429 onDestroy : function(){
24435 for (var i =0; i < this.toolbars.length;i++) {
24436 // fixme - ask toolbars for heights?
24437 this.toolbars[i].onDestroy();
24440 this.wrap.dom.innerHTML = '';
24441 this.wrap.remove();
24446 onFirstFocus : function(){
24447 //Roo.log("onFirstFocus");
24448 this.editorcore.onFirstFocus();
24449 for (var i =0; i < this.toolbars.length;i++) {
24450 this.toolbars[i].onFirstFocus();
24456 syncValue : function()
24458 this.editorcore.syncValue();
24461 pushValue : function()
24463 this.editorcore.pushValue();
24467 // hide stuff that is not compatible
24481 * @event specialkey
24485 * @cfg {String} fieldClass @hide
24488 * @cfg {String} focusClass @hide
24491 * @cfg {String} autoCreate @hide
24494 * @cfg {String} inputType @hide
24498 * @cfg {String} invalidText @hide
24501 * @cfg {String} msgFx @hide
24504 * @cfg {String} validateOnBlur @hide
24513 Roo.namespace('Roo.bootstrap.htmleditor');
24515 * @class Roo.bootstrap.HtmlEditorToolbar1
24521 new Roo.bootstrap.HtmlEditor({
24524 new Roo.bootstrap.HtmlEditorToolbar1({
24525 disable : { fonts: 1 , format: 1, ..., ... , ...],
24531 * @cfg {Object} disable List of elements to disable..
24532 * @cfg {Array} btns List of additional buttons.
24536 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24539 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24542 Roo.apply(this, config);
24544 // default disabled, based on 'good practice'..
24545 this.disable = this.disable || {};
24546 Roo.applyIf(this.disable, {
24549 specialElements : true
24551 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24553 this.editor = config.editor;
24554 this.editorcore = config.editor.editorcore;
24556 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24558 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24559 // dont call parent... till later.
24561 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
24566 editorcore : false,
24571 "h1","h2","h3","h4","h5","h6",
24573 "abbr", "acronym", "address", "cite", "samp", "var",
24577 onRender : function(ct, position)
24579 // Roo.log("Call onRender: " + this.xtype);
24581 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24583 this.el.dom.style.marginBottom = '0';
24585 var editorcore = this.editorcore;
24586 var editor= this.editor;
24589 var btn = function(id,cmd , toggle, handler, html){
24591 var event = toggle ? 'toggle' : 'click';
24596 xns: Roo.bootstrap,
24600 enableToggle:toggle !== false,
24602 pressed : toggle ? false : null,
24605 a.listeners[toggle ? 'toggle' : 'click'] = function() {
24606 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
24612 // var cb_box = function...
24617 xns: Roo.bootstrap,
24622 xns: Roo.bootstrap,
24626 Roo.each(this.formats, function(f) {
24627 style.menu.items.push({
24629 xns: Roo.bootstrap,
24630 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24635 editorcore.insertTag(this.tagname);
24642 children.push(style);
24644 btn('bold',false,true);
24645 btn('italic',false,true);
24646 btn('align-left', 'justifyleft',true);
24647 btn('align-center', 'justifycenter',true);
24648 btn('align-right' , 'justifyright',true);
24649 btn('link', false, false, function(btn) {
24650 //Roo.log("create link?");
24651 var url = prompt(this.createLinkText, this.defaultLinkValue);
24652 if(url && url != 'http:/'+'/'){
24653 this.editorcore.relayCmd('createlink', url);
24656 btn('list','insertunorderedlist',true);
24657 btn('pencil', false,true, function(btn){
24659 this.toggleSourceEdit(btn.pressed);
24662 if (this.editor.btns.length > 0) {
24663 for (var i = 0; i<this.editor.btns.length; i++) {
24664 children.push(this.editor.btns[i]);
24672 xns: Roo.bootstrap,
24677 xns: Roo.bootstrap,
24682 cog.menu.items.push({
24684 xns: Roo.bootstrap,
24685 html : Clean styles,
24690 editorcore.insertTag(this.tagname);
24699 this.xtype = 'NavSimplebar';
24701 for(var i=0;i< children.length;i++) {
24703 this.buttons.add(this.addxtypeChild(children[i]));
24707 editor.on('editorevent', this.updateToolbar, this);
24709 onBtnClick : function(id)
24711 this.editorcore.relayCmd(id);
24712 this.editorcore.focus();
24716 * Protected method that will not generally be called directly. It triggers
24717 * a toolbar update by reading the markup state of the current selection in the editor.
24719 updateToolbar: function(){
24721 if(!this.editorcore.activated){
24722 this.editor.onFirstFocus(); // is this neeed?
24726 var btns = this.buttons;
24727 var doc = this.editorcore.doc;
24728 btns.get('bold').setActive(doc.queryCommandState('bold'));
24729 btns.get('italic').setActive(doc.queryCommandState('italic'));
24730 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24732 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24733 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24734 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24736 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24737 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24740 var ans = this.editorcore.getAllAncestors();
24741 if (this.formatCombo) {
24744 var store = this.formatCombo.store;
24745 this.formatCombo.setValue("");
24746 for (var i =0; i < ans.length;i++) {
24747 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24749 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24757 // hides menus... - so this cant be on a menu...
24758 Roo.bootstrap.MenuMgr.hideAll();
24760 Roo.bootstrap.MenuMgr.hideAll();
24761 //this.editorsyncValue();
24763 onFirstFocus: function() {
24764 this.buttons.each(function(item){
24768 toggleSourceEdit : function(sourceEditMode){
24771 if(sourceEditMode){
24772 Roo.log("disabling buttons");
24773 this.buttons.each( function(item){
24774 if(item.cmd != 'pencil'){
24780 Roo.log("enabling buttons");
24781 if(this.editorcore.initialized){
24782 this.buttons.each( function(item){
24788 Roo.log("calling toggole on editor");
24789 // tell the editor that it's been pressed..
24790 this.editor.toggleSourceEdit(sourceEditMode);
24800 * @class Roo.bootstrap.Table.AbstractSelectionModel
24801 * @extends Roo.util.Observable
24802 * Abstract base class for grid SelectionModels. It provides the interface that should be
24803 * implemented by descendant classes. This class should not be directly instantiated.
24806 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24807 this.locked = false;
24808 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24812 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24813 /** @ignore Called by the grid automatically. Do not call directly. */
24814 init : function(grid){
24820 * Locks the selections.
24823 this.locked = true;
24827 * Unlocks the selections.
24829 unlock : function(){
24830 this.locked = false;
24834 * Returns true if the selections are locked.
24835 * @return {Boolean}
24837 isLocked : function(){
24838 return this.locked;
24842 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24843 * @class Roo.bootstrap.Table.RowSelectionModel
24844 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24845 * It supports multiple selections and keyboard selection/navigation.
24847 * @param {Object} config
24850 Roo.bootstrap.Table.RowSelectionModel = function(config){
24851 Roo.apply(this, config);
24852 this.selections = new Roo.util.MixedCollection(false, function(o){
24857 this.lastActive = false;
24861 * @event selectionchange
24862 * Fires when the selection changes
24863 * @param {SelectionModel} this
24865 "selectionchange" : true,
24867 * @event afterselectionchange
24868 * Fires after the selection changes (eg. by key press or clicking)
24869 * @param {SelectionModel} this
24871 "afterselectionchange" : true,
24873 * @event beforerowselect
24874 * Fires when a row is selected being selected, return false to cancel.
24875 * @param {SelectionModel} this
24876 * @param {Number} rowIndex The selected index
24877 * @param {Boolean} keepExisting False if other selections will be cleared
24879 "beforerowselect" : true,
24882 * Fires when a row is selected.
24883 * @param {SelectionModel} this
24884 * @param {Number} rowIndex The selected index
24885 * @param {Roo.data.Record} r The record
24887 "rowselect" : true,
24889 * @event rowdeselect
24890 * Fires when a row is deselected.
24891 * @param {SelectionModel} this
24892 * @param {Number} rowIndex The selected index
24894 "rowdeselect" : true
24896 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24897 this.locked = false;
24900 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24902 * @cfg {Boolean} singleSelect
24903 * True to allow selection of only one row at a time (defaults to false)
24905 singleSelect : false,
24908 initEvents : function()
24911 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24912 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24913 //}else{ // allow click to work like normal
24914 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24916 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24917 this.grid.on("rowclick", this.handleMouseDown, this);
24919 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24920 "up" : function(e){
24922 this.selectPrevious(e.shiftKey);
24923 }else if(this.last !== false && this.lastActive !== false){
24924 var last = this.last;
24925 this.selectRange(this.last, this.lastActive-1);
24926 this.grid.getView().focusRow(this.lastActive);
24927 if(last !== false){
24931 this.selectFirstRow();
24933 this.fireEvent("afterselectionchange", this);
24935 "down" : function(e){
24937 this.selectNext(e.shiftKey);
24938 }else if(this.last !== false && this.lastActive !== false){
24939 var last = this.last;
24940 this.selectRange(this.last, this.lastActive+1);
24941 this.grid.getView().focusRow(this.lastActive);
24942 if(last !== false){
24946 this.selectFirstRow();
24948 this.fireEvent("afterselectionchange", this);
24952 this.grid.store.on('load', function(){
24953 this.selections.clear();
24956 var view = this.grid.view;
24957 view.on("refresh", this.onRefresh, this);
24958 view.on("rowupdated", this.onRowUpdated, this);
24959 view.on("rowremoved", this.onRemove, this);
24964 onRefresh : function()
24966 var ds = this.grid.store, i, v = this.grid.view;
24967 var s = this.selections;
24968 s.each(function(r){
24969 if((i = ds.indexOfId(r.id)) != -1){
24978 onRemove : function(v, index, r){
24979 this.selections.remove(r);
24983 onRowUpdated : function(v, index, r){
24984 if(this.isSelected(r)){
24985 v.onRowSelect(index);
24991 * @param {Array} records The records to select
24992 * @param {Boolean} keepExisting (optional) True to keep existing selections
24994 selectRecords : function(records, keepExisting)
24997 this.clearSelections();
24999 var ds = this.grid.store;
25000 for(var i = 0, len = records.length; i < len; i++){
25001 this.selectRow(ds.indexOf(records[i]), true);
25006 * Gets the number of selected rows.
25009 getCount : function(){
25010 return this.selections.length;
25014 * Selects the first row in the grid.
25016 selectFirstRow : function(){
25021 * Select the last row.
25022 * @param {Boolean} keepExisting (optional) True to keep existing selections
25024 selectLastRow : function(keepExisting){
25025 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
25026 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
25030 * Selects the row immediately following the last selected row.
25031 * @param {Boolean} keepExisting (optional) True to keep existing selections
25033 selectNext : function(keepExisting)
25035 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
25036 this.selectRow(this.last+1, keepExisting);
25037 this.grid.getView().focusRow(this.last);
25042 * Selects the row that precedes the last selected row.
25043 * @param {Boolean} keepExisting (optional) True to keep existing selections
25045 selectPrevious : function(keepExisting){
25047 this.selectRow(this.last-1, keepExisting);
25048 this.grid.getView().focusRow(this.last);
25053 * Returns the selected records
25054 * @return {Array} Array of selected records
25056 getSelections : function(){
25057 return [].concat(this.selections.items);
25061 * Returns the first selected record.
25064 getSelected : function(){
25065 return this.selections.itemAt(0);
25070 * Clears all selections.
25072 clearSelections : function(fast)
25078 var ds = this.grid.store;
25079 var s = this.selections;
25080 s.each(function(r){
25081 this.deselectRow(ds.indexOfId(r.id));
25085 this.selections.clear();
25092 * Selects all rows.
25094 selectAll : function(){
25098 this.selections.clear();
25099 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
25100 this.selectRow(i, true);
25105 * Returns True if there is a selection.
25106 * @return {Boolean}
25108 hasSelection : function(){
25109 return this.selections.length > 0;
25113 * Returns True if the specified row is selected.
25114 * @param {Number/Record} record The record or index of the record to check
25115 * @return {Boolean}
25117 isSelected : function(index){
25118 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
25119 return (r && this.selections.key(r.id) ? true : false);
25123 * Returns True if the specified record id is selected.
25124 * @param {String} id The id of record to check
25125 * @return {Boolean}
25127 isIdSelected : function(id){
25128 return (this.selections.key(id) ? true : false);
25133 handleMouseDBClick : function(e, t){
25137 handleMouseDown : function(e, t)
25139 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
25140 if(this.isLocked() || rowIndex < 0 ){
25143 if(e.shiftKey && this.last !== false){
25144 var last = this.last;
25145 this.selectRange(last, rowIndex, e.ctrlKey);
25146 this.last = last; // reset the last
25150 var isSelected = this.isSelected(rowIndex);
25151 //Roo.log("select row:" + rowIndex);
25153 this.deselectRow(rowIndex);
25155 this.selectRow(rowIndex, true);
25159 if(e.button !== 0 && isSelected){
25160 alert('rowIndex 2: ' + rowIndex);
25161 view.focusRow(rowIndex);
25162 }else if(e.ctrlKey && isSelected){
25163 this.deselectRow(rowIndex);
25164 }else if(!isSelected){
25165 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
25166 view.focusRow(rowIndex);
25170 this.fireEvent("afterselectionchange", this);
25173 handleDragableRowClick : function(grid, rowIndex, e)
25175 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
25176 this.selectRow(rowIndex, false);
25177 grid.view.focusRow(rowIndex);
25178 this.fireEvent("afterselectionchange", this);
25183 * Selects multiple rows.
25184 * @param {Array} rows Array of the indexes of the row to select
25185 * @param {Boolean} keepExisting (optional) True to keep existing selections
25187 selectRows : function(rows, keepExisting){
25189 this.clearSelections();
25191 for(var i = 0, len = rows.length; i < len; i++){
25192 this.selectRow(rows[i], true);
25197 * Selects a range of rows. All rows in between startRow and endRow are also selected.
25198 * @param {Number} startRow The index of the first row in the range
25199 * @param {Number} endRow The index of the last row in the range
25200 * @param {Boolean} keepExisting (optional) True to retain existing selections
25202 selectRange : function(startRow, endRow, keepExisting){
25207 this.clearSelections();
25209 if(startRow <= endRow){
25210 for(var i = startRow; i <= endRow; i++){
25211 this.selectRow(i, true);
25214 for(var i = startRow; i >= endRow; i--){
25215 this.selectRow(i, true);
25221 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
25222 * @param {Number} startRow The index of the first row in the range
25223 * @param {Number} endRow The index of the last row in the range
25225 deselectRange : function(startRow, endRow, preventViewNotify){
25229 for(var i = startRow; i <= endRow; i++){
25230 this.deselectRow(i, preventViewNotify);
25236 * @param {Number} row The index of the row to select
25237 * @param {Boolean} keepExisting (optional) True to keep existing selections
25239 selectRow : function(index, keepExisting, preventViewNotify)
25241 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
25244 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
25245 if(!keepExisting || this.singleSelect){
25246 this.clearSelections();
25249 var r = this.grid.store.getAt(index);
25250 //console.log('selectRow - record id :' + r.id);
25252 this.selections.add(r);
25253 this.last = this.lastActive = index;
25254 if(!preventViewNotify){
25255 var proxy = new Roo.Element(
25256 this.grid.getRowDom(index)
25258 proxy.addClass('bg-info info');
25260 this.fireEvent("rowselect", this, index, r);
25261 this.fireEvent("selectionchange", this);
25267 * @param {Number} row The index of the row to deselect
25269 deselectRow : function(index, preventViewNotify)
25274 if(this.last == index){
25277 if(this.lastActive == index){
25278 this.lastActive = false;
25281 var r = this.grid.store.getAt(index);
25286 this.selections.remove(r);
25287 //.console.log('deselectRow - record id :' + r.id);
25288 if(!preventViewNotify){
25290 var proxy = new Roo.Element(
25291 this.grid.getRowDom(index)
25293 proxy.removeClass('bg-info info');
25295 this.fireEvent("rowdeselect", this, index);
25296 this.fireEvent("selectionchange", this);
25300 restoreLast : function(){
25302 this.last = this._last;
25307 acceptsNav : function(row, col, cm){
25308 return !cm.isHidden(col) && cm.isCellEditable(col, row);
25312 onEditorKey : function(field, e){
25313 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25318 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25320 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25322 }else if(k == e.ENTER && !e.ctrlKey){
25326 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25328 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25330 }else if(k == e.ESC){
25334 g.startEditing(newCell[0], newCell[1]);
25340 * Ext JS Library 1.1.1
25341 * Copyright(c) 2006-2007, Ext JS, LLC.
25343 * Originally Released Under LGPL - original licence link has changed is not relivant.
25346 * <script type="text/javascript">
25350 * @class Roo.bootstrap.PagingToolbar
25351 * @extends Roo.bootstrap.NavSimplebar
25352 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25354 * Create a new PagingToolbar
25355 * @param {Object} config The config object
25356 * @param {Roo.data.Store} store
25358 Roo.bootstrap.PagingToolbar = function(config)
25360 // old args format still supported... - xtype is prefered..
25361 // created from xtype...
25363 this.ds = config.dataSource;
25365 if (config.store && !this.ds) {
25366 this.store= Roo.factory(config.store, Roo.data);
25367 this.ds = this.store;
25368 this.ds.xmodule = this.xmodule || false;
25371 this.toolbarItems = [];
25372 if (config.items) {
25373 this.toolbarItems = config.items;
25376 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25381 this.bind(this.ds);
25384 if (Roo.bootstrap.version == 4) {
25385 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25387 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25392 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25394 * @cfg {Roo.data.Store} dataSource
25395 * The underlying data store providing the paged data
25398 * @cfg {String/HTMLElement/Element} container
25399 * container The id or element that will contain the toolbar
25402 * @cfg {Boolean} displayInfo
25403 * True to display the displayMsg (defaults to false)
25406 * @cfg {Number} pageSize
25407 * The number of records to display per page (defaults to 20)
25411 * @cfg {String} displayMsg
25412 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25414 displayMsg : 'Displaying {0} - {1} of {2}',
25416 * @cfg {String} emptyMsg
25417 * The message to display when no records are found (defaults to "No data to display")
25419 emptyMsg : 'No data to display',
25421 * Customizable piece of the default paging text (defaults to "Page")
25424 beforePageText : "Page",
25426 * Customizable piece of the default paging text (defaults to "of %0")
25429 afterPageText : "of {0}",
25431 * Customizable piece of the default paging text (defaults to "First Page")
25434 firstText : "First Page",
25436 * Customizable piece of the default paging text (defaults to "Previous Page")
25439 prevText : "Previous Page",
25441 * Customizable piece of the default paging text (defaults to "Next Page")
25444 nextText : "Next Page",
25446 * Customizable piece of the default paging text (defaults to "Last Page")
25449 lastText : "Last Page",
25451 * Customizable piece of the default paging text (defaults to "Refresh")
25454 refreshText : "Refresh",
25458 onRender : function(ct, position)
25460 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25461 this.navgroup.parentId = this.id;
25462 this.navgroup.onRender(this.el, null);
25463 // add the buttons to the navgroup
25465 if(this.displayInfo){
25466 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25467 this.displayEl = this.el.select('.x-paging-info', true).first();
25468 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25469 // this.displayEl = navel.el.select('span',true).first();
25475 Roo.each(_this.buttons, function(e){ // this might need to use render????
25476 Roo.factory(e).render(_this.el);
25480 Roo.each(_this.toolbarItems, function(e) {
25481 _this.navgroup.addItem(e);
25485 this.first = this.navgroup.addItem({
25486 tooltip: this.firstText,
25487 cls: "prev btn-outline-secondary",
25488 html : ' <i class="fa fa-step-backward"></i>',
25490 preventDefault: true,
25491 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25494 this.prev = this.navgroup.addItem({
25495 tooltip: this.prevText,
25496 cls: "prev btn-outline-secondary",
25497 html : ' <i class="fa fa-backward"></i>',
25499 preventDefault: true,
25500 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
25502 //this.addSeparator();
25505 var field = this.navgroup.addItem( {
25507 cls : 'x-paging-position btn-outline-secondary',
25509 html : this.beforePageText +
25510 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25511 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
25514 this.field = field.el.select('input', true).first();
25515 this.field.on("keydown", this.onPagingKeydown, this);
25516 this.field.on("focus", function(){this.dom.select();});
25519 this.afterTextEl = field.el.select('.x-paging-after',true).first();
25520 //this.field.setHeight(18);
25521 //this.addSeparator();
25522 this.next = this.navgroup.addItem({
25523 tooltip: this.nextText,
25524 cls: "next btn-outline-secondary",
25525 html : ' <i class="fa fa-forward"></i>',
25527 preventDefault: true,
25528 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
25530 this.last = this.navgroup.addItem({
25531 tooltip: this.lastText,
25532 html : ' <i class="fa fa-step-forward"></i>',
25533 cls: "next btn-outline-secondary",
25535 preventDefault: true,
25536 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
25538 //this.addSeparator();
25539 this.loading = this.navgroup.addItem({
25540 tooltip: this.refreshText,
25541 cls: "btn-outline-secondary",
25542 html : ' <i class="fa fa-refresh"></i>',
25543 preventDefault: true,
25544 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25550 updateInfo : function(){
25551 if(this.displayEl){
25552 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25553 var msg = count == 0 ?
25557 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
25559 this.displayEl.update(msg);
25564 onLoad : function(ds, r, o)
25566 this.cursor = o.params.start ? o.params.start : 0;
25568 var d = this.getPageData(),
25573 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25574 this.field.dom.value = ap;
25575 this.first.setDisabled(ap == 1);
25576 this.prev.setDisabled(ap == 1);
25577 this.next.setDisabled(ap == ps);
25578 this.last.setDisabled(ap == ps);
25579 this.loading.enable();
25584 getPageData : function(){
25585 var total = this.ds.getTotalCount();
25588 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25589 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25594 onLoadError : function(){
25595 this.loading.enable();
25599 onPagingKeydown : function(e){
25600 var k = e.getKey();
25601 var d = this.getPageData();
25603 var v = this.field.dom.value, pageNum;
25604 if(!v || isNaN(pageNum = parseInt(v, 10))){
25605 this.field.dom.value = d.activePage;
25608 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25609 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25612 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))
25614 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25615 this.field.dom.value = pageNum;
25616 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25619 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25621 var v = this.field.dom.value, pageNum;
25622 var increment = (e.shiftKey) ? 10 : 1;
25623 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25626 if(!v || isNaN(pageNum = parseInt(v, 10))) {
25627 this.field.dom.value = d.activePage;
25630 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25632 this.field.dom.value = parseInt(v, 10) + increment;
25633 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25634 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25641 beforeLoad : function(){
25643 this.loading.disable();
25648 onClick : function(which){
25657 ds.load({params:{start: 0, limit: this.pageSize}});
25660 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25663 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25666 var total = ds.getTotalCount();
25667 var extra = total % this.pageSize;
25668 var lastStart = extra ? (total - extra) : total-this.pageSize;
25669 ds.load({params:{start: lastStart, limit: this.pageSize}});
25672 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25678 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25679 * @param {Roo.data.Store} store The data store to unbind
25681 unbind : function(ds){
25682 ds.un("beforeload", this.beforeLoad, this);
25683 ds.un("load", this.onLoad, this);
25684 ds.un("loadexception", this.onLoadError, this);
25685 ds.un("remove", this.updateInfo, this);
25686 ds.un("add", this.updateInfo, this);
25687 this.ds = undefined;
25691 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25692 * @param {Roo.data.Store} store The data store to bind
25694 bind : function(ds){
25695 ds.on("beforeload", this.beforeLoad, this);
25696 ds.on("load", this.onLoad, this);
25697 ds.on("loadexception", this.onLoadError, this);
25698 ds.on("remove", this.updateInfo, this);
25699 ds.on("add", this.updateInfo, this);
25710 * @class Roo.bootstrap.MessageBar
25711 * @extends Roo.bootstrap.Component
25712 * Bootstrap MessageBar class
25713 * @cfg {String} html contents of the MessageBar
25714 * @cfg {String} weight (info | success | warning | danger) default info
25715 * @cfg {String} beforeClass insert the bar before the given class
25716 * @cfg {Boolean} closable (true | false) default false
25717 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25720 * Create a new Element
25721 * @param {Object} config The config object
25724 Roo.bootstrap.MessageBar = function(config){
25725 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25728 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25734 beforeClass: 'bootstrap-sticky-wrap',
25736 getAutoCreate : function(){
25740 cls: 'alert alert-dismissable alert-' + this.weight,
25745 html: this.html || ''
25751 cfg.cls += ' alert-messages-fixed';
25765 onRender : function(ct, position)
25767 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25770 var cfg = Roo.apply({}, this.getAutoCreate());
25774 cfg.cls += ' ' + this.cls;
25777 cfg.style = this.style;
25779 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25781 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25784 this.el.select('>button.close').on('click', this.hide, this);
25790 if (!this.rendered) {
25796 this.fireEvent('show', this);
25802 if (!this.rendered) {
25808 this.fireEvent('hide', this);
25811 update : function()
25813 // var e = this.el.dom.firstChild;
25815 // if(this.closable){
25816 // e = e.nextSibling;
25819 // e.data = this.html || '';
25821 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25837 * @class Roo.bootstrap.Graph
25838 * @extends Roo.bootstrap.Component
25839 * Bootstrap Graph class
25843 @cfg {String} graphtype bar | vbar | pie
25844 @cfg {number} g_x coodinator | centre x (pie)
25845 @cfg {number} g_y coodinator | centre y (pie)
25846 @cfg {number} g_r radius (pie)
25847 @cfg {number} g_height height of the chart (respected by all elements in the set)
25848 @cfg {number} g_width width of the chart (respected by all elements in the set)
25849 @cfg {Object} title The title of the chart
25852 -opts (object) options for the chart
25854 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25855 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25857 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.
25858 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25860 o stretch (boolean)
25862 -opts (object) options for the pie
25865 o startAngle (number)
25866 o endAngle (number)
25870 * Create a new Input
25871 * @param {Object} config The config object
25874 Roo.bootstrap.Graph = function(config){
25875 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25881 * The img click event for the img.
25882 * @param {Roo.EventObject} e
25888 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25899 //g_colors: this.colors,
25906 getAutoCreate : function(){
25917 onRender : function(ct,position){
25920 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25922 if (typeof(Raphael) == 'undefined') {
25923 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25927 this.raphael = Raphael(this.el.dom);
25929 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25930 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25931 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25932 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25934 r.text(160, 10, "Single Series Chart").attr(txtattr);
25935 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25936 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25937 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25939 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25940 r.barchart(330, 10, 300, 220, data1);
25941 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25942 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25945 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25946 // r.barchart(30, 30, 560, 250, xdata, {
25947 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25948 // axis : "0 0 1 1",
25949 // axisxlabels : xdata
25950 // //yvalues : cols,
25953 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25955 // this.load(null,xdata,{
25956 // axis : "0 0 1 1",
25957 // axisxlabels : xdata
25962 load : function(graphtype,xdata,opts)
25964 this.raphael.clear();
25966 graphtype = this.graphtype;
25971 var r = this.raphael,
25972 fin = function () {
25973 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25975 fout = function () {
25976 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25978 pfin = function() {
25979 this.sector.stop();
25980 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25983 this.label[0].stop();
25984 this.label[0].attr({ r: 7.5 });
25985 this.label[1].attr({ "font-weight": 800 });
25988 pfout = function() {
25989 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25992 this.label[0].animate({ r: 5 }, 500, "bounce");
25993 this.label[1].attr({ "font-weight": 400 });
25999 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26002 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26005 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
26006 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
26008 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
26015 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
26020 setTitle: function(o)
26025 initEvents: function() {
26028 this.el.on('click', this.onClick, this);
26032 onClick : function(e)
26034 Roo.log('img onclick');
26035 this.fireEvent('click', this, e);
26047 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26050 * @class Roo.bootstrap.dash.NumberBox
26051 * @extends Roo.bootstrap.Component
26052 * Bootstrap NumberBox class
26053 * @cfg {String} headline Box headline
26054 * @cfg {String} content Box content
26055 * @cfg {String} icon Box icon
26056 * @cfg {String} footer Footer text
26057 * @cfg {String} fhref Footer href
26060 * Create a new NumberBox
26061 * @param {Object} config The config object
26065 Roo.bootstrap.dash.NumberBox = function(config){
26066 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
26070 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
26079 getAutoCreate : function(){
26083 cls : 'small-box ',
26091 cls : 'roo-headline',
26092 html : this.headline
26096 cls : 'roo-content',
26097 html : this.content
26111 cls : 'ion ' + this.icon
26120 cls : 'small-box-footer',
26121 href : this.fhref || '#',
26125 cfg.cn.push(footer);
26132 onRender : function(ct,position){
26133 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
26140 setHeadline: function (value)
26142 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
26145 setFooter: function (value, href)
26147 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
26150 this.el.select('a.small-box-footer',true).first().attr('href', href);
26155 setContent: function (value)
26157 this.el.select('.roo-content',true).first().dom.innerHTML = value;
26160 initEvents: function()
26174 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26177 * @class Roo.bootstrap.dash.TabBox
26178 * @extends Roo.bootstrap.Component
26179 * Bootstrap TabBox class
26180 * @cfg {String} title Title of the TabBox
26181 * @cfg {String} icon Icon of the TabBox
26182 * @cfg {Boolean} showtabs (true|false) show the tabs default true
26183 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
26186 * Create a new TabBox
26187 * @param {Object} config The config object
26191 Roo.bootstrap.dash.TabBox = function(config){
26192 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
26197 * When a pane is added
26198 * @param {Roo.bootstrap.dash.TabPane} pane
26202 * @event activatepane
26203 * When a pane is activated
26204 * @param {Roo.bootstrap.dash.TabPane} pane
26206 "activatepane" : true
26214 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
26219 tabScrollable : false,
26221 getChildContainer : function()
26223 return this.el.select('.tab-content', true).first();
26226 getAutoCreate : function(){
26230 cls: 'pull-left header',
26238 cls: 'fa ' + this.icon
26244 cls: 'nav nav-tabs pull-right',
26250 if(this.tabScrollable){
26257 cls: 'nav nav-tabs pull-right',
26268 cls: 'nav-tabs-custom',
26273 cls: 'tab-content no-padding',
26281 initEvents : function()
26283 //Roo.log('add add pane handler');
26284 this.on('addpane', this.onAddPane, this);
26287 * Updates the box title
26288 * @param {String} html to set the title to.
26290 setTitle : function(value)
26292 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
26294 onAddPane : function(pane)
26296 this.panes.push(pane);
26297 //Roo.log('addpane');
26299 // tabs are rendere left to right..
26300 if(!this.showtabs){
26304 var ctr = this.el.select('.nav-tabs', true).first();
26307 var existing = ctr.select('.nav-tab',true);
26308 var qty = existing.getCount();;
26311 var tab = ctr.createChild({
26313 cls : 'nav-tab' + (qty ? '' : ' active'),
26321 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26324 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26326 pane.el.addClass('active');
26331 onTabClick : function(ev,un,ob,pane)
26333 //Roo.log('tab - prev default');
26334 ev.preventDefault();
26337 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26338 pane.tab.addClass('active');
26339 //Roo.log(pane.title);
26340 this.getChildContainer().select('.tab-pane',true).removeClass('active');
26341 // technically we should have a deactivate event.. but maybe add later.
26342 // and it should not de-activate the selected tab...
26343 this.fireEvent('activatepane', pane);
26344 pane.el.addClass('active');
26345 pane.fireEvent('activate');
26350 getActivePane : function()
26353 Roo.each(this.panes, function(p) {
26354 if(p.el.hasClass('active')){
26375 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26377 * @class Roo.bootstrap.TabPane
26378 * @extends Roo.bootstrap.Component
26379 * Bootstrap TabPane class
26380 * @cfg {Boolean} active (false | true) Default false
26381 * @cfg {String} title title of panel
26385 * Create a new TabPane
26386 * @param {Object} config The config object
26389 Roo.bootstrap.dash.TabPane = function(config){
26390 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26396 * When a pane is activated
26397 * @param {Roo.bootstrap.dash.TabPane} pane
26404 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
26409 // the tabBox that this is attached to.
26412 getAutoCreate : function()
26420 cfg.cls += ' active';
26425 initEvents : function()
26427 //Roo.log('trigger add pane handler');
26428 this.parent().fireEvent('addpane', this)
26432 * Updates the tab title
26433 * @param {String} html to set the title to.
26435 setTitle: function(str)
26441 this.tab.select('a', true).first().dom.innerHTML = str;
26458 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26461 * @class Roo.bootstrap.menu.Menu
26462 * @extends Roo.bootstrap.Component
26463 * Bootstrap Menu class - container for Menu
26464 * @cfg {String} html Text of the menu
26465 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26466 * @cfg {String} icon Font awesome icon
26467 * @cfg {String} pos Menu align to (top | bottom) default bottom
26471 * Create a new Menu
26472 * @param {Object} config The config object
26476 Roo.bootstrap.menu.Menu = function(config){
26477 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26481 * @event beforeshow
26482 * Fires before this menu is displayed
26483 * @param {Roo.bootstrap.menu.Menu} this
26487 * @event beforehide
26488 * Fires before this menu is hidden
26489 * @param {Roo.bootstrap.menu.Menu} this
26494 * Fires after this menu is displayed
26495 * @param {Roo.bootstrap.menu.Menu} this
26500 * Fires after this menu is hidden
26501 * @param {Roo.bootstrap.menu.Menu} this
26506 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26507 * @param {Roo.bootstrap.menu.Menu} this
26508 * @param {Roo.EventObject} e
26515 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
26519 weight : 'default',
26524 getChildContainer : function() {
26525 if(this.isSubMenu){
26529 return this.el.select('ul.dropdown-menu', true).first();
26532 getAutoCreate : function()
26537 cls : 'roo-menu-text',
26545 cls : 'fa ' + this.icon
26556 cls : 'dropdown-button btn btn-' + this.weight,
26561 cls : 'dropdown-toggle btn btn-' + this.weight,
26571 cls : 'dropdown-menu'
26577 if(this.pos == 'top'){
26578 cfg.cls += ' dropup';
26581 if(this.isSubMenu){
26584 cls : 'dropdown-menu'
26591 onRender : function(ct, position)
26593 this.isSubMenu = ct.hasClass('dropdown-submenu');
26595 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26598 initEvents : function()
26600 if(this.isSubMenu){
26604 this.hidden = true;
26606 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26607 this.triggerEl.on('click', this.onTriggerPress, this);
26609 this.buttonEl = this.el.select('button.dropdown-button', true).first();
26610 this.buttonEl.on('click', this.onClick, this);
26616 if(this.isSubMenu){
26620 return this.el.select('ul.dropdown-menu', true).first();
26623 onClick : function(e)
26625 this.fireEvent("click", this, e);
26628 onTriggerPress : function(e)
26630 if (this.isVisible()) {
26637 isVisible : function(){
26638 return !this.hidden;
26643 this.fireEvent("beforeshow", this);
26645 this.hidden = false;
26646 this.el.addClass('open');
26648 Roo.get(document).on("mouseup", this.onMouseUp, this);
26650 this.fireEvent("show", this);
26657 this.fireEvent("beforehide", this);
26659 this.hidden = true;
26660 this.el.removeClass('open');
26662 Roo.get(document).un("mouseup", this.onMouseUp);
26664 this.fireEvent("hide", this);
26667 onMouseUp : function()
26681 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26684 * @class Roo.bootstrap.menu.Item
26685 * @extends Roo.bootstrap.Component
26686 * Bootstrap MenuItem class
26687 * @cfg {Boolean} submenu (true | false) default false
26688 * @cfg {String} html text of the item
26689 * @cfg {String} href the link
26690 * @cfg {Boolean} disable (true | false) default false
26691 * @cfg {Boolean} preventDefault (true | false) default true
26692 * @cfg {String} icon Font awesome icon
26693 * @cfg {String} pos Submenu align to (left | right) default right
26697 * Create a new Item
26698 * @param {Object} config The config object
26702 Roo.bootstrap.menu.Item = function(config){
26703 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26707 * Fires when the mouse is hovering over this menu
26708 * @param {Roo.bootstrap.menu.Item} this
26709 * @param {Roo.EventObject} e
26714 * Fires when the mouse exits this menu
26715 * @param {Roo.bootstrap.menu.Item} this
26716 * @param {Roo.EventObject} e
26722 * The raw click event for the entire grid.
26723 * @param {Roo.EventObject} e
26729 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26734 preventDefault: true,
26739 getAutoCreate : function()
26744 cls : 'roo-menu-item-text',
26752 cls : 'fa ' + this.icon
26761 href : this.href || '#',
26768 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26772 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26774 if(this.pos == 'left'){
26775 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26782 initEvents : function()
26784 this.el.on('mouseover', this.onMouseOver, this);
26785 this.el.on('mouseout', this.onMouseOut, this);
26787 this.el.select('a', true).first().on('click', this.onClick, this);
26791 onClick : function(e)
26793 if(this.preventDefault){
26794 e.preventDefault();
26797 this.fireEvent("click", this, e);
26800 onMouseOver : function(e)
26802 if(this.submenu && this.pos == 'left'){
26803 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26806 this.fireEvent("mouseover", this, e);
26809 onMouseOut : function(e)
26811 this.fireEvent("mouseout", this, e);
26823 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26826 * @class Roo.bootstrap.menu.Separator
26827 * @extends Roo.bootstrap.Component
26828 * Bootstrap Separator class
26831 * Create a new Separator
26832 * @param {Object} config The config object
26836 Roo.bootstrap.menu.Separator = function(config){
26837 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26840 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26842 getAutoCreate : function(){
26863 * @class Roo.bootstrap.Tooltip
26864 * Bootstrap Tooltip class
26865 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26866 * to determine which dom element triggers the tooltip.
26868 * It needs to add support for additional attributes like tooltip-position
26871 * Create a new Toolti
26872 * @param {Object} config The config object
26875 Roo.bootstrap.Tooltip = function(config){
26876 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26878 this.alignment = Roo.bootstrap.Tooltip.alignment;
26880 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26881 this.alignment = config.alignment;
26886 Roo.apply(Roo.bootstrap.Tooltip, {
26888 * @function init initialize tooltip monitoring.
26892 currentTip : false,
26893 currentRegion : false,
26899 Roo.get(document).on('mouseover', this.enter ,this);
26900 Roo.get(document).on('mouseout', this.leave, this);
26903 this.currentTip = new Roo.bootstrap.Tooltip();
26906 enter : function(ev)
26908 var dom = ev.getTarget();
26910 //Roo.log(['enter',dom]);
26911 var el = Roo.fly(dom);
26912 if (this.currentEl) {
26914 //Roo.log(this.currentEl);
26915 //Roo.log(this.currentEl.contains(dom));
26916 if (this.currentEl == el) {
26919 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26925 if (this.currentTip.el) {
26926 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26930 if(!el || el.dom == document){
26936 // you can not look for children, as if el is the body.. then everythign is the child..
26937 if (!el.attr('tooltip')) { //
26938 if (!el.select("[tooltip]").elements.length) {
26941 // is the mouse over this child...?
26942 bindEl = el.select("[tooltip]").first();
26943 var xy = ev.getXY();
26944 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26945 //Roo.log("not in region.");
26948 //Roo.log("child element over..");
26951 this.currentEl = bindEl;
26952 this.currentTip.bind(bindEl);
26953 this.currentRegion = Roo.lib.Region.getRegion(dom);
26954 this.currentTip.enter();
26957 leave : function(ev)
26959 var dom = ev.getTarget();
26960 //Roo.log(['leave',dom]);
26961 if (!this.currentEl) {
26966 if (dom != this.currentEl.dom) {
26969 var xy = ev.getXY();
26970 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26973 // only activate leave if mouse cursor is outside... bounding box..
26978 if (this.currentTip) {
26979 this.currentTip.leave();
26981 //Roo.log('clear currentEl');
26982 this.currentEl = false;
26987 'left' : ['r-l', [-2,0], 'right'],
26988 'right' : ['l-r', [2,0], 'left'],
26989 'bottom' : ['t-b', [0,2], 'top'],
26990 'top' : [ 'b-t', [0,-2], 'bottom']
26996 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
27001 delay : null, // can be { show : 300 , hide: 500}
27005 hoverState : null, //???
27007 placement : 'bottom',
27011 getAutoCreate : function(){
27018 cls : 'tooltip-arrow'
27021 cls : 'tooltip-inner'
27028 bind : function(el)
27034 enter : function () {
27036 if (this.timeout != null) {
27037 clearTimeout(this.timeout);
27040 this.hoverState = 'in';
27041 //Roo.log("enter - show");
27042 if (!this.delay || !this.delay.show) {
27047 this.timeout = setTimeout(function () {
27048 if (_t.hoverState == 'in') {
27051 }, this.delay.show);
27055 clearTimeout(this.timeout);
27057 this.hoverState = 'out';
27058 if (!this.delay || !this.delay.hide) {
27064 this.timeout = setTimeout(function () {
27065 //Roo.log("leave - timeout");
27067 if (_t.hoverState == 'out') {
27069 Roo.bootstrap.Tooltip.currentEl = false;
27074 show : function (msg)
27077 this.render(document.body);
27080 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
27082 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
27084 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
27086 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
27088 var placement = typeof this.placement == 'function' ?
27089 this.placement.call(this, this.el, on_el) :
27092 var autoToken = /\s?auto?\s?/i;
27093 var autoPlace = autoToken.test(placement);
27095 placement = placement.replace(autoToken, '') || 'top';
27099 //this.el.setXY([0,0]);
27101 //this.el.dom.style.display='block';
27103 //this.el.appendTo(on_el);
27105 var p = this.getPosition();
27106 var box = this.el.getBox();
27112 var align = this.alignment[placement];
27114 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
27116 if(placement == 'top' || placement == 'bottom'){
27118 placement = 'right';
27121 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
27122 placement = 'left';
27125 var scroll = Roo.select('body', true).first().getScroll();
27127 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
27131 align = this.alignment[placement];
27134 this.el.alignTo(this.bindEl, align[0],align[1]);
27135 //var arrow = this.el.select('.arrow',true).first();
27136 //arrow.set(align[2],
27138 this.el.addClass(placement);
27140 this.el.addClass('in fade');
27142 this.hoverState = null;
27144 if (this.el.hasClass('fade')) {
27155 //this.el.setXY([0,0]);
27156 this.el.removeClass('in');
27172 * @class Roo.bootstrap.LocationPicker
27173 * @extends Roo.bootstrap.Component
27174 * Bootstrap LocationPicker class
27175 * @cfg {Number} latitude Position when init default 0
27176 * @cfg {Number} longitude Position when init default 0
27177 * @cfg {Number} zoom default 15
27178 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
27179 * @cfg {Boolean} mapTypeControl default false
27180 * @cfg {Boolean} disableDoubleClickZoom default false
27181 * @cfg {Boolean} scrollwheel default true
27182 * @cfg {Boolean} streetViewControl default false
27183 * @cfg {Number} radius default 0
27184 * @cfg {String} locationName
27185 * @cfg {Boolean} draggable default true
27186 * @cfg {Boolean} enableAutocomplete default false
27187 * @cfg {Boolean} enableReverseGeocode default true
27188 * @cfg {String} markerTitle
27191 * Create a new LocationPicker
27192 * @param {Object} config The config object
27196 Roo.bootstrap.LocationPicker = function(config){
27198 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
27203 * Fires when the picker initialized.
27204 * @param {Roo.bootstrap.LocationPicker} this
27205 * @param {Google Location} location
27209 * @event positionchanged
27210 * Fires when the picker position changed.
27211 * @param {Roo.bootstrap.LocationPicker} this
27212 * @param {Google Location} location
27214 positionchanged : true,
27217 * Fires when the map resize.
27218 * @param {Roo.bootstrap.LocationPicker} this
27223 * Fires when the map show.
27224 * @param {Roo.bootstrap.LocationPicker} this
27229 * Fires when the map hide.
27230 * @param {Roo.bootstrap.LocationPicker} this
27235 * Fires when click the map.
27236 * @param {Roo.bootstrap.LocationPicker} this
27237 * @param {Map event} e
27241 * @event mapRightClick
27242 * Fires when right click the map.
27243 * @param {Roo.bootstrap.LocationPicker} this
27244 * @param {Map event} e
27246 mapRightClick : true,
27248 * @event markerClick
27249 * Fires when click the marker.
27250 * @param {Roo.bootstrap.LocationPicker} this
27251 * @param {Map event} e
27253 markerClick : true,
27255 * @event markerRightClick
27256 * Fires when right click the marker.
27257 * @param {Roo.bootstrap.LocationPicker} this
27258 * @param {Map event} e
27260 markerRightClick : true,
27262 * @event OverlayViewDraw
27263 * Fires when OverlayView Draw
27264 * @param {Roo.bootstrap.LocationPicker} this
27266 OverlayViewDraw : true,
27268 * @event OverlayViewOnAdd
27269 * Fires when OverlayView Draw
27270 * @param {Roo.bootstrap.LocationPicker} this
27272 OverlayViewOnAdd : true,
27274 * @event OverlayViewOnRemove
27275 * Fires when OverlayView Draw
27276 * @param {Roo.bootstrap.LocationPicker} this
27278 OverlayViewOnRemove : true,
27280 * @event OverlayViewShow
27281 * Fires when OverlayView Draw
27282 * @param {Roo.bootstrap.LocationPicker} this
27283 * @param {Pixel} cpx
27285 OverlayViewShow : true,
27287 * @event OverlayViewHide
27288 * Fires when OverlayView Draw
27289 * @param {Roo.bootstrap.LocationPicker} this
27291 OverlayViewHide : true,
27293 * @event loadexception
27294 * Fires when load google lib failed.
27295 * @param {Roo.bootstrap.LocationPicker} this
27297 loadexception : true
27302 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
27304 gMapContext: false,
27310 mapTypeControl: false,
27311 disableDoubleClickZoom: false,
27313 streetViewControl: false,
27317 enableAutocomplete: false,
27318 enableReverseGeocode: true,
27321 getAutoCreate: function()
27326 cls: 'roo-location-picker'
27332 initEvents: function(ct, position)
27334 if(!this.el.getWidth() || this.isApplied()){
27338 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27343 initial: function()
27345 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27346 this.fireEvent('loadexception', this);
27350 if(!this.mapTypeId){
27351 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27354 this.gMapContext = this.GMapContext();
27356 this.initOverlayView();
27358 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27362 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27363 _this.setPosition(_this.gMapContext.marker.position);
27366 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27367 _this.fireEvent('mapClick', this, event);
27371 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27372 _this.fireEvent('mapRightClick', this, event);
27376 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27377 _this.fireEvent('markerClick', this, event);
27381 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27382 _this.fireEvent('markerRightClick', this, event);
27386 this.setPosition(this.gMapContext.location);
27388 this.fireEvent('initial', this, this.gMapContext.location);
27391 initOverlayView: function()
27395 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27399 _this.fireEvent('OverlayViewDraw', _this);
27404 _this.fireEvent('OverlayViewOnAdd', _this);
27407 onRemove: function()
27409 _this.fireEvent('OverlayViewOnRemove', _this);
27412 show: function(cpx)
27414 _this.fireEvent('OverlayViewShow', _this, cpx);
27419 _this.fireEvent('OverlayViewHide', _this);
27425 fromLatLngToContainerPixel: function(event)
27427 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27430 isApplied: function()
27432 return this.getGmapContext() == false ? false : true;
27435 getGmapContext: function()
27437 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27440 GMapContext: function()
27442 var position = new google.maps.LatLng(this.latitude, this.longitude);
27444 var _map = new google.maps.Map(this.el.dom, {
27447 mapTypeId: this.mapTypeId,
27448 mapTypeControl: this.mapTypeControl,
27449 disableDoubleClickZoom: this.disableDoubleClickZoom,
27450 scrollwheel: this.scrollwheel,
27451 streetViewControl: this.streetViewControl,
27452 locationName: this.locationName,
27453 draggable: this.draggable,
27454 enableAutocomplete: this.enableAutocomplete,
27455 enableReverseGeocode: this.enableReverseGeocode
27458 var _marker = new google.maps.Marker({
27459 position: position,
27461 title: this.markerTitle,
27462 draggable: this.draggable
27469 location: position,
27470 radius: this.radius,
27471 locationName: this.locationName,
27472 addressComponents: {
27473 formatted_address: null,
27474 addressLine1: null,
27475 addressLine2: null,
27477 streetNumber: null,
27481 stateOrProvince: null
27484 domContainer: this.el.dom,
27485 geodecoder: new google.maps.Geocoder()
27489 drawCircle: function(center, radius, options)
27491 if (this.gMapContext.circle != null) {
27492 this.gMapContext.circle.setMap(null);
27496 options = Roo.apply({}, options, {
27497 strokeColor: "#0000FF",
27498 strokeOpacity: .35,
27500 fillColor: "#0000FF",
27504 options.map = this.gMapContext.map;
27505 options.radius = radius;
27506 options.center = center;
27507 this.gMapContext.circle = new google.maps.Circle(options);
27508 return this.gMapContext.circle;
27514 setPosition: function(location)
27516 this.gMapContext.location = location;
27517 this.gMapContext.marker.setPosition(location);
27518 this.gMapContext.map.panTo(location);
27519 this.drawCircle(location, this.gMapContext.radius, {});
27523 if (this.gMapContext.settings.enableReverseGeocode) {
27524 this.gMapContext.geodecoder.geocode({
27525 latLng: this.gMapContext.location
27526 }, function(results, status) {
27528 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27529 _this.gMapContext.locationName = results[0].formatted_address;
27530 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27532 _this.fireEvent('positionchanged', this, location);
27539 this.fireEvent('positionchanged', this, location);
27544 google.maps.event.trigger(this.gMapContext.map, "resize");
27546 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27548 this.fireEvent('resize', this);
27551 setPositionByLatLng: function(latitude, longitude)
27553 this.setPosition(new google.maps.LatLng(latitude, longitude));
27556 getCurrentPosition: function()
27559 latitude: this.gMapContext.location.lat(),
27560 longitude: this.gMapContext.location.lng()
27564 getAddressName: function()
27566 return this.gMapContext.locationName;
27569 getAddressComponents: function()
27571 return this.gMapContext.addressComponents;
27574 address_component_from_google_geocode: function(address_components)
27578 for (var i = 0; i < address_components.length; i++) {
27579 var component = address_components[i];
27580 if (component.types.indexOf("postal_code") >= 0) {
27581 result.postalCode = component.short_name;
27582 } else if (component.types.indexOf("street_number") >= 0) {
27583 result.streetNumber = component.short_name;
27584 } else if (component.types.indexOf("route") >= 0) {
27585 result.streetName = component.short_name;
27586 } else if (component.types.indexOf("neighborhood") >= 0) {
27587 result.city = component.short_name;
27588 } else if (component.types.indexOf("locality") >= 0) {
27589 result.city = component.short_name;
27590 } else if (component.types.indexOf("sublocality") >= 0) {
27591 result.district = component.short_name;
27592 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27593 result.stateOrProvince = component.short_name;
27594 } else if (component.types.indexOf("country") >= 0) {
27595 result.country = component.short_name;
27599 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27600 result.addressLine2 = "";
27604 setZoomLevel: function(zoom)
27606 this.gMapContext.map.setZoom(zoom);
27619 this.fireEvent('show', this);
27630 this.fireEvent('hide', this);
27635 Roo.apply(Roo.bootstrap.LocationPicker, {
27637 OverlayView : function(map, options)
27639 options = options || {};
27646 * @class Roo.bootstrap.Alert
27647 * @extends Roo.bootstrap.Component
27648 * Bootstrap Alert class - shows an alert area box
27650 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27651 Enter a valid email address
27654 * @cfg {String} title The title of alert
27655 * @cfg {String} html The content of alert
27656 * @cfg {String} weight ( success | info | warning | danger )
27657 * @cfg {String} faicon font-awesomeicon
27660 * Create a new alert
27661 * @param {Object} config The config object
27665 Roo.bootstrap.Alert = function(config){
27666 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27670 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27677 getAutoCreate : function()
27686 cls : 'roo-alert-icon'
27691 cls : 'roo-alert-title',
27696 cls : 'roo-alert-text',
27703 cfg.cn[0].cls += ' fa ' + this.faicon;
27707 cfg.cls += ' alert-' + this.weight;
27713 initEvents: function()
27715 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27718 setTitle : function(str)
27720 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27723 setText : function(str)
27725 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27728 setWeight : function(weight)
27731 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27734 this.weight = weight;
27736 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27739 setIcon : function(icon)
27742 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27745 this.faicon = icon;
27747 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27768 * @class Roo.bootstrap.UploadCropbox
27769 * @extends Roo.bootstrap.Component
27770 * Bootstrap UploadCropbox class
27771 * @cfg {String} emptyText show when image has been loaded
27772 * @cfg {String} rotateNotify show when image too small to rotate
27773 * @cfg {Number} errorTimeout default 3000
27774 * @cfg {Number} minWidth default 300
27775 * @cfg {Number} minHeight default 300
27776 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27777 * @cfg {Boolean} isDocument (true|false) default false
27778 * @cfg {String} url action url
27779 * @cfg {String} paramName default 'imageUpload'
27780 * @cfg {String} method default POST
27781 * @cfg {Boolean} loadMask (true|false) default true
27782 * @cfg {Boolean} loadingText default 'Loading...'
27785 * Create a new UploadCropbox
27786 * @param {Object} config The config object
27789 Roo.bootstrap.UploadCropbox = function(config){
27790 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27794 * @event beforeselectfile
27795 * Fire before select file
27796 * @param {Roo.bootstrap.UploadCropbox} this
27798 "beforeselectfile" : true,
27801 * Fire after initEvent
27802 * @param {Roo.bootstrap.UploadCropbox} this
27807 * Fire after initEvent
27808 * @param {Roo.bootstrap.UploadCropbox} this
27809 * @param {String} data
27814 * Fire when preparing the file data
27815 * @param {Roo.bootstrap.UploadCropbox} this
27816 * @param {Object} file
27821 * Fire when get exception
27822 * @param {Roo.bootstrap.UploadCropbox} this
27823 * @param {XMLHttpRequest} xhr
27825 "exception" : true,
27827 * @event beforeloadcanvas
27828 * Fire before load the canvas
27829 * @param {Roo.bootstrap.UploadCropbox} this
27830 * @param {String} src
27832 "beforeloadcanvas" : true,
27835 * Fire when trash image
27836 * @param {Roo.bootstrap.UploadCropbox} this
27841 * Fire when download the image
27842 * @param {Roo.bootstrap.UploadCropbox} this
27846 * @event footerbuttonclick
27847 * Fire when footerbuttonclick
27848 * @param {Roo.bootstrap.UploadCropbox} this
27849 * @param {String} type
27851 "footerbuttonclick" : true,
27855 * @param {Roo.bootstrap.UploadCropbox} this
27860 * Fire when rotate the image
27861 * @param {Roo.bootstrap.UploadCropbox} this
27862 * @param {String} pos
27867 * Fire when inspect the file
27868 * @param {Roo.bootstrap.UploadCropbox} this
27869 * @param {Object} file
27874 * Fire when xhr upload the file
27875 * @param {Roo.bootstrap.UploadCropbox} this
27876 * @param {Object} data
27881 * Fire when arrange the file data
27882 * @param {Roo.bootstrap.UploadCropbox} this
27883 * @param {Object} formData
27888 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27891 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27893 emptyText : 'Click to upload image',
27894 rotateNotify : 'Image is too small to rotate',
27895 errorTimeout : 3000,
27909 cropType : 'image/jpeg',
27911 canvasLoaded : false,
27912 isDocument : false,
27914 paramName : 'imageUpload',
27916 loadingText : 'Loading...',
27919 getAutoCreate : function()
27923 cls : 'roo-upload-cropbox',
27927 cls : 'roo-upload-cropbox-selector',
27932 cls : 'roo-upload-cropbox-body',
27933 style : 'cursor:pointer',
27937 cls : 'roo-upload-cropbox-preview'
27941 cls : 'roo-upload-cropbox-thumb'
27945 cls : 'roo-upload-cropbox-empty-notify',
27946 html : this.emptyText
27950 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27951 html : this.rotateNotify
27957 cls : 'roo-upload-cropbox-footer',
27960 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27970 onRender : function(ct, position)
27972 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27974 if (this.buttons.length) {
27976 Roo.each(this.buttons, function(bb) {
27978 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27980 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27986 this.maskEl = this.el;
27990 initEvents : function()
27992 this.urlAPI = (window.createObjectURL && window) ||
27993 (window.URL && URL.revokeObjectURL && URL) ||
27994 (window.webkitURL && webkitURL);
27996 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27997 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27999 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
28000 this.selectorEl.hide();
28002 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
28003 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28005 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
28006 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28007 this.thumbEl.hide();
28009 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
28010 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28012 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
28013 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28014 this.errorEl.hide();
28016 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
28017 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28018 this.footerEl.hide();
28020 this.setThumbBoxSize();
28026 this.fireEvent('initial', this);
28033 window.addEventListener("resize", function() { _this.resize(); } );
28035 this.bodyEl.on('click', this.beforeSelectFile, this);
28038 this.bodyEl.on('touchstart', this.onTouchStart, this);
28039 this.bodyEl.on('touchmove', this.onTouchMove, this);
28040 this.bodyEl.on('touchend', this.onTouchEnd, this);
28044 this.bodyEl.on('mousedown', this.onMouseDown, this);
28045 this.bodyEl.on('mousemove', this.onMouseMove, this);
28046 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
28047 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
28048 Roo.get(document).on('mouseup', this.onMouseUp, this);
28051 this.selectorEl.on('change', this.onFileSelected, this);
28057 this.baseScale = 1;
28059 this.baseRotate = 1;
28060 this.dragable = false;
28061 this.pinching = false;
28064 this.cropData = false;
28065 this.notifyEl.dom.innerHTML = this.emptyText;
28067 this.selectorEl.dom.value = '';
28071 resize : function()
28073 if(this.fireEvent('resize', this) != false){
28074 this.setThumbBoxPosition();
28075 this.setCanvasPosition();
28079 onFooterButtonClick : function(e, el, o, type)
28082 case 'rotate-left' :
28083 this.onRotateLeft(e);
28085 case 'rotate-right' :
28086 this.onRotateRight(e);
28089 this.beforeSelectFile(e);
28104 this.fireEvent('footerbuttonclick', this, type);
28107 beforeSelectFile : function(e)
28109 e.preventDefault();
28111 if(this.fireEvent('beforeselectfile', this) != false){
28112 this.selectorEl.dom.click();
28116 onFileSelected : function(e)
28118 e.preventDefault();
28120 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28124 var file = this.selectorEl.dom.files[0];
28126 if(this.fireEvent('inspect', this, file) != false){
28127 this.prepare(file);
28132 trash : function(e)
28134 this.fireEvent('trash', this);
28137 download : function(e)
28139 this.fireEvent('download', this);
28142 loadCanvas : function(src)
28144 if(this.fireEvent('beforeloadcanvas', this, src) != false){
28148 this.imageEl = document.createElement('img');
28152 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
28154 this.imageEl.src = src;
28158 onLoadCanvas : function()
28160 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
28161 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
28163 this.bodyEl.un('click', this.beforeSelectFile, this);
28165 this.notifyEl.hide();
28166 this.thumbEl.show();
28167 this.footerEl.show();
28169 this.baseRotateLevel();
28171 if(this.isDocument){
28172 this.setThumbBoxSize();
28175 this.setThumbBoxPosition();
28177 this.baseScaleLevel();
28183 this.canvasLoaded = true;
28186 this.maskEl.unmask();
28191 setCanvasPosition : function()
28193 if(!this.canvasEl){
28197 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
28198 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
28200 this.previewEl.setLeft(pw);
28201 this.previewEl.setTop(ph);
28205 onMouseDown : function(e)
28209 this.dragable = true;
28210 this.pinching = false;
28212 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
28213 this.dragable = false;
28217 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28218 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28222 onMouseMove : function(e)
28226 if(!this.canvasLoaded){
28230 if (!this.dragable){
28234 var minX = Math.ceil(this.thumbEl.getLeft(true));
28235 var minY = Math.ceil(this.thumbEl.getTop(true));
28237 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
28238 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
28240 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28241 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28243 x = x - this.mouseX;
28244 y = y - this.mouseY;
28246 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
28247 var bgY = Math.ceil(y + this.previewEl.getTop(true));
28249 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
28250 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
28252 this.previewEl.setLeft(bgX);
28253 this.previewEl.setTop(bgY);
28255 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28256 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28259 onMouseUp : function(e)
28263 this.dragable = false;
28266 onMouseWheel : function(e)
28270 this.startScale = this.scale;
28272 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
28274 if(!this.zoomable()){
28275 this.scale = this.startScale;
28284 zoomable : function()
28286 var minScale = this.thumbEl.getWidth() / this.minWidth;
28288 if(this.minWidth < this.minHeight){
28289 minScale = this.thumbEl.getHeight() / this.minHeight;
28292 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
28293 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
28297 (this.rotate == 0 || this.rotate == 180) &&
28299 width > this.imageEl.OriginWidth ||
28300 height > this.imageEl.OriginHeight ||
28301 (width < this.minWidth && height < this.minHeight)
28309 (this.rotate == 90 || this.rotate == 270) &&
28311 width > this.imageEl.OriginWidth ||
28312 height > this.imageEl.OriginHeight ||
28313 (width < this.minHeight && height < this.minWidth)
28320 !this.isDocument &&
28321 (this.rotate == 0 || this.rotate == 180) &&
28323 width < this.minWidth ||
28324 width > this.imageEl.OriginWidth ||
28325 height < this.minHeight ||
28326 height > this.imageEl.OriginHeight
28333 !this.isDocument &&
28334 (this.rotate == 90 || this.rotate == 270) &&
28336 width < this.minHeight ||
28337 width > this.imageEl.OriginWidth ||
28338 height < this.minWidth ||
28339 height > this.imageEl.OriginHeight
28349 onRotateLeft : function(e)
28351 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28353 var minScale = this.thumbEl.getWidth() / this.minWidth;
28355 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28356 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28358 this.startScale = this.scale;
28360 while (this.getScaleLevel() < minScale){
28362 this.scale = this.scale + 1;
28364 if(!this.zoomable()){
28369 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28370 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28375 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28382 this.scale = this.startScale;
28384 this.onRotateFail();
28389 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28391 if(this.isDocument){
28392 this.setThumbBoxSize();
28393 this.setThumbBoxPosition();
28394 this.setCanvasPosition();
28399 this.fireEvent('rotate', this, 'left');
28403 onRotateRight : function(e)
28405 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28407 var minScale = this.thumbEl.getWidth() / this.minWidth;
28409 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28410 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28412 this.startScale = this.scale;
28414 while (this.getScaleLevel() < minScale){
28416 this.scale = this.scale + 1;
28418 if(!this.zoomable()){
28423 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28424 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28429 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28436 this.scale = this.startScale;
28438 this.onRotateFail();
28443 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28445 if(this.isDocument){
28446 this.setThumbBoxSize();
28447 this.setThumbBoxPosition();
28448 this.setCanvasPosition();
28453 this.fireEvent('rotate', this, 'right');
28456 onRotateFail : function()
28458 this.errorEl.show(true);
28462 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28467 this.previewEl.dom.innerHTML = '';
28469 var canvasEl = document.createElement("canvas");
28471 var contextEl = canvasEl.getContext("2d");
28473 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28474 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28475 var center = this.imageEl.OriginWidth / 2;
28477 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28478 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28479 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28480 center = this.imageEl.OriginHeight / 2;
28483 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28485 contextEl.translate(center, center);
28486 contextEl.rotate(this.rotate * Math.PI / 180);
28488 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28490 this.canvasEl = document.createElement("canvas");
28492 this.contextEl = this.canvasEl.getContext("2d");
28494 switch (this.rotate) {
28497 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28498 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28500 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28505 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28506 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28508 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28509 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);
28513 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28518 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28519 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28521 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28522 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);
28526 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);
28531 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28532 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28534 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28535 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28539 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);
28546 this.previewEl.appendChild(this.canvasEl);
28548 this.setCanvasPosition();
28553 if(!this.canvasLoaded){
28557 var imageCanvas = document.createElement("canvas");
28559 var imageContext = imageCanvas.getContext("2d");
28561 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28562 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28564 var center = imageCanvas.width / 2;
28566 imageContext.translate(center, center);
28568 imageContext.rotate(this.rotate * Math.PI / 180);
28570 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28572 var canvas = document.createElement("canvas");
28574 var context = canvas.getContext("2d");
28576 canvas.width = this.minWidth;
28577 canvas.height = this.minHeight;
28579 switch (this.rotate) {
28582 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28583 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28585 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28586 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28588 var targetWidth = this.minWidth - 2 * x;
28589 var targetHeight = this.minHeight - 2 * y;
28593 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28594 scale = targetWidth / width;
28597 if(x > 0 && y == 0){
28598 scale = targetHeight / height;
28601 if(x > 0 && y > 0){
28602 scale = targetWidth / width;
28604 if(width < height){
28605 scale = targetHeight / height;
28609 context.scale(scale, scale);
28611 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28612 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28614 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28615 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28617 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28622 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28623 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28625 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28626 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28628 var targetWidth = this.minWidth - 2 * x;
28629 var targetHeight = this.minHeight - 2 * y;
28633 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28634 scale = targetWidth / width;
28637 if(x > 0 && y == 0){
28638 scale = targetHeight / height;
28641 if(x > 0 && y > 0){
28642 scale = targetWidth / width;
28644 if(width < height){
28645 scale = targetHeight / height;
28649 context.scale(scale, scale);
28651 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28652 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28654 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28655 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28657 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28659 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28664 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28665 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28667 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28668 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28670 var targetWidth = this.minWidth - 2 * x;
28671 var targetHeight = this.minHeight - 2 * y;
28675 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28676 scale = targetWidth / width;
28679 if(x > 0 && y == 0){
28680 scale = targetHeight / height;
28683 if(x > 0 && y > 0){
28684 scale = targetWidth / width;
28686 if(width < height){
28687 scale = targetHeight / height;
28691 context.scale(scale, scale);
28693 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28694 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28696 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28697 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28699 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28700 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28702 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28707 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28708 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28710 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28711 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28713 var targetWidth = this.minWidth - 2 * x;
28714 var targetHeight = this.minHeight - 2 * y;
28718 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28719 scale = targetWidth / width;
28722 if(x > 0 && y == 0){
28723 scale = targetHeight / height;
28726 if(x > 0 && y > 0){
28727 scale = targetWidth / width;
28729 if(width < height){
28730 scale = targetHeight / height;
28734 context.scale(scale, scale);
28736 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28737 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28739 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28740 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28742 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28744 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28751 this.cropData = canvas.toDataURL(this.cropType);
28753 if(this.fireEvent('crop', this, this.cropData) !== false){
28754 this.process(this.file, this.cropData);
28761 setThumbBoxSize : function()
28765 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28766 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28767 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28769 this.minWidth = width;
28770 this.minHeight = height;
28772 if(this.rotate == 90 || this.rotate == 270){
28773 this.minWidth = height;
28774 this.minHeight = width;
28779 width = Math.ceil(this.minWidth * height / this.minHeight);
28781 if(this.minWidth > this.minHeight){
28783 height = Math.ceil(this.minHeight * width / this.minWidth);
28786 this.thumbEl.setStyle({
28787 width : width + 'px',
28788 height : height + 'px'
28795 setThumbBoxPosition : function()
28797 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28798 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28800 this.thumbEl.setLeft(x);
28801 this.thumbEl.setTop(y);
28805 baseRotateLevel : function()
28807 this.baseRotate = 1;
28810 typeof(this.exif) != 'undefined' &&
28811 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28812 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28814 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28817 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28821 baseScaleLevel : function()
28825 if(this.isDocument){
28827 if(this.baseRotate == 6 || this.baseRotate == 8){
28829 height = this.thumbEl.getHeight();
28830 this.baseScale = height / this.imageEl.OriginWidth;
28832 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28833 width = this.thumbEl.getWidth();
28834 this.baseScale = width / this.imageEl.OriginHeight;
28840 height = this.thumbEl.getHeight();
28841 this.baseScale = height / this.imageEl.OriginHeight;
28843 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28844 width = this.thumbEl.getWidth();
28845 this.baseScale = width / this.imageEl.OriginWidth;
28851 if(this.baseRotate == 6 || this.baseRotate == 8){
28853 width = this.thumbEl.getHeight();
28854 this.baseScale = width / this.imageEl.OriginHeight;
28856 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28857 height = this.thumbEl.getWidth();
28858 this.baseScale = height / this.imageEl.OriginHeight;
28861 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28862 height = this.thumbEl.getWidth();
28863 this.baseScale = height / this.imageEl.OriginHeight;
28865 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28866 width = this.thumbEl.getHeight();
28867 this.baseScale = width / this.imageEl.OriginWidth;
28874 width = this.thumbEl.getWidth();
28875 this.baseScale = width / this.imageEl.OriginWidth;
28877 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28878 height = this.thumbEl.getHeight();
28879 this.baseScale = height / this.imageEl.OriginHeight;
28882 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28884 height = this.thumbEl.getHeight();
28885 this.baseScale = height / this.imageEl.OriginHeight;
28887 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28888 width = this.thumbEl.getWidth();
28889 this.baseScale = width / this.imageEl.OriginWidth;
28897 getScaleLevel : function()
28899 return this.baseScale * Math.pow(1.1, this.scale);
28902 onTouchStart : function(e)
28904 if(!this.canvasLoaded){
28905 this.beforeSelectFile(e);
28909 var touches = e.browserEvent.touches;
28915 if(touches.length == 1){
28916 this.onMouseDown(e);
28920 if(touches.length != 2){
28926 for(var i = 0, finger; finger = touches[i]; i++){
28927 coords.push(finger.pageX, finger.pageY);
28930 var x = Math.pow(coords[0] - coords[2], 2);
28931 var y = Math.pow(coords[1] - coords[3], 2);
28933 this.startDistance = Math.sqrt(x + y);
28935 this.startScale = this.scale;
28937 this.pinching = true;
28938 this.dragable = false;
28942 onTouchMove : function(e)
28944 if(!this.pinching && !this.dragable){
28948 var touches = e.browserEvent.touches;
28955 this.onMouseMove(e);
28961 for(var i = 0, finger; finger = touches[i]; i++){
28962 coords.push(finger.pageX, finger.pageY);
28965 var x = Math.pow(coords[0] - coords[2], 2);
28966 var y = Math.pow(coords[1] - coords[3], 2);
28968 this.endDistance = Math.sqrt(x + y);
28970 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28972 if(!this.zoomable()){
28973 this.scale = this.startScale;
28981 onTouchEnd : function(e)
28983 this.pinching = false;
28984 this.dragable = false;
28988 process : function(file, crop)
28991 this.maskEl.mask(this.loadingText);
28994 this.xhr = new XMLHttpRequest();
28996 file.xhr = this.xhr;
28998 this.xhr.open(this.method, this.url, true);
29001 "Accept": "application/json",
29002 "Cache-Control": "no-cache",
29003 "X-Requested-With": "XMLHttpRequest"
29006 for (var headerName in headers) {
29007 var headerValue = headers[headerName];
29009 this.xhr.setRequestHeader(headerName, headerValue);
29015 this.xhr.onload = function()
29017 _this.xhrOnLoad(_this.xhr);
29020 this.xhr.onerror = function()
29022 _this.xhrOnError(_this.xhr);
29025 var formData = new FormData();
29027 formData.append('returnHTML', 'NO');
29030 formData.append('crop', crop);
29033 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
29034 formData.append(this.paramName, file, file.name);
29037 if(typeof(file.filename) != 'undefined'){
29038 formData.append('filename', file.filename);
29041 if(typeof(file.mimetype) != 'undefined'){
29042 formData.append('mimetype', file.mimetype);
29045 if(this.fireEvent('arrange', this, formData) != false){
29046 this.xhr.send(formData);
29050 xhrOnLoad : function(xhr)
29053 this.maskEl.unmask();
29056 if (xhr.readyState !== 4) {
29057 this.fireEvent('exception', this, xhr);
29061 var response = Roo.decode(xhr.responseText);
29063 if(!response.success){
29064 this.fireEvent('exception', this, xhr);
29068 var response = Roo.decode(xhr.responseText);
29070 this.fireEvent('upload', this, response);
29074 xhrOnError : function()
29077 this.maskEl.unmask();
29080 Roo.log('xhr on error');
29082 var response = Roo.decode(xhr.responseText);
29088 prepare : function(file)
29091 this.maskEl.mask(this.loadingText);
29097 if(typeof(file) === 'string'){
29098 this.loadCanvas(file);
29102 if(!file || !this.urlAPI){
29107 this.cropType = file.type;
29111 if(this.fireEvent('prepare', this, this.file) != false){
29113 var reader = new FileReader();
29115 reader.onload = function (e) {
29116 if (e.target.error) {
29117 Roo.log(e.target.error);
29121 var buffer = e.target.result,
29122 dataView = new DataView(buffer),
29124 maxOffset = dataView.byteLength - 4,
29128 if (dataView.getUint16(0) === 0xffd8) {
29129 while (offset < maxOffset) {
29130 markerBytes = dataView.getUint16(offset);
29132 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
29133 markerLength = dataView.getUint16(offset + 2) + 2;
29134 if (offset + markerLength > dataView.byteLength) {
29135 Roo.log('Invalid meta data: Invalid segment size.');
29139 if(markerBytes == 0xffe1){
29140 _this.parseExifData(
29147 offset += markerLength;
29157 var url = _this.urlAPI.createObjectURL(_this.file);
29159 _this.loadCanvas(url);
29164 reader.readAsArrayBuffer(this.file);
29170 parseExifData : function(dataView, offset, length)
29172 var tiffOffset = offset + 10,
29176 if (dataView.getUint32(offset + 4) !== 0x45786966) {
29177 // No Exif data, might be XMP data instead
29181 // Check for the ASCII code for "Exif" (0x45786966):
29182 if (dataView.getUint32(offset + 4) !== 0x45786966) {
29183 // No Exif data, might be XMP data instead
29186 if (tiffOffset + 8 > dataView.byteLength) {
29187 Roo.log('Invalid Exif data: Invalid segment size.');
29190 // Check for the two null bytes:
29191 if (dataView.getUint16(offset + 8) !== 0x0000) {
29192 Roo.log('Invalid Exif data: Missing byte alignment offset.');
29195 // Check the byte alignment:
29196 switch (dataView.getUint16(tiffOffset)) {
29198 littleEndian = true;
29201 littleEndian = false;
29204 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
29207 // Check for the TIFF tag marker (0x002A):
29208 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
29209 Roo.log('Invalid Exif data: Missing TIFF marker.');
29212 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
29213 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
29215 this.parseExifTags(
29218 tiffOffset + dirOffset,
29223 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
29228 if (dirOffset + 6 > dataView.byteLength) {
29229 Roo.log('Invalid Exif data: Invalid directory offset.');
29232 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
29233 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
29234 if (dirEndOffset + 4 > dataView.byteLength) {
29235 Roo.log('Invalid Exif data: Invalid directory size.');
29238 for (i = 0; i < tagsNumber; i += 1) {
29242 dirOffset + 2 + 12 * i, // tag offset
29246 // Return the offset to the next directory:
29247 return dataView.getUint32(dirEndOffset, littleEndian);
29250 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
29252 var tag = dataView.getUint16(offset, littleEndian);
29254 this.exif[tag] = this.getExifValue(
29258 dataView.getUint16(offset + 2, littleEndian), // tag type
29259 dataView.getUint32(offset + 4, littleEndian), // tag length
29264 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
29266 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
29275 Roo.log('Invalid Exif data: Invalid tag type.');
29279 tagSize = tagType.size * length;
29280 // Determine if the value is contained in the dataOffset bytes,
29281 // or if the value at the dataOffset is a pointer to the actual data:
29282 dataOffset = tagSize > 4 ?
29283 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
29284 if (dataOffset + tagSize > dataView.byteLength) {
29285 Roo.log('Invalid Exif data: Invalid data offset.');
29288 if (length === 1) {
29289 return tagType.getValue(dataView, dataOffset, littleEndian);
29292 for (i = 0; i < length; i += 1) {
29293 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
29296 if (tagType.ascii) {
29298 // Concatenate the chars:
29299 for (i = 0; i < values.length; i += 1) {
29301 // Ignore the terminating NULL byte(s):
29302 if (c === '\u0000') {
29314 Roo.apply(Roo.bootstrap.UploadCropbox, {
29316 'Orientation': 0x0112
29320 1: 0, //'top-left',
29322 3: 180, //'bottom-right',
29323 // 4: 'bottom-left',
29325 6: 90, //'right-top',
29326 // 7: 'right-bottom',
29327 8: 270 //'left-bottom'
29331 // byte, 8-bit unsigned int:
29333 getValue: function (dataView, dataOffset) {
29334 return dataView.getUint8(dataOffset);
29338 // ascii, 8-bit byte:
29340 getValue: function (dataView, dataOffset) {
29341 return String.fromCharCode(dataView.getUint8(dataOffset));
29346 // short, 16 bit int:
29348 getValue: function (dataView, dataOffset, littleEndian) {
29349 return dataView.getUint16(dataOffset, littleEndian);
29353 // long, 32 bit int:
29355 getValue: function (dataView, dataOffset, littleEndian) {
29356 return dataView.getUint32(dataOffset, littleEndian);
29360 // rational = two long values, first is numerator, second is denominator:
29362 getValue: function (dataView, dataOffset, littleEndian) {
29363 return dataView.getUint32(dataOffset, littleEndian) /
29364 dataView.getUint32(dataOffset + 4, littleEndian);
29368 // slong, 32 bit signed int:
29370 getValue: function (dataView, dataOffset, littleEndian) {
29371 return dataView.getInt32(dataOffset, littleEndian);
29375 // srational, two slongs, first is numerator, second is denominator:
29377 getValue: function (dataView, dataOffset, littleEndian) {
29378 return dataView.getInt32(dataOffset, littleEndian) /
29379 dataView.getInt32(dataOffset + 4, littleEndian);
29389 cls : 'btn-group roo-upload-cropbox-rotate-left',
29390 action : 'rotate-left',
29394 cls : 'btn btn-default',
29395 html : '<i class="fa fa-undo"></i>'
29401 cls : 'btn-group roo-upload-cropbox-picture',
29402 action : 'picture',
29406 cls : 'btn btn-default',
29407 html : '<i class="fa fa-picture-o"></i>'
29413 cls : 'btn-group roo-upload-cropbox-rotate-right',
29414 action : 'rotate-right',
29418 cls : 'btn btn-default',
29419 html : '<i class="fa fa-repeat"></i>'
29427 cls : 'btn-group roo-upload-cropbox-rotate-left',
29428 action : 'rotate-left',
29432 cls : 'btn btn-default',
29433 html : '<i class="fa fa-undo"></i>'
29439 cls : 'btn-group roo-upload-cropbox-download',
29440 action : 'download',
29444 cls : 'btn btn-default',
29445 html : '<i class="fa fa-download"></i>'
29451 cls : 'btn-group roo-upload-cropbox-crop',
29456 cls : 'btn btn-default',
29457 html : '<i class="fa fa-crop"></i>'
29463 cls : 'btn-group roo-upload-cropbox-trash',
29468 cls : 'btn btn-default',
29469 html : '<i class="fa fa-trash"></i>'
29475 cls : 'btn-group roo-upload-cropbox-rotate-right',
29476 action : 'rotate-right',
29480 cls : 'btn btn-default',
29481 html : '<i class="fa fa-repeat"></i>'
29489 cls : 'btn-group roo-upload-cropbox-rotate-left',
29490 action : 'rotate-left',
29494 cls : 'btn btn-default',
29495 html : '<i class="fa fa-undo"></i>'
29501 cls : 'btn-group roo-upload-cropbox-rotate-right',
29502 action : 'rotate-right',
29506 cls : 'btn btn-default',
29507 html : '<i class="fa fa-repeat"></i>'
29520 * @class Roo.bootstrap.DocumentManager
29521 * @extends Roo.bootstrap.Component
29522 * Bootstrap DocumentManager class
29523 * @cfg {String} paramName default 'imageUpload'
29524 * @cfg {String} toolTipName default 'filename'
29525 * @cfg {String} method default POST
29526 * @cfg {String} url action url
29527 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29528 * @cfg {Boolean} multiple multiple upload default true
29529 * @cfg {Number} thumbSize default 300
29530 * @cfg {String} fieldLabel
29531 * @cfg {Number} labelWidth default 4
29532 * @cfg {String} labelAlign (left|top) default left
29533 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29534 * @cfg {Number} labellg set the width of label (1-12)
29535 * @cfg {Number} labelmd set the width of label (1-12)
29536 * @cfg {Number} labelsm set the width of label (1-12)
29537 * @cfg {Number} labelxs set the width of label (1-12)
29540 * Create a new DocumentManager
29541 * @param {Object} config The config object
29544 Roo.bootstrap.DocumentManager = function(config){
29545 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29548 this.delegates = [];
29553 * Fire when initial the DocumentManager
29554 * @param {Roo.bootstrap.DocumentManager} this
29559 * inspect selected file
29560 * @param {Roo.bootstrap.DocumentManager} this
29561 * @param {File} file
29566 * Fire when xhr load exception
29567 * @param {Roo.bootstrap.DocumentManager} this
29568 * @param {XMLHttpRequest} xhr
29570 "exception" : true,
29572 * @event afterupload
29573 * Fire when xhr load exception
29574 * @param {Roo.bootstrap.DocumentManager} this
29575 * @param {XMLHttpRequest} xhr
29577 "afterupload" : true,
29580 * prepare the form data
29581 * @param {Roo.bootstrap.DocumentManager} this
29582 * @param {Object} formData
29587 * Fire when remove the file
29588 * @param {Roo.bootstrap.DocumentManager} this
29589 * @param {Object} file
29594 * Fire after refresh the file
29595 * @param {Roo.bootstrap.DocumentManager} this
29600 * Fire after click the image
29601 * @param {Roo.bootstrap.DocumentManager} this
29602 * @param {Object} file
29607 * Fire when upload a image and editable set to true
29608 * @param {Roo.bootstrap.DocumentManager} this
29609 * @param {Object} file
29613 * @event beforeselectfile
29614 * Fire before select file
29615 * @param {Roo.bootstrap.DocumentManager} this
29617 "beforeselectfile" : true,
29620 * Fire before process file
29621 * @param {Roo.bootstrap.DocumentManager} this
29622 * @param {Object} file
29626 * @event previewrendered
29627 * Fire when preview rendered
29628 * @param {Roo.bootstrap.DocumentManager} this
29629 * @param {Object} file
29631 "previewrendered" : true,
29634 "previewResize" : true
29639 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
29648 paramName : 'imageUpload',
29649 toolTipName : 'filename',
29652 labelAlign : 'left',
29662 getAutoCreate : function()
29664 var managerWidget = {
29666 cls : 'roo-document-manager',
29670 cls : 'roo-document-manager-selector',
29675 cls : 'roo-document-manager-uploader',
29679 cls : 'roo-document-manager-upload-btn',
29680 html : '<i class="fa fa-plus"></i>'
29691 cls : 'column col-md-12',
29696 if(this.fieldLabel.length){
29701 cls : 'column col-md-12',
29702 html : this.fieldLabel
29706 cls : 'column col-md-12',
29711 if(this.labelAlign == 'left'){
29716 html : this.fieldLabel
29725 if(this.labelWidth > 12){
29726 content[0].style = "width: " + this.labelWidth + 'px';
29729 if(this.labelWidth < 13 && this.labelmd == 0){
29730 this.labelmd = this.labelWidth;
29733 if(this.labellg > 0){
29734 content[0].cls += ' col-lg-' + this.labellg;
29735 content[1].cls += ' col-lg-' + (12 - this.labellg);
29738 if(this.labelmd > 0){
29739 content[0].cls += ' col-md-' + this.labelmd;
29740 content[1].cls += ' col-md-' + (12 - this.labelmd);
29743 if(this.labelsm > 0){
29744 content[0].cls += ' col-sm-' + this.labelsm;
29745 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29748 if(this.labelxs > 0){
29749 content[0].cls += ' col-xs-' + this.labelxs;
29750 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29758 cls : 'row clearfix',
29766 initEvents : function()
29768 this.managerEl = this.el.select('.roo-document-manager', true).first();
29769 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29771 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29772 this.selectorEl.hide();
29775 this.selectorEl.attr('multiple', 'multiple');
29778 this.selectorEl.on('change', this.onFileSelected, this);
29780 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29781 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29783 this.uploader.on('click', this.onUploaderClick, this);
29785 this.renderProgressDialog();
29789 window.addEventListener("resize", function() { _this.refresh(); } );
29791 this.fireEvent('initial', this);
29794 renderProgressDialog : function()
29798 this.progressDialog = new Roo.bootstrap.Modal({
29799 cls : 'roo-document-manager-progress-dialog',
29800 allow_close : false,
29811 btnclick : function() {
29812 _this.uploadCancel();
29818 this.progressDialog.render(Roo.get(document.body));
29820 this.progress = new Roo.bootstrap.Progress({
29821 cls : 'roo-document-manager-progress',
29826 this.progress.render(this.progressDialog.getChildContainer());
29828 this.progressBar = new Roo.bootstrap.ProgressBar({
29829 cls : 'roo-document-manager-progress-bar',
29832 aria_valuemax : 12,
29836 this.progressBar.render(this.progress.getChildContainer());
29839 onUploaderClick : function(e)
29841 e.preventDefault();
29843 if(this.fireEvent('beforeselectfile', this) != false){
29844 this.selectorEl.dom.click();
29849 onFileSelected : function(e)
29851 e.preventDefault();
29853 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29857 Roo.each(this.selectorEl.dom.files, function(file){
29858 if(this.fireEvent('inspect', this, file) != false){
29859 this.files.push(file);
29869 this.selectorEl.dom.value = '';
29871 if(!this.files || !this.files.length){
29875 if(this.boxes > 0 && this.files.length > this.boxes){
29876 this.files = this.files.slice(0, this.boxes);
29879 this.uploader.show();
29881 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29882 this.uploader.hide();
29891 Roo.each(this.files, function(file){
29893 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29894 var f = this.renderPreview(file);
29899 if(file.type.indexOf('image') != -1){
29900 this.delegates.push(
29902 _this.process(file);
29903 }).createDelegate(this)
29911 _this.process(file);
29912 }).createDelegate(this)
29917 this.files = files;
29919 this.delegates = this.delegates.concat(docs);
29921 if(!this.delegates.length){
29926 this.progressBar.aria_valuemax = this.delegates.length;
29933 arrange : function()
29935 if(!this.delegates.length){
29936 this.progressDialog.hide();
29941 var delegate = this.delegates.shift();
29943 this.progressDialog.show();
29945 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29947 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29952 refresh : function()
29954 this.uploader.show();
29956 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29957 this.uploader.hide();
29960 Roo.isTouch ? this.closable(false) : this.closable(true);
29962 this.fireEvent('refresh', this);
29965 onRemove : function(e, el, o)
29967 e.preventDefault();
29969 this.fireEvent('remove', this, o);
29973 remove : function(o)
29977 Roo.each(this.files, function(file){
29978 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29987 this.files = files;
29994 Roo.each(this.files, function(file){
29999 file.target.remove();
30008 onClick : function(e, el, o)
30010 e.preventDefault();
30012 this.fireEvent('click', this, o);
30016 closable : function(closable)
30018 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
30020 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30032 xhrOnLoad : function(xhr)
30034 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30038 if (xhr.readyState !== 4) {
30040 this.fireEvent('exception', this, xhr);
30044 var response = Roo.decode(xhr.responseText);
30046 if(!response.success){
30048 this.fireEvent('exception', this, xhr);
30052 var file = this.renderPreview(response.data);
30054 this.files.push(file);
30058 this.fireEvent('afterupload', this, xhr);
30062 xhrOnError : function(xhr)
30064 Roo.log('xhr on error');
30066 var response = Roo.decode(xhr.responseText);
30073 process : function(file)
30075 if(this.fireEvent('process', this, file) !== false){
30076 if(this.editable && file.type.indexOf('image') != -1){
30077 this.fireEvent('edit', this, file);
30081 this.uploadStart(file, false);
30088 uploadStart : function(file, crop)
30090 this.xhr = new XMLHttpRequest();
30092 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30097 file.xhr = this.xhr;
30099 this.managerEl.createChild({
30101 cls : 'roo-document-manager-loading',
30105 tooltip : file.name,
30106 cls : 'roo-document-manager-thumb',
30107 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30113 this.xhr.open(this.method, this.url, true);
30116 "Accept": "application/json",
30117 "Cache-Control": "no-cache",
30118 "X-Requested-With": "XMLHttpRequest"
30121 for (var headerName in headers) {
30122 var headerValue = headers[headerName];
30124 this.xhr.setRequestHeader(headerName, headerValue);
30130 this.xhr.onload = function()
30132 _this.xhrOnLoad(_this.xhr);
30135 this.xhr.onerror = function()
30137 _this.xhrOnError(_this.xhr);
30140 var formData = new FormData();
30142 formData.append('returnHTML', 'NO');
30145 formData.append('crop', crop);
30148 formData.append(this.paramName, file, file.name);
30155 if(this.fireEvent('prepare', this, formData, options) != false){
30157 if(options.manually){
30161 this.xhr.send(formData);
30165 this.uploadCancel();
30168 uploadCancel : function()
30174 this.delegates = [];
30176 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30183 renderPreview : function(file)
30185 if(typeof(file.target) != 'undefined' && file.target){
30189 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
30191 var previewEl = this.managerEl.createChild({
30193 cls : 'roo-document-manager-preview',
30197 tooltip : file[this.toolTipName],
30198 cls : 'roo-document-manager-thumb',
30199 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
30204 html : '<i class="fa fa-times-circle"></i>'
30209 var close = previewEl.select('button.close', true).first();
30211 close.on('click', this.onRemove, this, file);
30213 file.target = previewEl;
30215 var image = previewEl.select('img', true).first();
30219 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
30221 image.on('click', this.onClick, this, file);
30223 this.fireEvent('previewrendered', this, file);
30229 onPreviewLoad : function(file, image)
30231 if(typeof(file.target) == 'undefined' || !file.target){
30235 var width = image.dom.naturalWidth || image.dom.width;
30236 var height = image.dom.naturalHeight || image.dom.height;
30238 if(!this.previewResize) {
30242 if(width > height){
30243 file.target.addClass('wide');
30247 file.target.addClass('tall');
30252 uploadFromSource : function(file, crop)
30254 this.xhr = new XMLHttpRequest();
30256 this.managerEl.createChild({
30258 cls : 'roo-document-manager-loading',
30262 tooltip : file.name,
30263 cls : 'roo-document-manager-thumb',
30264 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30270 this.xhr.open(this.method, this.url, true);
30273 "Accept": "application/json",
30274 "Cache-Control": "no-cache",
30275 "X-Requested-With": "XMLHttpRequest"
30278 for (var headerName in headers) {
30279 var headerValue = headers[headerName];
30281 this.xhr.setRequestHeader(headerName, headerValue);
30287 this.xhr.onload = function()
30289 _this.xhrOnLoad(_this.xhr);
30292 this.xhr.onerror = function()
30294 _this.xhrOnError(_this.xhr);
30297 var formData = new FormData();
30299 formData.append('returnHTML', 'NO');
30301 formData.append('crop', crop);
30303 if(typeof(file.filename) != 'undefined'){
30304 formData.append('filename', file.filename);
30307 if(typeof(file.mimetype) != 'undefined'){
30308 formData.append('mimetype', file.mimetype);
30313 if(this.fireEvent('prepare', this, formData) != false){
30314 this.xhr.send(formData);
30324 * @class Roo.bootstrap.DocumentViewer
30325 * @extends Roo.bootstrap.Component
30326 * Bootstrap DocumentViewer class
30327 * @cfg {Boolean} showDownload (true|false) show download button (default true)
30328 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30331 * Create a new DocumentViewer
30332 * @param {Object} config The config object
30335 Roo.bootstrap.DocumentViewer = function(config){
30336 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30341 * Fire after initEvent
30342 * @param {Roo.bootstrap.DocumentViewer} this
30348 * @param {Roo.bootstrap.DocumentViewer} this
30353 * Fire after download button
30354 * @param {Roo.bootstrap.DocumentViewer} this
30359 * Fire after trash button
30360 * @param {Roo.bootstrap.DocumentViewer} this
30367 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
30369 showDownload : true,
30373 getAutoCreate : function()
30377 cls : 'roo-document-viewer',
30381 cls : 'roo-document-viewer-body',
30385 cls : 'roo-document-viewer-thumb',
30389 cls : 'roo-document-viewer-image'
30397 cls : 'roo-document-viewer-footer',
30400 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30404 cls : 'btn-group roo-document-viewer-download',
30408 cls : 'btn btn-default',
30409 html : '<i class="fa fa-download"></i>'
30415 cls : 'btn-group roo-document-viewer-trash',
30419 cls : 'btn btn-default',
30420 html : '<i class="fa fa-trash"></i>'
30433 initEvents : function()
30435 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30436 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30438 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30439 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30441 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30442 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30444 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30445 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30447 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30448 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30450 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30451 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30453 this.bodyEl.on('click', this.onClick, this);
30454 this.downloadBtn.on('click', this.onDownload, this);
30455 this.trashBtn.on('click', this.onTrash, this);
30457 this.downloadBtn.hide();
30458 this.trashBtn.hide();
30460 if(this.showDownload){
30461 this.downloadBtn.show();
30464 if(this.showTrash){
30465 this.trashBtn.show();
30468 if(!this.showDownload && !this.showTrash) {
30469 this.footerEl.hide();
30474 initial : function()
30476 this.fireEvent('initial', this);
30480 onClick : function(e)
30482 e.preventDefault();
30484 this.fireEvent('click', this);
30487 onDownload : function(e)
30489 e.preventDefault();
30491 this.fireEvent('download', this);
30494 onTrash : function(e)
30496 e.preventDefault();
30498 this.fireEvent('trash', this);
30510 * @class Roo.bootstrap.NavProgressBar
30511 * @extends Roo.bootstrap.Component
30512 * Bootstrap NavProgressBar class
30515 * Create a new nav progress bar
30516 * @param {Object} config The config object
30519 Roo.bootstrap.NavProgressBar = function(config){
30520 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30522 this.bullets = this.bullets || [];
30524 // Roo.bootstrap.NavProgressBar.register(this);
30528 * Fires when the active item changes
30529 * @param {Roo.bootstrap.NavProgressBar} this
30530 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30531 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
30538 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
30543 getAutoCreate : function()
30545 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30549 cls : 'roo-navigation-bar-group',
30553 cls : 'roo-navigation-top-bar'
30557 cls : 'roo-navigation-bullets-bar',
30561 cls : 'roo-navigation-bar'
30568 cls : 'roo-navigation-bottom-bar'
30578 initEvents: function()
30583 onRender : function(ct, position)
30585 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30587 if(this.bullets.length){
30588 Roo.each(this.bullets, function(b){
30597 addItem : function(cfg)
30599 var item = new Roo.bootstrap.NavProgressItem(cfg);
30601 item.parentId = this.id;
30602 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30605 var top = new Roo.bootstrap.Element({
30607 cls : 'roo-navigation-bar-text'
30610 var bottom = new Roo.bootstrap.Element({
30612 cls : 'roo-navigation-bar-text'
30615 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30616 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30618 var topText = new Roo.bootstrap.Element({
30620 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30623 var bottomText = new Roo.bootstrap.Element({
30625 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30628 topText.onRender(top.el, null);
30629 bottomText.onRender(bottom.el, null);
30632 item.bottomEl = bottom;
30635 this.barItems.push(item);
30640 getActive : function()
30642 var active = false;
30644 Roo.each(this.barItems, function(v){
30646 if (!v.isActive()) {
30658 setActiveItem : function(item)
30662 Roo.each(this.barItems, function(v){
30663 if (v.rid == item.rid) {
30667 if (v.isActive()) {
30668 v.setActive(false);
30673 item.setActive(true);
30675 this.fireEvent('changed', this, item, prev);
30678 getBarItem: function(rid)
30682 Roo.each(this.barItems, function(e) {
30683 if (e.rid != rid) {
30694 indexOfItem : function(item)
30698 Roo.each(this.barItems, function(v, i){
30700 if (v.rid != item.rid) {
30711 setActiveNext : function()
30713 var i = this.indexOfItem(this.getActive());
30715 if (i > this.barItems.length) {
30719 this.setActiveItem(this.barItems[i+1]);
30722 setActivePrev : function()
30724 var i = this.indexOfItem(this.getActive());
30730 this.setActiveItem(this.barItems[i-1]);
30733 format : function()
30735 if(!this.barItems.length){
30739 var width = 100 / this.barItems.length;
30741 Roo.each(this.barItems, function(i){
30742 i.el.setStyle('width', width + '%');
30743 i.topEl.el.setStyle('width', width + '%');
30744 i.bottomEl.el.setStyle('width', width + '%');
30753 * Nav Progress Item
30758 * @class Roo.bootstrap.NavProgressItem
30759 * @extends Roo.bootstrap.Component
30760 * Bootstrap NavProgressItem class
30761 * @cfg {String} rid the reference id
30762 * @cfg {Boolean} active (true|false) Is item active default false
30763 * @cfg {Boolean} disabled (true|false) Is item active default false
30764 * @cfg {String} html
30765 * @cfg {String} position (top|bottom) text position default bottom
30766 * @cfg {String} icon show icon instead of number
30769 * Create a new NavProgressItem
30770 * @param {Object} config The config object
30772 Roo.bootstrap.NavProgressItem = function(config){
30773 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30778 * The raw click event for the entire grid.
30779 * @param {Roo.bootstrap.NavProgressItem} this
30780 * @param {Roo.EventObject} e
30787 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30793 position : 'bottom',
30796 getAutoCreate : function()
30798 var iconCls = 'roo-navigation-bar-item-icon';
30800 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30804 cls: 'roo-navigation-bar-item',
30814 cfg.cls += ' active';
30817 cfg.cls += ' disabled';
30823 disable : function()
30825 this.setDisabled(true);
30828 enable : function()
30830 this.setDisabled(false);
30833 initEvents: function()
30835 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30837 this.iconEl.on('click', this.onClick, this);
30840 onClick : function(e)
30842 e.preventDefault();
30848 if(this.fireEvent('click', this, e) === false){
30852 this.parent().setActiveItem(this);
30855 isActive: function ()
30857 return this.active;
30860 setActive : function(state)
30862 if(this.active == state){
30866 this.active = state;
30869 this.el.addClass('active');
30873 this.el.removeClass('active');
30878 setDisabled : function(state)
30880 if(this.disabled == state){
30884 this.disabled = state;
30887 this.el.addClass('disabled');
30891 this.el.removeClass('disabled');
30894 tooltipEl : function()
30896 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30909 * @class Roo.bootstrap.FieldLabel
30910 * @extends Roo.bootstrap.Component
30911 * Bootstrap FieldLabel class
30912 * @cfg {String} html contents of the element
30913 * @cfg {String} tag tag of the element default label
30914 * @cfg {String} cls class of the element
30915 * @cfg {String} target label target
30916 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30917 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30918 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30919 * @cfg {String} iconTooltip default "This field is required"
30920 * @cfg {String} indicatorpos (left|right) default left
30923 * Create a new FieldLabel
30924 * @param {Object} config The config object
30927 Roo.bootstrap.FieldLabel = function(config){
30928 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30933 * Fires after the field has been marked as invalid.
30934 * @param {Roo.form.FieldLabel} this
30935 * @param {String} msg The validation message
30940 * Fires after the field has been validated with no errors.
30941 * @param {Roo.form.FieldLabel} this
30947 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30954 invalidClass : 'has-warning',
30955 validClass : 'has-success',
30956 iconTooltip : 'This field is required',
30957 indicatorpos : 'left',
30959 getAutoCreate : function(){
30962 if (!this.allowBlank) {
30968 cls : 'roo-bootstrap-field-label ' + this.cls,
30973 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30974 tooltip : this.iconTooltip
30983 if(this.indicatorpos == 'right'){
30986 cls : 'roo-bootstrap-field-label ' + this.cls,
30995 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30996 tooltip : this.iconTooltip
31005 initEvents: function()
31007 Roo.bootstrap.Element.superclass.initEvents.call(this);
31009 this.indicator = this.indicatorEl();
31011 if(this.indicator){
31012 this.indicator.removeClass('visible');
31013 this.indicator.addClass('invisible');
31016 Roo.bootstrap.FieldLabel.register(this);
31019 indicatorEl : function()
31021 var indicator = this.el.select('i.roo-required-indicator',true).first();
31032 * Mark this field as valid
31034 markValid : function()
31036 if(this.indicator){
31037 this.indicator.removeClass('visible');
31038 this.indicator.addClass('invisible');
31040 if (Roo.bootstrap.version == 3) {
31041 this.el.removeClass(this.invalidClass);
31042 this.el.addClass(this.validClass);
31044 this.el.removeClass('is-invalid');
31045 this.el.addClass('is-valid');
31049 this.fireEvent('valid', this);
31053 * Mark this field as invalid
31054 * @param {String} msg The validation message
31056 markInvalid : function(msg)
31058 if(this.indicator){
31059 this.indicator.removeClass('invisible');
31060 this.indicator.addClass('visible');
31062 if (Roo.bootstrap.version == 3) {
31063 this.el.removeClass(this.validClass);
31064 this.el.addClass(this.invalidClass);
31066 this.el.removeClass('is-valid');
31067 this.el.addClass('is-invalid');
31071 this.fireEvent('invalid', this, msg);
31077 Roo.apply(Roo.bootstrap.FieldLabel, {
31082 * register a FieldLabel Group
31083 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
31085 register : function(label)
31087 if(this.groups.hasOwnProperty(label.target)){
31091 this.groups[label.target] = label;
31095 * fetch a FieldLabel Group based on the target
31096 * @param {string} target
31097 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
31099 get: function(target) {
31100 if (typeof(this.groups[target]) == 'undefined') {
31104 return this.groups[target] ;
31113 * page DateSplitField.
31119 * @class Roo.bootstrap.DateSplitField
31120 * @extends Roo.bootstrap.Component
31121 * Bootstrap DateSplitField class
31122 * @cfg {string} fieldLabel - the label associated
31123 * @cfg {Number} labelWidth set the width of label (0-12)
31124 * @cfg {String} labelAlign (top|left)
31125 * @cfg {Boolean} dayAllowBlank (true|false) default false
31126 * @cfg {Boolean} monthAllowBlank (true|false) default false
31127 * @cfg {Boolean} yearAllowBlank (true|false) default false
31128 * @cfg {string} dayPlaceholder
31129 * @cfg {string} monthPlaceholder
31130 * @cfg {string} yearPlaceholder
31131 * @cfg {string} dayFormat default 'd'
31132 * @cfg {string} monthFormat default 'm'
31133 * @cfg {string} yearFormat default 'Y'
31134 * @cfg {Number} labellg set the width of label (1-12)
31135 * @cfg {Number} labelmd set the width of label (1-12)
31136 * @cfg {Number} labelsm set the width of label (1-12)
31137 * @cfg {Number} labelxs set the width of label (1-12)
31141 * Create a new DateSplitField
31142 * @param {Object} config The config object
31145 Roo.bootstrap.DateSplitField = function(config){
31146 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
31152 * getting the data of years
31153 * @param {Roo.bootstrap.DateSplitField} this
31154 * @param {Object} years
31159 * getting the data of days
31160 * @param {Roo.bootstrap.DateSplitField} this
31161 * @param {Object} days
31166 * Fires after the field has been marked as invalid.
31167 * @param {Roo.form.Field} this
31168 * @param {String} msg The validation message
31173 * Fires after the field has been validated with no errors.
31174 * @param {Roo.form.Field} this
31180 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
31183 labelAlign : 'top',
31185 dayAllowBlank : false,
31186 monthAllowBlank : false,
31187 yearAllowBlank : false,
31188 dayPlaceholder : '',
31189 monthPlaceholder : '',
31190 yearPlaceholder : '',
31194 isFormField : true,
31200 getAutoCreate : function()
31204 cls : 'row roo-date-split-field-group',
31209 cls : 'form-hidden-field roo-date-split-field-group-value',
31215 var labelCls = 'col-md-12';
31216 var contentCls = 'col-md-4';
31218 if(this.fieldLabel){
31222 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
31226 html : this.fieldLabel
31231 if(this.labelAlign == 'left'){
31233 if(this.labelWidth > 12){
31234 label.style = "width: " + this.labelWidth + 'px';
31237 if(this.labelWidth < 13 && this.labelmd == 0){
31238 this.labelmd = this.labelWidth;
31241 if(this.labellg > 0){
31242 labelCls = ' col-lg-' + this.labellg;
31243 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
31246 if(this.labelmd > 0){
31247 labelCls = ' col-md-' + this.labelmd;
31248 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
31251 if(this.labelsm > 0){
31252 labelCls = ' col-sm-' + this.labelsm;
31253 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
31256 if(this.labelxs > 0){
31257 labelCls = ' col-xs-' + this.labelxs;
31258 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
31262 label.cls += ' ' + labelCls;
31264 cfg.cn.push(label);
31267 Roo.each(['day', 'month', 'year'], function(t){
31270 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
31277 inputEl: function ()
31279 return this.el.select('.roo-date-split-field-group-value', true).first();
31282 onRender : function(ct, position)
31286 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31288 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
31290 this.dayField = new Roo.bootstrap.ComboBox({
31291 allowBlank : this.dayAllowBlank,
31292 alwaysQuery : true,
31293 displayField : 'value',
31296 forceSelection : true,
31298 placeholder : this.dayPlaceholder,
31299 selectOnFocus : true,
31300 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31301 triggerAction : 'all',
31303 valueField : 'value',
31304 store : new Roo.data.SimpleStore({
31305 data : (function() {
31307 _this.fireEvent('days', _this, days);
31310 fields : [ 'value' ]
31313 select : function (_self, record, index)
31315 _this.setValue(_this.getValue());
31320 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31322 this.monthField = new Roo.bootstrap.MonthField({
31323 after : '<i class=\"fa fa-calendar\"></i>',
31324 allowBlank : this.monthAllowBlank,
31325 placeholder : this.monthPlaceholder,
31328 render : function (_self)
31330 this.el.select('span.input-group-addon', true).first().on('click', function(e){
31331 e.preventDefault();
31335 select : function (_self, oldvalue, newvalue)
31337 _this.setValue(_this.getValue());
31342 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31344 this.yearField = new Roo.bootstrap.ComboBox({
31345 allowBlank : this.yearAllowBlank,
31346 alwaysQuery : true,
31347 displayField : 'value',
31350 forceSelection : true,
31352 placeholder : this.yearPlaceholder,
31353 selectOnFocus : true,
31354 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31355 triggerAction : 'all',
31357 valueField : 'value',
31358 store : new Roo.data.SimpleStore({
31359 data : (function() {
31361 _this.fireEvent('years', _this, years);
31364 fields : [ 'value' ]
31367 select : function (_self, record, index)
31369 _this.setValue(_this.getValue());
31374 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31377 setValue : function(v, format)
31379 this.inputEl.dom.value = v;
31381 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31383 var d = Date.parseDate(v, f);
31390 this.setDay(d.format(this.dayFormat));
31391 this.setMonth(d.format(this.monthFormat));
31392 this.setYear(d.format(this.yearFormat));
31399 setDay : function(v)
31401 this.dayField.setValue(v);
31402 this.inputEl.dom.value = this.getValue();
31407 setMonth : function(v)
31409 this.monthField.setValue(v, true);
31410 this.inputEl.dom.value = this.getValue();
31415 setYear : function(v)
31417 this.yearField.setValue(v);
31418 this.inputEl.dom.value = this.getValue();
31423 getDay : function()
31425 return this.dayField.getValue();
31428 getMonth : function()
31430 return this.monthField.getValue();
31433 getYear : function()
31435 return this.yearField.getValue();
31438 getValue : function()
31440 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31442 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31452 this.inputEl.dom.value = '';
31457 validate : function()
31459 var d = this.dayField.validate();
31460 var m = this.monthField.validate();
31461 var y = this.yearField.validate();
31466 (!this.dayAllowBlank && !d) ||
31467 (!this.monthAllowBlank && !m) ||
31468 (!this.yearAllowBlank && !y)
31473 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31482 this.markInvalid();
31487 markValid : function()
31490 var label = this.el.select('label', true).first();
31491 var icon = this.el.select('i.fa-star', true).first();
31497 this.fireEvent('valid', this);
31501 * Mark this field as invalid
31502 * @param {String} msg The validation message
31504 markInvalid : function(msg)
31507 var label = this.el.select('label', true).first();
31508 var icon = this.el.select('i.fa-star', true).first();
31510 if(label && !icon){
31511 this.el.select('.roo-date-split-field-label', true).createChild({
31513 cls : 'text-danger fa fa-lg fa-star',
31514 tooltip : 'This field is required',
31515 style : 'margin-right:5px;'
31519 this.fireEvent('invalid', this, msg);
31522 clearInvalid : function()
31524 var label = this.el.select('label', true).first();
31525 var icon = this.el.select('i.fa-star', true).first();
31531 this.fireEvent('valid', this);
31534 getName: function()
31544 * http://masonry.desandro.com
31546 * The idea is to render all the bricks based on vertical width...
31548 * The original code extends 'outlayer' - we might need to use that....
31554 * @class Roo.bootstrap.LayoutMasonry
31555 * @extends Roo.bootstrap.Component
31556 * Bootstrap Layout Masonry class
31559 * Create a new Element
31560 * @param {Object} config The config object
31563 Roo.bootstrap.LayoutMasonry = function(config){
31565 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31569 Roo.bootstrap.LayoutMasonry.register(this);
31575 * Fire after layout the items
31576 * @param {Roo.bootstrap.LayoutMasonry} this
31577 * @param {Roo.EventObject} e
31584 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
31587 * @cfg {Boolean} isLayoutInstant = no animation?
31589 isLayoutInstant : false, // needed?
31592 * @cfg {Number} boxWidth width of the columns
31597 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
31602 * @cfg {Number} padWidth padding below box..
31607 * @cfg {Number} gutter gutter width..
31612 * @cfg {Number} maxCols maximum number of columns
31618 * @cfg {Boolean} isAutoInitial defalut true
31620 isAutoInitial : true,
31625 * @cfg {Boolean} isHorizontal defalut false
31627 isHorizontal : false,
31629 currentSize : null,
31635 bricks: null, //CompositeElement
31639 _isLayoutInited : false,
31641 // isAlternative : false, // only use for vertical layout...
31644 * @cfg {Number} alternativePadWidth padding below box..
31646 alternativePadWidth : 50,
31648 selectedBrick : [],
31650 getAutoCreate : function(){
31652 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31656 cls: 'blog-masonary-wrapper ' + this.cls,
31658 cls : 'mas-boxes masonary'
31665 getChildContainer: function( )
31667 if (this.boxesEl) {
31668 return this.boxesEl;
31671 this.boxesEl = this.el.select('.mas-boxes').first();
31673 return this.boxesEl;
31677 initEvents : function()
31681 if(this.isAutoInitial){
31682 Roo.log('hook children rendered');
31683 this.on('childrenrendered', function() {
31684 Roo.log('children rendered');
31690 initial : function()
31692 this.selectedBrick = [];
31694 this.currentSize = this.el.getBox(true);
31696 Roo.EventManager.onWindowResize(this.resize, this);
31698 if(!this.isAutoInitial){
31706 //this.layout.defer(500,this);
31710 resize : function()
31712 var cs = this.el.getBox(true);
31715 this.currentSize.width == cs.width &&
31716 this.currentSize.x == cs.x &&
31717 this.currentSize.height == cs.height &&
31718 this.currentSize.y == cs.y
31720 Roo.log("no change in with or X or Y");
31724 this.currentSize = cs;
31730 layout : function()
31732 this._resetLayout();
31734 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31736 this.layoutItems( isInstant );
31738 this._isLayoutInited = true;
31740 this.fireEvent('layout', this);
31744 _resetLayout : function()
31746 if(this.isHorizontal){
31747 this.horizontalMeasureColumns();
31751 this.verticalMeasureColumns();
31755 verticalMeasureColumns : function()
31757 this.getContainerWidth();
31759 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31760 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31764 var boxWidth = this.boxWidth + this.padWidth;
31766 if(this.containerWidth < this.boxWidth){
31767 boxWidth = this.containerWidth
31770 var containerWidth = this.containerWidth;
31772 var cols = Math.floor(containerWidth / boxWidth);
31774 this.cols = Math.max( cols, 1 );
31776 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31778 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31780 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31782 this.colWidth = boxWidth + avail - this.padWidth;
31784 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31785 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31788 horizontalMeasureColumns : function()
31790 this.getContainerWidth();
31792 var boxWidth = this.boxWidth;
31794 if(this.containerWidth < boxWidth){
31795 boxWidth = this.containerWidth;
31798 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31800 this.el.setHeight(boxWidth);
31804 getContainerWidth : function()
31806 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31809 layoutItems : function( isInstant )
31811 Roo.log(this.bricks);
31813 var items = Roo.apply([], this.bricks);
31815 if(this.isHorizontal){
31816 this._horizontalLayoutItems( items , isInstant );
31820 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31821 // this._verticalAlternativeLayoutItems( items , isInstant );
31825 this._verticalLayoutItems( items , isInstant );
31829 _verticalLayoutItems : function ( items , isInstant)
31831 if ( !items || !items.length ) {
31836 ['xs', 'xs', 'xs', 'tall'],
31837 ['xs', 'xs', 'tall'],
31838 ['xs', 'xs', 'sm'],
31839 ['xs', 'xs', 'xs'],
31845 ['sm', 'xs', 'xs'],
31849 ['tall', 'xs', 'xs', 'xs'],
31850 ['tall', 'xs', 'xs'],
31862 Roo.each(items, function(item, k){
31864 switch (item.size) {
31865 // these layouts take up a full box,
31876 boxes.push([item]);
31899 var filterPattern = function(box, length)
31907 var pattern = box.slice(0, length);
31911 Roo.each(pattern, function(i){
31912 format.push(i.size);
31915 Roo.each(standard, function(s){
31917 if(String(s) != String(format)){
31926 if(!match && length == 1){
31931 filterPattern(box, length - 1);
31935 queue.push(pattern);
31937 box = box.slice(length, box.length);
31939 filterPattern(box, 4);
31945 Roo.each(boxes, function(box, k){
31951 if(box.length == 1){
31956 filterPattern(box, 4);
31960 this._processVerticalLayoutQueue( queue, isInstant );
31964 // _verticalAlternativeLayoutItems : function( items , isInstant )
31966 // if ( !items || !items.length ) {
31970 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31974 _horizontalLayoutItems : function ( items , isInstant)
31976 if ( !items || !items.length || items.length < 3) {
31982 var eItems = items.slice(0, 3);
31984 items = items.slice(3, items.length);
31987 ['xs', 'xs', 'xs', 'wide'],
31988 ['xs', 'xs', 'wide'],
31989 ['xs', 'xs', 'sm'],
31990 ['xs', 'xs', 'xs'],
31996 ['sm', 'xs', 'xs'],
32000 ['wide', 'xs', 'xs', 'xs'],
32001 ['wide', 'xs', 'xs'],
32014 Roo.each(items, function(item, k){
32016 switch (item.size) {
32027 boxes.push([item]);
32051 var filterPattern = function(box, length)
32059 var pattern = box.slice(0, length);
32063 Roo.each(pattern, function(i){
32064 format.push(i.size);
32067 Roo.each(standard, function(s){
32069 if(String(s) != String(format)){
32078 if(!match && length == 1){
32083 filterPattern(box, length - 1);
32087 queue.push(pattern);
32089 box = box.slice(length, box.length);
32091 filterPattern(box, 4);
32097 Roo.each(boxes, function(box, k){
32103 if(box.length == 1){
32108 filterPattern(box, 4);
32115 var pos = this.el.getBox(true);
32119 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32121 var hit_end = false;
32123 Roo.each(queue, function(box){
32127 Roo.each(box, function(b){
32129 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32139 Roo.each(box, function(b){
32141 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32144 mx = Math.max(mx, b.x);
32148 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
32152 Roo.each(box, function(b){
32154 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32168 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
32171 /** Sets position of item in DOM
32172 * @param {Element} item
32173 * @param {Number} x - horizontal position
32174 * @param {Number} y - vertical position
32175 * @param {Boolean} isInstant - disables transitions
32177 _processVerticalLayoutQueue : function( queue, isInstant )
32179 var pos = this.el.getBox(true);
32184 for (var i = 0; i < this.cols; i++){
32188 Roo.each(queue, function(box, k){
32190 var col = k % this.cols;
32192 Roo.each(box, function(b,kk){
32194 b.el.position('absolute');
32196 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32197 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32199 if(b.size == 'md-left' || b.size == 'md-right'){
32200 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32201 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32204 b.el.setWidth(width);
32205 b.el.setHeight(height);
32207 b.el.select('iframe',true).setSize(width,height);
32211 for (var i = 0; i < this.cols; i++){
32213 if(maxY[i] < maxY[col]){
32218 col = Math.min(col, i);
32222 x = pos.x + col * (this.colWidth + this.padWidth);
32226 var positions = [];
32228 switch (box.length){
32230 positions = this.getVerticalOneBoxColPositions(x, y, box);
32233 positions = this.getVerticalTwoBoxColPositions(x, y, box);
32236 positions = this.getVerticalThreeBoxColPositions(x, y, box);
32239 positions = this.getVerticalFourBoxColPositions(x, y, box);
32245 Roo.each(box, function(b,kk){
32247 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32249 var sz = b.el.getSize();
32251 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
32259 for (var i = 0; i < this.cols; i++){
32260 mY = Math.max(mY, maxY[i]);
32263 this.el.setHeight(mY - pos.y);
32267 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
32269 // var pos = this.el.getBox(true);
32272 // var maxX = pos.right;
32274 // var maxHeight = 0;
32276 // Roo.each(items, function(item, k){
32280 // item.el.position('absolute');
32282 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
32284 // item.el.setWidth(width);
32286 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
32288 // item.el.setHeight(height);
32291 // item.el.setXY([x, y], isInstant ? false : true);
32293 // item.el.setXY([maxX - width, y], isInstant ? false : true);
32296 // y = y + height + this.alternativePadWidth;
32298 // maxHeight = maxHeight + height + this.alternativePadWidth;
32302 // this.el.setHeight(maxHeight);
32306 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32308 var pos = this.el.getBox(true);
32313 var maxX = pos.right;
32315 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32317 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32319 Roo.each(queue, function(box, k){
32321 Roo.each(box, function(b, kk){
32323 b.el.position('absolute');
32325 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32326 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32328 if(b.size == 'md-left' || b.size == 'md-right'){
32329 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32330 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32333 b.el.setWidth(width);
32334 b.el.setHeight(height);
32342 var positions = [];
32344 switch (box.length){
32346 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32349 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32352 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32355 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32361 Roo.each(box, function(b,kk){
32363 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32365 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32373 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32375 Roo.each(eItems, function(b,k){
32377 b.size = (k == 0) ? 'sm' : 'xs';
32378 b.x = (k == 0) ? 2 : 1;
32379 b.y = (k == 0) ? 2 : 1;
32381 b.el.position('absolute');
32383 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32385 b.el.setWidth(width);
32387 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32389 b.el.setHeight(height);
32393 var positions = [];
32396 x : maxX - this.unitWidth * 2 - this.gutter,
32401 x : maxX - this.unitWidth,
32402 y : minY + (this.unitWidth + this.gutter) * 2
32406 x : maxX - this.unitWidth * 3 - this.gutter * 2,
32410 Roo.each(eItems, function(b,k){
32412 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32418 getVerticalOneBoxColPositions : function(x, y, box)
32422 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32424 if(box[0].size == 'md-left'){
32428 if(box[0].size == 'md-right'){
32433 x : x + (this.unitWidth + this.gutter) * rand,
32440 getVerticalTwoBoxColPositions : function(x, y, box)
32444 if(box[0].size == 'xs'){
32448 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32452 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32466 x : x + (this.unitWidth + this.gutter) * 2,
32467 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32474 getVerticalThreeBoxColPositions : function(x, y, box)
32478 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32486 x : x + (this.unitWidth + this.gutter) * 1,
32491 x : x + (this.unitWidth + this.gutter) * 2,
32499 if(box[0].size == 'xs' && box[1].size == 'xs'){
32508 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32512 x : x + (this.unitWidth + this.gutter) * 1,
32526 x : x + (this.unitWidth + this.gutter) * 2,
32531 x : x + (this.unitWidth + this.gutter) * 2,
32532 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32539 getVerticalFourBoxColPositions : function(x, y, box)
32543 if(box[0].size == 'xs'){
32552 y : y + (this.unitHeight + this.gutter) * 1
32557 y : y + (this.unitHeight + this.gutter) * 2
32561 x : x + (this.unitWidth + this.gutter) * 1,
32575 x : x + (this.unitWidth + this.gutter) * 2,
32580 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32581 y : y + (this.unitHeight + this.gutter) * 1
32585 x : x + (this.unitWidth + this.gutter) * 2,
32586 y : y + (this.unitWidth + this.gutter) * 2
32593 getHorizontalOneBoxColPositions : function(maxX, minY, box)
32597 if(box[0].size == 'md-left'){
32599 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32606 if(box[0].size == 'md-right'){
32608 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32609 y : minY + (this.unitWidth + this.gutter) * 1
32615 var rand = Math.floor(Math.random() * (4 - box[0].y));
32618 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32619 y : minY + (this.unitWidth + this.gutter) * rand
32626 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32630 if(box[0].size == 'xs'){
32633 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32638 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32639 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32647 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32652 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32653 y : minY + (this.unitWidth + this.gutter) * 2
32660 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32664 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32667 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32672 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32673 y : minY + (this.unitWidth + this.gutter) * 1
32677 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32678 y : minY + (this.unitWidth + this.gutter) * 2
32685 if(box[0].size == 'xs' && box[1].size == 'xs'){
32688 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32693 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32698 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32699 y : minY + (this.unitWidth + this.gutter) * 1
32707 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32712 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32713 y : minY + (this.unitWidth + this.gutter) * 2
32717 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32718 y : minY + (this.unitWidth + this.gutter) * 2
32725 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32729 if(box[0].size == 'xs'){
32732 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32737 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32742 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),
32747 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32748 y : minY + (this.unitWidth + this.gutter) * 1
32756 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32761 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32762 y : minY + (this.unitWidth + this.gutter) * 2
32766 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32767 y : minY + (this.unitWidth + this.gutter) * 2
32771 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),
32772 y : minY + (this.unitWidth + this.gutter) * 2
32780 * remove a Masonry Brick
32781 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32783 removeBrick : function(brick_id)
32789 for (var i = 0; i<this.bricks.length; i++) {
32790 if (this.bricks[i].id == brick_id) {
32791 this.bricks.splice(i,1);
32792 this.el.dom.removeChild(Roo.get(brick_id).dom);
32799 * adds a Masonry Brick
32800 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32802 addBrick : function(cfg)
32804 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32805 //this.register(cn);
32806 cn.parentId = this.id;
32807 cn.render(this.el);
32812 * register a Masonry Brick
32813 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32816 register : function(brick)
32818 this.bricks.push(brick);
32819 brick.masonryId = this.id;
32823 * clear all the Masonry Brick
32825 clearAll : function()
32828 //this.getChildContainer().dom.innerHTML = "";
32829 this.el.dom.innerHTML = '';
32832 getSelected : function()
32834 if (!this.selectedBrick) {
32838 return this.selectedBrick;
32842 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32846 * register a Masonry Layout
32847 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32850 register : function(layout)
32852 this.groups[layout.id] = layout;
32855 * fetch a Masonry Layout based on the masonry layout ID
32856 * @param {string} the masonry layout to add
32857 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32860 get: function(layout_id) {
32861 if (typeof(this.groups[layout_id]) == 'undefined') {
32864 return this.groups[layout_id] ;
32876 * http://masonry.desandro.com
32878 * The idea is to render all the bricks based on vertical width...
32880 * The original code extends 'outlayer' - we might need to use that....
32886 * @class Roo.bootstrap.LayoutMasonryAuto
32887 * @extends Roo.bootstrap.Component
32888 * Bootstrap Layout Masonry class
32891 * Create a new Element
32892 * @param {Object} config The config object
32895 Roo.bootstrap.LayoutMasonryAuto = function(config){
32896 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32899 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32902 * @cfg {Boolean} isFitWidth - resize the width..
32904 isFitWidth : false, // options..
32906 * @cfg {Boolean} isOriginLeft = left align?
32908 isOriginLeft : true,
32910 * @cfg {Boolean} isOriginTop = top align?
32912 isOriginTop : false,
32914 * @cfg {Boolean} isLayoutInstant = no animation?
32916 isLayoutInstant : false, // needed?
32918 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32920 isResizingContainer : true,
32922 * @cfg {Number} columnWidth width of the columns
32928 * @cfg {Number} maxCols maximum number of columns
32933 * @cfg {Number} padHeight padding below box..
32939 * @cfg {Boolean} isAutoInitial defalut true
32942 isAutoInitial : true,
32948 initialColumnWidth : 0,
32949 currentSize : null,
32951 colYs : null, // array.
32958 bricks: null, //CompositeElement
32959 cols : 0, // array?
32960 // element : null, // wrapped now this.el
32961 _isLayoutInited : null,
32964 getAutoCreate : function(){
32968 cls: 'blog-masonary-wrapper ' + this.cls,
32970 cls : 'mas-boxes masonary'
32977 getChildContainer: function( )
32979 if (this.boxesEl) {
32980 return this.boxesEl;
32983 this.boxesEl = this.el.select('.mas-boxes').first();
32985 return this.boxesEl;
32989 initEvents : function()
32993 if(this.isAutoInitial){
32994 Roo.log('hook children rendered');
32995 this.on('childrenrendered', function() {
32996 Roo.log('children rendered');
33003 initial : function()
33005 this.reloadItems();
33007 this.currentSize = this.el.getBox(true);
33009 /// was window resize... - let's see if this works..
33010 Roo.EventManager.onWindowResize(this.resize, this);
33012 if(!this.isAutoInitial){
33017 this.layout.defer(500,this);
33020 reloadItems: function()
33022 this.bricks = this.el.select('.masonry-brick', true);
33024 this.bricks.each(function(b) {
33025 //Roo.log(b.getSize());
33026 if (!b.attr('originalwidth')) {
33027 b.attr('originalwidth', b.getSize().width);
33032 Roo.log(this.bricks.elements.length);
33035 resize : function()
33038 var cs = this.el.getBox(true);
33040 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
33041 Roo.log("no change in with or X");
33044 this.currentSize = cs;
33048 layout : function()
33051 this._resetLayout();
33052 //this._manageStamps();
33054 // don't animate first layout
33055 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33056 this.layoutItems( isInstant );
33058 // flag for initalized
33059 this._isLayoutInited = true;
33062 layoutItems : function( isInstant )
33064 //var items = this._getItemsForLayout( this.items );
33065 // original code supports filtering layout items.. we just ignore it..
33067 this._layoutItems( this.bricks , isInstant );
33069 this._postLayout();
33071 _layoutItems : function ( items , isInstant)
33073 //this.fireEvent( 'layout', this, items );
33076 if ( !items || !items.elements.length ) {
33077 // no items, emit event with empty array
33082 items.each(function(item) {
33083 Roo.log("layout item");
33085 // get x/y object from method
33086 var position = this._getItemLayoutPosition( item );
33088 position.item = item;
33089 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
33090 queue.push( position );
33093 this._processLayoutQueue( queue );
33095 /** Sets position of item in DOM
33096 * @param {Element} item
33097 * @param {Number} x - horizontal position
33098 * @param {Number} y - vertical position
33099 * @param {Boolean} isInstant - disables transitions
33101 _processLayoutQueue : function( queue )
33103 for ( var i=0, len = queue.length; i < len; i++ ) {
33104 var obj = queue[i];
33105 obj.item.position('absolute');
33106 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
33112 * Any logic you want to do after each layout,
33113 * i.e. size the container
33115 _postLayout : function()
33117 this.resizeContainer();
33120 resizeContainer : function()
33122 if ( !this.isResizingContainer ) {
33125 var size = this._getContainerSize();
33127 this.el.setSize(size.width,size.height);
33128 this.boxesEl.setSize(size.width,size.height);
33134 _resetLayout : function()
33136 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
33137 this.colWidth = this.el.getWidth();
33138 //this.gutter = this.el.getWidth();
33140 this.measureColumns();
33146 this.colYs.push( 0 );
33152 measureColumns : function()
33154 this.getContainerWidth();
33155 // if columnWidth is 0, default to outerWidth of first item
33156 if ( !this.columnWidth ) {
33157 var firstItem = this.bricks.first();
33158 Roo.log(firstItem);
33159 this.columnWidth = this.containerWidth;
33160 if (firstItem && firstItem.attr('originalwidth') ) {
33161 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
33163 // columnWidth fall back to item of first element
33164 Roo.log("set column width?");
33165 this.initialColumnWidth = this.columnWidth ;
33167 // if first elem has no width, default to size of container
33172 if (this.initialColumnWidth) {
33173 this.columnWidth = this.initialColumnWidth;
33178 // column width is fixed at the top - however if container width get's smaller we should
33181 // this bit calcs how man columns..
33183 var columnWidth = this.columnWidth += this.gutter;
33185 // calculate columns
33186 var containerWidth = this.containerWidth + this.gutter;
33188 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
33189 // fix rounding errors, typically with gutters
33190 var excess = columnWidth - containerWidth % columnWidth;
33193 // if overshoot is less than a pixel, round up, otherwise floor it
33194 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
33195 cols = Math[ mathMethod ]( cols );
33196 this.cols = Math.max( cols, 1 );
33197 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33199 // padding positioning..
33200 var totalColWidth = this.cols * this.columnWidth;
33201 var padavail = this.containerWidth - totalColWidth;
33202 // so for 2 columns - we need 3 'pads'
33204 var padNeeded = (1+this.cols) * this.padWidth;
33206 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
33208 this.columnWidth += padExtra
33209 //this.padWidth = Math.floor(padavail / ( this.cols));
33211 // adjust colum width so that padding is fixed??
33213 // we have 3 columns ... total = width * 3
33214 // we have X left over... that should be used by
33216 //if (this.expandC) {
33224 getContainerWidth : function()
33226 /* // container is parent if fit width
33227 var container = this.isFitWidth ? this.element.parentNode : this.element;
33228 // check that this.size and size are there
33229 // IE8 triggers resize on body size change, so they might not be
33231 var size = getSize( container ); //FIXME
33232 this.containerWidth = size && size.innerWidth; //FIXME
33235 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33239 _getItemLayoutPosition : function( item ) // what is item?
33241 // we resize the item to our columnWidth..
33243 item.setWidth(this.columnWidth);
33244 item.autoBoxAdjust = false;
33246 var sz = item.getSize();
33248 // how many columns does this brick span
33249 var remainder = this.containerWidth % this.columnWidth;
33251 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
33252 // round if off by 1 pixel, otherwise use ceil
33253 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
33254 colSpan = Math.min( colSpan, this.cols );
33256 // normally this should be '1' as we dont' currently allow multi width columns..
33258 var colGroup = this._getColGroup( colSpan );
33259 // get the minimum Y value from the columns
33260 var minimumY = Math.min.apply( Math, colGroup );
33261 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
33263 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
33265 // position the brick
33267 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
33268 y: this.currentSize.y + minimumY + this.padHeight
33272 // apply setHeight to necessary columns
33273 var setHeight = minimumY + sz.height + this.padHeight;
33274 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
33276 var setSpan = this.cols + 1 - colGroup.length;
33277 for ( var i = 0; i < setSpan; i++ ) {
33278 this.colYs[ shortColIndex + i ] = setHeight ;
33285 * @param {Number} colSpan - number of columns the element spans
33286 * @returns {Array} colGroup
33288 _getColGroup : function( colSpan )
33290 if ( colSpan < 2 ) {
33291 // if brick spans only one column, use all the column Ys
33296 // how many different places could this brick fit horizontally
33297 var groupCount = this.cols + 1 - colSpan;
33298 // for each group potential horizontal position
33299 for ( var i = 0; i < groupCount; i++ ) {
33300 // make an array of colY values for that one group
33301 var groupColYs = this.colYs.slice( i, i + colSpan );
33302 // and get the max value of the array
33303 colGroup[i] = Math.max.apply( Math, groupColYs );
33308 _manageStamp : function( stamp )
33310 var stampSize = stamp.getSize();
33311 var offset = stamp.getBox();
33312 // get the columns that this stamp affects
33313 var firstX = this.isOriginLeft ? offset.x : offset.right;
33314 var lastX = firstX + stampSize.width;
33315 var firstCol = Math.floor( firstX / this.columnWidth );
33316 firstCol = Math.max( 0, firstCol );
33318 var lastCol = Math.floor( lastX / this.columnWidth );
33319 // lastCol should not go over if multiple of columnWidth #425
33320 lastCol -= lastX % this.columnWidth ? 0 : 1;
33321 lastCol = Math.min( this.cols - 1, lastCol );
33323 // set colYs to bottom of the stamp
33324 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33327 for ( var i = firstCol; i <= lastCol; i++ ) {
33328 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33333 _getContainerSize : function()
33335 this.maxY = Math.max.apply( Math, this.colYs );
33340 if ( this.isFitWidth ) {
33341 size.width = this._getContainerFitWidth();
33347 _getContainerFitWidth : function()
33349 var unusedCols = 0;
33350 // count unused columns
33353 if ( this.colYs[i] !== 0 ) {
33358 // fit container to columns that have been used
33359 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33362 needsResizeLayout : function()
33364 var previousWidth = this.containerWidth;
33365 this.getContainerWidth();
33366 return previousWidth !== this.containerWidth;
33381 * @class Roo.bootstrap.MasonryBrick
33382 * @extends Roo.bootstrap.Component
33383 * Bootstrap MasonryBrick class
33386 * Create a new MasonryBrick
33387 * @param {Object} config The config object
33390 Roo.bootstrap.MasonryBrick = function(config){
33392 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33394 Roo.bootstrap.MasonryBrick.register(this);
33400 * When a MasonryBrick is clcik
33401 * @param {Roo.bootstrap.MasonryBrick} this
33402 * @param {Roo.EventObject} e
33408 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
33411 * @cfg {String} title
33415 * @cfg {String} html
33419 * @cfg {String} bgimage
33423 * @cfg {String} videourl
33427 * @cfg {String} cls
33431 * @cfg {String} href
33435 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33440 * @cfg {String} placetitle (center|bottom)
33445 * @cfg {Boolean} isFitContainer defalut true
33447 isFitContainer : true,
33450 * @cfg {Boolean} preventDefault defalut false
33452 preventDefault : false,
33455 * @cfg {Boolean} inverse defalut false
33457 maskInverse : false,
33459 getAutoCreate : function()
33461 if(!this.isFitContainer){
33462 return this.getSplitAutoCreate();
33465 var cls = 'masonry-brick masonry-brick-full';
33467 if(this.href.length){
33468 cls += ' masonry-brick-link';
33471 if(this.bgimage.length){
33472 cls += ' masonry-brick-image';
33475 if(this.maskInverse){
33476 cls += ' mask-inverse';
33479 if(!this.html.length && !this.maskInverse && !this.videourl.length){
33480 cls += ' enable-mask';
33484 cls += ' masonry-' + this.size + '-brick';
33487 if(this.placetitle.length){
33489 switch (this.placetitle) {
33491 cls += ' masonry-center-title';
33494 cls += ' masonry-bottom-title';
33501 if(!this.html.length && !this.bgimage.length){
33502 cls += ' masonry-center-title';
33505 if(!this.html.length && this.bgimage.length){
33506 cls += ' masonry-bottom-title';
33511 cls += ' ' + this.cls;
33515 tag: (this.href.length) ? 'a' : 'div',
33520 cls: 'masonry-brick-mask'
33524 cls: 'masonry-brick-paragraph',
33530 if(this.href.length){
33531 cfg.href = this.href;
33534 var cn = cfg.cn[1].cn;
33536 if(this.title.length){
33539 cls: 'masonry-brick-title',
33544 if(this.html.length){
33547 cls: 'masonry-brick-text',
33552 if (!this.title.length && !this.html.length) {
33553 cfg.cn[1].cls += ' hide';
33556 if(this.bgimage.length){
33559 cls: 'masonry-brick-image-view',
33564 if(this.videourl.length){
33565 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33566 // youtube support only?
33569 cls: 'masonry-brick-image-view',
33572 allowfullscreen : true
33580 getSplitAutoCreate : function()
33582 var cls = 'masonry-brick masonry-brick-split';
33584 if(this.href.length){
33585 cls += ' masonry-brick-link';
33588 if(this.bgimage.length){
33589 cls += ' masonry-brick-image';
33593 cls += ' masonry-' + this.size + '-brick';
33596 switch (this.placetitle) {
33598 cls += ' masonry-center-title';
33601 cls += ' masonry-bottom-title';
33604 if(!this.bgimage.length){
33605 cls += ' masonry-center-title';
33608 if(this.bgimage.length){
33609 cls += ' masonry-bottom-title';
33615 cls += ' ' + this.cls;
33619 tag: (this.href.length) ? 'a' : 'div',
33624 cls: 'masonry-brick-split-head',
33628 cls: 'masonry-brick-paragraph',
33635 cls: 'masonry-brick-split-body',
33641 if(this.href.length){
33642 cfg.href = this.href;
33645 if(this.title.length){
33646 cfg.cn[0].cn[0].cn.push({
33648 cls: 'masonry-brick-title',
33653 if(this.html.length){
33654 cfg.cn[1].cn.push({
33656 cls: 'masonry-brick-text',
33661 if(this.bgimage.length){
33662 cfg.cn[0].cn.push({
33664 cls: 'masonry-brick-image-view',
33669 if(this.videourl.length){
33670 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33671 // youtube support only?
33672 cfg.cn[0].cn.cn.push({
33674 cls: 'masonry-brick-image-view',
33677 allowfullscreen : true
33684 initEvents: function()
33686 switch (this.size) {
33719 this.el.on('touchstart', this.onTouchStart, this);
33720 this.el.on('touchmove', this.onTouchMove, this);
33721 this.el.on('touchend', this.onTouchEnd, this);
33722 this.el.on('contextmenu', this.onContextMenu, this);
33724 this.el.on('mouseenter' ,this.enter, this);
33725 this.el.on('mouseleave', this.leave, this);
33726 this.el.on('click', this.onClick, this);
33729 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33730 this.parent().bricks.push(this);
33735 onClick: function(e, el)
33737 var time = this.endTimer - this.startTimer;
33738 // Roo.log(e.preventDefault());
33741 e.preventDefault();
33746 if(!this.preventDefault){
33750 e.preventDefault();
33752 if (this.activeClass != '') {
33753 this.selectBrick();
33756 this.fireEvent('click', this, e);
33759 enter: function(e, el)
33761 e.preventDefault();
33763 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33767 if(this.bgimage.length && this.html.length){
33768 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33772 leave: function(e, el)
33774 e.preventDefault();
33776 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33780 if(this.bgimage.length && this.html.length){
33781 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33785 onTouchStart: function(e, el)
33787 // e.preventDefault();
33789 this.touchmoved = false;
33791 if(!this.isFitContainer){
33795 if(!this.bgimage.length || !this.html.length){
33799 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33801 this.timer = new Date().getTime();
33805 onTouchMove: function(e, el)
33807 this.touchmoved = true;
33810 onContextMenu : function(e,el)
33812 e.preventDefault();
33813 e.stopPropagation();
33817 onTouchEnd: function(e, el)
33819 // e.preventDefault();
33821 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33828 if(!this.bgimage.length || !this.html.length){
33830 if(this.href.length){
33831 window.location.href = this.href;
33837 if(!this.isFitContainer){
33841 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33843 window.location.href = this.href;
33846 //selection on single brick only
33847 selectBrick : function() {
33849 if (!this.parentId) {
33853 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33854 var index = m.selectedBrick.indexOf(this.id);
33857 m.selectedBrick.splice(index,1);
33858 this.el.removeClass(this.activeClass);
33862 for(var i = 0; i < m.selectedBrick.length; i++) {
33863 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33864 b.el.removeClass(b.activeClass);
33867 m.selectedBrick = [];
33869 m.selectedBrick.push(this.id);
33870 this.el.addClass(this.activeClass);
33874 isSelected : function(){
33875 return this.el.hasClass(this.activeClass);
33880 Roo.apply(Roo.bootstrap.MasonryBrick, {
33883 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33885 * register a Masonry Brick
33886 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33889 register : function(brick)
33891 //this.groups[brick.id] = brick;
33892 this.groups.add(brick.id, brick);
33895 * fetch a masonry brick based on the masonry brick ID
33896 * @param {string} the masonry brick to add
33897 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33900 get: function(brick_id)
33902 // if (typeof(this.groups[brick_id]) == 'undefined') {
33905 // return this.groups[brick_id] ;
33907 if(this.groups.key(brick_id)) {
33908 return this.groups.key(brick_id);
33926 * @class Roo.bootstrap.Brick
33927 * @extends Roo.bootstrap.Component
33928 * Bootstrap Brick class
33931 * Create a new Brick
33932 * @param {Object} config The config object
33935 Roo.bootstrap.Brick = function(config){
33936 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33942 * When a Brick is click
33943 * @param {Roo.bootstrap.Brick} this
33944 * @param {Roo.EventObject} e
33950 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33953 * @cfg {String} title
33957 * @cfg {String} html
33961 * @cfg {String} bgimage
33965 * @cfg {String} cls
33969 * @cfg {String} href
33973 * @cfg {String} video
33977 * @cfg {Boolean} square
33981 getAutoCreate : function()
33983 var cls = 'roo-brick';
33985 if(this.href.length){
33986 cls += ' roo-brick-link';
33989 if(this.bgimage.length){
33990 cls += ' roo-brick-image';
33993 if(!this.html.length && !this.bgimage.length){
33994 cls += ' roo-brick-center-title';
33997 if(!this.html.length && this.bgimage.length){
33998 cls += ' roo-brick-bottom-title';
34002 cls += ' ' + this.cls;
34006 tag: (this.href.length) ? 'a' : 'div',
34011 cls: 'roo-brick-paragraph',
34017 if(this.href.length){
34018 cfg.href = this.href;
34021 var cn = cfg.cn[0].cn;
34023 if(this.title.length){
34026 cls: 'roo-brick-title',
34031 if(this.html.length){
34034 cls: 'roo-brick-text',
34041 if(this.bgimage.length){
34044 cls: 'roo-brick-image-view',
34052 initEvents: function()
34054 if(this.title.length || this.html.length){
34055 this.el.on('mouseenter' ,this.enter, this);
34056 this.el.on('mouseleave', this.leave, this);
34059 Roo.EventManager.onWindowResize(this.resize, this);
34061 if(this.bgimage.length){
34062 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
34063 this.imageEl.on('load', this.onImageLoad, this);
34070 onImageLoad : function()
34075 resize : function()
34077 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
34079 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
34081 if(this.bgimage.length){
34082 var image = this.el.select('.roo-brick-image-view', true).first();
34084 image.setWidth(paragraph.getWidth());
34087 image.setHeight(paragraph.getWidth());
34090 this.el.setHeight(image.getHeight());
34091 paragraph.setHeight(image.getHeight());
34097 enter: function(e, el)
34099 e.preventDefault();
34101 if(this.bgimage.length){
34102 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
34103 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
34107 leave: function(e, el)
34109 e.preventDefault();
34111 if(this.bgimage.length){
34112 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
34113 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
34128 * @class Roo.bootstrap.NumberField
34129 * @extends Roo.bootstrap.Input
34130 * Bootstrap NumberField class
34136 * Create a new NumberField
34137 * @param {Object} config The config object
34140 Roo.bootstrap.NumberField = function(config){
34141 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
34144 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
34147 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
34149 allowDecimals : true,
34151 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
34153 decimalSeparator : ".",
34155 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
34157 decimalPrecision : 2,
34159 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
34161 allowNegative : true,
34164 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
34168 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
34170 minValue : Number.NEGATIVE_INFINITY,
34172 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
34174 maxValue : Number.MAX_VALUE,
34176 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
34178 minText : "The minimum value for this field is {0}",
34180 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
34182 maxText : "The maximum value for this field is {0}",
34184 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
34185 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
34187 nanText : "{0} is not a valid number",
34189 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
34191 thousandsDelimiter : false,
34193 * @cfg {String} valueAlign alignment of value
34195 valueAlign : "left",
34197 getAutoCreate : function()
34199 var hiddenInput = {
34203 cls: 'hidden-number-input'
34207 hiddenInput.name = this.name;
34212 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
34214 this.name = hiddenInput.name;
34216 if(cfg.cn.length > 0) {
34217 cfg.cn.push(hiddenInput);
34224 initEvents : function()
34226 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
34228 var allowed = "0123456789";
34230 if(this.allowDecimals){
34231 allowed += this.decimalSeparator;
34234 if(this.allowNegative){
34238 if(this.thousandsDelimiter) {
34242 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
34244 var keyPress = function(e){
34246 var k = e.getKey();
34248 var c = e.getCharCode();
34251 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
34252 allowed.indexOf(String.fromCharCode(c)) === -1
34258 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
34262 if(allowed.indexOf(String.fromCharCode(c)) === -1){
34267 this.el.on("keypress", keyPress, this);
34270 validateValue : function(value)
34273 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
34277 var num = this.parseValue(value);
34280 this.markInvalid(String.format(this.nanText, value));
34284 if(num < this.minValue){
34285 this.markInvalid(String.format(this.minText, this.minValue));
34289 if(num > this.maxValue){
34290 this.markInvalid(String.format(this.maxText, this.maxValue));
34297 getValue : function()
34299 var v = this.hiddenEl().getValue();
34301 return this.fixPrecision(this.parseValue(v));
34304 parseValue : function(value)
34306 if(this.thousandsDelimiter) {
34308 r = new RegExp(",", "g");
34309 value = value.replace(r, "");
34312 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34313 return isNaN(value) ? '' : value;
34316 fixPrecision : function(value)
34318 if(this.thousandsDelimiter) {
34320 r = new RegExp(",", "g");
34321 value = value.replace(r, "");
34324 var nan = isNaN(value);
34326 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34327 return nan ? '' : value;
34329 return parseFloat(value).toFixed(this.decimalPrecision);
34332 setValue : function(v)
34334 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34340 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34342 this.inputEl().dom.value = (v == '') ? '' :
34343 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34345 if(!this.allowZero && v === '0') {
34346 this.hiddenEl().dom.value = '';
34347 this.inputEl().dom.value = '';
34354 decimalPrecisionFcn : function(v)
34356 return Math.floor(v);
34359 beforeBlur : function()
34361 var v = this.parseValue(this.getRawValue());
34363 if(v || v === 0 || v === ''){
34368 hiddenEl : function()
34370 return this.el.select('input.hidden-number-input',true).first();
34382 * @class Roo.bootstrap.DocumentSlider
34383 * @extends Roo.bootstrap.Component
34384 * Bootstrap DocumentSlider class
34387 * Create a new DocumentViewer
34388 * @param {Object} config The config object
34391 Roo.bootstrap.DocumentSlider = function(config){
34392 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34399 * Fire after initEvent
34400 * @param {Roo.bootstrap.DocumentSlider} this
34405 * Fire after update
34406 * @param {Roo.bootstrap.DocumentSlider} this
34412 * @param {Roo.bootstrap.DocumentSlider} this
34418 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
34424 getAutoCreate : function()
34428 cls : 'roo-document-slider',
34432 cls : 'roo-document-slider-header',
34436 cls : 'roo-document-slider-header-title'
34442 cls : 'roo-document-slider-body',
34446 cls : 'roo-document-slider-prev',
34450 cls : 'fa fa-chevron-left'
34456 cls : 'roo-document-slider-thumb',
34460 cls : 'roo-document-slider-image'
34466 cls : 'roo-document-slider-next',
34470 cls : 'fa fa-chevron-right'
34482 initEvents : function()
34484 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34485 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34487 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34488 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34490 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34491 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34493 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34494 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34496 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34497 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34499 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34500 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34502 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34503 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34505 this.thumbEl.on('click', this.onClick, this);
34507 this.prevIndicator.on('click', this.prev, this);
34509 this.nextIndicator.on('click', this.next, this);
34513 initial : function()
34515 if(this.files.length){
34516 this.indicator = 1;
34520 this.fireEvent('initial', this);
34523 update : function()
34525 this.imageEl.attr('src', this.files[this.indicator - 1]);
34527 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34529 this.prevIndicator.show();
34531 if(this.indicator == 1){
34532 this.prevIndicator.hide();
34535 this.nextIndicator.show();
34537 if(this.indicator == this.files.length){
34538 this.nextIndicator.hide();
34541 this.thumbEl.scrollTo('top');
34543 this.fireEvent('update', this);
34546 onClick : function(e)
34548 e.preventDefault();
34550 this.fireEvent('click', this);
34555 e.preventDefault();
34557 this.indicator = Math.max(1, this.indicator - 1);
34564 e.preventDefault();
34566 this.indicator = Math.min(this.files.length, this.indicator + 1);
34580 * @class Roo.bootstrap.RadioSet
34581 * @extends Roo.bootstrap.Input
34582 * Bootstrap RadioSet class
34583 * @cfg {String} indicatorpos (left|right) default left
34584 * @cfg {Boolean} inline (true|false) inline the element (default true)
34585 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34587 * Create a new RadioSet
34588 * @param {Object} config The config object
34591 Roo.bootstrap.RadioSet = function(config){
34593 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34597 Roo.bootstrap.RadioSet.register(this);
34602 * Fires when the element is checked or unchecked.
34603 * @param {Roo.bootstrap.RadioSet} this This radio
34604 * @param {Roo.bootstrap.Radio} item The checked item
34609 * Fires when the element is click.
34610 * @param {Roo.bootstrap.RadioSet} this This radio set
34611 * @param {Roo.bootstrap.Radio} item The checked item
34612 * @param {Roo.EventObject} e The event object
34619 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
34627 indicatorpos : 'left',
34629 getAutoCreate : function()
34633 cls : 'roo-radio-set-label',
34637 html : this.fieldLabel
34641 if (Roo.bootstrap.version == 3) {
34644 if(this.indicatorpos == 'left'){
34647 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34648 tooltip : 'This field is required'
34653 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34654 tooltip : 'This field is required'
34660 cls : 'roo-radio-set-items'
34663 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34665 if (align === 'left' && this.fieldLabel.length) {
34668 cls : "roo-radio-set-right",
34674 if(this.labelWidth > 12){
34675 label.style = "width: " + this.labelWidth + 'px';
34678 if(this.labelWidth < 13 && this.labelmd == 0){
34679 this.labelmd = this.labelWidth;
34682 if(this.labellg > 0){
34683 label.cls += ' col-lg-' + this.labellg;
34684 items.cls += ' col-lg-' + (12 - this.labellg);
34687 if(this.labelmd > 0){
34688 label.cls += ' col-md-' + this.labelmd;
34689 items.cls += ' col-md-' + (12 - this.labelmd);
34692 if(this.labelsm > 0){
34693 label.cls += ' col-sm-' + this.labelsm;
34694 items.cls += ' col-sm-' + (12 - this.labelsm);
34697 if(this.labelxs > 0){
34698 label.cls += ' col-xs-' + this.labelxs;
34699 items.cls += ' col-xs-' + (12 - this.labelxs);
34705 cls : 'roo-radio-set',
34709 cls : 'roo-radio-set-input',
34712 value : this.value ? this.value : ''
34719 if(this.weight.length){
34720 cfg.cls += ' roo-radio-' + this.weight;
34724 cfg.cls += ' roo-radio-set-inline';
34728 ['xs','sm','md','lg'].map(function(size){
34729 if (settings[size]) {
34730 cfg.cls += ' col-' + size + '-' + settings[size];
34738 initEvents : function()
34740 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34741 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34743 if(!this.fieldLabel.length){
34744 this.labelEl.hide();
34747 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34748 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34750 this.indicator = this.indicatorEl();
34752 if(this.indicator){
34753 this.indicator.addClass('invisible');
34756 this.originalValue = this.getValue();
34760 inputEl: function ()
34762 return this.el.select('.roo-radio-set-input', true).first();
34765 getChildContainer : function()
34767 return this.itemsEl;
34770 register : function(item)
34772 this.radioes.push(item);
34776 validate : function()
34778 if(this.getVisibilityEl().hasClass('hidden')){
34784 Roo.each(this.radioes, function(i){
34793 if(this.allowBlank) {
34797 if(this.disabled || valid){
34802 this.markInvalid();
34807 markValid : function()
34809 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34810 this.indicatorEl().removeClass('visible');
34811 this.indicatorEl().addClass('invisible');
34815 if (Roo.bootstrap.version == 3) {
34816 this.el.removeClass([this.invalidClass, this.validClass]);
34817 this.el.addClass(this.validClass);
34819 this.el.removeClass(['is-invalid','is-valid']);
34820 this.el.addClass(['is-valid']);
34822 this.fireEvent('valid', this);
34825 markInvalid : function(msg)
34827 if(this.allowBlank || this.disabled){
34831 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34832 this.indicatorEl().removeClass('invisible');
34833 this.indicatorEl().addClass('visible');
34835 if (Roo.bootstrap.version == 3) {
34836 this.el.removeClass([this.invalidClass, this.validClass]);
34837 this.el.addClass(this.invalidClass);
34839 this.el.removeClass(['is-invalid','is-valid']);
34840 this.el.addClass(['is-invalid']);
34843 this.fireEvent('invalid', this, msg);
34847 setValue : function(v, suppressEvent)
34849 if(this.value === v){
34856 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34859 Roo.each(this.radioes, function(i){
34861 i.el.removeClass('checked');
34864 Roo.each(this.radioes, function(i){
34866 if(i.value === v || i.value.toString() === v.toString()){
34868 i.el.addClass('checked');
34870 if(suppressEvent !== true){
34871 this.fireEvent('check', this, i);
34882 clearInvalid : function(){
34884 if(!this.el || this.preventMark){
34888 this.el.removeClass([this.invalidClass]);
34890 this.fireEvent('valid', this);
34895 Roo.apply(Roo.bootstrap.RadioSet, {
34899 register : function(set)
34901 this.groups[set.name] = set;
34904 get: function(name)
34906 if (typeof(this.groups[name]) == 'undefined') {
34910 return this.groups[name] ;
34916 * Ext JS Library 1.1.1
34917 * Copyright(c) 2006-2007, Ext JS, LLC.
34919 * Originally Released Under LGPL - original licence link has changed is not relivant.
34922 * <script type="text/javascript">
34927 * @class Roo.bootstrap.SplitBar
34928 * @extends Roo.util.Observable
34929 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34933 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34934 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34935 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34936 split.minSize = 100;
34937 split.maxSize = 600;
34938 split.animate = true;
34939 split.on('moved', splitterMoved);
34942 * Create a new SplitBar
34943 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34944 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34945 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34946 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34947 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34948 position of the SplitBar).
34950 Roo.bootstrap.SplitBar = function(cfg){
34955 // dragElement : elm
34956 // resizingElement: el,
34958 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34959 // placement : Roo.bootstrap.SplitBar.LEFT ,
34960 // existingProxy ???
34963 this.el = Roo.get(cfg.dragElement, true);
34964 this.el.dom.unselectable = "on";
34966 this.resizingEl = Roo.get(cfg.resizingElement, true);
34970 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34971 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34974 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34977 * The minimum size of the resizing element. (Defaults to 0)
34983 * The maximum size of the resizing element. (Defaults to 2000)
34986 this.maxSize = 2000;
34989 * Whether to animate the transition to the new size
34992 this.animate = false;
34995 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34998 this.useShim = false;
35003 if(!cfg.existingProxy){
35005 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
35007 this.proxy = Roo.get(cfg.existingProxy).dom;
35010 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
35013 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
35016 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
35019 this.dragSpecs = {};
35022 * @private The adapter to use to positon and resize elements
35024 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35025 this.adapter.init(this);
35027 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35029 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
35030 this.el.addClass("roo-splitbar-h");
35033 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
35034 this.el.addClass("roo-splitbar-v");
35040 * Fires when the splitter is moved (alias for {@link #event-moved})
35041 * @param {Roo.bootstrap.SplitBar} this
35042 * @param {Number} newSize the new width or height
35047 * Fires when the splitter is moved
35048 * @param {Roo.bootstrap.SplitBar} this
35049 * @param {Number} newSize the new width or height
35053 * @event beforeresize
35054 * Fires before the splitter is dragged
35055 * @param {Roo.bootstrap.SplitBar} this
35057 "beforeresize" : true,
35059 "beforeapply" : true
35062 Roo.util.Observable.call(this);
35065 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
35066 onStartProxyDrag : function(x, y){
35067 this.fireEvent("beforeresize", this);
35069 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
35071 o.enableDisplayMode("block");
35072 // all splitbars share the same overlay
35073 Roo.bootstrap.SplitBar.prototype.overlay = o;
35075 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
35076 this.overlay.show();
35077 Roo.get(this.proxy).setDisplayed("block");
35078 var size = this.adapter.getElementSize(this);
35079 this.activeMinSize = this.getMinimumSize();;
35080 this.activeMaxSize = this.getMaximumSize();;
35081 var c1 = size - this.activeMinSize;
35082 var c2 = Math.max(this.activeMaxSize - size, 0);
35083 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35084 this.dd.resetConstraints();
35085 this.dd.setXConstraint(
35086 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
35087 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
35089 this.dd.setYConstraint(0, 0);
35091 this.dd.resetConstraints();
35092 this.dd.setXConstraint(0, 0);
35093 this.dd.setYConstraint(
35094 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
35095 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
35098 this.dragSpecs.startSize = size;
35099 this.dragSpecs.startPoint = [x, y];
35100 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
35104 * @private Called after the drag operation by the DDProxy
35106 onEndProxyDrag : function(e){
35107 Roo.get(this.proxy).setDisplayed(false);
35108 var endPoint = Roo.lib.Event.getXY(e);
35110 this.overlay.hide();
35113 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35114 newSize = this.dragSpecs.startSize +
35115 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
35116 endPoint[0] - this.dragSpecs.startPoint[0] :
35117 this.dragSpecs.startPoint[0] - endPoint[0]
35120 newSize = this.dragSpecs.startSize +
35121 (this.placement == Roo.bootstrap.SplitBar.TOP ?
35122 endPoint[1] - this.dragSpecs.startPoint[1] :
35123 this.dragSpecs.startPoint[1] - endPoint[1]
35126 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
35127 if(newSize != this.dragSpecs.startSize){
35128 if(this.fireEvent('beforeapply', this, newSize) !== false){
35129 this.adapter.setElementSize(this, newSize);
35130 this.fireEvent("moved", this, newSize);
35131 this.fireEvent("resize", this, newSize);
35137 * Get the adapter this SplitBar uses
35138 * @return The adapter object
35140 getAdapter : function(){
35141 return this.adapter;
35145 * Set the adapter this SplitBar uses
35146 * @param {Object} adapter A SplitBar adapter object
35148 setAdapter : function(adapter){
35149 this.adapter = adapter;
35150 this.adapter.init(this);
35154 * Gets the minimum size for the resizing element
35155 * @return {Number} The minimum size
35157 getMinimumSize : function(){
35158 return this.minSize;
35162 * Sets the minimum size for the resizing element
35163 * @param {Number} minSize The minimum size
35165 setMinimumSize : function(minSize){
35166 this.minSize = minSize;
35170 * Gets the maximum size for the resizing element
35171 * @return {Number} The maximum size
35173 getMaximumSize : function(){
35174 return this.maxSize;
35178 * Sets the maximum size for the resizing element
35179 * @param {Number} maxSize The maximum size
35181 setMaximumSize : function(maxSize){
35182 this.maxSize = maxSize;
35186 * Sets the initialize size for the resizing element
35187 * @param {Number} size The initial size
35189 setCurrentSize : function(size){
35190 var oldAnimate = this.animate;
35191 this.animate = false;
35192 this.adapter.setElementSize(this, size);
35193 this.animate = oldAnimate;
35197 * Destroy this splitbar.
35198 * @param {Boolean} removeEl True to remove the element
35200 destroy : function(removeEl){
35202 this.shim.remove();
35205 this.proxy.parentNode.removeChild(this.proxy);
35213 * @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.
35215 Roo.bootstrap.SplitBar.createProxy = function(dir){
35216 var proxy = new Roo.Element(document.createElement("div"));
35217 proxy.unselectable();
35218 var cls = 'roo-splitbar-proxy';
35219 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
35220 document.body.appendChild(proxy.dom);
35225 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
35226 * Default Adapter. It assumes the splitter and resizing element are not positioned
35227 * elements and only gets/sets the width of the element. Generally used for table based layouts.
35229 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
35232 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
35233 // do nothing for now
35234 init : function(s){
35238 * Called before drag operations to get the current size of the resizing element.
35239 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35241 getElementSize : function(s){
35242 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35243 return s.resizingEl.getWidth();
35245 return s.resizingEl.getHeight();
35250 * Called after drag operations to set the size of the resizing element.
35251 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35252 * @param {Number} newSize The new size to set
35253 * @param {Function} onComplete A function to be invoked when resizing is complete
35255 setElementSize : function(s, newSize, onComplete){
35256 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35258 s.resizingEl.setWidth(newSize);
35260 onComplete(s, newSize);
35263 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
35268 s.resizingEl.setHeight(newSize);
35270 onComplete(s, newSize);
35273 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
35280 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
35281 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
35282 * Adapter that moves the splitter element to align with the resized sizing element.
35283 * Used with an absolute positioned SplitBar.
35284 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
35285 * document.body, make sure you assign an id to the body element.
35287 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
35288 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35289 this.container = Roo.get(container);
35292 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
35293 init : function(s){
35294 this.basic.init(s);
35297 getElementSize : function(s){
35298 return this.basic.getElementSize(s);
35301 setElementSize : function(s, newSize, onComplete){
35302 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
35305 moveSplitter : function(s){
35306 var yes = Roo.bootstrap.SplitBar;
35307 switch(s.placement){
35309 s.el.setX(s.resizingEl.getRight());
35312 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35315 s.el.setY(s.resizingEl.getBottom());
35318 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35325 * Orientation constant - Create a vertical SplitBar
35329 Roo.bootstrap.SplitBar.VERTICAL = 1;
35332 * Orientation constant - Create a horizontal SplitBar
35336 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35339 * Placement constant - The resizing element is to the left of the splitter element
35343 Roo.bootstrap.SplitBar.LEFT = 1;
35346 * Placement constant - The resizing element is to the right of the splitter element
35350 Roo.bootstrap.SplitBar.RIGHT = 2;
35353 * Placement constant - The resizing element is positioned above the splitter element
35357 Roo.bootstrap.SplitBar.TOP = 3;
35360 * Placement constant - The resizing element is positioned under splitter element
35364 Roo.bootstrap.SplitBar.BOTTOM = 4;
35365 Roo.namespace("Roo.bootstrap.layout");/*
35367 * Ext JS Library 1.1.1
35368 * Copyright(c) 2006-2007, Ext JS, LLC.
35370 * Originally Released Under LGPL - original licence link has changed is not relivant.
35373 * <script type="text/javascript">
35377 * @class Roo.bootstrap.layout.Manager
35378 * @extends Roo.bootstrap.Component
35379 * Base class for layout managers.
35381 Roo.bootstrap.layout.Manager = function(config)
35383 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35389 /** false to disable window resize monitoring @type Boolean */
35390 this.monitorWindowResize = true;
35395 * Fires when a layout is performed.
35396 * @param {Roo.LayoutManager} this
35400 * @event regionresized
35401 * Fires when the user resizes a region.
35402 * @param {Roo.LayoutRegion} region The resized region
35403 * @param {Number} newSize The new size (width for east/west, height for north/south)
35405 "regionresized" : true,
35407 * @event regioncollapsed
35408 * Fires when a region is collapsed.
35409 * @param {Roo.LayoutRegion} region The collapsed region
35411 "regioncollapsed" : true,
35413 * @event regionexpanded
35414 * Fires when a region is expanded.
35415 * @param {Roo.LayoutRegion} region The expanded region
35417 "regionexpanded" : true
35419 this.updating = false;
35422 this.el = Roo.get(config.el);
35428 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35433 monitorWindowResize : true,
35439 onRender : function(ct, position)
35442 this.el = Roo.get(ct);
35445 //this.fireEvent('render',this);
35449 initEvents: function()
35453 // ie scrollbar fix
35454 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35455 document.body.scroll = "no";
35456 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35457 this.el.position('relative');
35459 this.id = this.el.id;
35460 this.el.addClass("roo-layout-container");
35461 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35462 if(this.el.dom != document.body ) {
35463 this.el.on('resize', this.layout,this);
35464 this.el.on('show', this.layout,this);
35470 * Returns true if this layout is currently being updated
35471 * @return {Boolean}
35473 isUpdating : function(){
35474 return this.updating;
35478 * Suspend the LayoutManager from doing auto-layouts while
35479 * making multiple add or remove calls
35481 beginUpdate : function(){
35482 this.updating = true;
35486 * Restore auto-layouts and optionally disable the manager from performing a layout
35487 * @param {Boolean} noLayout true to disable a layout update
35489 endUpdate : function(noLayout){
35490 this.updating = false;
35496 layout: function(){
35500 onRegionResized : function(region, newSize){
35501 this.fireEvent("regionresized", region, newSize);
35505 onRegionCollapsed : function(region){
35506 this.fireEvent("regioncollapsed", region);
35509 onRegionExpanded : function(region){
35510 this.fireEvent("regionexpanded", region);
35514 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35515 * performs box-model adjustments.
35516 * @return {Object} The size as an object {width: (the width), height: (the height)}
35518 getViewSize : function()
35521 if(this.el.dom != document.body){
35522 size = this.el.getSize();
35524 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35526 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35527 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35532 * Returns the Element this layout is bound to.
35533 * @return {Roo.Element}
35535 getEl : function(){
35540 * Returns the specified region.
35541 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35542 * @return {Roo.LayoutRegion}
35544 getRegion : function(target){
35545 return this.regions[target.toLowerCase()];
35548 onWindowResize : function(){
35549 if(this.monitorWindowResize){
35556 * Ext JS Library 1.1.1
35557 * Copyright(c) 2006-2007, Ext JS, LLC.
35559 * Originally Released Under LGPL - original licence link has changed is not relivant.
35562 * <script type="text/javascript">
35565 * @class Roo.bootstrap.layout.Border
35566 * @extends Roo.bootstrap.layout.Manager
35567 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35568 * please see: examples/bootstrap/nested.html<br><br>
35570 <b>The container the layout is rendered into can be either the body element or any other element.
35571 If it is not the body element, the container needs to either be an absolute positioned element,
35572 or you will need to add "position:relative" to the css of the container. You will also need to specify
35573 the container size if it is not the body element.</b>
35576 * Create a new Border
35577 * @param {Object} config Configuration options
35579 Roo.bootstrap.layout.Border = function(config){
35580 config = config || {};
35581 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35585 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35586 if(config[region]){
35587 config[region].region = region;
35588 this.addRegion(config[region]);
35594 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
35596 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35598 parent : false, // this might point to a 'nest' or a ???
35601 * Creates and adds a new region if it doesn't already exist.
35602 * @param {String} target The target region key (north, south, east, west or center).
35603 * @param {Object} config The regions config object
35604 * @return {BorderLayoutRegion} The new region
35606 addRegion : function(config)
35608 if(!this.regions[config.region]){
35609 var r = this.factory(config);
35610 this.bindRegion(r);
35612 return this.regions[config.region];
35616 bindRegion : function(r){
35617 this.regions[r.config.region] = r;
35619 r.on("visibilitychange", this.layout, this);
35620 r.on("paneladded", this.layout, this);
35621 r.on("panelremoved", this.layout, this);
35622 r.on("invalidated", this.layout, this);
35623 r.on("resized", this.onRegionResized, this);
35624 r.on("collapsed", this.onRegionCollapsed, this);
35625 r.on("expanded", this.onRegionExpanded, this);
35629 * Performs a layout update.
35631 layout : function()
35633 if(this.updating) {
35637 // render all the rebions if they have not been done alreayd?
35638 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35639 if(this.regions[region] && !this.regions[region].bodyEl){
35640 this.regions[region].onRender(this.el)
35644 var size = this.getViewSize();
35645 var w = size.width;
35646 var h = size.height;
35651 //var x = 0, y = 0;
35653 var rs = this.regions;
35654 var north = rs["north"];
35655 var south = rs["south"];
35656 var west = rs["west"];
35657 var east = rs["east"];
35658 var center = rs["center"];
35659 //if(this.hideOnLayout){ // not supported anymore
35660 //c.el.setStyle("display", "none");
35662 if(north && north.isVisible()){
35663 var b = north.getBox();
35664 var m = north.getMargins();
35665 b.width = w - (m.left+m.right);
35668 centerY = b.height + b.y + m.bottom;
35669 centerH -= centerY;
35670 north.updateBox(this.safeBox(b));
35672 if(south && south.isVisible()){
35673 var b = south.getBox();
35674 var m = south.getMargins();
35675 b.width = w - (m.left+m.right);
35677 var totalHeight = (b.height + m.top + m.bottom);
35678 b.y = h - totalHeight + m.top;
35679 centerH -= totalHeight;
35680 south.updateBox(this.safeBox(b));
35682 if(west && west.isVisible()){
35683 var b = west.getBox();
35684 var m = west.getMargins();
35685 b.height = centerH - (m.top+m.bottom);
35687 b.y = centerY + m.top;
35688 var totalWidth = (b.width + m.left + m.right);
35689 centerX += totalWidth;
35690 centerW -= totalWidth;
35691 west.updateBox(this.safeBox(b));
35693 if(east && east.isVisible()){
35694 var b = east.getBox();
35695 var m = east.getMargins();
35696 b.height = centerH - (m.top+m.bottom);
35697 var totalWidth = (b.width + m.left + m.right);
35698 b.x = w - totalWidth + m.left;
35699 b.y = centerY + m.top;
35700 centerW -= totalWidth;
35701 east.updateBox(this.safeBox(b));
35704 var m = center.getMargins();
35706 x: centerX + m.left,
35707 y: centerY + m.top,
35708 width: centerW - (m.left+m.right),
35709 height: centerH - (m.top+m.bottom)
35711 //if(this.hideOnLayout){
35712 //center.el.setStyle("display", "block");
35714 center.updateBox(this.safeBox(centerBox));
35717 this.fireEvent("layout", this);
35721 safeBox : function(box){
35722 box.width = Math.max(0, box.width);
35723 box.height = Math.max(0, box.height);
35728 * Adds a ContentPanel (or subclass) to this layout.
35729 * @param {String} target The target region key (north, south, east, west or center).
35730 * @param {Roo.ContentPanel} panel The panel to add
35731 * @return {Roo.ContentPanel} The added panel
35733 add : function(target, panel){
35735 target = target.toLowerCase();
35736 return this.regions[target].add(panel);
35740 * Remove a ContentPanel (or subclass) to this layout.
35741 * @param {String} target The target region key (north, south, east, west or center).
35742 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35743 * @return {Roo.ContentPanel} The removed panel
35745 remove : function(target, panel){
35746 target = target.toLowerCase();
35747 return this.regions[target].remove(panel);
35751 * Searches all regions for a panel with the specified id
35752 * @param {String} panelId
35753 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35755 findPanel : function(panelId){
35756 var rs = this.regions;
35757 for(var target in rs){
35758 if(typeof rs[target] != "function"){
35759 var p = rs[target].getPanel(panelId);
35769 * Searches all regions for a panel with the specified id and activates (shows) it.
35770 * @param {String/ContentPanel} panelId The panels id or the panel itself
35771 * @return {Roo.ContentPanel} The shown panel or null
35773 showPanel : function(panelId) {
35774 var rs = this.regions;
35775 for(var target in rs){
35776 var r = rs[target];
35777 if(typeof r != "function"){
35778 if(r.hasPanel(panelId)){
35779 return r.showPanel(panelId);
35787 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35788 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35791 restoreState : function(provider){
35793 provider = Roo.state.Manager;
35795 var sm = new Roo.LayoutStateManager();
35796 sm.init(this, provider);
35802 * Adds a xtype elements to the layout.
35806 xtype : 'ContentPanel',
35813 xtype : 'NestedLayoutPanel',
35819 items : [ ... list of content panels or nested layout panels.. ]
35823 * @param {Object} cfg Xtype definition of item to add.
35825 addxtype : function(cfg)
35827 // basically accepts a pannel...
35828 // can accept a layout region..!?!?
35829 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35832 // theory? children can only be panels??
35834 //if (!cfg.xtype.match(/Panel$/)) {
35839 if (typeof(cfg.region) == 'undefined') {
35840 Roo.log("Failed to add Panel, region was not set");
35844 var region = cfg.region;
35850 xitems = cfg.items;
35855 if ( region == 'center') {
35856 Roo.log("Center: " + cfg.title);
35862 case 'Content': // ContentPanel (el, cfg)
35863 case 'Scroll': // ContentPanel (el, cfg)
35865 cfg.autoCreate = cfg.autoCreate || true;
35866 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35868 // var el = this.el.createChild();
35869 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35872 this.add(region, ret);
35876 case 'TreePanel': // our new panel!
35877 cfg.el = this.el.createChild();
35878 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35879 this.add(region, ret);
35884 // create a new Layout (which is a Border Layout...
35886 var clayout = cfg.layout;
35887 clayout.el = this.el.createChild();
35888 clayout.items = clayout.items || [];
35892 // replace this exitems with the clayout ones..
35893 xitems = clayout.items;
35895 // force background off if it's in center...
35896 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35897 cfg.background = false;
35899 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35902 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35903 //console.log('adding nested layout panel ' + cfg.toSource());
35904 this.add(region, ret);
35905 nb = {}; /// find first...
35910 // needs grid and region
35912 //var el = this.getRegion(region).el.createChild();
35914 *var el = this.el.createChild();
35915 // create the grid first...
35916 cfg.grid.container = el;
35917 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35920 if (region == 'center' && this.active ) {
35921 cfg.background = false;
35924 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35926 this.add(region, ret);
35928 if (cfg.background) {
35929 // render grid on panel activation (if panel background)
35930 ret.on('activate', function(gp) {
35931 if (!gp.grid.rendered) {
35932 // gp.grid.render(el);
35936 // cfg.grid.render(el);
35942 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35943 // it was the old xcomponent building that caused this before.
35944 // espeically if border is the top element in the tree.
35954 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35956 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35957 this.add(region, ret);
35961 throw "Can not add '" + cfg.xtype + "' to Border";
35967 this.beginUpdate();
35971 Roo.each(xitems, function(i) {
35972 region = nb && i.region ? i.region : false;
35974 var add = ret.addxtype(i);
35977 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35978 if (!i.background) {
35979 abn[region] = nb[region] ;
35986 // make the last non-background panel active..
35987 //if (nb) { Roo.log(abn); }
35990 for(var r in abn) {
35991 region = this.getRegion(r);
35993 // tried using nb[r], but it does not work..
35995 region.showPanel(abn[r]);
36006 factory : function(cfg)
36009 var validRegions = Roo.bootstrap.layout.Border.regions;
36011 var target = cfg.region;
36014 var r = Roo.bootstrap.layout;
36018 return new r.North(cfg);
36020 return new r.South(cfg);
36022 return new r.East(cfg);
36024 return new r.West(cfg);
36026 return new r.Center(cfg);
36028 throw 'Layout region "'+target+'" not supported.';
36035 * Ext JS Library 1.1.1
36036 * Copyright(c) 2006-2007, Ext JS, LLC.
36038 * Originally Released Under LGPL - original licence link has changed is not relivant.
36041 * <script type="text/javascript">
36045 * @class Roo.bootstrap.layout.Basic
36046 * @extends Roo.util.Observable
36047 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
36048 * and does not have a titlebar, tabs or any other features. All it does is size and position
36049 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
36050 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
36051 * @cfg {string} region the region that it inhabits..
36052 * @cfg {bool} skipConfig skip config?
36056 Roo.bootstrap.layout.Basic = function(config){
36058 this.mgr = config.mgr;
36060 this.position = config.region;
36062 var skipConfig = config.skipConfig;
36066 * @scope Roo.BasicLayoutRegion
36070 * @event beforeremove
36071 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
36072 * @param {Roo.LayoutRegion} this
36073 * @param {Roo.ContentPanel} panel The panel
36074 * @param {Object} e The cancel event object
36076 "beforeremove" : true,
36078 * @event invalidated
36079 * Fires when the layout for this region is changed.
36080 * @param {Roo.LayoutRegion} this
36082 "invalidated" : true,
36084 * @event visibilitychange
36085 * Fires when this region is shown or hidden
36086 * @param {Roo.LayoutRegion} this
36087 * @param {Boolean} visibility true or false
36089 "visibilitychange" : true,
36091 * @event paneladded
36092 * Fires when a panel is added.
36093 * @param {Roo.LayoutRegion} this
36094 * @param {Roo.ContentPanel} panel The panel
36096 "paneladded" : true,
36098 * @event panelremoved
36099 * Fires when a panel is removed.
36100 * @param {Roo.LayoutRegion} this
36101 * @param {Roo.ContentPanel} panel The panel
36103 "panelremoved" : true,
36105 * @event beforecollapse
36106 * Fires when this region before collapse.
36107 * @param {Roo.LayoutRegion} this
36109 "beforecollapse" : true,
36112 * Fires when this region is collapsed.
36113 * @param {Roo.LayoutRegion} this
36115 "collapsed" : true,
36118 * Fires when this region is expanded.
36119 * @param {Roo.LayoutRegion} this
36124 * Fires when this region is slid into view.
36125 * @param {Roo.LayoutRegion} this
36127 "slideshow" : true,
36130 * Fires when this region slides out of view.
36131 * @param {Roo.LayoutRegion} this
36133 "slidehide" : true,
36135 * @event panelactivated
36136 * Fires when a panel is activated.
36137 * @param {Roo.LayoutRegion} this
36138 * @param {Roo.ContentPanel} panel The activated panel
36140 "panelactivated" : true,
36143 * Fires when the user resizes this region.
36144 * @param {Roo.LayoutRegion} this
36145 * @param {Number} newSize The new size (width for east/west, height for north/south)
36149 /** A collection of panels in this region. @type Roo.util.MixedCollection */
36150 this.panels = new Roo.util.MixedCollection();
36151 this.panels.getKey = this.getPanelId.createDelegate(this);
36153 this.activePanel = null;
36154 // ensure listeners are added...
36156 if (config.listeners || config.events) {
36157 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
36158 listeners : config.listeners || {},
36159 events : config.events || {}
36163 if(skipConfig !== true){
36164 this.applyConfig(config);
36168 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
36170 getPanelId : function(p){
36174 applyConfig : function(config){
36175 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36176 this.config = config;
36181 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
36182 * the width, for horizontal (north, south) the height.
36183 * @param {Number} newSize The new width or height
36185 resizeTo : function(newSize){
36186 var el = this.el ? this.el :
36187 (this.activePanel ? this.activePanel.getEl() : null);
36189 switch(this.position){
36192 el.setWidth(newSize);
36193 this.fireEvent("resized", this, newSize);
36197 el.setHeight(newSize);
36198 this.fireEvent("resized", this, newSize);
36204 getBox : function(){
36205 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
36208 getMargins : function(){
36209 return this.margins;
36212 updateBox : function(box){
36214 var el = this.activePanel.getEl();
36215 el.dom.style.left = box.x + "px";
36216 el.dom.style.top = box.y + "px";
36217 this.activePanel.setSize(box.width, box.height);
36221 * Returns the container element for this region.
36222 * @return {Roo.Element}
36224 getEl : function(){
36225 return this.activePanel;
36229 * Returns true if this region is currently visible.
36230 * @return {Boolean}
36232 isVisible : function(){
36233 return this.activePanel ? true : false;
36236 setActivePanel : function(panel){
36237 panel = this.getPanel(panel);
36238 if(this.activePanel && this.activePanel != panel){
36239 this.activePanel.setActiveState(false);
36240 this.activePanel.getEl().setLeftTop(-10000,-10000);
36242 this.activePanel = panel;
36243 panel.setActiveState(true);
36245 panel.setSize(this.box.width, this.box.height);
36247 this.fireEvent("panelactivated", this, panel);
36248 this.fireEvent("invalidated");
36252 * Show the specified panel.
36253 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
36254 * @return {Roo.ContentPanel} The shown panel or null
36256 showPanel : function(panel){
36257 panel = this.getPanel(panel);
36259 this.setActivePanel(panel);
36265 * Get the active panel for this region.
36266 * @return {Roo.ContentPanel} The active panel or null
36268 getActivePanel : function(){
36269 return this.activePanel;
36273 * Add the passed ContentPanel(s)
36274 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36275 * @return {Roo.ContentPanel} The panel added (if only one was added)
36277 add : function(panel){
36278 if(arguments.length > 1){
36279 for(var i = 0, len = arguments.length; i < len; i++) {
36280 this.add(arguments[i]);
36284 if(this.hasPanel(panel)){
36285 this.showPanel(panel);
36288 var el = panel.getEl();
36289 if(el.dom.parentNode != this.mgr.el.dom){
36290 this.mgr.el.dom.appendChild(el.dom);
36292 if(panel.setRegion){
36293 panel.setRegion(this);
36295 this.panels.add(panel);
36296 el.setStyle("position", "absolute");
36297 if(!panel.background){
36298 this.setActivePanel(panel);
36299 if(this.config.initialSize && this.panels.getCount()==1){
36300 this.resizeTo(this.config.initialSize);
36303 this.fireEvent("paneladded", this, panel);
36308 * Returns true if the panel is in this region.
36309 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36310 * @return {Boolean}
36312 hasPanel : function(panel){
36313 if(typeof panel == "object"){ // must be panel obj
36314 panel = panel.getId();
36316 return this.getPanel(panel) ? true : false;
36320 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36321 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36322 * @param {Boolean} preservePanel Overrides the config preservePanel option
36323 * @return {Roo.ContentPanel} The panel that was removed
36325 remove : function(panel, preservePanel){
36326 panel = this.getPanel(panel);
36331 this.fireEvent("beforeremove", this, panel, e);
36332 if(e.cancel === true){
36335 var panelId = panel.getId();
36336 this.panels.removeKey(panelId);
36341 * Returns the panel specified or null if it's not in this region.
36342 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36343 * @return {Roo.ContentPanel}
36345 getPanel : function(id){
36346 if(typeof id == "object"){ // must be panel obj
36349 return this.panels.get(id);
36353 * Returns this regions position (north/south/east/west/center).
36356 getPosition: function(){
36357 return this.position;
36361 * Ext JS Library 1.1.1
36362 * Copyright(c) 2006-2007, Ext JS, LLC.
36364 * Originally Released Under LGPL - original licence link has changed is not relivant.
36367 * <script type="text/javascript">
36371 * @class Roo.bootstrap.layout.Region
36372 * @extends Roo.bootstrap.layout.Basic
36373 * This class represents a region in a layout manager.
36375 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36376 * @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})
36377 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
36378 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
36379 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
36380 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
36381 * @cfg {String} title The title for the region (overrides panel titles)
36382 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
36383 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36384 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
36385 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36386 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
36387 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36388 * the space available, similar to FireFox 1.5 tabs (defaults to false)
36389 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
36390 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
36391 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
36393 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
36394 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
36395 * @cfg {Boolean} disableTabTips True to disable tab tooltips
36396 * @cfg {Number} width For East/West panels
36397 * @cfg {Number} height For North/South panels
36398 * @cfg {Boolean} split To show the splitter
36399 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
36401 * @cfg {string} cls Extra CSS classes to add to region
36403 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
36404 * @cfg {string} region the region that it inhabits..
36407 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
36408 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
36410 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
36411 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
36412 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
36414 Roo.bootstrap.layout.Region = function(config)
36416 this.applyConfig(config);
36418 var mgr = config.mgr;
36419 var pos = config.region;
36420 config.skipConfig = true;
36421 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36424 this.onRender(mgr.el);
36427 this.visible = true;
36428 this.collapsed = false;
36429 this.unrendered_panels = [];
36432 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36434 position: '', // set by wrapper (eg. north/south etc..)
36435 unrendered_panels : null, // unrendered panels.
36437 tabPosition : false,
36439 mgr: false, // points to 'Border'
36442 createBody : function(){
36443 /** This region's body element
36444 * @type Roo.Element */
36445 this.bodyEl = this.el.createChild({
36447 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36451 onRender: function(ctr, pos)
36453 var dh = Roo.DomHelper;
36454 /** This region's container element
36455 * @type Roo.Element */
36456 this.el = dh.append(ctr.dom, {
36458 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36460 /** This region's title element
36461 * @type Roo.Element */
36463 this.titleEl = dh.append(this.el.dom, {
36465 unselectable: "on",
36466 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36468 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
36469 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36473 this.titleEl.enableDisplayMode();
36474 /** This region's title text element
36475 * @type HTMLElement */
36476 this.titleTextEl = this.titleEl.dom.firstChild;
36477 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36479 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36480 this.closeBtn.enableDisplayMode();
36481 this.closeBtn.on("click", this.closeClicked, this);
36482 this.closeBtn.hide();
36484 this.createBody(this.config);
36485 if(this.config.hideWhenEmpty){
36487 this.on("paneladded", this.validateVisibility, this);
36488 this.on("panelremoved", this.validateVisibility, this);
36490 if(this.autoScroll){
36491 this.bodyEl.setStyle("overflow", "auto");
36493 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36495 //if(c.titlebar !== false){
36496 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36497 this.titleEl.hide();
36499 this.titleEl.show();
36500 if(this.config.title){
36501 this.titleTextEl.innerHTML = this.config.title;
36505 if(this.config.collapsed){
36506 this.collapse(true);
36508 if(this.config.hidden){
36512 if (this.unrendered_panels && this.unrendered_panels.length) {
36513 for (var i =0;i< this.unrendered_panels.length; i++) {
36514 this.add(this.unrendered_panels[i]);
36516 this.unrendered_panels = null;
36522 applyConfig : function(c)
36525 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36526 var dh = Roo.DomHelper;
36527 if(c.titlebar !== false){
36528 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36529 this.collapseBtn.on("click", this.collapse, this);
36530 this.collapseBtn.enableDisplayMode();
36532 if(c.showPin === true || this.showPin){
36533 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36534 this.stickBtn.enableDisplayMode();
36535 this.stickBtn.on("click", this.expand, this);
36536 this.stickBtn.hide();
36541 /** This region's collapsed element
36542 * @type Roo.Element */
36545 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36546 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36549 if(c.floatable !== false){
36550 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36551 this.collapsedEl.on("click", this.collapseClick, this);
36554 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36555 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36556 id: "message", unselectable: "on", style:{"float":"left"}});
36557 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36559 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36560 this.expandBtn.on("click", this.expand, this);
36564 if(this.collapseBtn){
36565 this.collapseBtn.setVisible(c.collapsible == true);
36568 this.cmargins = c.cmargins || this.cmargins ||
36569 (this.position == "west" || this.position == "east" ?
36570 {top: 0, left: 2, right:2, bottom: 0} :
36571 {top: 2, left: 0, right:0, bottom: 2});
36573 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36576 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36578 this.autoScroll = c.autoScroll || false;
36583 this.duration = c.duration || .30;
36584 this.slideDuration = c.slideDuration || .45;
36589 * Returns true if this region is currently visible.
36590 * @return {Boolean}
36592 isVisible : function(){
36593 return this.visible;
36597 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36598 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
36600 //setCollapsedTitle : function(title){
36601 // title = title || " ";
36602 // if(this.collapsedTitleTextEl){
36603 // this.collapsedTitleTextEl.innerHTML = title;
36607 getBox : function(){
36609 // if(!this.collapsed){
36610 b = this.el.getBox(false, true);
36612 // b = this.collapsedEl.getBox(false, true);
36617 getMargins : function(){
36618 return this.margins;
36619 //return this.collapsed ? this.cmargins : this.margins;
36622 highlight : function(){
36623 this.el.addClass("x-layout-panel-dragover");
36626 unhighlight : function(){
36627 this.el.removeClass("x-layout-panel-dragover");
36630 updateBox : function(box)
36632 if (!this.bodyEl) {
36633 return; // not rendered yet..
36637 if(!this.collapsed){
36638 this.el.dom.style.left = box.x + "px";
36639 this.el.dom.style.top = box.y + "px";
36640 this.updateBody(box.width, box.height);
36642 this.collapsedEl.dom.style.left = box.x + "px";
36643 this.collapsedEl.dom.style.top = box.y + "px";
36644 this.collapsedEl.setSize(box.width, box.height);
36647 this.tabs.autoSizeTabs();
36651 updateBody : function(w, h)
36654 this.el.setWidth(w);
36655 w -= this.el.getBorderWidth("rl");
36656 if(this.config.adjustments){
36657 w += this.config.adjustments[0];
36660 if(h !== null && h > 0){
36661 this.el.setHeight(h);
36662 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36663 h -= this.el.getBorderWidth("tb");
36664 if(this.config.adjustments){
36665 h += this.config.adjustments[1];
36667 this.bodyEl.setHeight(h);
36669 h = this.tabs.syncHeight(h);
36672 if(this.panelSize){
36673 w = w !== null ? w : this.panelSize.width;
36674 h = h !== null ? h : this.panelSize.height;
36676 if(this.activePanel){
36677 var el = this.activePanel.getEl();
36678 w = w !== null ? w : el.getWidth();
36679 h = h !== null ? h : el.getHeight();
36680 this.panelSize = {width: w, height: h};
36681 this.activePanel.setSize(w, h);
36683 if(Roo.isIE && this.tabs){
36684 this.tabs.el.repaint();
36689 * Returns the container element for this region.
36690 * @return {Roo.Element}
36692 getEl : function(){
36697 * Hides this region.
36700 //if(!this.collapsed){
36701 this.el.dom.style.left = "-2000px";
36704 // this.collapsedEl.dom.style.left = "-2000px";
36705 // this.collapsedEl.hide();
36707 this.visible = false;
36708 this.fireEvent("visibilitychange", this, false);
36712 * Shows this region if it was previously hidden.
36715 //if(!this.collapsed){
36718 // this.collapsedEl.show();
36720 this.visible = true;
36721 this.fireEvent("visibilitychange", this, true);
36724 closeClicked : function(){
36725 if(this.activePanel){
36726 this.remove(this.activePanel);
36730 collapseClick : function(e){
36732 e.stopPropagation();
36735 e.stopPropagation();
36741 * Collapses this region.
36742 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36745 collapse : function(skipAnim, skipCheck = false){
36746 if(this.collapsed) {
36750 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36752 this.collapsed = true;
36754 this.split.el.hide();
36756 if(this.config.animate && skipAnim !== true){
36757 this.fireEvent("invalidated", this);
36758 this.animateCollapse();
36760 this.el.setLocation(-20000,-20000);
36762 this.collapsedEl.show();
36763 this.fireEvent("collapsed", this);
36764 this.fireEvent("invalidated", this);
36770 animateCollapse : function(){
36775 * Expands this region if it was previously collapsed.
36776 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36777 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36780 expand : function(e, skipAnim){
36782 e.stopPropagation();
36784 if(!this.collapsed || this.el.hasActiveFx()) {
36788 this.afterSlideIn();
36791 this.collapsed = false;
36792 if(this.config.animate && skipAnim !== true){
36793 this.animateExpand();
36797 this.split.el.show();
36799 this.collapsedEl.setLocation(-2000,-2000);
36800 this.collapsedEl.hide();
36801 this.fireEvent("invalidated", this);
36802 this.fireEvent("expanded", this);
36806 animateExpand : function(){
36810 initTabs : function()
36812 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36814 var ts = new Roo.bootstrap.panel.Tabs({
36815 el: this.bodyEl.dom,
36817 tabPosition: this.tabPosition ? this.tabPosition : 'top',
36818 disableTooltips: this.config.disableTabTips,
36819 toolbar : this.config.toolbar
36822 if(this.config.hideTabs){
36823 ts.stripWrap.setDisplayed(false);
36826 ts.resizeTabs = this.config.resizeTabs === true;
36827 ts.minTabWidth = this.config.minTabWidth || 40;
36828 ts.maxTabWidth = this.config.maxTabWidth || 250;
36829 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36830 ts.monitorResize = false;
36831 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36832 ts.bodyEl.addClass('roo-layout-tabs-body');
36833 this.panels.each(this.initPanelAsTab, this);
36836 initPanelAsTab : function(panel){
36837 var ti = this.tabs.addTab(
36841 this.config.closeOnTab && panel.isClosable(),
36844 if(panel.tabTip !== undefined){
36845 ti.setTooltip(panel.tabTip);
36847 ti.on("activate", function(){
36848 this.setActivePanel(panel);
36851 if(this.config.closeOnTab){
36852 ti.on("beforeclose", function(t, e){
36854 this.remove(panel);
36858 panel.tabItem = ti;
36863 updatePanelTitle : function(panel, title)
36865 if(this.activePanel == panel){
36866 this.updateTitle(title);
36869 var ti = this.tabs.getTab(panel.getEl().id);
36871 if(panel.tabTip !== undefined){
36872 ti.setTooltip(panel.tabTip);
36877 updateTitle : function(title){
36878 if(this.titleTextEl && !this.config.title){
36879 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36883 setActivePanel : function(panel)
36885 panel = this.getPanel(panel);
36886 if(this.activePanel && this.activePanel != panel){
36887 if(this.activePanel.setActiveState(false) === false){
36891 this.activePanel = panel;
36892 panel.setActiveState(true);
36893 if(this.panelSize){
36894 panel.setSize(this.panelSize.width, this.panelSize.height);
36897 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36899 this.updateTitle(panel.getTitle());
36901 this.fireEvent("invalidated", this);
36903 this.fireEvent("panelactivated", this, panel);
36907 * Shows the specified panel.
36908 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36909 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36911 showPanel : function(panel)
36913 panel = this.getPanel(panel);
36916 var tab = this.tabs.getTab(panel.getEl().id);
36917 if(tab.isHidden()){
36918 this.tabs.unhideTab(tab.id);
36922 this.setActivePanel(panel);
36929 * Get the active panel for this region.
36930 * @return {Roo.ContentPanel} The active panel or null
36932 getActivePanel : function(){
36933 return this.activePanel;
36936 validateVisibility : function(){
36937 if(this.panels.getCount() < 1){
36938 this.updateTitle(" ");
36939 this.closeBtn.hide();
36942 if(!this.isVisible()){
36949 * Adds the passed ContentPanel(s) to this region.
36950 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36951 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36953 add : function(panel)
36955 if(arguments.length > 1){
36956 for(var i = 0, len = arguments.length; i < len; i++) {
36957 this.add(arguments[i]);
36962 // if we have not been rendered yet, then we can not really do much of this..
36963 if (!this.bodyEl) {
36964 this.unrendered_panels.push(panel);
36971 if(this.hasPanel(panel)){
36972 this.showPanel(panel);
36975 panel.setRegion(this);
36976 this.panels.add(panel);
36977 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36978 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36979 // and hide them... ???
36980 this.bodyEl.dom.appendChild(panel.getEl().dom);
36981 if(panel.background !== true){
36982 this.setActivePanel(panel);
36984 this.fireEvent("paneladded", this, panel);
36991 this.initPanelAsTab(panel);
36995 if(panel.background !== true){
36996 this.tabs.activate(panel.getEl().id);
36998 this.fireEvent("paneladded", this, panel);
37003 * Hides the tab for the specified panel.
37004 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37006 hidePanel : function(panel){
37007 if(this.tabs && (panel = this.getPanel(panel))){
37008 this.tabs.hideTab(panel.getEl().id);
37013 * Unhides the tab for a previously hidden panel.
37014 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37016 unhidePanel : function(panel){
37017 if(this.tabs && (panel = this.getPanel(panel))){
37018 this.tabs.unhideTab(panel.getEl().id);
37022 clearPanels : function(){
37023 while(this.panels.getCount() > 0){
37024 this.remove(this.panels.first());
37029 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37030 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37031 * @param {Boolean} preservePanel Overrides the config preservePanel option
37032 * @return {Roo.ContentPanel} The panel that was removed
37034 remove : function(panel, preservePanel)
37036 panel = this.getPanel(panel);
37041 this.fireEvent("beforeremove", this, panel, e);
37042 if(e.cancel === true){
37045 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
37046 var panelId = panel.getId();
37047 this.panels.removeKey(panelId);
37049 document.body.appendChild(panel.getEl().dom);
37052 this.tabs.removeTab(panel.getEl().id);
37053 }else if (!preservePanel){
37054 this.bodyEl.dom.removeChild(panel.getEl().dom);
37056 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
37057 var p = this.panels.first();
37058 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
37059 tempEl.appendChild(p.getEl().dom);
37060 this.bodyEl.update("");
37061 this.bodyEl.dom.appendChild(p.getEl().dom);
37063 this.updateTitle(p.getTitle());
37065 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
37066 this.setActivePanel(p);
37068 panel.setRegion(null);
37069 if(this.activePanel == panel){
37070 this.activePanel = null;
37072 if(this.config.autoDestroy !== false && preservePanel !== true){
37073 try{panel.destroy();}catch(e){}
37075 this.fireEvent("panelremoved", this, panel);
37080 * Returns the TabPanel component used by this region
37081 * @return {Roo.TabPanel}
37083 getTabs : function(){
37087 createTool : function(parentEl, className){
37088 var btn = Roo.DomHelper.append(parentEl, {
37090 cls: "x-layout-tools-button",
37093 cls: "roo-layout-tools-button-inner " + className,
37097 btn.addClassOnOver("roo-layout-tools-button-over");
37102 * Ext JS Library 1.1.1
37103 * Copyright(c) 2006-2007, Ext JS, LLC.
37105 * Originally Released Under LGPL - original licence link has changed is not relivant.
37108 * <script type="text/javascript">
37114 * @class Roo.SplitLayoutRegion
37115 * @extends Roo.LayoutRegion
37116 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
37118 Roo.bootstrap.layout.Split = function(config){
37119 this.cursor = config.cursor;
37120 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
37123 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
37125 splitTip : "Drag to resize.",
37126 collapsibleSplitTip : "Drag to resize. Double click to hide.",
37127 useSplitTips : false,
37129 applyConfig : function(config){
37130 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
37133 onRender : function(ctr,pos) {
37135 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
37136 if(!this.config.split){
37141 var splitEl = Roo.DomHelper.append(ctr.dom, {
37143 id: this.el.id + "-split",
37144 cls: "roo-layout-split roo-layout-split-"+this.position,
37147 /** The SplitBar for this region
37148 * @type Roo.SplitBar */
37149 // does not exist yet...
37150 Roo.log([this.position, this.orientation]);
37152 this.split = new Roo.bootstrap.SplitBar({
37153 dragElement : splitEl,
37154 resizingElement: this.el,
37155 orientation : this.orientation
37158 this.split.on("moved", this.onSplitMove, this);
37159 this.split.useShim = this.config.useShim === true;
37160 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
37161 if(this.useSplitTips){
37162 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
37164 //if(config.collapsible){
37165 // this.split.el.on("dblclick", this.collapse, this);
37168 if(typeof this.config.minSize != "undefined"){
37169 this.split.minSize = this.config.minSize;
37171 if(typeof this.config.maxSize != "undefined"){
37172 this.split.maxSize = this.config.maxSize;
37174 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
37175 this.hideSplitter();
37180 getHMaxSize : function(){
37181 var cmax = this.config.maxSize || 10000;
37182 var center = this.mgr.getRegion("center");
37183 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
37186 getVMaxSize : function(){
37187 var cmax = this.config.maxSize || 10000;
37188 var center = this.mgr.getRegion("center");
37189 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
37192 onSplitMove : function(split, newSize){
37193 this.fireEvent("resized", this, newSize);
37197 * Returns the {@link Roo.SplitBar} for this region.
37198 * @return {Roo.SplitBar}
37200 getSplitBar : function(){
37205 this.hideSplitter();
37206 Roo.bootstrap.layout.Split.superclass.hide.call(this);
37209 hideSplitter : function(){
37211 this.split.el.setLocation(-2000,-2000);
37212 this.split.el.hide();
37218 this.split.el.show();
37220 Roo.bootstrap.layout.Split.superclass.show.call(this);
37223 beforeSlide: function(){
37224 if(Roo.isGecko){// firefox overflow auto bug workaround
37225 this.bodyEl.clip();
37227 this.tabs.bodyEl.clip();
37229 if(this.activePanel){
37230 this.activePanel.getEl().clip();
37232 if(this.activePanel.beforeSlide){
37233 this.activePanel.beforeSlide();
37239 afterSlide : function(){
37240 if(Roo.isGecko){// firefox overflow auto bug workaround
37241 this.bodyEl.unclip();
37243 this.tabs.bodyEl.unclip();
37245 if(this.activePanel){
37246 this.activePanel.getEl().unclip();
37247 if(this.activePanel.afterSlide){
37248 this.activePanel.afterSlide();
37254 initAutoHide : function(){
37255 if(this.autoHide !== false){
37256 if(!this.autoHideHd){
37257 var st = new Roo.util.DelayedTask(this.slideIn, this);
37258 this.autoHideHd = {
37259 "mouseout": function(e){
37260 if(!e.within(this.el, true)){
37264 "mouseover" : function(e){
37270 this.el.on(this.autoHideHd);
37274 clearAutoHide : function(){
37275 if(this.autoHide !== false){
37276 this.el.un("mouseout", this.autoHideHd.mouseout);
37277 this.el.un("mouseover", this.autoHideHd.mouseover);
37281 clearMonitor : function(){
37282 Roo.get(document).un("click", this.slideInIf, this);
37285 // these names are backwards but not changed for compat
37286 slideOut : function(){
37287 if(this.isSlid || this.el.hasActiveFx()){
37290 this.isSlid = true;
37291 if(this.collapseBtn){
37292 this.collapseBtn.hide();
37294 this.closeBtnState = this.closeBtn.getStyle('display');
37295 this.closeBtn.hide();
37297 this.stickBtn.show();
37300 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37301 this.beforeSlide();
37302 this.el.setStyle("z-index", 10001);
37303 this.el.slideIn(this.getSlideAnchor(), {
37304 callback: function(){
37306 this.initAutoHide();
37307 Roo.get(document).on("click", this.slideInIf, this);
37308 this.fireEvent("slideshow", this);
37315 afterSlideIn : function(){
37316 this.clearAutoHide();
37317 this.isSlid = false;
37318 this.clearMonitor();
37319 this.el.setStyle("z-index", "");
37320 if(this.collapseBtn){
37321 this.collapseBtn.show();
37323 this.closeBtn.setStyle('display', this.closeBtnState);
37325 this.stickBtn.hide();
37327 this.fireEvent("slidehide", this);
37330 slideIn : function(cb){
37331 if(!this.isSlid || this.el.hasActiveFx()){
37335 this.isSlid = false;
37336 this.beforeSlide();
37337 this.el.slideOut(this.getSlideAnchor(), {
37338 callback: function(){
37339 this.el.setLeftTop(-10000, -10000);
37341 this.afterSlideIn();
37349 slideInIf : function(e){
37350 if(!e.within(this.el)){
37355 animateCollapse : function(){
37356 this.beforeSlide();
37357 this.el.setStyle("z-index", 20000);
37358 var anchor = this.getSlideAnchor();
37359 this.el.slideOut(anchor, {
37360 callback : function(){
37361 this.el.setStyle("z-index", "");
37362 this.collapsedEl.slideIn(anchor, {duration:.3});
37364 this.el.setLocation(-10000,-10000);
37366 this.fireEvent("collapsed", this);
37373 animateExpand : function(){
37374 this.beforeSlide();
37375 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37376 this.el.setStyle("z-index", 20000);
37377 this.collapsedEl.hide({
37380 this.el.slideIn(this.getSlideAnchor(), {
37381 callback : function(){
37382 this.el.setStyle("z-index", "");
37385 this.split.el.show();
37387 this.fireEvent("invalidated", this);
37388 this.fireEvent("expanded", this);
37416 getAnchor : function(){
37417 return this.anchors[this.position];
37420 getCollapseAnchor : function(){
37421 return this.canchors[this.position];
37424 getSlideAnchor : function(){
37425 return this.sanchors[this.position];
37428 getAlignAdj : function(){
37429 var cm = this.cmargins;
37430 switch(this.position){
37446 getExpandAdj : function(){
37447 var c = this.collapsedEl, cm = this.cmargins;
37448 switch(this.position){
37450 return [-(cm.right+c.getWidth()+cm.left), 0];
37453 return [cm.right+c.getWidth()+cm.left, 0];
37456 return [0, -(cm.top+cm.bottom+c.getHeight())];
37459 return [0, cm.top+cm.bottom+c.getHeight()];
37465 * Ext JS Library 1.1.1
37466 * Copyright(c) 2006-2007, Ext JS, LLC.
37468 * Originally Released Under LGPL - original licence link has changed is not relivant.
37471 * <script type="text/javascript">
37474 * These classes are private internal classes
37476 Roo.bootstrap.layout.Center = function(config){
37477 config.region = "center";
37478 Roo.bootstrap.layout.Region.call(this, config);
37479 this.visible = true;
37480 this.minWidth = config.minWidth || 20;
37481 this.minHeight = config.minHeight || 20;
37484 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37486 // center panel can't be hidden
37490 // center panel can't be hidden
37493 getMinWidth: function(){
37494 return this.minWidth;
37497 getMinHeight: function(){
37498 return this.minHeight;
37512 Roo.bootstrap.layout.North = function(config)
37514 config.region = 'north';
37515 config.cursor = 'n-resize';
37517 Roo.bootstrap.layout.Split.call(this, config);
37521 this.split.placement = Roo.bootstrap.SplitBar.TOP;
37522 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37523 this.split.el.addClass("roo-layout-split-v");
37525 var size = config.initialSize || config.height;
37526 if(typeof size != "undefined"){
37527 this.el.setHeight(size);
37530 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37532 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37536 getBox : function(){
37537 if(this.collapsed){
37538 return this.collapsedEl.getBox();
37540 var box = this.el.getBox();
37542 box.height += this.split.el.getHeight();
37547 updateBox : function(box){
37548 if(this.split && !this.collapsed){
37549 box.height -= this.split.el.getHeight();
37550 this.split.el.setLeft(box.x);
37551 this.split.el.setTop(box.y+box.height);
37552 this.split.el.setWidth(box.width);
37554 if(this.collapsed){
37555 this.updateBody(box.width, null);
37557 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37565 Roo.bootstrap.layout.South = function(config){
37566 config.region = 'south';
37567 config.cursor = 's-resize';
37568 Roo.bootstrap.layout.Split.call(this, config);
37570 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37571 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37572 this.split.el.addClass("roo-layout-split-v");
37574 var size = config.initialSize || config.height;
37575 if(typeof size != "undefined"){
37576 this.el.setHeight(size);
37580 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37581 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37582 getBox : function(){
37583 if(this.collapsed){
37584 return this.collapsedEl.getBox();
37586 var box = this.el.getBox();
37588 var sh = this.split.el.getHeight();
37595 updateBox : function(box){
37596 if(this.split && !this.collapsed){
37597 var sh = this.split.el.getHeight();
37600 this.split.el.setLeft(box.x);
37601 this.split.el.setTop(box.y-sh);
37602 this.split.el.setWidth(box.width);
37604 if(this.collapsed){
37605 this.updateBody(box.width, null);
37607 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37611 Roo.bootstrap.layout.East = function(config){
37612 config.region = "east";
37613 config.cursor = "e-resize";
37614 Roo.bootstrap.layout.Split.call(this, config);
37616 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37617 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37618 this.split.el.addClass("roo-layout-split-h");
37620 var size = config.initialSize || config.width;
37621 if(typeof size != "undefined"){
37622 this.el.setWidth(size);
37625 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37626 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37627 getBox : function(){
37628 if(this.collapsed){
37629 return this.collapsedEl.getBox();
37631 var box = this.el.getBox();
37633 var sw = this.split.el.getWidth();
37640 updateBox : function(box){
37641 if(this.split && !this.collapsed){
37642 var sw = this.split.el.getWidth();
37644 this.split.el.setLeft(box.x);
37645 this.split.el.setTop(box.y);
37646 this.split.el.setHeight(box.height);
37649 if(this.collapsed){
37650 this.updateBody(null, box.height);
37652 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37656 Roo.bootstrap.layout.West = function(config){
37657 config.region = "west";
37658 config.cursor = "w-resize";
37660 Roo.bootstrap.layout.Split.call(this, config);
37662 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37663 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37664 this.split.el.addClass("roo-layout-split-h");
37668 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37669 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37671 onRender: function(ctr, pos)
37673 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37674 var size = this.config.initialSize || this.config.width;
37675 if(typeof size != "undefined"){
37676 this.el.setWidth(size);
37680 getBox : function(){
37681 if(this.collapsed){
37682 return this.collapsedEl.getBox();
37684 var box = this.el.getBox();
37686 box.width += this.split.el.getWidth();
37691 updateBox : function(box){
37692 if(this.split && !this.collapsed){
37693 var sw = this.split.el.getWidth();
37695 this.split.el.setLeft(box.x+box.width);
37696 this.split.el.setTop(box.y);
37697 this.split.el.setHeight(box.height);
37699 if(this.collapsed){
37700 this.updateBody(null, box.height);
37702 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37704 });Roo.namespace("Roo.bootstrap.panel");/*
37706 * Ext JS Library 1.1.1
37707 * Copyright(c) 2006-2007, Ext JS, LLC.
37709 * Originally Released Under LGPL - original licence link has changed is not relivant.
37712 * <script type="text/javascript">
37715 * @class Roo.ContentPanel
37716 * @extends Roo.util.Observable
37717 * A basic ContentPanel element.
37718 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37719 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37720 * @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
37721 * @cfg {Boolean} closable True if the panel can be closed/removed
37722 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37723 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37724 * @cfg {Toolbar} toolbar A toolbar for this panel
37725 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37726 * @cfg {String} title The title for this panel
37727 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37728 * @cfg {String} url Calls {@link #setUrl} with this value
37729 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37730 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37731 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37732 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37733 * @cfg {Boolean} badges render the badges
37736 * Create a new ContentPanel.
37737 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37738 * @param {String/Object} config A string to set only the title or a config object
37739 * @param {String} content (optional) Set the HTML content for this panel
37740 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37742 Roo.bootstrap.panel.Content = function( config){
37744 this.tpl = config.tpl || false;
37746 var el = config.el;
37747 var content = config.content;
37749 if(config.autoCreate){ // xtype is available if this is called from factory
37752 this.el = Roo.get(el);
37753 if(!this.el && config && config.autoCreate){
37754 if(typeof config.autoCreate == "object"){
37755 if(!config.autoCreate.id){
37756 config.autoCreate.id = config.id||el;
37758 this.el = Roo.DomHelper.append(document.body,
37759 config.autoCreate, true);
37761 var elcfg = { tag: "div",
37762 cls: "roo-layout-inactive-content",
37766 elcfg.html = config.html;
37770 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37773 this.closable = false;
37774 this.loaded = false;
37775 this.active = false;
37778 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37780 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37782 this.wrapEl = this.el; //this.el.wrap();
37784 if (config.toolbar.items) {
37785 ti = config.toolbar.items ;
37786 delete config.toolbar.items ;
37790 this.toolbar.render(this.wrapEl, 'before');
37791 for(var i =0;i < ti.length;i++) {
37792 // Roo.log(['add child', items[i]]);
37793 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37795 this.toolbar.items = nitems;
37796 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37797 delete config.toolbar;
37801 // xtype created footer. - not sure if will work as we normally have to render first..
37802 if (this.footer && !this.footer.el && this.footer.xtype) {
37803 if (!this.wrapEl) {
37804 this.wrapEl = this.el.wrap();
37807 this.footer.container = this.wrapEl.createChild();
37809 this.footer = Roo.factory(this.footer, Roo);
37814 if(typeof config == "string"){
37815 this.title = config;
37817 Roo.apply(this, config);
37821 this.resizeEl = Roo.get(this.resizeEl, true);
37823 this.resizeEl = this.el;
37825 // handle view.xtype
37833 * Fires when this panel is activated.
37834 * @param {Roo.ContentPanel} this
37838 * @event deactivate
37839 * Fires when this panel is activated.
37840 * @param {Roo.ContentPanel} this
37842 "deactivate" : true,
37846 * Fires when this panel is resized if fitToFrame is true.
37847 * @param {Roo.ContentPanel} this
37848 * @param {Number} width The width after any component adjustments
37849 * @param {Number} height The height after any component adjustments
37855 * Fires when this tab is created
37856 * @param {Roo.ContentPanel} this
37867 if(this.autoScroll){
37868 this.resizeEl.setStyle("overflow", "auto");
37870 // fix randome scrolling
37871 //this.el.on('scroll', function() {
37872 // Roo.log('fix random scolling');
37873 // this.scrollTo('top',0);
37876 content = content || this.content;
37878 this.setContent(content);
37880 if(config && config.url){
37881 this.setUrl(this.url, this.params, this.loadOnce);
37886 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37888 if (this.view && typeof(this.view.xtype) != 'undefined') {
37889 this.view.el = this.el.appendChild(document.createElement("div"));
37890 this.view = Roo.factory(this.view);
37891 this.view.render && this.view.render(false, '');
37895 this.fireEvent('render', this);
37898 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37902 setRegion : function(region){
37903 this.region = region;
37904 this.setActiveClass(region && !this.background);
37908 setActiveClass: function(state)
37911 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37912 this.el.setStyle('position','relative');
37914 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37915 this.el.setStyle('position', 'absolute');
37920 * Returns the toolbar for this Panel if one was configured.
37921 * @return {Roo.Toolbar}
37923 getToolbar : function(){
37924 return this.toolbar;
37927 setActiveState : function(active)
37929 this.active = active;
37930 this.setActiveClass(active);
37932 if(this.fireEvent("deactivate", this) === false){
37937 this.fireEvent("activate", this);
37941 * Updates this panel's element
37942 * @param {String} content The new content
37943 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37945 setContent : function(content, loadScripts){
37946 this.el.update(content, loadScripts);
37949 ignoreResize : function(w, h){
37950 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37953 this.lastSize = {width: w, height: h};
37958 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37959 * @return {Roo.UpdateManager} The UpdateManager
37961 getUpdateManager : function(){
37962 return this.el.getUpdateManager();
37965 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37966 * @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:
37969 url: "your-url.php",
37970 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37971 callback: yourFunction,
37972 scope: yourObject, //(optional scope)
37975 text: "Loading...",
37980 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37981 * 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.
37982 * @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}
37983 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37984 * @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.
37985 * @return {Roo.ContentPanel} this
37988 var um = this.el.getUpdateManager();
37989 um.update.apply(um, arguments);
37995 * 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.
37996 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37997 * @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)
37998 * @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)
37999 * @return {Roo.UpdateManager} The UpdateManager
38001 setUrl : function(url, params, loadOnce){
38002 if(this.refreshDelegate){
38003 this.removeListener("activate", this.refreshDelegate);
38005 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38006 this.on("activate", this.refreshDelegate);
38007 return this.el.getUpdateManager();
38010 _handleRefresh : function(url, params, loadOnce){
38011 if(!loadOnce || !this.loaded){
38012 var updater = this.el.getUpdateManager();
38013 updater.update(url, params, this._setLoaded.createDelegate(this));
38017 _setLoaded : function(){
38018 this.loaded = true;
38022 * Returns this panel's id
38025 getId : function(){
38030 * Returns this panel's element - used by regiosn to add.
38031 * @return {Roo.Element}
38033 getEl : function(){
38034 return this.wrapEl || this.el;
38039 adjustForComponents : function(width, height)
38041 //Roo.log('adjustForComponents ');
38042 if(this.resizeEl != this.el){
38043 width -= this.el.getFrameWidth('lr');
38044 height -= this.el.getFrameWidth('tb');
38047 var te = this.toolbar.getEl();
38048 te.setWidth(width);
38049 height -= te.getHeight();
38052 var te = this.footer.getEl();
38053 te.setWidth(width);
38054 height -= te.getHeight();
38058 if(this.adjustments){
38059 width += this.adjustments[0];
38060 height += this.adjustments[1];
38062 return {"width": width, "height": height};
38065 setSize : function(width, height){
38066 if(this.fitToFrame && !this.ignoreResize(width, height)){
38067 if(this.fitContainer && this.resizeEl != this.el){
38068 this.el.setSize(width, height);
38070 var size = this.adjustForComponents(width, height);
38071 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
38072 this.fireEvent('resize', this, size.width, size.height);
38077 * Returns this panel's title
38080 getTitle : function(){
38082 if (typeof(this.title) != 'object') {
38087 for (var k in this.title) {
38088 if (!this.title.hasOwnProperty(k)) {
38092 if (k.indexOf('-') >= 0) {
38093 var s = k.split('-');
38094 for (var i = 0; i<s.length; i++) {
38095 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
38098 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
38105 * Set this panel's title
38106 * @param {String} title
38108 setTitle : function(title){
38109 this.title = title;
38111 this.region.updatePanelTitle(this, title);
38116 * Returns true is this panel was configured to be closable
38117 * @return {Boolean}
38119 isClosable : function(){
38120 return this.closable;
38123 beforeSlide : function(){
38125 this.resizeEl.clip();
38128 afterSlide : function(){
38130 this.resizeEl.unclip();
38134 * Force a content refresh from the URL specified in the {@link #setUrl} method.
38135 * Will fail silently if the {@link #setUrl} method has not been called.
38136 * This does not activate the panel, just updates its content.
38138 refresh : function(){
38139 if(this.refreshDelegate){
38140 this.loaded = false;
38141 this.refreshDelegate();
38146 * Destroys this panel
38148 destroy : function(){
38149 this.el.removeAllListeners();
38150 var tempEl = document.createElement("span");
38151 tempEl.appendChild(this.el.dom);
38152 tempEl.innerHTML = "";
38158 * form - if the content panel contains a form - this is a reference to it.
38159 * @type {Roo.form.Form}
38163 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
38164 * This contains a reference to it.
38170 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
38180 * @param {Object} cfg Xtype definition of item to add.
38184 getChildContainer: function () {
38185 return this.getEl();
38190 var ret = new Roo.factory(cfg);
38195 if (cfg.xtype.match(/^Form$/)) {
38198 //if (this.footer) {
38199 // el = this.footer.container.insertSibling(false, 'before');
38201 el = this.el.createChild();
38204 this.form = new Roo.form.Form(cfg);
38207 if ( this.form.allItems.length) {
38208 this.form.render(el.dom);
38212 // should only have one of theses..
38213 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
38214 // views.. should not be just added - used named prop 'view''
38216 cfg.el = this.el.appendChild(document.createElement("div"));
38219 var ret = new Roo.factory(cfg);
38221 ret.render && ret.render(false, ''); // render blank..
38231 * @class Roo.bootstrap.panel.Grid
38232 * @extends Roo.bootstrap.panel.Content
38234 * Create a new GridPanel.
38235 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
38236 * @param {Object} config A the config object
38242 Roo.bootstrap.panel.Grid = function(config)
38246 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
38247 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
38249 config.el = this.wrapper;
38250 //this.el = this.wrapper;
38252 if (config.container) {
38253 // ctor'ed from a Border/panel.grid
38256 this.wrapper.setStyle("overflow", "hidden");
38257 this.wrapper.addClass('roo-grid-container');
38262 if(config.toolbar){
38263 var tool_el = this.wrapper.createChild();
38264 this.toolbar = Roo.factory(config.toolbar);
38266 if (config.toolbar.items) {
38267 ti = config.toolbar.items ;
38268 delete config.toolbar.items ;
38272 this.toolbar.render(tool_el);
38273 for(var i =0;i < ti.length;i++) {
38274 // Roo.log(['add child', items[i]]);
38275 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38277 this.toolbar.items = nitems;
38279 delete config.toolbar;
38282 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
38283 config.grid.scrollBody = true;;
38284 config.grid.monitorWindowResize = false; // turn off autosizing
38285 config.grid.autoHeight = false;
38286 config.grid.autoWidth = false;
38288 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
38290 if (config.background) {
38291 // render grid on panel activation (if panel background)
38292 this.on('activate', function(gp) {
38293 if (!gp.grid.rendered) {
38294 gp.grid.render(this.wrapper);
38295 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
38300 this.grid.render(this.wrapper);
38301 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
38304 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
38305 // ??? needed ??? config.el = this.wrapper;
38310 // xtype created footer. - not sure if will work as we normally have to render first..
38311 if (this.footer && !this.footer.el && this.footer.xtype) {
38313 var ctr = this.grid.getView().getFooterPanel(true);
38314 this.footer.dataSource = this.grid.dataSource;
38315 this.footer = Roo.factory(this.footer, Roo);
38316 this.footer.render(ctr);
38326 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38327 getId : function(){
38328 return this.grid.id;
38332 * Returns the grid for this panel
38333 * @return {Roo.bootstrap.Table}
38335 getGrid : function(){
38339 setSize : function(width, height){
38340 if(!this.ignoreResize(width, height)){
38341 var grid = this.grid;
38342 var size = this.adjustForComponents(width, height);
38343 var gridel = grid.getGridEl();
38344 gridel.setSize(size.width, size.height);
38346 var thd = grid.getGridEl().select('thead',true).first();
38347 var tbd = grid.getGridEl().select('tbody', true).first();
38349 tbd.setSize(width, height - thd.getHeight());
38358 beforeSlide : function(){
38359 this.grid.getView().scroller.clip();
38362 afterSlide : function(){
38363 this.grid.getView().scroller.unclip();
38366 destroy : function(){
38367 this.grid.destroy();
38369 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
38374 * @class Roo.bootstrap.panel.Nest
38375 * @extends Roo.bootstrap.panel.Content
38377 * Create a new Panel, that can contain a layout.Border.
38380 * @param {Roo.BorderLayout} layout The layout for this panel
38381 * @param {String/Object} config A string to set only the title or a config object
38383 Roo.bootstrap.panel.Nest = function(config)
38385 // construct with only one argument..
38386 /* FIXME - implement nicer consturctors
38387 if (layout.layout) {
38389 layout = config.layout;
38390 delete config.layout;
38392 if (layout.xtype && !layout.getEl) {
38393 // then layout needs constructing..
38394 layout = Roo.factory(layout, Roo);
38398 config.el = config.layout.getEl();
38400 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38402 config.layout.monitorWindowResize = false; // turn off autosizing
38403 this.layout = config.layout;
38404 this.layout.getEl().addClass("roo-layout-nested-layout");
38405 this.layout.parent = this;
38412 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38414 setSize : function(width, height){
38415 if(!this.ignoreResize(width, height)){
38416 var size = this.adjustForComponents(width, height);
38417 var el = this.layout.getEl();
38418 if (size.height < 1) {
38419 el.setWidth(size.width);
38421 el.setSize(size.width, size.height);
38423 var touch = el.dom.offsetWidth;
38424 this.layout.layout();
38425 // ie requires a double layout on the first pass
38426 if(Roo.isIE && !this.initialized){
38427 this.initialized = true;
38428 this.layout.layout();
38433 // activate all subpanels if not currently active..
38435 setActiveState : function(active){
38436 this.active = active;
38437 this.setActiveClass(active);
38440 this.fireEvent("deactivate", this);
38444 this.fireEvent("activate", this);
38445 // not sure if this should happen before or after..
38446 if (!this.layout) {
38447 return; // should not happen..
38450 for (var r in this.layout.regions) {
38451 reg = this.layout.getRegion(r);
38452 if (reg.getActivePanel()) {
38453 //reg.showPanel(reg.getActivePanel()); // force it to activate..
38454 reg.setActivePanel(reg.getActivePanel());
38457 if (!reg.panels.length) {
38460 reg.showPanel(reg.getPanel(0));
38469 * Returns the nested BorderLayout for this panel
38470 * @return {Roo.BorderLayout}
38472 getLayout : function(){
38473 return this.layout;
38477 * Adds a xtype elements to the layout of the nested panel
38481 xtype : 'ContentPanel',
38488 xtype : 'NestedLayoutPanel',
38494 items : [ ... list of content panels or nested layout panels.. ]
38498 * @param {Object} cfg Xtype definition of item to add.
38500 addxtype : function(cfg) {
38501 return this.layout.addxtype(cfg);
38506 * Ext JS Library 1.1.1
38507 * Copyright(c) 2006-2007, Ext JS, LLC.
38509 * Originally Released Under LGPL - original licence link has changed is not relivant.
38512 * <script type="text/javascript">
38515 * @class Roo.TabPanel
38516 * @extends Roo.util.Observable
38517 * A lightweight tab container.
38521 // basic tabs 1, built from existing content
38522 var tabs = new Roo.TabPanel("tabs1");
38523 tabs.addTab("script", "View Script");
38524 tabs.addTab("markup", "View Markup");
38525 tabs.activate("script");
38527 // more advanced tabs, built from javascript
38528 var jtabs = new Roo.TabPanel("jtabs");
38529 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38531 // set up the UpdateManager
38532 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38533 var updater = tab2.getUpdateManager();
38534 updater.setDefaultUrl("ajax1.htm");
38535 tab2.on('activate', updater.refresh, updater, true);
38537 // Use setUrl for Ajax loading
38538 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38539 tab3.setUrl("ajax2.htm", null, true);
38542 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38545 jtabs.activate("jtabs-1");
38548 * Create a new TabPanel.
38549 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38550 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38552 Roo.bootstrap.panel.Tabs = function(config){
38554 * The container element for this TabPanel.
38555 * @type Roo.Element
38557 this.el = Roo.get(config.el);
38560 if(typeof config == "boolean"){
38561 this.tabPosition = config ? "bottom" : "top";
38563 Roo.apply(this, config);
38567 if(this.tabPosition == "bottom"){
38568 // if tabs are at the bottom = create the body first.
38569 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38570 this.el.addClass("roo-tabs-bottom");
38572 // next create the tabs holders
38574 if (this.tabPosition == "west"){
38576 var reg = this.region; // fake it..
38578 if (!reg.mgr.parent) {
38581 reg = reg.mgr.parent.region;
38583 Roo.log("got nest?");
38585 if (reg.mgr.getRegion('west')) {
38586 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38587 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38588 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38589 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38590 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38598 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38599 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38600 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38601 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38606 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38609 // finally - if tabs are at the top, then create the body last..
38610 if(this.tabPosition != "bottom"){
38611 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38612 * @type Roo.Element
38614 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38615 this.el.addClass("roo-tabs-top");
38619 this.bodyEl.setStyle("position", "relative");
38621 this.active = null;
38622 this.activateDelegate = this.activate.createDelegate(this);
38627 * Fires when the active tab changes
38628 * @param {Roo.TabPanel} this
38629 * @param {Roo.TabPanelItem} activePanel The new active tab
38633 * @event beforetabchange
38634 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38635 * @param {Roo.TabPanel} this
38636 * @param {Object} e Set cancel to true on this object to cancel the tab change
38637 * @param {Roo.TabPanelItem} tab The tab being changed to
38639 "beforetabchange" : true
38642 Roo.EventManager.onWindowResize(this.onResize, this);
38643 this.cpad = this.el.getPadding("lr");
38644 this.hiddenCount = 0;
38647 // toolbar on the tabbar support...
38648 if (this.toolbar) {
38649 alert("no toolbar support yet");
38650 this.toolbar = false;
38652 var tcfg = this.toolbar;
38653 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
38654 this.toolbar = new Roo.Toolbar(tcfg);
38655 if (Roo.isSafari) {
38656 var tbl = tcfg.container.child('table', true);
38657 tbl.setAttribute('width', '100%');
38665 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38668 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38670 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38672 tabPosition : "top",
38674 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38676 currentTabWidth : 0,
38678 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38682 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38686 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38688 preferredTabWidth : 175,
38690 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38692 resizeTabs : false,
38694 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38696 monitorResize : true,
38698 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
38700 toolbar : false, // set by caller..
38702 region : false, /// set by caller
38704 disableTooltips : true, // not used yet...
38707 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38708 * @param {String} id The id of the div to use <b>or create</b>
38709 * @param {String} text The text for the tab
38710 * @param {String} content (optional) Content to put in the TabPanelItem body
38711 * @param {Boolean} closable (optional) True to create a close icon on the tab
38712 * @return {Roo.TabPanelItem} The created TabPanelItem
38714 addTab : function(id, text, content, closable, tpl)
38716 var item = new Roo.bootstrap.panel.TabItem({
38720 closable : closable,
38723 this.addTabItem(item);
38725 item.setContent(content);
38731 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38732 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38733 * @return {Roo.TabPanelItem}
38735 getTab : function(id){
38736 return this.items[id];
38740 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38741 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38743 hideTab : function(id){
38744 var t = this.items[id];
38747 this.hiddenCount++;
38748 this.autoSizeTabs();
38753 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38754 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38756 unhideTab : function(id){
38757 var t = this.items[id];
38759 t.setHidden(false);
38760 this.hiddenCount--;
38761 this.autoSizeTabs();
38766 * Adds an existing {@link Roo.TabPanelItem}.
38767 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38769 addTabItem : function(item)
38771 this.items[item.id] = item;
38772 this.items.push(item);
38773 this.autoSizeTabs();
38774 // if(this.resizeTabs){
38775 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38776 // this.autoSizeTabs();
38778 // item.autoSize();
38783 * Removes a {@link Roo.TabPanelItem}.
38784 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38786 removeTab : function(id){
38787 var items = this.items;
38788 var tab = items[id];
38789 if(!tab) { return; }
38790 var index = items.indexOf(tab);
38791 if(this.active == tab && items.length > 1){
38792 var newTab = this.getNextAvailable(index);
38797 this.stripEl.dom.removeChild(tab.pnode.dom);
38798 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38799 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38801 items.splice(index, 1);
38802 delete this.items[tab.id];
38803 tab.fireEvent("close", tab);
38804 tab.purgeListeners();
38805 this.autoSizeTabs();
38808 getNextAvailable : function(start){
38809 var items = this.items;
38811 // look for a next tab that will slide over to
38812 // replace the one being removed
38813 while(index < items.length){
38814 var item = items[++index];
38815 if(item && !item.isHidden()){
38819 // if one isn't found select the previous tab (on the left)
38822 var item = items[--index];
38823 if(item && !item.isHidden()){
38831 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38832 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38834 disableTab : function(id){
38835 var tab = this.items[id];
38836 if(tab && this.active != tab){
38842 * Enables a {@link Roo.TabPanelItem} that is disabled.
38843 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38845 enableTab : function(id){
38846 var tab = this.items[id];
38851 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38852 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38853 * @return {Roo.TabPanelItem} The TabPanelItem.
38855 activate : function(id)
38857 //Roo.log('activite:' + id);
38859 var tab = this.items[id];
38863 if(tab == this.active || tab.disabled){
38867 this.fireEvent("beforetabchange", this, e, tab);
38868 if(e.cancel !== true && !tab.disabled){
38870 this.active.hide();
38872 this.active = this.items[id];
38873 this.active.show();
38874 this.fireEvent("tabchange", this, this.active);
38880 * Gets the active {@link Roo.TabPanelItem}.
38881 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38883 getActiveTab : function(){
38884 return this.active;
38888 * Updates the tab body element to fit the height of the container element
38889 * for overflow scrolling
38890 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38892 syncHeight : function(targetHeight){
38893 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38894 var bm = this.bodyEl.getMargins();
38895 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38896 this.bodyEl.setHeight(newHeight);
38900 onResize : function(){
38901 if(this.monitorResize){
38902 this.autoSizeTabs();
38907 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38909 beginUpdate : function(){
38910 this.updating = true;
38914 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38916 endUpdate : function(){
38917 this.updating = false;
38918 this.autoSizeTabs();
38922 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38924 autoSizeTabs : function()
38926 var count = this.items.length;
38927 var vcount = count - this.hiddenCount;
38930 this.stripEl.hide();
38932 this.stripEl.show();
38935 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38940 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38941 var availWidth = Math.floor(w / vcount);
38942 var b = this.stripBody;
38943 if(b.getWidth() > w){
38944 var tabs = this.items;
38945 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38946 if(availWidth < this.minTabWidth){
38947 /*if(!this.sleft){ // incomplete scrolling code
38948 this.createScrollButtons();
38951 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38954 if(this.currentTabWidth < this.preferredTabWidth){
38955 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38961 * Returns the number of tabs in this TabPanel.
38964 getCount : function(){
38965 return this.items.length;
38969 * Resizes all the tabs to the passed width
38970 * @param {Number} The new width
38972 setTabWidth : function(width){
38973 this.currentTabWidth = width;
38974 for(var i = 0, len = this.items.length; i < len; i++) {
38975 if(!this.items[i].isHidden()) {
38976 this.items[i].setWidth(width);
38982 * Destroys this TabPanel
38983 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38985 destroy : function(removeEl){
38986 Roo.EventManager.removeResizeListener(this.onResize, this);
38987 for(var i = 0, len = this.items.length; i < len; i++){
38988 this.items[i].purgeListeners();
38990 if(removeEl === true){
38991 this.el.update("");
38996 createStrip : function(container)
38998 var strip = document.createElement("nav");
38999 strip.className = Roo.bootstrap.version == 4 ?
39000 "navbar-light bg-light" :
39001 "navbar navbar-default"; //"x-tabs-wrap";
39002 container.appendChild(strip);
39006 createStripList : function(strip)
39008 // div wrapper for retard IE
39009 // returns the "tr" element.
39010 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
39011 //'<div class="x-tabs-strip-wrap">'+
39012 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
39013 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
39014 return strip.firstChild; //.firstChild.firstChild.firstChild;
39016 createBody : function(container)
39018 var body = document.createElement("div");
39019 Roo.id(body, "tab-body");
39020 //Roo.fly(body).addClass("x-tabs-body");
39021 Roo.fly(body).addClass("tab-content");
39022 container.appendChild(body);
39025 createItemBody :function(bodyEl, id){
39026 var body = Roo.getDom(id);
39028 body = document.createElement("div");
39031 //Roo.fly(body).addClass("x-tabs-item-body");
39032 Roo.fly(body).addClass("tab-pane");
39033 bodyEl.insertBefore(body, bodyEl.firstChild);
39037 createStripElements : function(stripEl, text, closable, tpl)
39039 var td = document.createElement("li"); // was td..
39040 td.className = 'nav-item';
39042 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
39045 stripEl.appendChild(td);
39047 td.className = "x-tabs-closable";
39048 if(!this.closeTpl){
39049 this.closeTpl = new Roo.Template(
39050 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39051 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
39052 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
39055 var el = this.closeTpl.overwrite(td, {"text": text});
39056 var close = el.getElementsByTagName("div")[0];
39057 var inner = el.getElementsByTagName("em")[0];
39058 return {"el": el, "close": close, "inner": inner};
39061 // not sure what this is..
39062 // if(!this.tabTpl){
39063 //this.tabTpl = new Roo.Template(
39064 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39065 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
39067 // this.tabTpl = new Roo.Template(
39068 // '<a href="#">' +
39069 // '<span unselectable="on"' +
39070 // (this.disableTooltips ? '' : ' title="{text}"') +
39071 // ' >{text}</span></a>'
39077 var template = tpl || this.tabTpl || false;
39080 template = new Roo.Template(
39081 Roo.bootstrap.version == 4 ?
39083 '<a class="nav-link" href="#" unselectable="on"' +
39084 (this.disableTooltips ? '' : ' title="{text}"') +
39087 '<a class="nav-link" href="#">' +
39088 '<span unselectable="on"' +
39089 (this.disableTooltips ? '' : ' title="{text}"') +
39090 ' >{text}</span></a>'
39095 switch (typeof(template)) {
39099 template = new Roo.Template(template);
39105 var el = template.overwrite(td, {"text": text});
39107 var inner = el.getElementsByTagName("span")[0];
39109 return {"el": el, "inner": inner};
39117 * @class Roo.TabPanelItem
39118 * @extends Roo.util.Observable
39119 * Represents an individual item (tab plus body) in a TabPanel.
39120 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
39121 * @param {String} id The id of this TabPanelItem
39122 * @param {String} text The text for the tab of this TabPanelItem
39123 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
39125 Roo.bootstrap.panel.TabItem = function(config){
39127 * The {@link Roo.TabPanel} this TabPanelItem belongs to
39128 * @type Roo.TabPanel
39130 this.tabPanel = config.panel;
39132 * The id for this TabPanelItem
39135 this.id = config.id;
39137 this.disabled = false;
39139 this.text = config.text;
39141 this.loaded = false;
39142 this.closable = config.closable;
39145 * The body element for this TabPanelItem.
39146 * @type Roo.Element
39148 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
39149 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
39150 this.bodyEl.setStyle("display", "block");
39151 this.bodyEl.setStyle("zoom", "1");
39152 //this.hideAction();
39154 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
39156 this.el = Roo.get(els.el);
39157 this.inner = Roo.get(els.inner, true);
39158 this.textEl = Roo.bootstrap.version == 4 ?
39159 this.el : Roo.get(this.el.dom.firstChild, true);
39161 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
39162 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
39165 // this.el.on("mousedown", this.onTabMouseDown, this);
39166 this.el.on("click", this.onTabClick, this);
39168 if(config.closable){
39169 var c = Roo.get(els.close, true);
39170 c.dom.title = this.closeText;
39171 c.addClassOnOver("close-over");
39172 c.on("click", this.closeClick, this);
39178 * Fires when this tab becomes the active tab.
39179 * @param {Roo.TabPanel} tabPanel The parent TabPanel
39180 * @param {Roo.TabPanelItem} this
39184 * @event beforeclose
39185 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
39186 * @param {Roo.TabPanelItem} this
39187 * @param {Object} e Set cancel to true on this object to cancel the close.
39189 "beforeclose": true,
39192 * Fires when this tab is closed.
39193 * @param {Roo.TabPanelItem} this
39197 * @event deactivate
39198 * Fires when this tab is no longer the active tab.
39199 * @param {Roo.TabPanel} tabPanel The parent TabPanel
39200 * @param {Roo.TabPanelItem} this
39202 "deactivate" : true
39204 this.hidden = false;
39206 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
39209 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
39211 purgeListeners : function(){
39212 Roo.util.Observable.prototype.purgeListeners.call(this);
39213 this.el.removeAllListeners();
39216 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
39219 this.status_node.addClass("active");
39222 this.tabPanel.stripWrap.repaint();
39224 this.fireEvent("activate", this.tabPanel, this);
39228 * Returns true if this tab is the active tab.
39229 * @return {Boolean}
39231 isActive : function(){
39232 return this.tabPanel.getActiveTab() == this;
39236 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
39239 this.status_node.removeClass("active");
39241 this.fireEvent("deactivate", this.tabPanel, this);
39244 hideAction : function(){
39245 this.bodyEl.hide();
39246 this.bodyEl.setStyle("position", "absolute");
39247 this.bodyEl.setLeft("-20000px");
39248 this.bodyEl.setTop("-20000px");
39251 showAction : function(){
39252 this.bodyEl.setStyle("position", "relative");
39253 this.bodyEl.setTop("");
39254 this.bodyEl.setLeft("");
39255 this.bodyEl.show();
39259 * Set the tooltip for the tab.
39260 * @param {String} tooltip The tab's tooltip
39262 setTooltip : function(text){
39263 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
39264 this.textEl.dom.qtip = text;
39265 this.textEl.dom.removeAttribute('title');
39267 this.textEl.dom.title = text;
39271 onTabClick : function(e){
39272 e.preventDefault();
39273 this.tabPanel.activate(this.id);
39276 onTabMouseDown : function(e){
39277 e.preventDefault();
39278 this.tabPanel.activate(this.id);
39281 getWidth : function(){
39282 return this.inner.getWidth();
39285 setWidth : function(width){
39286 var iwidth = width - this.linode.getPadding("lr");
39287 this.inner.setWidth(iwidth);
39288 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
39289 this.linode.setWidth(width);
39293 * Show or hide the tab
39294 * @param {Boolean} hidden True to hide or false to show.
39296 setHidden : function(hidden){
39297 this.hidden = hidden;
39298 this.linode.setStyle("display", hidden ? "none" : "");
39302 * Returns true if this tab is "hidden"
39303 * @return {Boolean}
39305 isHidden : function(){
39306 return this.hidden;
39310 * Returns the text for this tab
39313 getText : function(){
39317 autoSize : function(){
39318 //this.el.beginMeasure();
39319 this.textEl.setWidth(1);
39321 * #2804 [new] Tabs in Roojs
39322 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39324 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39325 //this.el.endMeasure();
39329 * Sets the text for the tab (Note: this also sets the tooltip text)
39330 * @param {String} text The tab's text and tooltip
39332 setText : function(text){
39334 this.textEl.update(text);
39335 this.setTooltip(text);
39336 //if(!this.tabPanel.resizeTabs){
39337 // this.autoSize();
39341 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39343 activate : function(){
39344 this.tabPanel.activate(this.id);
39348 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39350 disable : function(){
39351 if(this.tabPanel.active != this){
39352 this.disabled = true;
39353 this.status_node.addClass("disabled");
39358 * Enables this TabPanelItem if it was previously disabled.
39360 enable : function(){
39361 this.disabled = false;
39362 this.status_node.removeClass("disabled");
39366 * Sets the content for this TabPanelItem.
39367 * @param {String} content The content
39368 * @param {Boolean} loadScripts true to look for and load scripts
39370 setContent : function(content, loadScripts){
39371 this.bodyEl.update(content, loadScripts);
39375 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39376 * @return {Roo.UpdateManager} The UpdateManager
39378 getUpdateManager : function(){
39379 return this.bodyEl.getUpdateManager();
39383 * Set a URL to be used to load the content for this TabPanelItem.
39384 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39385 * @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)
39386 * @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)
39387 * @return {Roo.UpdateManager} The UpdateManager
39389 setUrl : function(url, params, loadOnce){
39390 if(this.refreshDelegate){
39391 this.un('activate', this.refreshDelegate);
39393 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39394 this.on("activate", this.refreshDelegate);
39395 return this.bodyEl.getUpdateManager();
39399 _handleRefresh : function(url, params, loadOnce){
39400 if(!loadOnce || !this.loaded){
39401 var updater = this.bodyEl.getUpdateManager();
39402 updater.update(url, params, this._setLoaded.createDelegate(this));
39407 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
39408 * Will fail silently if the setUrl method has not been called.
39409 * This does not activate the panel, just updates its content.
39411 refresh : function(){
39412 if(this.refreshDelegate){
39413 this.loaded = false;
39414 this.refreshDelegate();
39419 _setLoaded : function(){
39420 this.loaded = true;
39424 closeClick : function(e){
39427 this.fireEvent("beforeclose", this, o);
39428 if(o.cancel !== true){
39429 this.tabPanel.removeTab(this.id);
39433 * The text displayed in the tooltip for the close icon.
39436 closeText : "Close this tab"
39439 * This script refer to:
39440 * Title: International Telephone Input
39441 * Author: Jack O'Connor
39442 * Code version: v12.1.12
39443 * Availability: https://github.com/jackocnr/intl-tel-input.git
39446 Roo.bootstrap.PhoneInputData = function() {
39449 "Afghanistan (افغانستان)",
39454 "Albania (Shqipëri)",
39459 "Algeria (الجزائر)",
39484 "Antigua and Barbuda",
39494 "Armenia (Հայաստան)",
39510 "Austria (Österreich)",
39515 "Azerbaijan (Azərbaycan)",
39525 "Bahrain (البحرين)",
39530 "Bangladesh (বাংলাদেশ)",
39540 "Belarus (Беларусь)",
39545 "Belgium (België)",
39575 "Bosnia and Herzegovina (Босна и Херцеговина)",
39590 "British Indian Ocean Territory",
39595 "British Virgin Islands",
39605 "Bulgaria (България)",
39615 "Burundi (Uburundi)",
39620 "Cambodia (កម្ពុជា)",
39625 "Cameroon (Cameroun)",
39634 ["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"]
39637 "Cape Verde (Kabu Verdi)",
39642 "Caribbean Netherlands",
39653 "Central African Republic (République centrafricaine)",
39673 "Christmas Island",
39679 "Cocos (Keeling) Islands",
39690 "Comoros (جزر القمر)",
39695 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39700 "Congo (Republic) (Congo-Brazzaville)",
39720 "Croatia (Hrvatska)",
39741 "Czech Republic (Česká republika)",
39746 "Denmark (Danmark)",
39761 "Dominican Republic (República Dominicana)",
39765 ["809", "829", "849"]
39783 "Equatorial Guinea (Guinea Ecuatorial)",
39803 "Falkland Islands (Islas Malvinas)",
39808 "Faroe Islands (Føroyar)",
39829 "French Guiana (Guyane française)",
39834 "French Polynesia (Polynésie française)",
39849 "Georgia (საქართველო)",
39854 "Germany (Deutschland)",
39874 "Greenland (Kalaallit Nunaat)",
39911 "Guinea-Bissau (Guiné Bissau)",
39936 "Hungary (Magyarország)",
39941 "Iceland (Ísland)",
39961 "Iraq (العراق)",
39977 "Israel (ישראל)",
40004 "Jordan (الأردن)",
40009 "Kazakhstan (Казахстан)",
40030 "Kuwait (الكويت)",
40035 "Kyrgyzstan (Кыргызстан)",
40045 "Latvia (Latvija)",
40050 "Lebanon (لبنان)",
40065 "Libya (ليبيا)",
40075 "Lithuania (Lietuva)",
40090 "Macedonia (FYROM) (Македонија)",
40095 "Madagascar (Madagasikara)",
40125 "Marshall Islands",
40135 "Mauritania (موريتانيا)",
40140 "Mauritius (Moris)",
40161 "Moldova (Republica Moldova)",
40171 "Mongolia (Монгол)",
40176 "Montenegro (Crna Gora)",
40186 "Morocco (المغرب)",
40192 "Mozambique (Moçambique)",
40197 "Myanmar (Burma) (မြန်မာ)",
40202 "Namibia (Namibië)",
40217 "Netherlands (Nederland)",
40222 "New Caledonia (Nouvelle-Calédonie)",
40257 "North Korea (조선 민주주의 인민 공화국)",
40262 "Northern Mariana Islands",
40278 "Pakistan (پاکستان)",
40288 "Palestine (فلسطين)",
40298 "Papua New Guinea",
40340 "Réunion (La Réunion)",
40346 "Romania (România)",
40362 "Saint Barthélemy",
40373 "Saint Kitts and Nevis",
40383 "Saint Martin (Saint-Martin (partie française))",
40389 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40394 "Saint Vincent and the Grenadines",
40409 "São Tomé and Príncipe (São Tomé e Príncipe)",
40414 "Saudi Arabia (المملكة العربية السعودية)",
40419 "Senegal (Sénégal)",
40449 "Slovakia (Slovensko)",
40454 "Slovenia (Slovenija)",
40464 "Somalia (Soomaaliya)",
40474 "South Korea (대한민국)",
40479 "South Sudan (جنوب السودان)",
40489 "Sri Lanka (ශ්රී ලංකාව)",
40494 "Sudan (السودان)",
40504 "Svalbard and Jan Mayen",
40515 "Sweden (Sverige)",
40520 "Switzerland (Schweiz)",
40525 "Syria (سوريا)",
40570 "Trinidad and Tobago",
40575 "Tunisia (تونس)",
40580 "Turkey (Türkiye)",
40590 "Turks and Caicos Islands",
40600 "U.S. Virgin Islands",
40610 "Ukraine (Україна)",
40615 "United Arab Emirates (الإمارات العربية المتحدة)",
40637 "Uzbekistan (Oʻzbekiston)",
40647 "Vatican City (Città del Vaticano)",
40658 "Vietnam (Việt Nam)",
40663 "Wallis and Futuna (Wallis-et-Futuna)",
40668 "Western Sahara (الصحراء الغربية)",
40674 "Yemen (اليمن)",
40698 * This script refer to:
40699 * Title: International Telephone Input
40700 * Author: Jack O'Connor
40701 * Code version: v12.1.12
40702 * Availability: https://github.com/jackocnr/intl-tel-input.git
40706 * @class Roo.bootstrap.PhoneInput
40707 * @extends Roo.bootstrap.TriggerField
40708 * An input with International dial-code selection
40710 * @cfg {String} defaultDialCode default '+852'
40711 * @cfg {Array} preferedCountries default []
40714 * Create a new PhoneInput.
40715 * @param {Object} config Configuration options
40718 Roo.bootstrap.PhoneInput = function(config) {
40719 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40722 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40724 listWidth: undefined,
40726 selectedClass: 'active',
40728 invalidClass : "has-warning",
40730 validClass: 'has-success',
40732 allowed: '0123456789',
40737 * @cfg {String} defaultDialCode The default dial code when initializing the input
40739 defaultDialCode: '+852',
40742 * @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
40744 preferedCountries: false,
40746 getAutoCreate : function()
40748 var data = Roo.bootstrap.PhoneInputData();
40749 var align = this.labelAlign || this.parentLabelAlign();
40752 this.allCountries = [];
40753 this.dialCodeMapping = [];
40755 for (var i = 0; i < data.length; i++) {
40757 this.allCountries[i] = {
40761 priority: c[3] || 0,
40762 areaCodes: c[4] || null
40764 this.dialCodeMapping[c[2]] = {
40767 priority: c[3] || 0,
40768 areaCodes: c[4] || null
40780 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40781 maxlength: this.max_length,
40782 cls : 'form-control tel-input',
40783 autocomplete: 'new-password'
40786 var hiddenInput = {
40789 cls: 'hidden-tel-input'
40793 hiddenInput.name = this.name;
40796 if (this.disabled) {
40797 input.disabled = true;
40800 var flag_container = {
40817 cls: this.hasFeedback ? 'has-feedback' : '',
40823 cls: 'dial-code-holder',
40830 cls: 'roo-select2-container input-group',
40837 if (this.fieldLabel.length) {
40840 tooltip: 'This field is required'
40846 cls: 'control-label',
40852 html: this.fieldLabel
40855 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40861 if(this.indicatorpos == 'right') {
40862 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40869 if(align == 'left') {
40877 if(this.labelWidth > 12){
40878 label.style = "width: " + this.labelWidth + 'px';
40880 if(this.labelWidth < 13 && this.labelmd == 0){
40881 this.labelmd = this.labelWidth;
40883 if(this.labellg > 0){
40884 label.cls += ' col-lg-' + this.labellg;
40885 input.cls += ' col-lg-' + (12 - this.labellg);
40887 if(this.labelmd > 0){
40888 label.cls += ' col-md-' + this.labelmd;
40889 container.cls += ' col-md-' + (12 - this.labelmd);
40891 if(this.labelsm > 0){
40892 label.cls += ' col-sm-' + this.labelsm;
40893 container.cls += ' col-sm-' + (12 - this.labelsm);
40895 if(this.labelxs > 0){
40896 label.cls += ' col-xs-' + this.labelxs;
40897 container.cls += ' col-xs-' + (12 - this.labelxs);
40907 var settings = this;
40909 ['xs','sm','md','lg'].map(function(size){
40910 if (settings[size]) {
40911 cfg.cls += ' col-' + size + '-' + settings[size];
40915 this.store = new Roo.data.Store({
40916 proxy : new Roo.data.MemoryProxy({}),
40917 reader : new Roo.data.JsonReader({
40928 'name' : 'dialCode',
40932 'name' : 'priority',
40936 'name' : 'areaCodes',
40943 if(!this.preferedCountries) {
40944 this.preferedCountries = [
40951 var p = this.preferedCountries.reverse();
40954 for (var i = 0; i < p.length; i++) {
40955 for (var j = 0; j < this.allCountries.length; j++) {
40956 if(this.allCountries[j].iso2 == p[i]) {
40957 var t = this.allCountries[j];
40958 this.allCountries.splice(j,1);
40959 this.allCountries.unshift(t);
40965 this.store.proxy.data = {
40967 data: this.allCountries
40973 initEvents : function()
40976 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40978 this.indicator = this.indicatorEl();
40979 this.flag = this.flagEl();
40980 this.dialCodeHolder = this.dialCodeHolderEl();
40982 this.trigger = this.el.select('div.flag-box',true).first();
40983 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40988 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40989 _this.list.setWidth(lw);
40992 this.list.on('mouseover', this.onViewOver, this);
40993 this.list.on('mousemove', this.onViewMove, this);
40994 this.inputEl().on("keyup", this.onKeyUp, this);
40995 this.inputEl().on("keypress", this.onKeyPress, this);
40997 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40999 this.view = new Roo.View(this.list, this.tpl, {
41000 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41003 this.view.on('click', this.onViewClick, this);
41004 this.setValue(this.defaultDialCode);
41007 onTriggerClick : function(e)
41009 Roo.log('trigger click');
41014 if(this.isExpanded()){
41016 this.hasFocus = false;
41018 this.store.load({});
41019 this.hasFocus = true;
41024 isExpanded : function()
41026 return this.list.isVisible();
41029 collapse : function()
41031 if(!this.isExpanded()){
41035 Roo.get(document).un('mousedown', this.collapseIf, this);
41036 Roo.get(document).un('mousewheel', this.collapseIf, this);
41037 this.fireEvent('collapse', this);
41041 expand : function()
41045 if(this.isExpanded() || !this.hasFocus){
41049 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
41050 this.list.setWidth(lw);
41053 this.restrictHeight();
41055 Roo.get(document).on('mousedown', this.collapseIf, this);
41056 Roo.get(document).on('mousewheel', this.collapseIf, this);
41058 this.fireEvent('expand', this);
41061 restrictHeight : function()
41063 this.list.alignTo(this.inputEl(), this.listAlign);
41064 this.list.alignTo(this.inputEl(), this.listAlign);
41067 onViewOver : function(e, t)
41069 if(this.inKeyMode){
41072 var item = this.view.findItemFromChild(t);
41075 var index = this.view.indexOf(item);
41076 this.select(index, false);
41081 onViewClick : function(view, doFocus, el, e)
41083 var index = this.view.getSelectedIndexes()[0];
41085 var r = this.store.getAt(index);
41088 this.onSelect(r, index);
41090 if(doFocus !== false && !this.blockFocus){
41091 this.inputEl().focus();
41095 onViewMove : function(e, t)
41097 this.inKeyMode = false;
41100 select : function(index, scrollIntoView)
41102 this.selectedIndex = index;
41103 this.view.select(index);
41104 if(scrollIntoView !== false){
41105 var el = this.view.getNode(index);
41107 this.list.scrollChildIntoView(el, false);
41112 createList : function()
41114 this.list = Roo.get(document.body).createChild({
41116 cls: 'typeahead typeahead-long dropdown-menu tel-list',
41117 style: 'display:none'
41120 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
41123 collapseIf : function(e)
41125 var in_combo = e.within(this.el);
41126 var in_list = e.within(this.list);
41127 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
41129 if (in_combo || in_list || is_list) {
41135 onSelect : function(record, index)
41137 if(this.fireEvent('beforeselect', this, record, index) !== false){
41139 this.setFlagClass(record.data.iso2);
41140 this.setDialCode(record.data.dialCode);
41141 this.hasFocus = false;
41143 this.fireEvent('select', this, record, index);
41147 flagEl : function()
41149 var flag = this.el.select('div.flag',true).first();
41156 dialCodeHolderEl : function()
41158 var d = this.el.select('input.dial-code-holder',true).first();
41165 setDialCode : function(v)
41167 this.dialCodeHolder.dom.value = '+'+v;
41170 setFlagClass : function(n)
41172 this.flag.dom.className = 'flag '+n;
41175 getValue : function()
41177 var v = this.inputEl().getValue();
41178 if(this.dialCodeHolder) {
41179 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
41184 setValue : function(v)
41186 var d = this.getDialCode(v);
41188 //invalid dial code
41189 if(v.length == 0 || !d || d.length == 0) {
41191 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41192 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41198 this.setFlagClass(this.dialCodeMapping[d].iso2);
41199 this.setDialCode(d);
41200 this.inputEl().dom.value = v.replace('+'+d,'');
41201 this.hiddenEl().dom.value = this.getValue();
41206 getDialCode : function(v)
41210 if (v.length == 0) {
41211 return this.dialCodeHolder.dom.value;
41215 if (v.charAt(0) != "+") {
41218 var numericChars = "";
41219 for (var i = 1; i < v.length; i++) {
41220 var c = v.charAt(i);
41223 if (this.dialCodeMapping[numericChars]) {
41224 dialCode = v.substr(1, i);
41226 if (numericChars.length == 4) {
41236 this.setValue(this.defaultDialCode);
41240 hiddenEl : function()
41242 return this.el.select('input.hidden-tel-input',true).first();
41245 // after setting val
41246 onKeyUp : function(e){
41247 this.setValue(this.getValue());
41250 onKeyPress : function(e){
41251 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
41258 * @class Roo.bootstrap.MoneyField
41259 * @extends Roo.bootstrap.ComboBox
41260 * Bootstrap MoneyField class
41263 * Create a new MoneyField.
41264 * @param {Object} config Configuration options
41267 Roo.bootstrap.MoneyField = function(config) {
41269 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
41273 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
41276 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41278 allowDecimals : true,
41280 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41282 decimalSeparator : ".",
41284 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41286 decimalPrecision : 0,
41288 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41290 allowNegative : true,
41292 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41296 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41298 minValue : Number.NEGATIVE_INFINITY,
41300 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41302 maxValue : Number.MAX_VALUE,
41304 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41306 minText : "The minimum value for this field is {0}",
41308 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41310 maxText : "The maximum value for this field is {0}",
41312 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
41313 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41315 nanText : "{0} is not a valid number",
41317 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41321 * @cfg {String} defaults currency of the MoneyField
41322 * value should be in lkey
41324 defaultCurrency : false,
41326 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41328 thousandsDelimiter : false,
41330 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41341 getAutoCreate : function()
41343 var align = this.labelAlign || this.parentLabelAlign();
41355 cls : 'form-control roo-money-amount-input',
41356 autocomplete: 'new-password'
41359 var hiddenInput = {
41363 cls: 'hidden-number-input'
41366 if(this.max_length) {
41367 input.maxlength = this.max_length;
41371 hiddenInput.name = this.name;
41374 if (this.disabled) {
41375 input.disabled = true;
41378 var clg = 12 - this.inputlg;
41379 var cmd = 12 - this.inputmd;
41380 var csm = 12 - this.inputsm;
41381 var cxs = 12 - this.inputxs;
41385 cls : 'row roo-money-field',
41389 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41393 cls: 'roo-select2-container input-group',
41397 cls : 'form-control roo-money-currency-input',
41398 autocomplete: 'new-password',
41400 name : this.currencyName
41404 cls : 'input-group-addon',
41418 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41422 cls: this.hasFeedback ? 'has-feedback' : '',
41433 if (this.fieldLabel.length) {
41436 tooltip: 'This field is required'
41442 cls: 'control-label',
41448 html: this.fieldLabel
41451 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41457 if(this.indicatorpos == 'right') {
41458 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41465 if(align == 'left') {
41473 if(this.labelWidth > 12){
41474 label.style = "width: " + this.labelWidth + 'px';
41476 if(this.labelWidth < 13 && this.labelmd == 0){
41477 this.labelmd = this.labelWidth;
41479 if(this.labellg > 0){
41480 label.cls += ' col-lg-' + this.labellg;
41481 input.cls += ' col-lg-' + (12 - this.labellg);
41483 if(this.labelmd > 0){
41484 label.cls += ' col-md-' + this.labelmd;
41485 container.cls += ' col-md-' + (12 - this.labelmd);
41487 if(this.labelsm > 0){
41488 label.cls += ' col-sm-' + this.labelsm;
41489 container.cls += ' col-sm-' + (12 - this.labelsm);
41491 if(this.labelxs > 0){
41492 label.cls += ' col-xs-' + this.labelxs;
41493 container.cls += ' col-xs-' + (12 - this.labelxs);
41504 var settings = this;
41506 ['xs','sm','md','lg'].map(function(size){
41507 if (settings[size]) {
41508 cfg.cls += ' col-' + size + '-' + settings[size];
41515 initEvents : function()
41517 this.indicator = this.indicatorEl();
41519 this.initCurrencyEvent();
41521 this.initNumberEvent();
41524 initCurrencyEvent : function()
41527 throw "can not find store for combo";
41530 this.store = Roo.factory(this.store, Roo.data);
41531 this.store.parent = this;
41535 this.triggerEl = this.el.select('.input-group-addon', true).first();
41537 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41542 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41543 _this.list.setWidth(lw);
41546 this.list.on('mouseover', this.onViewOver, this);
41547 this.list.on('mousemove', this.onViewMove, this);
41548 this.list.on('scroll', this.onViewScroll, this);
41551 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41554 this.view = new Roo.View(this.list, this.tpl, {
41555 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41558 this.view.on('click', this.onViewClick, this);
41560 this.store.on('beforeload', this.onBeforeLoad, this);
41561 this.store.on('load', this.onLoad, this);
41562 this.store.on('loadexception', this.onLoadException, this);
41564 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41565 "up" : function(e){
41566 this.inKeyMode = true;
41570 "down" : function(e){
41571 if(!this.isExpanded()){
41572 this.onTriggerClick();
41574 this.inKeyMode = true;
41579 "enter" : function(e){
41582 if(this.fireEvent("specialkey", this, e)){
41583 this.onViewClick(false);
41589 "esc" : function(e){
41593 "tab" : function(e){
41596 if(this.fireEvent("specialkey", this, e)){
41597 this.onViewClick(false);
41605 doRelay : function(foo, bar, hname){
41606 if(hname == 'down' || this.scope.isExpanded()){
41607 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41615 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41619 initNumberEvent : function(e)
41621 this.inputEl().on("keydown" , this.fireKey, this);
41622 this.inputEl().on("focus", this.onFocus, this);
41623 this.inputEl().on("blur", this.onBlur, this);
41625 this.inputEl().relayEvent('keyup', this);
41627 if(this.indicator){
41628 this.indicator.addClass('invisible');
41631 this.originalValue = this.getValue();
41633 if(this.validationEvent == 'keyup'){
41634 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41635 this.inputEl().on('keyup', this.filterValidation, this);
41637 else if(this.validationEvent !== false){
41638 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41641 if(this.selectOnFocus){
41642 this.on("focus", this.preFocus, this);
41645 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41646 this.inputEl().on("keypress", this.filterKeys, this);
41648 this.inputEl().relayEvent('keypress', this);
41651 var allowed = "0123456789";
41653 if(this.allowDecimals){
41654 allowed += this.decimalSeparator;
41657 if(this.allowNegative){
41661 if(this.thousandsDelimiter) {
41665 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41667 var keyPress = function(e){
41669 var k = e.getKey();
41671 var c = e.getCharCode();
41674 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41675 allowed.indexOf(String.fromCharCode(c)) === -1
41681 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41685 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41690 this.inputEl().on("keypress", keyPress, this);
41694 onTriggerClick : function(e)
41701 this.loadNext = false;
41703 if(this.isExpanded()){
41708 this.hasFocus = true;
41710 if(this.triggerAction == 'all') {
41711 this.doQuery(this.allQuery, true);
41715 this.doQuery(this.getRawValue());
41718 getCurrency : function()
41720 var v = this.currencyEl().getValue();
41725 restrictHeight : function()
41727 this.list.alignTo(this.currencyEl(), this.listAlign);
41728 this.list.alignTo(this.currencyEl(), this.listAlign);
41731 onViewClick : function(view, doFocus, el, e)
41733 var index = this.view.getSelectedIndexes()[0];
41735 var r = this.store.getAt(index);
41738 this.onSelect(r, index);
41742 onSelect : function(record, index){
41744 if(this.fireEvent('beforeselect', this, record, index) !== false){
41746 this.setFromCurrencyData(index > -1 ? record.data : false);
41750 this.fireEvent('select', this, record, index);
41754 setFromCurrencyData : function(o)
41758 this.lastCurrency = o;
41760 if (this.currencyField) {
41761 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41763 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41766 this.lastSelectionText = currency;
41768 //setting default currency
41769 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41770 this.setCurrency(this.defaultCurrency);
41774 this.setCurrency(currency);
41777 setFromData : function(o)
41781 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41783 this.setFromCurrencyData(c);
41788 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41790 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41793 this.setValue(value);
41797 setCurrency : function(v)
41799 this.currencyValue = v;
41802 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41807 setValue : function(v)
41809 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41815 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41817 this.inputEl().dom.value = (v == '') ? '' :
41818 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41820 if(!this.allowZero && v === '0') {
41821 this.hiddenEl().dom.value = '';
41822 this.inputEl().dom.value = '';
41829 getRawValue : function()
41831 var v = this.inputEl().getValue();
41836 getValue : function()
41838 return this.fixPrecision(this.parseValue(this.getRawValue()));
41841 parseValue : function(value)
41843 if(this.thousandsDelimiter) {
41845 r = new RegExp(",", "g");
41846 value = value.replace(r, "");
41849 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41850 return isNaN(value) ? '' : value;
41854 fixPrecision : function(value)
41856 if(this.thousandsDelimiter) {
41858 r = new RegExp(",", "g");
41859 value = value.replace(r, "");
41862 var nan = isNaN(value);
41864 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41865 return nan ? '' : value;
41867 return parseFloat(value).toFixed(this.decimalPrecision);
41870 decimalPrecisionFcn : function(v)
41872 return Math.floor(v);
41875 validateValue : function(value)
41877 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41881 var num = this.parseValue(value);
41884 this.markInvalid(String.format(this.nanText, value));
41888 if(num < this.minValue){
41889 this.markInvalid(String.format(this.minText, this.minValue));
41893 if(num > this.maxValue){
41894 this.markInvalid(String.format(this.maxText, this.maxValue));
41901 validate : function()
41903 if(this.disabled || this.allowBlank){
41908 var currency = this.getCurrency();
41910 if(this.validateValue(this.getRawValue()) && currency.length){
41915 this.markInvalid();
41919 getName: function()
41924 beforeBlur : function()
41930 var v = this.parseValue(this.getRawValue());
41937 onBlur : function()
41941 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41942 //this.el.removeClass(this.focusClass);
41945 this.hasFocus = false;
41947 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41951 var v = this.getValue();
41953 if(String(v) !== String(this.startValue)){
41954 this.fireEvent('change', this, v, this.startValue);
41957 this.fireEvent("blur", this);
41960 inputEl : function()
41962 return this.el.select('.roo-money-amount-input', true).first();
41965 currencyEl : function()
41967 return this.el.select('.roo-money-currency-input', true).first();
41970 hiddenEl : function()
41972 return this.el.select('input.hidden-number-input',true).first();
41976 * @class Roo.bootstrap.BezierSignature
41977 * @extends Roo.bootstrap.Component
41978 * Bootstrap BezierSignature class
41979 * This script refer to:
41980 * Title: Signature Pad
41982 * Availability: https://github.com/szimek/signature_pad
41985 * Create a new BezierSignature
41986 * @param {Object} config The config object
41989 Roo.bootstrap.BezierSignature = function(config){
41990 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41996 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
42003 mouse_btn_down: true,
42006 * @cfg {int} canvas height
42008 canvas_height: '200px',
42011 * @cfg {float|function} Radius of a single dot.
42016 * @cfg {float} Minimum width of a line. Defaults to 0.5.
42021 * @cfg {float} Maximum width of a line. Defaults to 2.5.
42026 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
42031 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
42036 * @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.
42038 bg_color: 'rgba(0, 0, 0, 0)',
42041 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
42043 dot_color: 'black',
42046 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
42048 velocity_filter_weight: 0.7,
42051 * @cfg {function} Callback when stroke begin.
42056 * @cfg {function} Callback when stroke end.
42060 getAutoCreate : function()
42062 var cls = 'roo-signature column';
42065 cls += ' ' + this.cls;
42075 for(var i = 0; i < col_sizes.length; i++) {
42076 if(this[col_sizes[i]]) {
42077 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
42087 cls: 'roo-signature-body',
42091 cls: 'roo-signature-body-canvas',
42092 height: this.canvas_height,
42093 width: this.canvas_width
42100 style: 'display: none'
42108 initEvents: function()
42110 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
42112 var canvas = this.canvasEl();
42114 // mouse && touch event swapping...
42115 canvas.dom.style.touchAction = 'none';
42116 canvas.dom.style.msTouchAction = 'none';
42118 this.mouse_btn_down = false;
42119 canvas.on('mousedown', this._handleMouseDown, this);
42120 canvas.on('mousemove', this._handleMouseMove, this);
42121 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
42123 if (window.PointerEvent) {
42124 canvas.on('pointerdown', this._handleMouseDown, this);
42125 canvas.on('pointermove', this._handleMouseMove, this);
42126 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
42129 if ('ontouchstart' in window) {
42130 canvas.on('touchstart', this._handleTouchStart, this);
42131 canvas.on('touchmove', this._handleTouchMove, this);
42132 canvas.on('touchend', this._handleTouchEnd, this);
42135 Roo.EventManager.onWindowResize(this.resize, this, true);
42137 // file input event
42138 this.fileEl().on('change', this.uploadImage, this);
42145 resize: function(){
42147 var canvas = this.canvasEl().dom;
42148 var ctx = this.canvasElCtx();
42149 var img_data = false;
42151 if(canvas.width > 0) {
42152 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
42154 // setting canvas width will clean img data
42157 var style = window.getComputedStyle ?
42158 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
42160 var padding_left = parseInt(style.paddingLeft) || 0;
42161 var padding_right = parseInt(style.paddingRight) || 0;
42163 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
42166 ctx.putImageData(img_data, 0, 0);
42170 _handleMouseDown: function(e)
42172 if (e.browserEvent.which === 1) {
42173 this.mouse_btn_down = true;
42174 this.strokeBegin(e);
42178 _handleMouseMove: function (e)
42180 if (this.mouse_btn_down) {
42181 this.strokeMoveUpdate(e);
42185 _handleMouseUp: function (e)
42187 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
42188 this.mouse_btn_down = false;
42193 _handleTouchStart: function (e) {
42195 e.preventDefault();
42196 if (e.browserEvent.targetTouches.length === 1) {
42197 // var touch = e.browserEvent.changedTouches[0];
42198 // this.strokeBegin(touch);
42200 this.strokeBegin(e); // assume e catching the correct xy...
42204 _handleTouchMove: function (e) {
42205 e.preventDefault();
42206 // var touch = event.targetTouches[0];
42207 // _this._strokeMoveUpdate(touch);
42208 this.strokeMoveUpdate(e);
42211 _handleTouchEnd: function (e) {
42212 var wasCanvasTouched = e.target === this.canvasEl().dom;
42213 if (wasCanvasTouched) {
42214 e.preventDefault();
42215 // var touch = event.changedTouches[0];
42216 // _this._strokeEnd(touch);
42221 reset: function () {
42222 this._lastPoints = [];
42223 this._lastVelocity = 0;
42224 this._lastWidth = (this.min_width + this.max_width) / 2;
42225 this.canvasElCtx().fillStyle = this.dot_color;
42228 strokeMoveUpdate: function(e)
42230 this.strokeUpdate(e);
42232 if (this.throttle) {
42233 this.throttleStroke(this.strokeUpdate, this.throttle);
42236 this.strokeUpdate(e);
42240 strokeBegin: function(e)
42242 var newPointGroup = {
42243 color: this.dot_color,
42247 if (typeof this.onBegin === 'function') {
42251 this.curve_data.push(newPointGroup);
42253 this.strokeUpdate(e);
42256 strokeUpdate: function(e)
42258 var rect = this.canvasEl().dom.getBoundingClientRect();
42259 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
42260 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
42261 var lastPoints = lastPointGroup.points;
42262 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
42263 var isLastPointTooClose = lastPoint
42264 ? point.distanceTo(lastPoint) <= this.min_distance
42266 var color = lastPointGroup.color;
42267 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
42268 var curve = this.addPoint(point);
42270 this.drawDot({color: color, point: point});
42273 this.drawCurve({color: color, curve: curve});
42283 strokeEnd: function(e)
42285 this.strokeUpdate(e);
42286 if (typeof this.onEnd === 'function') {
42291 addPoint: function (point) {
42292 var _lastPoints = this._lastPoints;
42293 _lastPoints.push(point);
42294 if (_lastPoints.length > 2) {
42295 if (_lastPoints.length === 3) {
42296 _lastPoints.unshift(_lastPoints[0]);
42298 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
42299 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
42300 _lastPoints.shift();
42306 calculateCurveWidths: function (startPoint, endPoint) {
42307 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42308 (1 - this.velocity_filter_weight) * this._lastVelocity;
42310 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42313 start: this._lastWidth
42316 this._lastVelocity = velocity;
42317 this._lastWidth = newWidth;
42321 drawDot: function (_a) {
42322 var color = _a.color, point = _a.point;
42323 var ctx = this.canvasElCtx();
42324 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42326 this.drawCurveSegment(point.x, point.y, width);
42328 ctx.fillStyle = color;
42332 drawCurve: function (_a) {
42333 var color = _a.color, curve = _a.curve;
42334 var ctx = this.canvasElCtx();
42335 var widthDelta = curve.endWidth - curve.startWidth;
42336 var drawSteps = Math.floor(curve.length()) * 2;
42338 ctx.fillStyle = color;
42339 for (var i = 0; i < drawSteps; i += 1) {
42340 var t = i / drawSteps;
42346 var x = uuu * curve.startPoint.x;
42347 x += 3 * uu * t * curve.control1.x;
42348 x += 3 * u * tt * curve.control2.x;
42349 x += ttt * curve.endPoint.x;
42350 var y = uuu * curve.startPoint.y;
42351 y += 3 * uu * t * curve.control1.y;
42352 y += 3 * u * tt * curve.control2.y;
42353 y += ttt * curve.endPoint.y;
42354 var width = curve.startWidth + ttt * widthDelta;
42355 this.drawCurveSegment(x, y, width);
42361 drawCurveSegment: function (x, y, width) {
42362 var ctx = this.canvasElCtx();
42364 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42365 this.is_empty = false;
42370 var ctx = this.canvasElCtx();
42371 var canvas = this.canvasEl().dom;
42372 ctx.fillStyle = this.bg_color;
42373 ctx.clearRect(0, 0, canvas.width, canvas.height);
42374 ctx.fillRect(0, 0, canvas.width, canvas.height);
42375 this.curve_data = [];
42377 this.is_empty = true;
42382 return this.el.select('input',true).first();
42385 canvasEl: function()
42387 return this.el.select('canvas',true).first();
42390 canvasElCtx: function()
42392 return this.el.select('canvas',true).first().dom.getContext('2d');
42395 getImage: function(type)
42397 if(this.is_empty) {
42402 return this.canvasEl().dom.toDataURL('image/'+type, 1);
42405 drawFromImage: function(img_src)
42407 var img = new Image();
42409 img.onload = function(){
42410 this.canvasElCtx().drawImage(img, 0, 0);
42415 this.is_empty = false;
42418 selectImage: function()
42420 this.fileEl().dom.click();
42423 uploadImage: function(e)
42425 var reader = new FileReader();
42427 reader.onload = function(e){
42428 var img = new Image();
42429 img.onload = function(){
42431 this.canvasElCtx().drawImage(img, 0, 0);
42433 img.src = e.target.result;
42436 reader.readAsDataURL(e.target.files[0]);
42439 // Bezier Point Constructor
42440 Point: (function () {
42441 function Point(x, y, time) {
42444 this.time = time || Date.now();
42446 Point.prototype.distanceTo = function (start) {
42447 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42449 Point.prototype.equals = function (other) {
42450 return this.x === other.x && this.y === other.y && this.time === other.time;
42452 Point.prototype.velocityFrom = function (start) {
42453 return this.time !== start.time
42454 ? this.distanceTo(start) / (this.time - start.time)
42461 // Bezier Constructor
42462 Bezier: (function () {
42463 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42464 this.startPoint = startPoint;
42465 this.control2 = control2;
42466 this.control1 = control1;
42467 this.endPoint = endPoint;
42468 this.startWidth = startWidth;
42469 this.endWidth = endWidth;
42471 Bezier.fromPoints = function (points, widths, scope) {
42472 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42473 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42474 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42476 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42477 var dx1 = s1.x - s2.x;
42478 var dy1 = s1.y - s2.y;
42479 var dx2 = s2.x - s3.x;
42480 var dy2 = s2.y - s3.y;
42481 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42482 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42483 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42484 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42485 var dxm = m1.x - m2.x;
42486 var dym = m1.y - m2.y;
42487 var k = l2 / (l1 + l2);
42488 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42489 var tx = s2.x - cm.x;
42490 var ty = s2.y - cm.y;
42492 c1: new scope.Point(m1.x + tx, m1.y + ty),
42493 c2: new scope.Point(m2.x + tx, m2.y + ty)
42496 Bezier.prototype.length = function () {
42501 for (var i = 0; i <= steps; i += 1) {
42503 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42504 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42506 var xdiff = cx - px;
42507 var ydiff = cy - py;
42508 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42515 Bezier.prototype.point = function (t, start, c1, c2, end) {
42516 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42517 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42518 + (3.0 * c2 * (1.0 - t) * t * t)
42519 + (end * t * t * t);
42524 throttleStroke: function(fn, wait) {
42525 if (wait === void 0) { wait = 250; }
42527 var timeout = null;
42531 var later = function () {
42532 previous = Date.now();
42534 result = fn.apply(storedContext, storedArgs);
42536 storedContext = null;
42540 return function wrapper() {
42542 for (var _i = 0; _i < arguments.length; _i++) {
42543 args[_i] = arguments[_i];
42545 var now = Date.now();
42546 var remaining = wait - (now - previous);
42547 storedContext = this;
42549 if (remaining <= 0 || remaining > wait) {
42551 clearTimeout(timeout);
42555 result = fn.apply(storedContext, storedArgs);
42557 storedContext = null;
42561 else if (!timeout) {
42562 timeout = window.setTimeout(later, remaining);