2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = (
9 Roo.each(document.styleSheets, function(s) {
10 if ( s.href && s.href.match(/css-bootstrap4/)) {
18 * base class for bootstrap elements.
22 Roo.bootstrap = Roo.bootstrap || {};
24 * @class Roo.bootstrap.Component
25 * @extends Roo.Component
26 * Bootstrap Component base class
27 * @cfg {String} cls css class
28 * @cfg {String} style any extra css
29 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
31 * @cfg {string} dataId cutomer id
32 * @cfg {string} name Specifies name attribute
33 * @cfg {string} tooltip Text for the tooltip
34 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
35 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
38 * Do not use directly - it does not do anything..
39 * @param {Object} config The config object
44 Roo.bootstrap.Component = function(config){
45 Roo.bootstrap.Component.superclass.constructor.call(this, config);
49 * @event childrenrendered
50 * Fires when the children have been rendered..
51 * @param {Roo.bootstrap.Component} this
53 "childrenrendered" : true
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
65 allowDomMove : false, // to stop relocations in parent onRender...
75 * Initialize Events for the element
77 initEvents : function() { },
83 can_build_overlaid : true,
85 container_method : false,
92 // returns the parent component..
93 return Roo.ComponentMgr.get(this.parentId)
99 onRender : function(ct, position)
101 // Roo.log("Call onRender: " + this.xtype);
103 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
106 if (this.el.attr('xtype')) {
107 this.el.attr('xtypex', this.el.attr('xtype'));
108 this.el.dom.removeAttribute('xtype');
118 var cfg = Roo.apply({}, this.getAutoCreate());
120 cfg.id = this.id || Roo.id();
122 // fill in the extra attributes
123 if (this.xattr && typeof(this.xattr) =='object') {
124 for (var i in this.xattr) {
125 cfg[i] = this.xattr[i];
130 cfg.dataId = this.dataId;
134 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
137 if (this.style) { // fixme needs to support more complex style data.
138 cfg.style = this.style;
142 cfg.name = this.name;
145 this.el = ct.createChild(cfg, position);
148 this.tooltipEl().attr('tooltip', this.tooltip);
151 if(this.tabIndex !== undefined){
152 this.el.dom.setAttribute('tabIndex', this.tabIndex);
159 * Fetch the element to add children to
160 * @return {Roo.Element} defaults to this.el
162 getChildContainer : function()
167 * Fetch the element to display the tooltip on.
168 * @return {Roo.Element} defaults to this.el
170 tooltipEl : function()
175 addxtype : function(tree,cntr)
179 cn = Roo.factory(tree);
180 //Roo.log(['addxtype', cn]);
182 cn.parentType = this.xtype; //??
183 cn.parentId = this.id;
185 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186 if (typeof(cn.container_method) == 'string') {
187 cntr = cn.container_method;
191 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
193 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
195 var build_from_html = Roo.XComponent.build_from_html;
197 var is_body = (tree.xtype == 'Body') ;
199 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
201 var self_cntr_el = Roo.get(this[cntr](false));
203 // do not try and build conditional elements
204 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
208 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210 return this.addxtypeChild(tree,cntr, is_body);
213 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
216 return this.addxtypeChild(Roo.apply({}, tree),cntr);
219 Roo.log('skipping render');
225 if (!build_from_html) {
229 // this i think handles overlaying multiple children of the same type
230 // with the sam eelement.. - which might be buggy..
232 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
238 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
242 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
249 addxtypeChild : function (tree, cntr, is_body)
251 Roo.debug && Roo.log('addxtypeChild:' + cntr);
253 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
256 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257 (typeof(tree['flexy:foreach']) != 'undefined');
261 skip_children = false;
262 // render the element if it's not BODY.
265 // if parent was disabled, then do not try and create the children..
266 if(!this[cntr](true)){
271 cn = Roo.factory(tree);
273 cn.parentType = this.xtype; //??
274 cn.parentId = this.id;
276 var build_from_html = Roo.XComponent.build_from_html;
279 // does the container contain child eleemnts with 'xtype' attributes.
280 // that match this xtype..
281 // note - when we render we create these as well..
282 // so we should check to see if body has xtype set.
283 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
285 var self_cntr_el = Roo.get(this[cntr](false));
286 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
288 //Roo.log(Roo.XComponent.build_from_html);
289 //Roo.log("got echild:");
292 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293 // and are not displayed -this causes this to use up the wrong element when matching.
294 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
297 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
304 //echild.dom.removeAttribute('xtype');
306 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307 Roo.debug && Roo.log(self_cntr_el);
308 Roo.debug && Roo.log(echild);
309 Roo.debug && Roo.log(cn);
315 // if object has flexy:if - then it may or may not be rendered.
316 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
317 // skip a flexy if element.
318 Roo.debug && Roo.log('skipping render');
319 Roo.debug && Roo.log(tree);
321 Roo.debug && Roo.log('skipping all children');
322 skip_children = true;
327 // actually if flexy:foreach is found, we really want to create
328 // multiple copies here...
330 //Roo.log(this[cntr]());
331 // some elements do not have render methods.. like the layouts...
333 if(this[cntr](true) === false){
338 cn.render && cn.render(this[cntr](true));
341 // then add the element..
348 if (typeof (tree.menu) != 'undefined') {
349 tree.menu.parentType = cn.xtype;
350 tree.menu.triggerEl = cn.el;
351 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
355 if (!tree.items || !tree.items.length) {
357 //Roo.log(["no children", this]);
362 var items = tree.items;
365 //Roo.log(items.length);
367 if (!skip_children) {
368 for(var i =0;i < items.length;i++) {
369 // Roo.log(['add child', items[i]]);
370 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
376 //Roo.log("fire childrenrendered");
378 cn.fireEvent('childrenrendered', this);
384 * Set the element that will be used to show or hide
386 setVisibilityEl : function(el)
388 this.visibilityEl = el;
392 * Get the element that will be used to show or hide
394 getVisibilityEl : function()
396 if (typeof(this.visibilityEl) == 'object') {
397 return this.visibilityEl;
400 if (typeof(this.visibilityEl) == 'string') {
401 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
408 * Show a component - removes 'hidden' class
412 if(!this.getVisibilityEl()){
416 this.getVisibilityEl().removeClass(['hidden','d-none']);
418 this.fireEvent('show', this);
423 * Hide a component - adds 'hidden' class
427 if(!this.getVisibilityEl()){
431 this.getVisibilityEl().addClass(['hidden','d-none']);
433 this.fireEvent('hide', this);
446 * @class Roo.bootstrap.Body
447 * @extends Roo.bootstrap.Component
448 * Bootstrap Body class
452 * @param {Object} config The config object
455 Roo.bootstrap.Body = function(config){
457 config = config || {};
459 Roo.bootstrap.Body.superclass.constructor.call(this, config);
460 this.el = Roo.get(config.el ? config.el : document.body );
461 if (this.cls && this.cls.length) {
462 Roo.get(document.body).addClass(this.cls);
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
468 is_body : true,// just to make sure it's constructed?
473 onRender : function(ct, position)
475 /* Roo.log("Roo.bootstrap.Body - onRender");
476 if (this.cls && this.cls.length) {
477 Roo.get(document.body).addClass(this.cls);
496 * @class Roo.bootstrap.ButtonGroup
497 * @extends Roo.bootstrap.Component
498 * Bootstrap ButtonGroup class
499 * @cfg {String} size lg | sm | xs (default empty normal)
500 * @cfg {String} align vertical | justified (default none)
501 * @cfg {String} direction up | down (default down)
502 * @cfg {Boolean} toolbar false | true
503 * @cfg {Boolean} btn true | false
508 * @param {Object} config The config object
511 Roo.bootstrap.ButtonGroup = function(config){
512 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
523 getAutoCreate : function(){
529 cfg.html = this.html || cfg.html;
540 if (['vertical','justified'].indexOf(this.align)!==-1) {
541 cfg.cls = 'btn-group-' + this.align;
543 if (this.align == 'justified') {
544 console.log(this.items);
548 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549 cfg.cls += ' btn-group-' + this.size;
552 if (this.direction == 'up') {
553 cfg.cls += ' dropup' ;
559 * Add a button to the group (similar to NavItem API.)
561 addItem : function(cfg)
563 var cn = new Roo.bootstrap.Button(cfg);
565 cn.parentId = this.id;
566 cn.onRender(this.el, null);
580 * @class Roo.bootstrap.Button
581 * @extends Roo.bootstrap.Component
582 * Bootstrap Button class
583 * @cfg {String} html The button content
584 * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585 * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587 * @cfg {String} size ( lg | sm | xs)
588 * @cfg {String} tag ( a | input | submit)
589 * @cfg {String} href empty or href
590 * @cfg {Boolean} disabled default false;
591 * @cfg {Boolean} isClose default false;
592 * @cfg {String} glyphicon depricated - use fa
593 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
594 * @cfg {String} badge text for badge
595 * @cfg {String} theme (default|glow)
596 * @cfg {Boolean} inverse dark themed version
597 * @cfg {Boolean} toggle is it a slidy toggle button
598 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
599 * @cfg {String} ontext text for on slidy toggle state
600 * @cfg {String} offtext text for off slidy toggle state
601 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
602 * @cfg {Boolean} removeClass remove the standard class..
603 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
606 * Create a new button
607 * @param {Object} config The config object
611 Roo.bootstrap.Button = function(config){
612 Roo.bootstrap.Button.superclass.constructor.call(this, config);
613 this.weightClass = ["btn-default btn-outline-secondary",
625 * When a butotn is pressed
626 * @param {Roo.bootstrap.Button} btn
627 * @param {Roo.EventObject} e
632 * After the button has been toggles
633 * @param {Roo.bootstrap.Button} btn
634 * @param {Roo.EventObject} e
635 * @param {boolean} pressed (also available as button.pressed)
641 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
662 preventDefault: true,
670 getAutoCreate : function(){
678 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
679 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
684 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
686 if (this.toggle == true) {
689 cls: 'slider-frame roo-button',
694 'data-off-text':'OFF',
695 cls: 'slider-button',
701 if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
702 cfg.cls += ' '+this.weight;
711 cfg["aria-hidden"] = true;
713 cfg.html = "×";
719 if (this.theme==='default') {
720 cfg.cls = 'btn roo-button';
722 //if (this.parentType != 'Navbar') {
723 this.weight = this.weight.length ? this.weight : 'default';
725 if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
727 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
728 var weight = this.weight == 'default' ? 'secondary' : this.weight;
729 cfg.cls += ' btn-' + outline + weight;
730 if (this.weight == 'default') {
732 cfg.cls += ' btn-' + this.weight;
735 } else if (this.theme==='glow') {
738 cfg.cls = 'btn-glow roo-button';
740 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
742 cfg.cls += ' ' + this.weight;
748 this.cls += ' inverse';
752 if (this.active || this.pressed === true) {
753 cfg.cls += ' active';
757 cfg.disabled = 'disabled';
761 Roo.log('changing to ul' );
763 this.glyphicon = 'caret';
764 if (Roo.bootstrap.version == 4) {
765 this.fa = 'caret-down';
770 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
772 //gsRoo.log(this.parentType);
773 if (this.parentType === 'Navbar' && !this.parent().bar) {
774 Roo.log('changing to li?');
783 href : this.href || '#'
786 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
787 cfg.cls += ' dropdown';
794 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
796 if (this.glyphicon) {
797 cfg.html = ' ' + cfg.html;
802 cls: 'glyphicon glyphicon-' + this.glyphicon
807 cfg.html = ' ' + cfg.html;
812 cls: 'fa fas fa-' + this.fa
822 // cfg.cls='btn roo-button';
826 var value = cfg.html;
831 cls: 'glyphicon glyphicon-' + this.glyphicon,
838 cls: 'fa fas fa-' + this.fa,
843 var bw = this.badge_weight.length ? this.badge_weight :
844 (this.weight.length ? this.weight : 'secondary');
845 bw = bw == 'default' ? 'secondary' : bw;
851 cls: 'badge badge-' + bw,
860 cfg.cls += ' dropdown';
861 cfg.html = typeof(cfg.html) != 'undefined' ?
862 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
865 if (cfg.tag !== 'a' && this.href !== '') {
866 throw "Tag must be a to set href.";
867 } else if (this.href.length > 0) {
868 cfg.href = this.href;
871 if(this.removeClass){
876 cfg.target = this.target;
881 initEvents: function() {
882 // Roo.log('init events?');
883 // Roo.log(this.el.dom);
886 if (typeof (this.menu) != 'undefined') {
887 this.menu.parentType = this.xtype;
888 this.menu.triggerEl = this.el;
889 this.addxtype(Roo.apply({}, this.menu));
893 if (this.el.hasClass('roo-button')) {
894 this.el.on('click', this.onClick, this);
896 this.el.select('.roo-button').on('click', this.onClick, this);
899 if(this.removeClass){
900 this.el.on('click', this.onClick, this);
903 this.el.enableDisplayMode();
906 onClick : function(e)
912 Roo.log('button on click ');
913 if(this.preventDefault){
917 if (this.pressed === true || this.pressed === false) {
918 this.toggleActive(e);
922 this.fireEvent('click', this, e);
926 * Enables this button
930 this.disabled = false;
931 this.el.removeClass('disabled');
935 * Disable this button
939 this.disabled = true;
940 this.el.addClass('disabled');
943 * sets the active state on/off,
944 * @param {Boolean} state (optional) Force a particular state
946 setActive : function(v) {
948 this.el[v ? 'addClass' : 'removeClass']('active');
952 * toggles the current active state
954 toggleActive : function(e)
956 this.setActive(!this.pressed);
957 this.fireEvent('toggle', this, e, !this.pressed);
960 * get the current active state
961 * @return {boolean} true if it's active
963 isActive : function()
965 return this.el.hasClass('active');
968 * set the text of the first selected button
970 setText : function(str)
972 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
975 * get the text of the first selected button
979 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
982 setWeight : function(str)
984 this.el.removeClass(this.weightClass);
986 var outline = this.outline ? 'outline-' : '';
987 if (str == 'default') {
988 this.el.addClass('btn-default btn-outline-secondary');
991 this.el.addClass('btn-' + outline + str);
1005 * @class Roo.bootstrap.Column
1006 * @extends Roo.bootstrap.Component
1007 * Bootstrap Column class
1008 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1009 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1010 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1011 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1012 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1013 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1014 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1015 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1018 * @cfg {Boolean} hidden (true|false) hide the element
1019 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1020 * @cfg {String} fa (ban|check|...) font awesome icon
1021 * @cfg {Number} fasize (1|2|....) font awsome size
1023 * @cfg {String} icon (info-sign|check|...) glyphicon name
1025 * @cfg {String} html content of column.
1028 * Create a new Column
1029 * @param {Object} config The config object
1032 Roo.bootstrap.Column = function(config){
1033 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1036 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1054 getAutoCreate : function(){
1055 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1063 ['xs','sm','md','lg'].map(function(size){
1064 //Roo.log( size + ':' + settings[size]);
1066 if (settings[size+'off'] !== false) {
1067 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1070 if (settings[size] === false) {
1074 if (!settings[size]) { // 0 = hidden
1075 cfg.cls += ' hidden-' + size + ' hidden' + size + '-down';;
1078 cfg.cls += ' col-' + size + '-' + settings[size] + (
1079 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1085 cfg.cls += ' hidden';
1088 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1089 cfg.cls +=' alert alert-' + this.alert;
1093 if (this.html.length) {
1094 cfg.html = this.html;
1098 if (this.fasize > 1) {
1099 fasize = ' fa-' + this.fasize + 'x';
1101 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1106 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1125 * @class Roo.bootstrap.Container
1126 * @extends Roo.bootstrap.Component
1127 * Bootstrap Container class
1128 * @cfg {Boolean} jumbotron is it a jumbotron element
1129 * @cfg {String} html content of element
1130 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1131 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1132 * @cfg {String} header content of header (for panel)
1133 * @cfg {String} footer content of footer (for panel)
1134 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1135 * @cfg {String} tag (header|aside|section) type of HTML tag.
1136 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1137 * @cfg {String} fa font awesome icon
1138 * @cfg {String} icon (info-sign|check|...) glyphicon name
1139 * @cfg {Boolean} hidden (true|false) hide the element
1140 * @cfg {Boolean} expandable (true|false) default false
1141 * @cfg {Boolean} expanded (true|false) default true
1142 * @cfg {String} rheader contet on the right of header
1143 * @cfg {Boolean} clickable (true|false) default false
1147 * Create a new Container
1148 * @param {Object} config The config object
1151 Roo.bootstrap.Container = function(config){
1152 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1158 * After the panel has been expand
1160 * @param {Roo.bootstrap.Container} this
1165 * After the panel has been collapsed
1167 * @param {Roo.bootstrap.Container} this
1172 * When a element is chick
1173 * @param {Roo.bootstrap.Container} this
1174 * @param {Roo.EventObject} e
1180 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1198 getChildContainer : function() {
1204 if (this.panel.length) {
1205 return this.el.select('.panel-body',true).first();
1212 getAutoCreate : function(){
1215 tag : this.tag || 'div',
1219 if (this.jumbotron) {
1220 cfg.cls = 'jumbotron';
1225 // - this is applied by the parent..
1227 // cfg.cls = this.cls + '';
1230 if (this.sticky.length) {
1232 var bd = Roo.get(document.body);
1233 if (!bd.hasClass('bootstrap-sticky')) {
1234 bd.addClass('bootstrap-sticky');
1235 Roo.select('html',true).setStyle('height', '100%');
1238 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1242 if (this.well.length) {
1243 switch (this.well) {
1246 cfg.cls +=' well well-' +this.well;
1255 cfg.cls += ' hidden';
1259 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1260 cfg.cls +=' alert alert-' + this.alert;
1265 if (this.panel.length) {
1266 cfg.cls += ' panel panel-' + this.panel;
1268 if (this.header.length) {
1272 if(this.expandable){
1274 cfg.cls = cfg.cls + ' expandable';
1278 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1286 cls : 'panel-title',
1287 html : (this.expandable ? ' ' : '') + this.header
1291 cls: 'panel-header-right',
1297 cls : 'panel-heading',
1298 style : this.expandable ? 'cursor: pointer' : '',
1306 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1311 if (this.footer.length) {
1313 cls : 'panel-footer',
1322 body.html = this.html || cfg.html;
1323 // prefix with the icons..
1325 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1328 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1333 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1334 cfg.cls = 'container';
1340 initEvents: function()
1342 if(this.expandable){
1343 var headerEl = this.headerEl();
1346 headerEl.on('click', this.onToggleClick, this);
1351 this.el.on('click', this.onClick, this);
1356 onToggleClick : function()
1358 var headerEl = this.headerEl();
1374 if(this.fireEvent('expand', this)) {
1376 this.expanded = true;
1378 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1380 this.el.select('.panel-body',true).first().removeClass('hide');
1382 var toggleEl = this.toggleEl();
1388 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1393 collapse : function()
1395 if(this.fireEvent('collapse', this)) {
1397 this.expanded = false;
1399 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1400 this.el.select('.panel-body',true).first().addClass('hide');
1402 var toggleEl = this.toggleEl();
1408 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1412 toggleEl : function()
1414 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1418 return this.el.select('.panel-heading .fa',true).first();
1421 headerEl : function()
1423 if(!this.el || !this.panel.length || !this.header.length){
1427 return this.el.select('.panel-heading',true).first()
1432 if(!this.el || !this.panel.length){
1436 return this.el.select('.panel-body',true).first()
1439 titleEl : function()
1441 if(!this.el || !this.panel.length || !this.header.length){
1445 return this.el.select('.panel-title',true).first();
1448 setTitle : function(v)
1450 var titleEl = this.titleEl();
1456 titleEl.dom.innerHTML = v;
1459 getTitle : function()
1462 var titleEl = this.titleEl();
1468 return titleEl.dom.innerHTML;
1471 setRightTitle : function(v)
1473 var t = this.el.select('.panel-header-right',true).first();
1479 t.dom.innerHTML = v;
1482 onClick : function(e)
1486 this.fireEvent('click', this, e);
1493 * This is BS4's Card element.. - similar to our containers probably..
1497 * @class Roo.bootstrap.Card
1498 * @extends Roo.bootstrap.Component
1499 * Bootstrap Card class
1502 * possible... may not be implemented..
1503 * @cfg {String} header_image src url of image.
1504 * @cfg {String} header
1505 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1507 * @cfg {String} title
1508 * @cfg {String} subtitle
1509 * @cfg {String} html -- html contents - or just use children..
1510 * @cfg {String} footer
1512 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1514 * @cfg {String} margin (0|1|2|3|4|5|auto)
1515 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1516 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1517 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1518 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1519 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1520 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1522 * @cfg {String} padding (0|1|2|3|4|5)
1523 * @cfg {String} padding_top (0|1|2|3|4|5)
1524 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1525 * @cfg {String} padding_left (0|1|2|3|4|5)
1526 * @cfg {String} padding_right (0|1|2|3|4|5)
1527 * @cfg {String} padding_x (0|1|2|3|4|5)
1528 * @cfg {String} padding_y (0|1|2|3|4|5)
1530 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1531 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1532 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1533 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1534 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1537 * Create a new Container
1538 * @param {Object} config The config object
1541 Roo.bootstrap.Card = function(config){
1542 Roo.bootstrap.Card.superclass.constructor.call(this, config);
1550 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
1555 margin: '', /// may be better in component?
1585 childContainer : false,
1587 layoutCls : function()
1591 Roo.log(this.margin_bottom.length);
1592 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
1593 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
1595 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
1596 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
1598 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
1599 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
1603 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
1604 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
1605 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
1609 // more generic support?
1617 // Roo.log("Call onRender: " + this.xtype);
1618 /* We are looking at something like this.
1620 <img src="..." class="card-img-top" alt="...">
1621 <div class="card-body">
1622 <h5 class="card-title">Card title</h5>
1623 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
1625 >> this bit is really the body...
1626 <div> << we will ad dthis in hopefully it will not break shit.
1628 ** card text does not actually have any styling...
1630 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
1633 <a href="#" class="card-link">Card link</a>
1636 <div class="card-footer">
1637 <small class="text-muted">Last updated 3 mins ago</small>
1641 getAutoCreate : function(){
1649 if (this.weight.length && this.weight != 'light') {
1650 cfg.cls += ' text-white'
1652 if (this.weight.length) {
1653 cfg.cls += ' bg-' + this.weight;
1656 cfg.cls += this.layoutCls();
1658 if (this.header.length) {
1660 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
1661 cls : 'card-header',
1662 html : this.header // escape?
1665 if (this.header_image.length) {
1668 cls : 'card-img-top',
1669 src: this.header_image // escape?
1680 if (this.title.length) {
1684 src: this.title // escape?
1688 if (this.subtitle.length) {
1692 src: this.subtitle // escape?
1698 cls : 'roo-card-body-ctr'
1701 if (this.html.length) {
1707 // fixme ? handle objects?
1708 if (this.footer.length) {
1711 cls : 'card-footer',
1712 html: this.footer // escape?
1721 getChildContainer : function()
1727 return this.el.select('.roo-card-body-ctr',true).first();
1741 * @class Roo.bootstrap.Img
1742 * @extends Roo.bootstrap.Component
1743 * Bootstrap Img class
1744 * @cfg {Boolean} imgResponsive false | true
1745 * @cfg {String} border rounded | circle | thumbnail
1746 * @cfg {String} src image source
1747 * @cfg {String} alt image alternative text
1748 * @cfg {String} href a tag href
1749 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1750 * @cfg {String} xsUrl xs image source
1751 * @cfg {String} smUrl sm image source
1752 * @cfg {String} mdUrl md image source
1753 * @cfg {String} lgUrl lg image source
1756 * Create a new Input
1757 * @param {Object} config The config object
1760 Roo.bootstrap.Img = function(config){
1761 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1767 * The img click event for the img.
1768 * @param {Roo.EventObject} e
1774 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1776 imgResponsive: true,
1786 getAutoCreate : function()
1788 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1789 return this.createSingleImg();
1794 cls: 'roo-image-responsive-group',
1799 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1801 if(!_this[size + 'Url']){
1807 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1808 html: _this.html || cfg.html,
1809 src: _this[size + 'Url']
1812 img.cls += ' roo-image-responsive-' + size;
1814 var s = ['xs', 'sm', 'md', 'lg'];
1816 s.splice(s.indexOf(size), 1);
1818 Roo.each(s, function(ss){
1819 img.cls += ' hidden-' + ss;
1822 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1823 cfg.cls += ' img-' + _this.border;
1827 cfg.alt = _this.alt;
1840 a.target = _this.target;
1844 cfg.cn.push((_this.href) ? a : img);
1851 createSingleImg : function()
1855 cls: (this.imgResponsive) ? 'img-responsive' : '',
1857 src : 'about:blank' // just incase src get's set to undefined?!?
1860 cfg.html = this.html || cfg.html;
1862 cfg.src = this.src || cfg.src;
1864 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1865 cfg.cls += ' img-' + this.border;
1882 a.target = this.target;
1887 return (this.href) ? a : cfg;
1890 initEvents: function()
1893 this.el.on('click', this.onClick, this);
1898 onClick : function(e)
1900 Roo.log('img onclick');
1901 this.fireEvent('click', this, e);
1904 * Sets the url of the image - used to update it
1905 * @param {String} url the url of the image
1908 setSrc : function(url)
1912 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1913 this.el.dom.src = url;
1917 this.el.select('img', true).first().dom.src = url;
1933 * @class Roo.bootstrap.Link
1934 * @extends Roo.bootstrap.Component
1935 * Bootstrap Link Class
1936 * @cfg {String} alt image alternative text
1937 * @cfg {String} href a tag href
1938 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1939 * @cfg {String} html the content of the link.
1940 * @cfg {String} anchor name for the anchor link
1941 * @cfg {String} fa - favicon
1943 * @cfg {Boolean} preventDefault (true | false) default false
1947 * Create a new Input
1948 * @param {Object} config The config object
1951 Roo.bootstrap.Link = function(config){
1952 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1958 * The img click event for the img.
1959 * @param {Roo.EventObject} e
1965 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1969 preventDefault: false,
1975 getAutoCreate : function()
1977 var html = this.html || '';
1979 if (this.fa !== false) {
1980 html = '<i class="fa fa-' + this.fa + '"></i>';
1985 // anchor's do not require html/href...
1986 if (this.anchor === false) {
1988 cfg.href = this.href || '#';
1990 cfg.name = this.anchor;
1991 if (this.html !== false || this.fa !== false) {
1994 if (this.href !== false) {
1995 cfg.href = this.href;
1999 if(this.alt !== false){
2004 if(this.target !== false) {
2005 cfg.target = this.target;
2011 initEvents: function() {
2013 if(!this.href || this.preventDefault){
2014 this.el.on('click', this.onClick, this);
2018 onClick : function(e)
2020 if(this.preventDefault){
2023 //Roo.log('img onclick');
2024 this.fireEvent('click', this, e);
2037 * @class Roo.bootstrap.Header
2038 * @extends Roo.bootstrap.Component
2039 * Bootstrap Header class
2040 * @cfg {String} html content of header
2041 * @cfg {Number} level (1|2|3|4|5|6) default 1
2044 * Create a new Header
2045 * @param {Object} config The config object
2049 Roo.bootstrap.Header = function(config){
2050 Roo.bootstrap.Header.superclass.constructor.call(this, config);
2053 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
2061 getAutoCreate : function(){
2066 tag: 'h' + (1 *this.level),
2067 html: this.html || ''
2079 * Ext JS Library 1.1.1
2080 * Copyright(c) 2006-2007, Ext JS, LLC.
2082 * Originally Released Under LGPL - original licence link has changed is not relivant.
2085 * <script type="text/javascript">
2089 * @class Roo.bootstrap.MenuMgr
2090 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
2093 Roo.bootstrap.MenuMgr = function(){
2094 var menus, active, groups = {}, attached = false, lastShow = new Date();
2096 // private - called when first menu is created
2099 active = new Roo.util.MixedCollection();
2100 Roo.get(document).addKeyListener(27, function(){
2101 if(active.length > 0){
2109 if(active && active.length > 0){
2110 var c = active.clone();
2120 if(active.length < 1){
2121 Roo.get(document).un("mouseup", onMouseDown);
2129 var last = active.last();
2130 lastShow = new Date();
2133 Roo.get(document).on("mouseup", onMouseDown);
2138 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
2139 m.parentMenu.activeChild = m;
2140 }else if(last && last.isVisible()){
2141 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
2146 function onBeforeHide(m){
2148 m.activeChild.hide();
2150 if(m.autoHideTimer){
2151 clearTimeout(m.autoHideTimer);
2152 delete m.autoHideTimer;
2157 function onBeforeShow(m){
2158 var pm = m.parentMenu;
2159 if(!pm && !m.allowOtherMenus){
2161 }else if(pm && pm.activeChild && active != m){
2162 pm.activeChild.hide();
2166 // private this should really trigger on mouseup..
2167 function onMouseDown(e){
2168 Roo.log("on Mouse Up");
2170 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
2171 Roo.log("MenuManager hideAll");
2180 function onBeforeCheck(mi, state){
2182 var g = groups[mi.group];
2183 for(var i = 0, l = g.length; i < l; i++){
2185 g[i].setChecked(false);
2194 * Hides all menus that are currently visible
2196 hideAll : function(){
2201 register : function(menu){
2205 menus[menu.id] = menu;
2206 menu.on("beforehide", onBeforeHide);
2207 menu.on("hide", onHide);
2208 menu.on("beforeshow", onBeforeShow);
2209 menu.on("show", onShow);
2211 if(g && menu.events["checkchange"]){
2215 groups[g].push(menu);
2216 menu.on("checkchange", onCheck);
2221 * Returns a {@link Roo.menu.Menu} object
2222 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
2223 * be used to generate and return a new Menu instance.
2225 get : function(menu){
2226 if(typeof menu == "string"){ // menu id
2228 }else if(menu.events){ // menu instance
2231 /*else if(typeof menu.length == 'number'){ // array of menu items?
2232 return new Roo.bootstrap.Menu({items:menu});
2233 }else{ // otherwise, must be a config
2234 return new Roo.bootstrap.Menu(menu);
2241 unregister : function(menu){
2242 delete menus[menu.id];
2243 menu.un("beforehide", onBeforeHide);
2244 menu.un("hide", onHide);
2245 menu.un("beforeshow", onBeforeShow);
2246 menu.un("show", onShow);
2248 if(g && menu.events["checkchange"]){
2249 groups[g].remove(menu);
2250 menu.un("checkchange", onCheck);
2255 registerCheckable : function(menuItem){
2256 var g = menuItem.group;
2261 groups[g].push(menuItem);
2262 menuItem.on("beforecheckchange", onBeforeCheck);
2267 unregisterCheckable : function(menuItem){
2268 var g = menuItem.group;
2270 groups[g].remove(menuItem);
2271 menuItem.un("beforecheckchange", onBeforeCheck);
2283 * @class Roo.bootstrap.Menu
2284 * @extends Roo.bootstrap.Component
2285 * Bootstrap Menu class - container for MenuItems
2286 * @cfg {String} type (dropdown|treeview|submenu) type of menu
2287 * @cfg {bool} hidden if the menu should be hidden when rendered.
2288 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
2289 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
2293 * @param {Object} config The config object
2297 Roo.bootstrap.Menu = function(config){
2298 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2299 if (this.registerMenu && this.type != 'treeview') {
2300 Roo.bootstrap.MenuMgr.register(this);
2307 * Fires before this menu is displayed (return false to block)
2308 * @param {Roo.menu.Menu} this
2313 * Fires before this menu is hidden (return false to block)
2314 * @param {Roo.menu.Menu} this
2319 * Fires after this menu is displayed
2320 * @param {Roo.menu.Menu} this
2325 * Fires after this menu is hidden
2326 * @param {Roo.menu.Menu} this
2331 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2332 * @param {Roo.menu.Menu} this
2333 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2334 * @param {Roo.EventObject} e
2339 * Fires when the mouse is hovering over this menu
2340 * @param {Roo.menu.Menu} this
2341 * @param {Roo.EventObject} e
2342 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2347 * Fires when the mouse exits this menu
2348 * @param {Roo.menu.Menu} this
2349 * @param {Roo.EventObject} e
2350 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2355 * Fires when a menu item contained in this menu is clicked
2356 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2357 * @param {Roo.EventObject} e
2361 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2364 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2368 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2371 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2373 registerMenu : true,
2375 menuItems :false, // stores the menu items..
2385 getChildContainer : function() {
2389 getAutoCreate : function(){
2391 //if (['right'].indexOf(this.align)!==-1) {
2392 // cfg.cn[1].cls += ' pull-right'
2398 cls : 'dropdown-menu' ,
2399 style : 'z-index:1000'
2403 if (this.type === 'submenu') {
2404 cfg.cls = 'submenu active';
2406 if (this.type === 'treeview') {
2407 cfg.cls = 'treeview-menu';
2412 initEvents : function() {
2414 // Roo.log("ADD event");
2415 // Roo.log(this.triggerEl.dom);
2417 this.triggerEl.on('click', this.onTriggerClick, this);
2419 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2422 if (this.triggerEl.hasClass('nav-item')) {
2423 // dropdown toggle on the 'a' in BS4?
2424 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2426 this.triggerEl.addClass('dropdown-toggle');
2429 this.el.on('touchstart' , this.onTouch, this);
2431 this.el.on('click' , this.onClick, this);
2433 this.el.on("mouseover", this.onMouseOver, this);
2434 this.el.on("mouseout", this.onMouseOut, this);
2438 findTargetItem : function(e)
2440 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2444 //Roo.log(t); Roo.log(t.id);
2446 //Roo.log(this.menuitems);
2447 return this.menuitems.get(t.id);
2449 //return this.items.get(t.menuItemId);
2455 onTouch : function(e)
2457 Roo.log("menu.onTouch");
2458 //e.stopEvent(); this make the user popdown broken
2462 onClick : function(e)
2464 Roo.log("menu.onClick");
2466 var t = this.findTargetItem(e);
2467 if(!t || t.isContainer){
2472 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2473 if(t == this.activeItem && t.shouldDeactivate(e)){
2474 this.activeItem.deactivate();
2475 delete this.activeItem;
2479 this.setActiveItem(t, true);
2487 Roo.log('pass click event');
2491 this.fireEvent("click", this, t, e);
2495 if(!t.href.length || t.href == '#'){
2496 (function() { _this.hide(); }).defer(100);
2501 onMouseOver : function(e){
2502 var t = this.findTargetItem(e);
2505 // if(t.canActivate && !t.disabled){
2506 // this.setActiveItem(t, true);
2510 this.fireEvent("mouseover", this, e, t);
2512 isVisible : function(){
2513 return !this.hidden;
2515 onMouseOut : function(e){
2516 var t = this.findTargetItem(e);
2519 // if(t == this.activeItem && t.shouldDeactivate(e)){
2520 // this.activeItem.deactivate();
2521 // delete this.activeItem;
2524 this.fireEvent("mouseout", this, e, t);
2529 * Displays this menu relative to another element
2530 * @param {String/HTMLElement/Roo.Element} element The element to align to
2531 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2532 * the element (defaults to this.defaultAlign)
2533 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2535 show : function(el, pos, parentMenu)
2537 if (false === this.fireEvent("beforeshow", this)) {
2538 Roo.log("show canceled");
2541 this.parentMenu = parentMenu;
2546 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2549 * Displays this menu at a specific xy position
2550 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2551 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2553 showAt : function(xy, parentMenu, /* private: */_e){
2554 this.parentMenu = parentMenu;
2559 this.fireEvent("beforeshow", this);
2560 //xy = this.el.adjustForConstraints(xy);
2564 this.hideMenuItems();
2565 this.hidden = false;
2566 this.triggerEl.addClass('open');
2567 this.el.addClass('show');
2569 // reassign x when hitting right
2570 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2571 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2574 // reassign y when hitting bottom
2575 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2576 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2579 // but the list may align on trigger left or trigger top... should it be a properity?
2581 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2586 this.fireEvent("show", this);
2592 this.doFocus.defer(50, this);
2596 doFocus : function(){
2598 this.focusEl.focus();
2603 * Hides this menu and optionally all parent menus
2604 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2606 hide : function(deep)
2608 if (false === this.fireEvent("beforehide", this)) {
2609 Roo.log("hide canceled");
2612 this.hideMenuItems();
2613 if(this.el && this.isVisible()){
2615 if(this.activeItem){
2616 this.activeItem.deactivate();
2617 this.activeItem = null;
2619 this.triggerEl.removeClass('open');;
2620 this.el.removeClass('show');
2622 this.fireEvent("hide", this);
2624 if(deep === true && this.parentMenu){
2625 this.parentMenu.hide(true);
2629 onTriggerClick : function(e)
2631 Roo.log('trigger click');
2633 var target = e.getTarget();
2635 Roo.log(target.nodeName.toLowerCase());
2637 if(target.nodeName.toLowerCase() === 'i'){
2643 onTriggerPress : function(e)
2645 Roo.log('trigger press');
2646 //Roo.log(e.getTarget());
2647 // Roo.log(this.triggerEl.dom);
2649 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2650 var pel = Roo.get(e.getTarget());
2651 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2652 Roo.log('is treeview or dropdown?');
2656 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2660 if (this.isVisible()) {
2665 this.show(this.triggerEl, '?', false);
2668 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2675 hideMenuItems : function()
2677 Roo.log("hide Menu Items");
2682 this.el.select('.open',true).each(function(aa) {
2684 aa.removeClass('open');
2688 addxtypeChild : function (tree, cntr) {
2689 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2691 this.menuitems.add(comp);
2703 this.getEl().dom.innerHTML = '';
2704 this.menuitems.clear();
2718 * @class Roo.bootstrap.MenuItem
2719 * @extends Roo.bootstrap.Component
2720 * Bootstrap MenuItem class
2721 * @cfg {String} html the menu label
2722 * @cfg {String} href the link
2723 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2724 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2725 * @cfg {Boolean} active used on sidebars to highlight active itesm
2726 * @cfg {String} fa favicon to show on left of menu item.
2727 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2731 * Create a new MenuItem
2732 * @param {Object} config The config object
2736 Roo.bootstrap.MenuItem = function(config){
2737 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2742 * The raw click event for the entire grid.
2743 * @param {Roo.bootstrap.MenuItem} this
2744 * @param {Roo.EventObject} e
2750 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2754 preventDefault: false,
2755 isContainer : false,
2759 getAutoCreate : function(){
2761 if(this.isContainer){
2764 cls: 'dropdown-menu-item '
2774 cls : 'dropdown-item',
2779 if (this.fa !== false) {
2782 cls : 'fa fa-' + this.fa
2791 cls: 'dropdown-menu-item',
2794 if (this.parent().type == 'treeview') {
2795 cfg.cls = 'treeview-menu';
2798 cfg.cls += ' active';
2803 anc.href = this.href || cfg.cn[0].href ;
2804 ctag.html = this.html || cfg.cn[0].html ;
2808 initEvents: function()
2810 if (this.parent().type == 'treeview') {
2811 this.el.select('a').on('click', this.onClick, this);
2815 this.menu.parentType = this.xtype;
2816 this.menu.triggerEl = this.el;
2817 this.menu = this.addxtype(Roo.apply({}, this.menu));
2821 onClick : function(e)
2823 Roo.log('item on click ');
2825 if(this.preventDefault){
2828 //this.parent().hideMenuItems();
2830 this.fireEvent('click', this, e);
2849 * @class Roo.bootstrap.MenuSeparator
2850 * @extends Roo.bootstrap.Component
2851 * Bootstrap MenuSeparator class
2854 * Create a new MenuItem
2855 * @param {Object} config The config object
2859 Roo.bootstrap.MenuSeparator = function(config){
2860 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2863 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2865 getAutoCreate : function(){
2884 * @class Roo.bootstrap.Modal
2885 * @extends Roo.bootstrap.Component
2886 * Bootstrap Modal class
2887 * @cfg {String} title Title of dialog
2888 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2889 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2890 * @cfg {Boolean} specificTitle default false
2891 * @cfg {Array} buttons Array of buttons or standard button set..
2892 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2893 * @cfg {Boolean} animate default true
2894 * @cfg {Boolean} allow_close default true
2895 * @cfg {Boolean} fitwindow default false
2896 * @cfg {Number} width fixed width - usefull for chrome extension only really.
2897 * @cfg {Number} height fixed height - usefull for chrome extension only really.
2898 * @cfg {String} size (sm|lg) default empty
2899 * @cfg {Number} max_width set the max width of modal
2903 * Create a new Modal Dialog
2904 * @param {Object} config The config object
2907 Roo.bootstrap.Modal = function(config){
2908 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2913 * The raw btnclick event for the button
2914 * @param {Roo.EventObject} e
2919 * Fire when dialog resize
2920 * @param {Roo.bootstrap.Modal} this
2921 * @param {Roo.EventObject} e
2925 this.buttons = this.buttons || [];
2928 this.tmpl = Roo.factory(this.tmpl);
2933 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2935 title : 'test dialog',
2945 specificTitle: false,
2947 buttonPosition: 'right',
2970 onRender : function(ct, position)
2972 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2975 var cfg = Roo.apply({}, this.getAutoCreate());
2978 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2980 //if (!cfg.name.length) {
2984 cfg.cls += ' ' + this.cls;
2987 cfg.style = this.style;
2989 this.el = Roo.get(document.body).createChild(cfg, position);
2991 //var type = this.el.dom.type;
2994 if(this.tabIndex !== undefined){
2995 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2998 this.dialogEl = this.el.select('.modal-dialog',true).first();
2999 this.bodyEl = this.el.select('.modal-body',true).first();
3000 this.closeEl = this.el.select('.modal-header .close', true).first();
3001 this.headerEl = this.el.select('.modal-header',true).first();
3002 this.titleEl = this.el.select('.modal-title',true).first();
3003 this.footerEl = this.el.select('.modal-footer',true).first();
3005 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3007 //this.el.addClass("x-dlg-modal");
3009 if (this.buttons.length) {
3010 Roo.each(this.buttons, function(bb) {
3011 var b = Roo.apply({}, bb);
3012 b.xns = b.xns || Roo.bootstrap;
3013 b.xtype = b.xtype || 'Button';
3014 if (typeof(b.listeners) == 'undefined') {
3015 b.listeners = { click : this.onButtonClick.createDelegate(this) };
3018 var btn = Roo.factory(b);
3020 btn.render(this.getButtonContainer());
3024 // render the children.
3027 if(typeof(this.items) != 'undefined'){
3028 var items = this.items;
3031 for(var i =0;i < items.length;i++) {
3032 nitems.push(this.addxtype(Roo.apply({}, items[i])));
3036 this.items = nitems;
3038 // where are these used - they used to be body/close/footer
3042 //this.el.addClass([this.fieldClass, this.cls]);
3046 getAutoCreate : function()
3048 // we will default to modal-body-overflow - might need to remove or make optional later.
3050 cls : 'modal-body enable-modal-body-overflow ',
3051 html : this.html || ''
3056 cls : 'modal-title',
3060 if(this.specificTitle){
3066 if (this.allow_close && Roo.bootstrap.version == 3) {
3076 if (this.allow_close && Roo.bootstrap.version == 4) {
3086 if(this.size.length){
3087 size = 'modal-' + this.size;
3090 var footer = Roo.bootstrap.version == 3 ?
3092 cls : 'modal-footer',
3096 cls: 'btn-' + this.buttonPosition
3101 { // BS4 uses mr-auto on left buttons....
3102 cls : 'modal-footer'
3113 cls: "modal-dialog " + size,
3116 cls : "modal-content",
3119 cls : 'modal-header',
3134 modal.cls += ' fade';
3140 getChildContainer : function() {
3145 getButtonContainer : function() {
3147 return Roo.bootstrap.version == 4 ?
3148 this.el.select('.modal-footer',true).first()
3149 : this.el.select('.modal-footer div',true).first();
3152 initEvents : function()
3154 if (this.allow_close) {
3155 this.closeEl.on('click', this.hide, this);
3157 Roo.EventManager.onWindowResize(this.resize, this, true);
3165 this.maskEl.setSize(
3166 Roo.lib.Dom.getViewWidth(true),
3167 Roo.lib.Dom.getViewHeight(true)
3170 if (this.fitwindow) {
3174 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
3175 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
3180 if(this.max_width !== 0) {
3182 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
3185 this.setSize(w, this.height);
3189 if(this.max_height) {
3190 this.setSize(w,Math.min(
3192 Roo.lib.Dom.getViewportHeight(true) - 60
3198 if(!this.fit_content) {
3199 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
3203 this.setSize(w, Math.min(
3205 this.headerEl.getHeight() +
3206 this.footerEl.getHeight() +
3207 this.getChildHeight(this.bodyEl.dom.childNodes),
3208 Roo.lib.Dom.getViewportHeight(true) - 60)
3214 setSize : function(w,h)
3225 if (!this.rendered) {
3229 //this.el.setStyle('display', 'block');
3230 this.el.removeClass('hideing');
3231 this.el.dom.style.display='block';
3233 Roo.get(document.body).addClass('modal-open');
3235 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
3238 this.el.addClass('show');
3239 this.el.addClass('in');
3242 this.el.addClass('show');
3243 this.el.addClass('in');
3246 // not sure how we can show data in here..
3248 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3251 Roo.get(document.body).addClass("x-body-masked");
3253 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3254 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3255 this.maskEl.dom.style.display = 'block';
3256 this.maskEl.addClass('show');
3261 this.fireEvent('show', this);
3263 // set zindex here - otherwise it appears to be ignored...
3264 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3267 this.items.forEach( function(e) {
3268 e.layout ? e.layout() : false;
3276 if(this.fireEvent("beforehide", this) !== false){
3278 this.maskEl.removeClass('show');
3280 this.maskEl.dom.style.display = '';
3281 Roo.get(document.body).removeClass("x-body-masked");
3282 this.el.removeClass('in');
3283 this.el.select('.modal-dialog', true).first().setStyle('transform','');
3285 if(this.animate){ // why
3286 this.el.addClass('hideing');
3287 this.el.removeClass('show');
3289 if (!this.el.hasClass('hideing')) {
3290 return; // it's been shown again...
3293 this.el.dom.style.display='';
3295 Roo.get(document.body).removeClass('modal-open');
3296 this.el.removeClass('hideing');
3300 this.el.removeClass('show');
3301 this.el.dom.style.display='';
3302 Roo.get(document.body).removeClass('modal-open');
3305 this.fireEvent('hide', this);
3308 isVisible : function()
3311 return this.el.hasClass('show') && !this.el.hasClass('hideing');
3315 addButton : function(str, cb)
3319 var b = Roo.apply({}, { html : str } );
3320 b.xns = b.xns || Roo.bootstrap;
3321 b.xtype = b.xtype || 'Button';
3322 if (typeof(b.listeners) == 'undefined') {
3323 b.listeners = { click : cb.createDelegate(this) };
3326 var btn = Roo.factory(b);
3328 btn.render(this.getButtonContainer());
3334 setDefaultButton : function(btn)
3336 //this.el.select('.modal-footer').()
3339 resizeTo: function(w,h)
3341 this.dialogEl.setWidth(w);
3343 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
3345 this.bodyEl.setHeight(h - diff);
3347 this.fireEvent('resize', this);
3350 setContentSize : function(w, h)
3354 onButtonClick: function(btn,e)
3357 this.fireEvent('btnclick', btn.name, e);
3360 * Set the title of the Dialog
3361 * @param {String} str new Title
3363 setTitle: function(str) {
3364 this.titleEl.dom.innerHTML = str;
3367 * Set the body of the Dialog
3368 * @param {String} str new Title
3370 setBody: function(str) {
3371 this.bodyEl.dom.innerHTML = str;
3374 * Set the body of the Dialog using the template
3375 * @param {Obj} data - apply this data to the template and replace the body contents.
3377 applyBody: function(obj)
3380 Roo.log("Error - using apply Body without a template");
3383 this.tmpl.overwrite(this.bodyEl, obj);
3386 getChildHeight : function(child_nodes)
3390 child_nodes.length == 0
3395 var child_height = 0;
3397 for(var i = 0; i < child_nodes.length; i++) {
3400 * for modal with tabs...
3401 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3403 var layout_childs = child_nodes[i].childNodes;
3405 for(var j = 0; j < layout_childs.length; j++) {
3407 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3409 var layout_body_childs = layout_childs[j].childNodes;
3411 for(var k = 0; k < layout_body_childs.length; k++) {
3413 if(layout_body_childs[k].classList.contains('navbar')) {
3414 child_height += layout_body_childs[k].offsetHeight;
3418 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3420 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3422 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3424 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3425 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3440 child_height += child_nodes[i].offsetHeight;
3441 // Roo.log(child_nodes[i].offsetHeight);
3444 return child_height;
3450 Roo.apply(Roo.bootstrap.Modal, {
3452 * Button config that displays a single OK button
3461 * Button config that displays Yes and No buttons
3477 * Button config that displays OK and Cancel buttons
3492 * Button config that displays Yes, No and Cancel buttons
3516 * messagebox - can be used as a replace
3520 * @class Roo.MessageBox
3521 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3525 Roo.Msg.alert('Status', 'Changes saved successfully.');
3527 // Prompt for user data:
3528 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3530 // process text value...
3534 // Show a dialog using config options:
3536 title:'Save Changes?',
3537 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3538 buttons: Roo.Msg.YESNOCANCEL,
3545 Roo.bootstrap.MessageBox = function(){
3546 var dlg, opt, mask, waitTimer;
3547 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3548 var buttons, activeTextEl, bwidth;
3552 var handleButton = function(button){
3554 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3558 var handleHide = function(){
3560 dlg.el.removeClass(opt.cls);
3563 // Roo.TaskMgr.stop(waitTimer);
3564 // waitTimer = null;
3569 var updateButtons = function(b){
3572 buttons["ok"].hide();
3573 buttons["cancel"].hide();
3574 buttons["yes"].hide();
3575 buttons["no"].hide();
3576 dlg.footerEl.hide();
3580 dlg.footerEl.show();
3581 for(var k in buttons){
3582 if(typeof buttons[k] != "function"){
3585 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3586 width += buttons[k].el.getWidth()+15;
3596 var handleEsc = function(d, k, e){
3597 if(opt && opt.closable !== false){
3607 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3608 * @return {Roo.BasicDialog} The BasicDialog element
3610 getDialog : function(){
3612 dlg = new Roo.bootstrap.Modal( {
3615 //constraintoviewport:false,
3617 //collapsible : false,
3622 //buttonAlign:"center",
3623 closeClick : function(){
3624 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3627 handleButton("cancel");
3632 dlg.on("hide", handleHide);
3634 //dlg.addKeyListener(27, handleEsc);
3636 this.buttons = buttons;
3637 var bt = this.buttonText;
3638 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3639 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3640 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3641 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3643 bodyEl = dlg.bodyEl.createChild({
3645 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3646 '<textarea class="roo-mb-textarea"></textarea>' +
3647 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3649 msgEl = bodyEl.dom.firstChild;
3650 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3651 textboxEl.enableDisplayMode();
3652 textboxEl.addKeyListener([10,13], function(){
3653 if(dlg.isVisible() && opt && opt.buttons){
3656 }else if(opt.buttons.yes){
3657 handleButton("yes");
3661 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3662 textareaEl.enableDisplayMode();
3663 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3664 progressEl.enableDisplayMode();
3666 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3667 var pf = progressEl.dom.firstChild;
3669 pp = Roo.get(pf.firstChild);
3670 pp.setHeight(pf.offsetHeight);
3678 * Updates the message box body text
3679 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3680 * the XHTML-compliant non-breaking space character '&#160;')
3681 * @return {Roo.MessageBox} This message box
3683 updateText : function(text)
3685 if(!dlg.isVisible() && !opt.width){
3686 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3687 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3689 msgEl.innerHTML = text || ' ';
3691 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3692 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3694 Math.min(opt.width || cw , this.maxWidth),
3695 Math.max(opt.minWidth || this.minWidth, bwidth)
3698 activeTextEl.setWidth(w);
3700 if(dlg.isVisible()){
3701 dlg.fixedcenter = false;
3703 // to big, make it scroll. = But as usual stupid IE does not support
3706 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3707 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3708 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3710 bodyEl.dom.style.height = '';
3711 bodyEl.dom.style.overflowY = '';
3714 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3716 bodyEl.dom.style.overflowX = '';
3719 dlg.setContentSize(w, bodyEl.getHeight());
3720 if(dlg.isVisible()){
3721 dlg.fixedcenter = true;
3727 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3728 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3729 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3730 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3731 * @return {Roo.MessageBox} This message box
3733 updateProgress : function(value, text){
3735 this.updateText(text);
3738 if (pp) { // weird bug on my firefox - for some reason this is not defined
3739 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3740 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3746 * Returns true if the message box is currently displayed
3747 * @return {Boolean} True if the message box is visible, else false
3749 isVisible : function(){
3750 return dlg && dlg.isVisible();
3754 * Hides the message box if it is displayed
3757 if(this.isVisible()){
3763 * Displays a new message box, or reinitializes an existing message box, based on the config options
3764 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3765 * The following config object properties are supported:
3767 Property Type Description
3768 ---------- --------------- ------------------------------------------------------------------------------------
3769 animEl String/Element An id or Element from which the message box should animate as it opens and
3770 closes (defaults to undefined)
3771 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3772 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3773 closable Boolean False to hide the top-right close button (defaults to true). Note that
3774 progress and wait dialogs will ignore this property and always hide the
3775 close button as they can only be closed programmatically.
3776 cls String A custom CSS class to apply to the message box element
3777 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3778 displayed (defaults to 75)
3779 fn Function A callback function to execute after closing the dialog. The arguments to the
3780 function will be btn (the name of the button that was clicked, if applicable,
3781 e.g. "ok"), and text (the value of the active text field, if applicable).
3782 Progress and wait dialogs will ignore this option since they do not respond to
3783 user actions and can only be closed programmatically, so any required function
3784 should be called by the same code after it closes the dialog.
3785 icon String A CSS class that provides a background image to be used as an icon for
3786 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3787 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3788 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3789 modal Boolean False to allow user interaction with the page while the message box is
3790 displayed (defaults to true)
3791 msg String A string that will replace the existing message box body text (defaults
3792 to the XHTML-compliant non-breaking space character ' ')
3793 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3794 progress Boolean True to display a progress bar (defaults to false)
3795 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3796 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3797 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3798 title String The title text
3799 value String The string value to set into the active textbox element if displayed
3800 wait Boolean True to display a progress bar (defaults to false)
3801 width Number The width of the dialog in pixels
3808 msg: 'Please enter your address:',
3810 buttons: Roo.MessageBox.OKCANCEL,
3813 animEl: 'addAddressBtn'
3816 * @param {Object} config Configuration options
3817 * @return {Roo.MessageBox} This message box
3819 show : function(options)
3822 // this causes nightmares if you show one dialog after another
3823 // especially on callbacks..
3825 if(this.isVisible()){
3828 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3829 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3830 Roo.log("New Dialog Message:" + options.msg )
3831 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3832 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3835 var d = this.getDialog();
3837 d.setTitle(opt.title || " ");
3838 d.closeEl.setDisplayed(opt.closable !== false);
3839 activeTextEl = textboxEl;
3840 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3845 textareaEl.setHeight(typeof opt.multiline == "number" ?
3846 opt.multiline : this.defaultTextHeight);
3847 activeTextEl = textareaEl;
3856 progressEl.setDisplayed(opt.progress === true);
3858 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3860 this.updateProgress(0);
3861 activeTextEl.dom.value = opt.value || "";
3863 dlg.setDefaultButton(activeTextEl);
3865 var bs = opt.buttons;
3869 }else if(bs && bs.yes){
3870 db = buttons["yes"];
3872 dlg.setDefaultButton(db);
3874 bwidth = updateButtons(opt.buttons);
3875 this.updateText(opt.msg);
3877 d.el.addClass(opt.cls);
3879 d.proxyDrag = opt.proxyDrag === true;
3880 d.modal = opt.modal !== false;
3881 d.mask = opt.modal !== false ? mask : false;
3883 // force it to the end of the z-index stack so it gets a cursor in FF
3884 document.body.appendChild(dlg.el.dom);
3885 d.animateTarget = null;
3886 d.show(options.animEl);
3892 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3893 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3894 * and closing the message box when the process is complete.
3895 * @param {String} title The title bar text
3896 * @param {String} msg The message box body text
3897 * @return {Roo.MessageBox} This message box
3899 progress : function(title, msg){
3906 minWidth: this.minProgressWidth,
3913 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3914 * If a callback function is passed it will be called after the user clicks the button, and the
3915 * id of the button that was clicked will be passed as the only parameter to the callback
3916 * (could also be the top-right close button).
3917 * @param {String} title The title bar text
3918 * @param {String} msg The message box body text
3919 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3920 * @param {Object} scope (optional) The scope of the callback function
3921 * @return {Roo.MessageBox} This message box
3923 alert : function(title, msg, fn, scope)
3938 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3939 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3940 * You are responsible for closing the message box when the process is complete.
3941 * @param {String} msg The message box body text
3942 * @param {String} title (optional) The title bar text
3943 * @return {Roo.MessageBox} This message box
3945 wait : function(msg, title){
3956 waitTimer = Roo.TaskMgr.start({
3958 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3966 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3967 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3968 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3969 * @param {String} title The title bar text
3970 * @param {String} msg The message box body text
3971 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3972 * @param {Object} scope (optional) The scope of the callback function
3973 * @return {Roo.MessageBox} This message box
3975 confirm : function(title, msg, fn, scope){
3979 buttons: this.YESNO,
3988 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3989 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3990 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3991 * (could also be the top-right close button) and the text that was entered will be passed as the two
3992 * parameters to the callback.
3993 * @param {String} title The title bar text
3994 * @param {String} msg The message box body text
3995 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3996 * @param {Object} scope (optional) The scope of the callback function
3997 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3998 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3999 * @return {Roo.MessageBox} This message box
4001 prompt : function(title, msg, fn, scope, multiline){
4005 buttons: this.OKCANCEL,
4010 multiline: multiline,
4017 * Button config that displays a single OK button
4022 * Button config that displays Yes and No buttons
4025 YESNO : {yes:true, no:true},
4027 * Button config that displays OK and Cancel buttons
4030 OKCANCEL : {ok:true, cancel:true},
4032 * Button config that displays Yes, No and Cancel buttons
4035 YESNOCANCEL : {yes:true, no:true, cancel:true},
4038 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
4041 defaultTextHeight : 75,
4043 * The maximum width in pixels of the message box (defaults to 600)
4048 * The minimum width in pixels of the message box (defaults to 100)
4053 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
4054 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
4057 minProgressWidth : 250,
4059 * An object containing the default button text strings that can be overriden for localized language support.
4060 * Supported properties are: ok, cancel, yes and no.
4061 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
4074 * Shorthand for {@link Roo.MessageBox}
4076 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
4077 Roo.Msg = Roo.Msg || Roo.MessageBox;
4086 * @class Roo.bootstrap.Navbar
4087 * @extends Roo.bootstrap.Component
4088 * Bootstrap Navbar class
4091 * Create a new Navbar
4092 * @param {Object} config The config object
4096 Roo.bootstrap.Navbar = function(config){
4097 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
4101 * @event beforetoggle
4102 * Fire before toggle the menu
4103 * @param {Roo.EventObject} e
4105 "beforetoggle" : true
4109 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
4118 getAutoCreate : function(){
4121 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
4125 initEvents :function ()
4127 //Roo.log(this.el.select('.navbar-toggle',true));
4128 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
4135 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
4137 var size = this.el.getSize();
4138 this.maskEl.setSize(size.width, size.height);
4139 this.maskEl.enableDisplayMode("block");
4148 getChildContainer : function()
4150 if (this.el && this.el.select('.collapse').getCount()) {
4151 return this.el.select('.collapse',true).first();
4166 onToggle : function()
4169 if(this.fireEvent('beforetoggle', this) === false){
4172 var ce = this.el.select('.navbar-collapse',true).first();
4174 if (!ce.hasClass('show')) {
4184 * Expand the navbar pulldown
4186 expand : function ()
4189 var ce = this.el.select('.navbar-collapse',true).first();
4190 if (ce.hasClass('collapsing')) {
4193 ce.dom.style.height = '';
4195 ce.addClass('in'); // old...
4196 ce.removeClass('collapse');
4197 ce.addClass('show');
4198 var h = ce.getHeight();
4200 ce.removeClass('show');
4201 // at this point we should be able to see it..
4202 ce.addClass('collapsing');
4204 ce.setHeight(0); // resize it ...
4205 ce.on('transitionend', function() {
4206 //Roo.log('done transition');
4207 ce.removeClass('collapsing');
4208 ce.addClass('show');
4209 ce.removeClass('collapse');
4211 ce.dom.style.height = '';
4212 }, this, { single: true} );
4214 ce.dom.scrollTop = 0;
4217 * Collapse the navbar pulldown
4219 collapse : function()
4221 var ce = this.el.select('.navbar-collapse',true).first();
4223 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
4224 // it's collapsed or collapsing..
4227 ce.removeClass('in'); // old...
4228 ce.setHeight(ce.getHeight());
4229 ce.removeClass('show');
4230 ce.addClass('collapsing');
4232 ce.on('transitionend', function() {
4233 ce.dom.style.height = '';
4234 ce.removeClass('collapsing');
4235 ce.addClass('collapse');
4236 }, this, { single: true} );
4256 * @class Roo.bootstrap.NavSimplebar
4257 * @extends Roo.bootstrap.Navbar
4258 * Bootstrap Sidebar class
4260 * @cfg {Boolean} inverse is inverted color
4262 * @cfg {String} type (nav | pills | tabs)
4263 * @cfg {Boolean} arrangement stacked | justified
4264 * @cfg {String} align (left | right) alignment
4266 * @cfg {Boolean} main (true|false) main nav bar? default false
4267 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4269 * @cfg {String} tag (header|footer|nav|div) default is nav
4271 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4275 * Create a new Sidebar
4276 * @param {Object} config The config object
4280 Roo.bootstrap.NavSimplebar = function(config){
4281 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4284 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
4300 getAutoCreate : function(){
4304 tag : this.tag || 'div',
4305 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
4307 if (['light','white'].indexOf(this.weight) > -1) {
4308 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4310 cfg.cls += ' bg-' + this.weight;
4313 cfg.cls += ' navbar-inverse';
4317 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4319 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4328 cls: 'nav nav-' + this.xtype,
4334 this.type = this.type || 'nav';
4335 if (['tabs','pills'].indexOf(this.type) != -1) {
4336 cfg.cn[0].cls += ' nav-' + this.type
4340 if (this.type!=='nav') {
4341 Roo.log('nav type must be nav/tabs/pills')
4343 cfg.cn[0].cls += ' navbar-nav'
4349 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4350 cfg.cn[0].cls += ' nav-' + this.arrangement;
4354 if (this.align === 'right') {
4355 cfg.cn[0].cls += ' navbar-right';
4380 * navbar-expand-md fixed-top
4384 * @class Roo.bootstrap.NavHeaderbar
4385 * @extends Roo.bootstrap.NavSimplebar
4386 * Bootstrap Sidebar class
4388 * @cfg {String} brand what is brand
4389 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4390 * @cfg {String} brand_href href of the brand
4391 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4392 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4393 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4394 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4397 * Create a new Sidebar
4398 * @param {Object} config The config object
4402 Roo.bootstrap.NavHeaderbar = function(config){
4403 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4407 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4414 desktopCenter : false,
4417 getAutoCreate : function(){
4420 tag: this.nav || 'nav',
4421 cls: 'navbar navbar-expand-md',
4427 if (this.desktopCenter) {
4428 cn.push({cls : 'container', cn : []});
4436 cls: 'navbar-toggle navbar-toggler',
4437 'data-toggle': 'collapse',
4442 html: 'Toggle navigation'
4446 cls: 'icon-bar navbar-toggler-icon'
4459 cn.push( Roo.bootstrap.version == 4 ? btn : {
4461 cls: 'navbar-header',
4470 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
4474 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4476 if (['light','white'].indexOf(this.weight) > -1) {
4477 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4479 cfg.cls += ' bg-' + this.weight;
4482 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4483 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4485 // tag can override this..
4487 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4490 if (this.brand !== '') {
4491 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4492 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4494 href: this.brand_href ? this.brand_href : '#',
4495 cls: 'navbar-brand',
4503 cfg.cls += ' main-nav';
4511 getHeaderChildContainer : function()
4513 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4514 return this.el.select('.navbar-header',true).first();
4517 return this.getChildContainer();
4521 initEvents : function()
4523 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4525 if (this.autohide) {
4530 Roo.get(document).on('scroll',function(e) {
4531 var ns = Roo.get(document).getScroll().top;
4532 var os = prevScroll;
4536 ft.removeClass('slideDown');
4537 ft.addClass('slideUp');
4540 ft.removeClass('slideUp');
4541 ft.addClass('slideDown');
4562 * @class Roo.bootstrap.NavSidebar
4563 * @extends Roo.bootstrap.Navbar
4564 * Bootstrap Sidebar class
4567 * Create a new Sidebar
4568 * @param {Object} config The config object
4572 Roo.bootstrap.NavSidebar = function(config){
4573 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4576 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4578 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4580 getAutoCreate : function(){
4585 cls: 'sidebar sidebar-nav'
4607 * @class Roo.bootstrap.NavGroup
4608 * @extends Roo.bootstrap.Component
4609 * Bootstrap NavGroup class
4610 * @cfg {String} align (left|right)
4611 * @cfg {Boolean} inverse
4612 * @cfg {String} type (nav|pills|tab) default nav
4613 * @cfg {String} navId - reference Id for navbar.
4617 * Create a new nav group
4618 * @param {Object} config The config object
4621 Roo.bootstrap.NavGroup = function(config){
4622 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4625 Roo.bootstrap.NavGroup.register(this);
4629 * Fires when the active item changes
4630 * @param {Roo.bootstrap.NavGroup} this
4631 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4632 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4639 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4650 getAutoCreate : function()
4652 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4658 if (Roo.bootstrap.version == 4) {
4659 if (['tabs','pills'].indexOf(this.type) != -1) {
4660 cfg.cls += ' nav-' + this.type;
4662 // trying to remove so header bar can right align top?
4663 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
4664 // do not use on header bar...
4665 cfg.cls += ' navbar-nav';
4670 if (['tabs','pills'].indexOf(this.type) != -1) {
4671 cfg.cls += ' nav-' + this.type
4673 if (this.type !== 'nav') {
4674 Roo.log('nav type must be nav/tabs/pills')
4676 cfg.cls += ' navbar-nav'
4680 if (this.parent() && this.parent().sidebar) {
4683 cls: 'dashboard-menu sidebar-menu'
4689 if (this.form === true) {
4692 cls: 'navbar-form form-inline'
4694 //nav navbar-right ml-md-auto
4695 if (this.align === 'right') {
4696 cfg.cls += ' navbar-right ml-md-auto';
4698 cfg.cls += ' navbar-left';
4702 if (this.align === 'right') {
4703 cfg.cls += ' navbar-right ml-md-auto';
4705 cfg.cls += ' mr-auto';
4709 cfg.cls += ' navbar-inverse';
4717 * sets the active Navigation item
4718 * @param {Roo.bootstrap.NavItem} the new current navitem
4720 setActiveItem : function(item)
4723 Roo.each(this.navItems, function(v){
4728 v.setActive(false, true);
4735 item.setActive(true, true);
4736 this.fireEvent('changed', this, item, prev);
4741 * gets the active Navigation item
4742 * @return {Roo.bootstrap.NavItem} the current navitem
4744 getActive : function()
4748 Roo.each(this.navItems, function(v){
4759 indexOfNav : function()
4763 Roo.each(this.navItems, function(v,i){
4774 * adds a Navigation item
4775 * @param {Roo.bootstrap.NavItem} the navitem to add
4777 addItem : function(cfg)
4779 if (this.form && Roo.bootstrap.version == 4) {
4782 var cn = new Roo.bootstrap.NavItem(cfg);
4784 cn.parentId = this.id;
4785 cn.onRender(this.el, null);
4789 * register a Navigation item
4790 * @param {Roo.bootstrap.NavItem} the navitem to add
4792 register : function(item)
4794 this.navItems.push( item);
4795 item.navId = this.navId;
4800 * clear all the Navigation item
4803 clearAll : function()
4806 this.el.dom.innerHTML = '';
4809 getNavItem: function(tabId)
4812 Roo.each(this.navItems, function(e) {
4813 if (e.tabId == tabId) {
4823 setActiveNext : function()
4825 var i = this.indexOfNav(this.getActive());
4826 if (i > this.navItems.length) {
4829 this.setActiveItem(this.navItems[i+1]);
4831 setActivePrev : function()
4833 var i = this.indexOfNav(this.getActive());
4837 this.setActiveItem(this.navItems[i-1]);
4839 clearWasActive : function(except) {
4840 Roo.each(this.navItems, function(e) {
4841 if (e.tabId != except.tabId && e.was_active) {
4842 e.was_active = false;
4849 getWasActive : function ()
4852 Roo.each(this.navItems, function(e) {
4867 Roo.apply(Roo.bootstrap.NavGroup, {
4871 * register a Navigation Group
4872 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4874 register : function(navgrp)
4876 this.groups[navgrp.navId] = navgrp;
4880 * fetch a Navigation Group based on the navigation ID
4881 * @param {string} the navgroup to add
4882 * @returns {Roo.bootstrap.NavGroup} the navgroup
4884 get: function(navId) {
4885 if (typeof(this.groups[navId]) == 'undefined') {
4887 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4889 return this.groups[navId] ;
4904 * @class Roo.bootstrap.NavItem
4905 * @extends Roo.bootstrap.Component
4906 * Bootstrap Navbar.NavItem class
4907 * @cfg {String} href link to
4908 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4910 * @cfg {String} html content of button
4911 * @cfg {String} badge text inside badge
4912 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4913 * @cfg {String} glyphicon DEPRICATED - use fa
4914 * @cfg {String} icon DEPRICATED - use fa
4915 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4916 * @cfg {Boolean} active Is item active
4917 * @cfg {Boolean} disabled Is item disabled
4919 * @cfg {Boolean} preventDefault (true | false) default false
4920 * @cfg {String} tabId the tab that this item activates.
4921 * @cfg {String} tagtype (a|span) render as a href or span?
4922 * @cfg {Boolean} animateRef (true|false) link to element default false
4925 * Create a new Navbar Item
4926 * @param {Object} config The config object
4928 Roo.bootstrap.NavItem = function(config){
4929 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4934 * The raw click event for the entire grid.
4935 * @param {Roo.EventObject} e
4940 * Fires when the active item active state changes
4941 * @param {Roo.bootstrap.NavItem} this
4942 * @param {boolean} state the new state
4948 * Fires when scroll to element
4949 * @param {Roo.bootstrap.NavItem} this
4950 * @param {Object} options
4951 * @param {Roo.EventObject} e
4959 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4968 preventDefault : false,
4976 button_outline : false,
4980 getAutoCreate : function(){
4988 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4990 if (this.disabled) {
4991 cfg.cls += ' disabled';
4995 if (this.button_weight.length) {
4996 cfg.tag = this.href ? 'a' : 'button';
4997 cfg.html = this.html || '';
4998 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
5000 cfg.href = this.href;
5003 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
5006 // menu .. should add dropdown-menu class - so no need for carat..
5008 if (this.badge !== '') {
5010 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5015 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
5019 href : this.href || "#",
5020 html: this.html || ''
5023 if (this.tagtype == 'a') {
5024 cfg.cn[0].cls = 'nav-link';
5027 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
5030 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
5032 if(this.glyphicon) {
5033 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
5038 cfg.cn[0].html += " <span class='caret'></span>";
5042 if (this.badge !== '') {
5044 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5052 onRender : function(ct, position)
5054 // Roo.log("Call onRender: " + this.xtype);
5055 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
5059 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
5060 this.navLink = this.el.select('.nav-link',true).first();
5065 initEvents: function()
5067 if (typeof (this.menu) != 'undefined') {
5068 this.menu.parentType = this.xtype;
5069 this.menu.triggerEl = this.el;
5070 this.menu = this.addxtype(Roo.apply({}, this.menu));
5073 this.el.select('a',true).on('click', this.onClick, this);
5075 if(this.tagtype == 'span'){
5076 this.el.select('span',true).on('click', this.onClick, this);
5079 // at this point parent should be available..
5080 this.parent().register(this);
5083 onClick : function(e)
5085 if (e.getTarget('.dropdown-menu-item')) {
5086 // did you click on a menu itemm.... - then don't trigger onclick..
5091 this.preventDefault ||
5094 Roo.log("NavItem - prevent Default?");
5098 if (this.disabled) {
5102 var tg = Roo.bootstrap.TabGroup.get(this.navId);
5103 if (tg && tg.transition) {
5104 Roo.log("waiting for the transitionend");
5110 //Roo.log("fire event clicked");
5111 if(this.fireEvent('click', this, e) === false){
5115 if(this.tagtype == 'span'){
5119 //Roo.log(this.href);
5120 var ael = this.el.select('a',true).first();
5123 if(ael && this.animateRef && this.href.indexOf('#') > -1){
5124 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
5125 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
5126 return; // ignore... - it's a 'hash' to another page.
5128 Roo.log("NavItem - prevent Default?");
5130 this.scrollToElement(e);
5134 var p = this.parent();
5136 if (['tabs','pills'].indexOf(p.type)!==-1) {
5137 if (typeof(p.setActiveItem) !== 'undefined') {
5138 p.setActiveItem(this);
5142 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
5143 if (p.parentType == 'NavHeaderbar' && !this.menu) {
5144 // remove the collapsed menu expand...
5145 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
5149 isActive: function () {
5152 setActive : function(state, fire, is_was_active)
5154 if (this.active && !state && this.navId) {
5155 this.was_active = true;
5156 var nv = Roo.bootstrap.NavGroup.get(this.navId);
5158 nv.clearWasActive(this);
5162 this.active = state;
5165 this.el.removeClass('active');
5166 this.navLink ? this.navLink.removeClass('active') : false;
5167 } else if (!this.el.hasClass('active')) {
5169 this.el.addClass('active');
5170 if (Roo.bootstrap.version == 4 && this.navLink ) {
5171 this.navLink.addClass('active');
5176 this.fireEvent('changed', this, state);
5179 // show a panel if it's registered and related..
5181 if (!this.navId || !this.tabId || !state || is_was_active) {
5185 var tg = Roo.bootstrap.TabGroup.get(this.navId);
5189 var pan = tg.getPanelByName(this.tabId);
5193 // if we can not flip to new panel - go back to old nav highlight..
5194 if (false == tg.showPanel(pan)) {
5195 var nv = Roo.bootstrap.NavGroup.get(this.navId);
5197 var onav = nv.getWasActive();
5199 onav.setActive(true, false, true);
5208 // this should not be here...
5209 setDisabled : function(state)
5211 this.disabled = state;
5213 this.el.removeClass('disabled');
5214 } else if (!this.el.hasClass('disabled')) {
5215 this.el.addClass('disabled');
5221 * Fetch the element to display the tooltip on.
5222 * @return {Roo.Element} defaults to this.el
5224 tooltipEl : function()
5226 return this.el.select('' + this.tagtype + '', true).first();
5229 scrollToElement : function(e)
5231 var c = document.body;
5234 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
5236 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
5237 c = document.documentElement;
5240 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5246 var o = target.calcOffsetsTo(c);
5253 this.fireEvent('scrollto', this, options, e);
5255 Roo.get(c).scrollTo('top', options.value, true);
5268 * <span> icon </span>
5269 * <span> text </span>
5270 * <span>badge </span>
5274 * @class Roo.bootstrap.NavSidebarItem
5275 * @extends Roo.bootstrap.NavItem
5276 * Bootstrap Navbar.NavSidebarItem class
5277 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5278 * {Boolean} open is the menu open
5279 * {Boolean} buttonView use button as the tigger el rather that a (default false)
5280 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5281 * {String} buttonSize (sm|md|lg)the extra classes for the button
5282 * {Boolean} showArrow show arrow next to the text (default true)
5284 * Create a new Navbar Button
5285 * @param {Object} config The config object
5287 Roo.bootstrap.NavSidebarItem = function(config){
5288 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5293 * The raw click event for the entire grid.
5294 * @param {Roo.EventObject} e
5299 * Fires when the active item active state changes
5300 * @param {Roo.bootstrap.NavSidebarItem} this
5301 * @param {boolean} state the new state
5309 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
5311 badgeWeight : 'default',
5317 buttonWeight : 'default',
5323 getAutoCreate : function(){
5328 href : this.href || '#',
5334 if(this.buttonView){
5337 href : this.href || '#',
5338 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5351 cfg.cls += ' active';
5354 if (this.disabled) {
5355 cfg.cls += ' disabled';
5358 cfg.cls += ' open x-open';
5361 if (this.glyphicon || this.icon) {
5362 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
5363 a.cn.push({ tag : 'i', cls : c }) ;
5366 if(!this.buttonView){
5369 html : this.html || ''
5376 if (this.badge !== '') {
5377 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
5383 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5386 a.cls += ' dropdown-toggle treeview' ;
5392 initEvents : function()
5394 if (typeof (this.menu) != 'undefined') {
5395 this.menu.parentType = this.xtype;
5396 this.menu.triggerEl = this.el;
5397 this.menu = this.addxtype(Roo.apply({}, this.menu));
5400 this.el.on('click', this.onClick, this);
5402 if(this.badge !== ''){
5403 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5408 onClick : function(e)
5415 if(this.preventDefault){
5419 this.fireEvent('click', this, e);
5422 disable : function()
5424 this.setDisabled(true);
5429 this.setDisabled(false);
5432 setDisabled : function(state)
5434 if(this.disabled == state){
5438 this.disabled = state;
5441 this.el.addClass('disabled');
5445 this.el.removeClass('disabled');
5450 setActive : function(state)
5452 if(this.active == state){
5456 this.active = state;
5459 this.el.addClass('active');
5463 this.el.removeClass('active');
5468 isActive: function ()
5473 setBadge : function(str)
5479 this.badgeEl.dom.innerHTML = str;
5496 * @class Roo.bootstrap.Row
5497 * @extends Roo.bootstrap.Component
5498 * Bootstrap Row class (contains columns...)
5502 * @param {Object} config The config object
5505 Roo.bootstrap.Row = function(config){
5506 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5509 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5511 getAutoCreate : function(){
5530 * @class Roo.bootstrap.Element
5531 * @extends Roo.bootstrap.Component
5532 * Bootstrap Element class
5533 * @cfg {String} html contents of the element
5534 * @cfg {String} tag tag of the element
5535 * @cfg {String} cls class of the element
5536 * @cfg {Boolean} preventDefault (true|false) default false
5537 * @cfg {Boolean} clickable (true|false) default false
5540 * Create a new Element
5541 * @param {Object} config The config object
5544 Roo.bootstrap.Element = function(config){
5545 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5551 * When a element is chick
5552 * @param {Roo.bootstrap.Element} this
5553 * @param {Roo.EventObject} e
5559 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5564 preventDefault: false,
5567 getAutoCreate : function(){
5571 // cls: this.cls, double assign in parent class Component.js :: onRender
5578 initEvents: function()
5580 Roo.bootstrap.Element.superclass.initEvents.call(this);
5583 this.el.on('click', this.onClick, this);
5588 onClick : function(e)
5590 if(this.preventDefault){
5594 this.fireEvent('click', this, e);
5597 getValue : function()
5599 return this.el.dom.innerHTML;
5602 setValue : function(value)
5604 this.el.dom.innerHTML = value;
5619 * @class Roo.bootstrap.Pagination
5620 * @extends Roo.bootstrap.Component
5621 * Bootstrap Pagination class
5622 * @cfg {String} size xs | sm | md | lg
5623 * @cfg {Boolean} inverse false | true
5626 * Create a new Pagination
5627 * @param {Object} config The config object
5630 Roo.bootstrap.Pagination = function(config){
5631 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5634 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5640 getAutoCreate : function(){
5646 cfg.cls += ' inverse';
5652 cfg.cls += " " + this.cls;
5670 * @class Roo.bootstrap.PaginationItem
5671 * @extends Roo.bootstrap.Component
5672 * Bootstrap PaginationItem class
5673 * @cfg {String} html text
5674 * @cfg {String} href the link
5675 * @cfg {Boolean} preventDefault (true | false) default true
5676 * @cfg {Boolean} active (true | false) default false
5677 * @cfg {Boolean} disabled default false
5681 * Create a new PaginationItem
5682 * @param {Object} config The config object
5686 Roo.bootstrap.PaginationItem = function(config){
5687 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5692 * The raw click event for the entire grid.
5693 * @param {Roo.EventObject} e
5699 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5703 preventDefault: true,
5708 getAutoCreate : function(){
5714 href : this.href ? this.href : '#',
5715 html : this.html ? this.html : ''
5725 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5729 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5735 initEvents: function() {
5737 this.el.on('click', this.onClick, this);
5740 onClick : function(e)
5742 Roo.log('PaginationItem on click ');
5743 if(this.preventDefault){
5751 this.fireEvent('click', this, e);
5767 * @class Roo.bootstrap.Slider
5768 * @extends Roo.bootstrap.Component
5769 * Bootstrap Slider class
5772 * Create a new Slider
5773 * @param {Object} config The config object
5776 Roo.bootstrap.Slider = function(config){
5777 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5780 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5782 getAutoCreate : function(){
5786 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5790 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5802 * Ext JS Library 1.1.1
5803 * Copyright(c) 2006-2007, Ext JS, LLC.
5805 * Originally Released Under LGPL - original licence link has changed is not relivant.
5808 * <script type="text/javascript">
5813 * @class Roo.grid.ColumnModel
5814 * @extends Roo.util.Observable
5815 * This is the default implementation of a ColumnModel used by the Grid. It defines
5816 * the columns in the grid.
5819 var colModel = new Roo.grid.ColumnModel([
5820 {header: "Ticker", width: 60, sortable: true, locked: true},
5821 {header: "Company Name", width: 150, sortable: true},
5822 {header: "Market Cap.", width: 100, sortable: true},
5823 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5824 {header: "Employees", width: 100, sortable: true, resizable: false}
5829 * The config options listed for this class are options which may appear in each
5830 * individual column definition.
5831 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5833 * @param {Object} config An Array of column config objects. See this class's
5834 * config objects for details.
5836 Roo.grid.ColumnModel = function(config){
5838 * The config passed into the constructor
5840 this.config = config;
5843 // if no id, create one
5844 // if the column does not have a dataIndex mapping,
5845 // map it to the order it is in the config
5846 for(var i = 0, len = config.length; i < len; i++){
5848 if(typeof c.dataIndex == "undefined"){
5851 if(typeof c.renderer == "string"){
5852 c.renderer = Roo.util.Format[c.renderer];
5854 if(typeof c.id == "undefined"){
5857 if(c.editor && c.editor.xtype){
5858 c.editor = Roo.factory(c.editor, Roo.grid);
5860 if(c.editor && c.editor.isFormField){
5861 c.editor = new Roo.grid.GridEditor(c.editor);
5863 this.lookup[c.id] = c;
5867 * The width of columns which have no width specified (defaults to 100)
5870 this.defaultWidth = 100;
5873 * Default sortable of columns which have no sortable specified (defaults to false)
5876 this.defaultSortable = false;
5880 * @event widthchange
5881 * Fires when the width of a column changes.
5882 * @param {ColumnModel} this
5883 * @param {Number} columnIndex The column index
5884 * @param {Number} newWidth The new width
5886 "widthchange": true,
5888 * @event headerchange
5889 * Fires when the text of a header changes.
5890 * @param {ColumnModel} this
5891 * @param {Number} columnIndex The column index
5892 * @param {Number} newText The new header text
5894 "headerchange": true,
5896 * @event hiddenchange
5897 * Fires when a column is hidden or "unhidden".
5898 * @param {ColumnModel} this
5899 * @param {Number} columnIndex The column index
5900 * @param {Boolean} hidden true if hidden, false otherwise
5902 "hiddenchange": true,
5904 * @event columnmoved
5905 * Fires when a column is moved.
5906 * @param {ColumnModel} this
5907 * @param {Number} oldIndex
5908 * @param {Number} newIndex
5910 "columnmoved" : true,
5912 * @event columlockchange
5913 * Fires when a column's locked state is changed
5914 * @param {ColumnModel} this
5915 * @param {Number} colIndex
5916 * @param {Boolean} locked true if locked
5918 "columnlockchange" : true
5920 Roo.grid.ColumnModel.superclass.constructor.call(this);
5922 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5924 * @cfg {String} header The header text to display in the Grid view.
5927 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5928 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5929 * specified, the column's index is used as an index into the Record's data Array.
5932 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5933 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5936 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5937 * Defaults to the value of the {@link #defaultSortable} property.
5938 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5941 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5944 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5947 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5950 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5953 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5954 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5955 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5956 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5959 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5962 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5965 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5968 * @cfg {String} cursor (Optional)
5971 * @cfg {String} tooltip (Optional)
5974 * @cfg {Number} xs (Optional)
5977 * @cfg {Number} sm (Optional)
5980 * @cfg {Number} md (Optional)
5983 * @cfg {Number} lg (Optional)
5986 * Returns the id of the column at the specified index.
5987 * @param {Number} index The column index
5988 * @return {String} the id
5990 getColumnId : function(index){
5991 return this.config[index].id;
5995 * Returns the column for a specified id.
5996 * @param {String} id The column id
5997 * @return {Object} the column
5999 getColumnById : function(id){
6000 return this.lookup[id];
6005 * Returns the column for a specified dataIndex.
6006 * @param {String} dataIndex The column dataIndex
6007 * @return {Object|Boolean} the column or false if not found
6009 getColumnByDataIndex: function(dataIndex){
6010 var index = this.findColumnIndex(dataIndex);
6011 return index > -1 ? this.config[index] : false;
6015 * Returns the index for a specified column id.
6016 * @param {String} id The column id
6017 * @return {Number} the index, or -1 if not found
6019 getIndexById : function(id){
6020 for(var i = 0, len = this.config.length; i < len; i++){
6021 if(this.config[i].id == id){
6029 * Returns the index for a specified column dataIndex.
6030 * @param {String} dataIndex The column dataIndex
6031 * @return {Number} the index, or -1 if not found
6034 findColumnIndex : function(dataIndex){
6035 for(var i = 0, len = this.config.length; i < len; i++){
6036 if(this.config[i].dataIndex == dataIndex){
6044 moveColumn : function(oldIndex, newIndex){
6045 var c = this.config[oldIndex];
6046 this.config.splice(oldIndex, 1);
6047 this.config.splice(newIndex, 0, c);
6048 this.dataMap = null;
6049 this.fireEvent("columnmoved", this, oldIndex, newIndex);
6052 isLocked : function(colIndex){
6053 return this.config[colIndex].locked === true;
6056 setLocked : function(colIndex, value, suppressEvent){
6057 if(this.isLocked(colIndex) == value){
6060 this.config[colIndex].locked = value;
6062 this.fireEvent("columnlockchange", this, colIndex, value);
6066 getTotalLockedWidth : function(){
6068 for(var i = 0; i < this.config.length; i++){
6069 if(this.isLocked(i) && !this.isHidden(i)){
6070 this.totalWidth += this.getColumnWidth(i);
6076 getLockedCount : function(){
6077 for(var i = 0, len = this.config.length; i < len; i++){
6078 if(!this.isLocked(i)){
6083 return this.config.length;
6087 * Returns the number of columns.
6090 getColumnCount : function(visibleOnly){
6091 if(visibleOnly === true){
6093 for(var i = 0, len = this.config.length; i < len; i++){
6094 if(!this.isHidden(i)){
6100 return this.config.length;
6104 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
6105 * @param {Function} fn
6106 * @param {Object} scope (optional)
6107 * @return {Array} result
6109 getColumnsBy : function(fn, scope){
6111 for(var i = 0, len = this.config.length; i < len; i++){
6112 var c = this.config[i];
6113 if(fn.call(scope||this, c, i) === true){
6121 * Returns true if the specified column is sortable.
6122 * @param {Number} col The column index
6125 isSortable : function(col){
6126 if(typeof this.config[col].sortable == "undefined"){
6127 return this.defaultSortable;
6129 return this.config[col].sortable;
6133 * Returns the rendering (formatting) function defined for the column.
6134 * @param {Number} col The column index.
6135 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
6137 getRenderer : function(col){
6138 if(!this.config[col].renderer){
6139 return Roo.grid.ColumnModel.defaultRenderer;
6141 return this.config[col].renderer;
6145 * Sets the rendering (formatting) function for a column.
6146 * @param {Number} col The column index
6147 * @param {Function} fn The function to use to process the cell's raw data
6148 * to return HTML markup for the grid view. The render function is called with
6149 * the following parameters:<ul>
6150 * <li>Data value.</li>
6151 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
6152 * <li>css A CSS style string to apply to the table cell.</li>
6153 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
6154 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
6155 * <li>Row index</li>
6156 * <li>Column index</li>
6157 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
6159 setRenderer : function(col, fn){
6160 this.config[col].renderer = fn;
6164 * Returns the width for the specified column.
6165 * @param {Number} col The column index
6168 getColumnWidth : function(col){
6169 return this.config[col].width * 1 || this.defaultWidth;
6173 * Sets the width for a column.
6174 * @param {Number} col The column index
6175 * @param {Number} width The new width
6177 setColumnWidth : function(col, width, suppressEvent){
6178 this.config[col].width = width;
6179 this.totalWidth = null;
6181 this.fireEvent("widthchange", this, col, width);
6186 * Returns the total width of all columns.
6187 * @param {Boolean} includeHidden True to include hidden column widths
6190 getTotalWidth : function(includeHidden){
6191 if(!this.totalWidth){
6192 this.totalWidth = 0;
6193 for(var i = 0, len = this.config.length; i < len; i++){
6194 if(includeHidden || !this.isHidden(i)){
6195 this.totalWidth += this.getColumnWidth(i);
6199 return this.totalWidth;
6203 * Returns the header for the specified column.
6204 * @param {Number} col The column index
6207 getColumnHeader : function(col){
6208 return this.config[col].header;
6212 * Sets the header for a column.
6213 * @param {Number} col The column index
6214 * @param {String} header The new header
6216 setColumnHeader : function(col, header){
6217 this.config[col].header = header;
6218 this.fireEvent("headerchange", this, col, header);
6222 * Returns the tooltip for the specified column.
6223 * @param {Number} col The column index
6226 getColumnTooltip : function(col){
6227 return this.config[col].tooltip;
6230 * Sets the tooltip for a column.
6231 * @param {Number} col The column index
6232 * @param {String} tooltip The new tooltip
6234 setColumnTooltip : function(col, tooltip){
6235 this.config[col].tooltip = tooltip;
6239 * Returns the dataIndex for the specified column.
6240 * @param {Number} col The column index
6243 getDataIndex : function(col){
6244 return this.config[col].dataIndex;
6248 * Sets the dataIndex for a column.
6249 * @param {Number} col The column index
6250 * @param {Number} dataIndex The new dataIndex
6252 setDataIndex : function(col, dataIndex){
6253 this.config[col].dataIndex = dataIndex;
6259 * Returns true if the cell is editable.
6260 * @param {Number} colIndex The column index
6261 * @param {Number} rowIndex The row index - this is nto actually used..?
6264 isCellEditable : function(colIndex, rowIndex){
6265 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6269 * Returns the editor defined for the cell/column.
6270 * return false or null to disable editing.
6271 * @param {Number} colIndex The column index
6272 * @param {Number} rowIndex The row index
6275 getCellEditor : function(colIndex, rowIndex){
6276 return this.config[colIndex].editor;
6280 * Sets if a column is editable.
6281 * @param {Number} col The column index
6282 * @param {Boolean} editable True if the column is editable
6284 setEditable : function(col, editable){
6285 this.config[col].editable = editable;
6290 * Returns true if the column is hidden.
6291 * @param {Number} colIndex The column index
6294 isHidden : function(colIndex){
6295 return this.config[colIndex].hidden;
6300 * Returns true if the column width cannot be changed
6302 isFixed : function(colIndex){
6303 return this.config[colIndex].fixed;
6307 * Returns true if the column can be resized
6310 isResizable : function(colIndex){
6311 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6314 * Sets if a column is hidden.
6315 * @param {Number} colIndex The column index
6316 * @param {Boolean} hidden True if the column is hidden
6318 setHidden : function(colIndex, hidden){
6319 this.config[colIndex].hidden = hidden;
6320 this.totalWidth = null;
6321 this.fireEvent("hiddenchange", this, colIndex, hidden);
6325 * Sets the editor for a column.
6326 * @param {Number} col The column index
6327 * @param {Object} editor The editor object
6329 setEditor : function(col, editor){
6330 this.config[col].editor = editor;
6334 Roo.grid.ColumnModel.defaultRenderer = function(value)
6336 if(typeof value == "object") {
6339 if(typeof value == "string" && value.length < 1){
6343 return String.format("{0}", value);
6346 // Alias for backwards compatibility
6347 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6350 * Ext JS Library 1.1.1
6351 * Copyright(c) 2006-2007, Ext JS, LLC.
6353 * Originally Released Under LGPL - original licence link has changed is not relivant.
6356 * <script type="text/javascript">
6360 * @class Roo.LoadMask
6361 * A simple utility class for generically masking elements while loading data. If the element being masked has
6362 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6363 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
6364 * element's UpdateManager load indicator and will be destroyed after the initial load.
6366 * Create a new LoadMask
6367 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6368 * @param {Object} config The config object
6370 Roo.LoadMask = function(el, config){
6371 this.el = Roo.get(el);
6372 Roo.apply(this, config);
6374 this.store.on('beforeload', this.onBeforeLoad, this);
6375 this.store.on('load', this.onLoad, this);
6376 this.store.on('loadexception', this.onLoadException, this);
6377 this.removeMask = false;
6379 var um = this.el.getUpdateManager();
6380 um.showLoadIndicator = false; // disable the default indicator
6381 um.on('beforeupdate', this.onBeforeLoad, this);
6382 um.on('update', this.onLoad, this);
6383 um.on('failure', this.onLoad, this);
6384 this.removeMask = true;
6388 Roo.LoadMask.prototype = {
6390 * @cfg {Boolean} removeMask
6391 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6392 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
6396 * The text to display in a centered loading message box (defaults to 'Loading...')
6400 * @cfg {String} msgCls
6401 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6403 msgCls : 'x-mask-loading',
6406 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6412 * Disables the mask to prevent it from being displayed
6414 disable : function(){
6415 this.disabled = true;
6419 * Enables the mask so that it can be displayed
6421 enable : function(){
6422 this.disabled = false;
6425 onLoadException : function()
6429 if (typeof(arguments[3]) != 'undefined') {
6430 Roo.MessageBox.alert("Error loading",arguments[3]);
6434 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6435 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6442 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6447 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6451 onBeforeLoad : function(){
6453 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6458 destroy : function(){
6460 this.store.un('beforeload', this.onBeforeLoad, this);
6461 this.store.un('load', this.onLoad, this);
6462 this.store.un('loadexception', this.onLoadException, this);
6464 var um = this.el.getUpdateManager();
6465 um.un('beforeupdate', this.onBeforeLoad, this);
6466 um.un('update', this.onLoad, this);
6467 um.un('failure', this.onLoad, this);
6478 * @class Roo.bootstrap.Table
6479 * @extends Roo.bootstrap.Component
6480 * Bootstrap Table class
6481 * @cfg {String} cls table class
6482 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6483 * @cfg {String} bgcolor Specifies the background color for a table
6484 * @cfg {Number} border Specifies whether the table cells should have borders or not
6485 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6486 * @cfg {Number} cellspacing Specifies the space between cells
6487 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6488 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6489 * @cfg {String} sortable Specifies that the table should be sortable
6490 * @cfg {String} summary Specifies a summary of the content of a table
6491 * @cfg {Number} width Specifies the width of a table
6492 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6494 * @cfg {boolean} striped Should the rows be alternative striped
6495 * @cfg {boolean} bordered Add borders to the table
6496 * @cfg {boolean} hover Add hover highlighting
6497 * @cfg {boolean} condensed Format condensed
6498 * @cfg {boolean} responsive Format condensed
6499 * @cfg {Boolean} loadMask (true|false) default false
6500 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6501 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6502 * @cfg {Boolean} rowSelection (true|false) default false
6503 * @cfg {Boolean} cellSelection (true|false) default false
6504 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6505 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6506 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6507 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6511 * Create a new Table
6512 * @param {Object} config The config object
6515 Roo.bootstrap.Table = function(config){
6516 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6521 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6522 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6523 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6524 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6526 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6528 this.sm.grid = this;
6529 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6530 this.sm = this.selModel;
6531 this.sm.xmodule = this.xmodule || false;
6534 if (this.cm && typeof(this.cm.config) == 'undefined') {
6535 this.colModel = new Roo.grid.ColumnModel(this.cm);
6536 this.cm = this.colModel;
6537 this.cm.xmodule = this.xmodule || false;
6540 this.store= Roo.factory(this.store, Roo.data);
6541 this.ds = this.store;
6542 this.ds.xmodule = this.xmodule || false;
6545 if (this.footer && this.store) {
6546 this.footer.dataSource = this.ds;
6547 this.footer = Roo.factory(this.footer);
6554 * Fires when a cell is clicked
6555 * @param {Roo.bootstrap.Table} this
6556 * @param {Roo.Element} el
6557 * @param {Number} rowIndex
6558 * @param {Number} columnIndex
6559 * @param {Roo.EventObject} e
6563 * @event celldblclick
6564 * Fires when a cell is double clicked
6565 * @param {Roo.bootstrap.Table} this
6566 * @param {Roo.Element} el
6567 * @param {Number} rowIndex
6568 * @param {Number} columnIndex
6569 * @param {Roo.EventObject} e
6571 "celldblclick" : true,
6574 * Fires when a row is clicked
6575 * @param {Roo.bootstrap.Table} this
6576 * @param {Roo.Element} el
6577 * @param {Number} rowIndex
6578 * @param {Roo.EventObject} e
6582 * @event rowdblclick
6583 * Fires when a row is double clicked
6584 * @param {Roo.bootstrap.Table} this
6585 * @param {Roo.Element} el
6586 * @param {Number} rowIndex
6587 * @param {Roo.EventObject} e
6589 "rowdblclick" : true,
6592 * Fires when a mouseover occur
6593 * @param {Roo.bootstrap.Table} this
6594 * @param {Roo.Element} el
6595 * @param {Number} rowIndex
6596 * @param {Number} columnIndex
6597 * @param {Roo.EventObject} e
6602 * Fires when a mouseout occur
6603 * @param {Roo.bootstrap.Table} this
6604 * @param {Roo.Element} el
6605 * @param {Number} rowIndex
6606 * @param {Number} columnIndex
6607 * @param {Roo.EventObject} e
6612 * Fires when a row is rendered, so you can change add a style to it.
6613 * @param {Roo.bootstrap.Table} this
6614 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6618 * @event rowsrendered
6619 * Fires when all the rows have been rendered
6620 * @param {Roo.bootstrap.Table} this
6622 'rowsrendered' : true,
6624 * @event contextmenu
6625 * The raw contextmenu event for the entire grid.
6626 * @param {Roo.EventObject} e
6628 "contextmenu" : true,
6630 * @event rowcontextmenu
6631 * Fires when a row is right clicked
6632 * @param {Roo.bootstrap.Table} this
6633 * @param {Number} rowIndex
6634 * @param {Roo.EventObject} e
6636 "rowcontextmenu" : true,
6638 * @event cellcontextmenu
6639 * Fires when a cell is right clicked
6640 * @param {Roo.bootstrap.Table} this
6641 * @param {Number} rowIndex
6642 * @param {Number} cellIndex
6643 * @param {Roo.EventObject} e
6645 "cellcontextmenu" : true,
6647 * @event headercontextmenu
6648 * Fires when a header is right clicked
6649 * @param {Roo.bootstrap.Table} this
6650 * @param {Number} columnIndex
6651 * @param {Roo.EventObject} e
6653 "headercontextmenu" : true
6657 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6683 rowSelection : false,
6684 cellSelection : false,
6687 // Roo.Element - the tbody
6689 // Roo.Element - thead element
6692 container: false, // used by gridpanel...
6698 auto_hide_footer : false,
6700 getAutoCreate : function()
6702 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6709 if (this.scrollBody) {
6710 cfg.cls += ' table-body-fixed';
6713 cfg.cls += ' table-striped';
6717 cfg.cls += ' table-hover';
6719 if (this.bordered) {
6720 cfg.cls += ' table-bordered';
6722 if (this.condensed) {
6723 cfg.cls += ' table-condensed';
6725 if (this.responsive) {
6726 cfg.cls += ' table-responsive';
6730 cfg.cls+= ' ' +this.cls;
6733 // this lot should be simplifed...
6746 ].forEach(function(k) {
6754 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6757 if(this.store || this.cm){
6758 if(this.headerShow){
6759 cfg.cn.push(this.renderHeader());
6762 cfg.cn.push(this.renderBody());
6764 if(this.footerShow){
6765 cfg.cn.push(this.renderFooter());
6767 // where does this come from?
6768 //cfg.cls+= ' TableGrid';
6771 return { cn : [ cfg ] };
6774 initEvents : function()
6776 if(!this.store || !this.cm){
6779 if (this.selModel) {
6780 this.selModel.initEvents();
6784 //Roo.log('initEvents with ds!!!!');
6786 this.mainBody = this.el.select('tbody', true).first();
6787 this.mainHead = this.el.select('thead', true).first();
6788 this.mainFoot = this.el.select('tfoot', true).first();
6794 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6795 e.on('click', _this.sort, _this);
6798 this.mainBody.on("click", this.onClick, this);
6799 this.mainBody.on("dblclick", this.onDblClick, this);
6801 // why is this done????? = it breaks dialogs??
6802 //this.parent().el.setStyle('position', 'relative');
6806 this.footer.parentId = this.id;
6807 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6810 this.el.select('tfoot tr td').first().addClass('hide');
6815 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6818 this.store.on('load', this.onLoad, this);
6819 this.store.on('beforeload', this.onBeforeLoad, this);
6820 this.store.on('update', this.onUpdate, this);
6821 this.store.on('add', this.onAdd, this);
6822 this.store.on("clear", this.clear, this);
6824 this.el.on("contextmenu", this.onContextMenu, this);
6826 this.mainBody.on('scroll', this.onBodyScroll, this);
6828 this.cm.on("headerchange", this.onHeaderChange, this);
6830 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6834 onContextMenu : function(e, t)
6836 this.processEvent("contextmenu", e);
6839 processEvent : function(name, e)
6841 if (name != 'touchstart' ) {
6842 this.fireEvent(name, e);
6845 var t = e.getTarget();
6847 var cell = Roo.get(t);
6853 if(cell.findParent('tfoot', false, true)){
6857 if(cell.findParent('thead', false, true)){
6859 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6860 cell = Roo.get(t).findParent('th', false, true);
6862 Roo.log("failed to find th in thead?");
6863 Roo.log(e.getTarget());
6868 var cellIndex = cell.dom.cellIndex;
6870 var ename = name == 'touchstart' ? 'click' : name;
6871 this.fireEvent("header" + ename, this, cellIndex, e);
6876 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6877 cell = Roo.get(t).findParent('td', false, true);
6879 Roo.log("failed to find th in tbody?");
6880 Roo.log(e.getTarget());
6885 var row = cell.findParent('tr', false, true);
6886 var cellIndex = cell.dom.cellIndex;
6887 var rowIndex = row.dom.rowIndex - 1;
6891 this.fireEvent("row" + name, this, rowIndex, e);
6895 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6901 onMouseover : function(e, el)
6903 var cell = Roo.get(el);
6909 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6910 cell = cell.findParent('td', false, true);
6913 var row = cell.findParent('tr', false, true);
6914 var cellIndex = cell.dom.cellIndex;
6915 var rowIndex = row.dom.rowIndex - 1; // start from 0
6917 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6921 onMouseout : function(e, el)
6923 var cell = Roo.get(el);
6929 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6930 cell = cell.findParent('td', false, true);
6933 var row = cell.findParent('tr', false, true);
6934 var cellIndex = cell.dom.cellIndex;
6935 var rowIndex = row.dom.rowIndex - 1; // start from 0
6937 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6941 onClick : function(e, el)
6943 var cell = Roo.get(el);
6945 if(!cell || (!this.cellSelection && !this.rowSelection)){
6949 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6950 cell = cell.findParent('td', false, true);
6953 if(!cell || typeof(cell) == 'undefined'){
6957 var row = cell.findParent('tr', false, true);
6959 if(!row || typeof(row) == 'undefined'){
6963 var cellIndex = cell.dom.cellIndex;
6964 var rowIndex = this.getRowIndex(row);
6966 // why??? - should these not be based on SelectionModel?
6967 if(this.cellSelection){
6968 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6971 if(this.rowSelection){
6972 this.fireEvent('rowclick', this, row, rowIndex, e);
6978 onDblClick : function(e,el)
6980 var cell = Roo.get(el);
6982 if(!cell || (!this.cellSelection && !this.rowSelection)){
6986 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6987 cell = cell.findParent('td', false, true);
6990 if(!cell || typeof(cell) == 'undefined'){
6994 var row = cell.findParent('tr', false, true);
6996 if(!row || typeof(row) == 'undefined'){
7000 var cellIndex = cell.dom.cellIndex;
7001 var rowIndex = this.getRowIndex(row);
7003 if(this.cellSelection){
7004 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7007 if(this.rowSelection){
7008 this.fireEvent('rowdblclick', this, row, rowIndex, e);
7012 sort : function(e,el)
7014 var col = Roo.get(el);
7016 if(!col.hasClass('sortable')){
7020 var sort = col.attr('sort');
7023 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7027 this.store.sortInfo = {field : sort, direction : dir};
7030 Roo.log("calling footer first");
7031 this.footer.onClick('first');
7034 this.store.load({ params : { start : 0 } });
7038 renderHeader : function()
7046 this.totalWidth = 0;
7048 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7050 var config = cm.config[i];
7054 cls : 'x-hcol-' + i,
7056 html: cm.getColumnHeader(i)
7061 if(typeof(config.sortable) != 'undefined' && config.sortable){
7063 c.html = '<i class="glyphicon"></i>' + c.html;
7066 // could use BS4 hidden-..-down
7068 if(typeof(config.lgHeader) != 'undefined'){
7069 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
7072 if(typeof(config.mdHeader) != 'undefined'){
7073 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
7076 if(typeof(config.smHeader) != 'undefined'){
7077 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
7080 if(typeof(config.xsHeader) != 'undefined'){
7081 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
7088 if(typeof(config.tooltip) != 'undefined'){
7089 c.tooltip = config.tooltip;
7092 if(typeof(config.colspan) != 'undefined'){
7093 c.colspan = config.colspan;
7096 if(typeof(config.hidden) != 'undefined' && config.hidden){
7097 c.style += ' display:none;';
7100 if(typeof(config.dataIndex) != 'undefined'){
7101 c.sort = config.dataIndex;
7106 if(typeof(config.align) != 'undefined' && config.align.length){
7107 c.style += ' text-align:' + config.align + ';';
7110 if(typeof(config.width) != 'undefined'){
7111 c.style += ' width:' + config.width + 'px;';
7112 this.totalWidth += config.width;
7114 this.totalWidth += 100; // assume minimum of 100 per column?
7117 if(typeof(config.cls) != 'undefined'){
7118 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
7121 ['xs','sm','md','lg'].map(function(size){
7123 if(typeof(config[size]) == 'undefined'){
7127 if (!config[size]) { // 0 = hidden
7128 // BS 4 '0' is treated as hide that column and below.
7129 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
7133 c.cls += ' col-' + size + '-' + config[size] + (
7134 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7146 renderBody : function()
7156 colspan : this.cm.getColumnCount()
7166 renderFooter : function()
7176 colspan : this.cm.getColumnCount()
7190 // Roo.log('ds onload');
7195 var ds = this.store;
7197 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7198 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
7199 if (_this.store.sortInfo) {
7201 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
7202 e.select('i', true).addClass(['glyphicon-arrow-up']);
7205 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
7206 e.select('i', true).addClass(['glyphicon-arrow-down']);
7211 var tbody = this.mainBody;
7213 if(ds.getCount() > 0){
7214 ds.data.each(function(d,rowIndex){
7215 var row = this.renderRow(cm, ds, rowIndex);
7217 tbody.createChild(row);
7221 if(row.cellObjects.length){
7222 Roo.each(row.cellObjects, function(r){
7223 _this.renderCellObject(r);
7230 var tfoot = this.el.select('tfoot', true).first();
7232 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7234 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7236 var total = this.ds.getTotalCount();
7238 if(this.footer.pageSize < total){
7239 this.mainFoot.show();
7243 Roo.each(this.el.select('tbody td', true).elements, function(e){
7244 e.on('mouseover', _this.onMouseover, _this);
7247 Roo.each(this.el.select('tbody td', true).elements, function(e){
7248 e.on('mouseout', _this.onMouseout, _this);
7250 this.fireEvent('rowsrendered', this);
7256 onUpdate : function(ds,record)
7258 this.refreshRow(record);
7262 onRemove : function(ds, record, index, isUpdate){
7263 if(isUpdate !== true){
7264 this.fireEvent("beforerowremoved", this, index, record);
7266 var bt = this.mainBody.dom;
7268 var rows = this.el.select('tbody > tr', true).elements;
7270 if(typeof(rows[index]) != 'undefined'){
7271 bt.removeChild(rows[index].dom);
7274 // if(bt.rows[index]){
7275 // bt.removeChild(bt.rows[index]);
7278 if(isUpdate !== true){
7279 //this.stripeRows(index);
7280 //this.syncRowHeights(index, index);
7282 this.fireEvent("rowremoved", this, index, record);
7286 onAdd : function(ds, records, rowIndex)
7288 //Roo.log('on Add called');
7289 // - note this does not handle multiple adding very well..
7290 var bt = this.mainBody.dom;
7291 for (var i =0 ; i < records.length;i++) {
7292 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7293 //Roo.log(records[i]);
7294 //Roo.log(this.store.getAt(rowIndex+i));
7295 this.insertRow(this.store, rowIndex + i, false);
7302 refreshRow : function(record){
7303 var ds = this.store, index;
7304 if(typeof record == 'number'){
7306 record = ds.getAt(index);
7308 index = ds.indexOf(record);
7310 this.insertRow(ds, index, true);
7312 this.onRemove(ds, record, index+1, true);
7314 //this.syncRowHeights(index, index);
7316 this.fireEvent("rowupdated", this, index, record);
7319 insertRow : function(dm, rowIndex, isUpdate){
7322 this.fireEvent("beforerowsinserted", this, rowIndex);
7324 //var s = this.getScrollState();
7325 var row = this.renderRow(this.cm, this.store, rowIndex);
7326 // insert before rowIndex..
7327 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7331 if(row.cellObjects.length){
7332 Roo.each(row.cellObjects, function(r){
7333 _this.renderCellObject(r);
7338 this.fireEvent("rowsinserted", this, rowIndex);
7339 //this.syncRowHeights(firstRow, lastRow);
7340 //this.stripeRows(firstRow);
7347 getRowDom : function(rowIndex)
7349 var rows = this.el.select('tbody > tr', true).elements;
7351 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7354 // returns the object tree for a tr..
7357 renderRow : function(cm, ds, rowIndex)
7359 var d = ds.getAt(rowIndex);
7363 cls : 'x-row-' + rowIndex,
7367 var cellObjects = [];
7369 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7370 var config = cm.config[i];
7372 var renderer = cm.getRenderer(i);
7376 if(typeof(renderer) !== 'undefined'){
7377 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7379 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7380 // and are rendered into the cells after the row is rendered - using the id for the element.
7382 if(typeof(value) === 'object'){
7392 rowIndex : rowIndex,
7397 this.fireEvent('rowclass', this, rowcfg);
7401 cls : rowcfg.rowClass + ' x-col-' + i,
7403 html: (typeof(value) === 'object') ? '' : value
7410 if(typeof(config.colspan) != 'undefined'){
7411 td.colspan = config.colspan;
7414 if(typeof(config.hidden) != 'undefined' && config.hidden){
7415 td.style += ' display:none;';
7418 if(typeof(config.align) != 'undefined' && config.align.length){
7419 td.style += ' text-align:' + config.align + ';';
7421 if(typeof(config.valign) != 'undefined' && config.valign.length){
7422 td.style += ' vertical-align:' + config.valign + ';';
7425 if(typeof(config.width) != 'undefined'){
7426 td.style += ' width:' + config.width + 'px;';
7429 if(typeof(config.cursor) != 'undefined'){
7430 td.style += ' cursor:' + config.cursor + ';';
7433 if(typeof(config.cls) != 'undefined'){
7434 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7437 ['xs','sm','md','lg'].map(function(size){
7439 if(typeof(config[size]) == 'undefined'){
7445 if (!config[size]) { // 0 = hidden
7446 // BS 4 '0' is treated as hide that column and below.
7447 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7451 td.cls += ' col-' + size + '-' + config[size] + (
7452 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7462 row.cellObjects = cellObjects;
7470 onBeforeLoad : function()
7479 this.el.select('tbody', true).first().dom.innerHTML = '';
7482 * Show or hide a row.
7483 * @param {Number} rowIndex to show or hide
7484 * @param {Boolean} state hide
7486 setRowVisibility : function(rowIndex, state)
7488 var bt = this.mainBody.dom;
7490 var rows = this.el.select('tbody > tr', true).elements;
7492 if(typeof(rows[rowIndex]) == 'undefined'){
7495 rows[rowIndex].dom.style.display = state ? '' : 'none';
7499 getSelectionModel : function(){
7501 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7503 return this.selModel;
7506 * Render the Roo.bootstrap object from renderder
7508 renderCellObject : function(r)
7512 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7514 var t = r.cfg.render(r.container);
7517 Roo.each(r.cfg.cn, function(c){
7519 container: t.getChildContainer(),
7522 _this.renderCellObject(child);
7527 getRowIndex : function(row)
7531 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7542 * Returns the grid's underlying element = used by panel.Grid
7543 * @return {Element} The element
7545 getGridEl : function(){
7549 * Forces a resize - used by panel.Grid
7550 * @return {Element} The element
7552 autoSize : function()
7554 //var ctr = Roo.get(this.container.dom.parentElement);
7555 var ctr = Roo.get(this.el.dom);
7557 var thd = this.getGridEl().select('thead',true).first();
7558 var tbd = this.getGridEl().select('tbody', true).first();
7559 var tfd = this.getGridEl().select('tfoot', true).first();
7561 var cw = ctr.getWidth();
7565 tbd.setWidth(ctr.getWidth());
7566 // if the body has a max height - and then scrolls - we should perhaps set up the height here
7567 // this needs fixing for various usage - currently only hydra job advers I think..
7569 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7571 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7574 cw = Math.max(cw, this.totalWidth);
7575 this.getGridEl().select('tr',true).setWidth(cw);
7576 // resize 'expandable coloumn?
7578 return; // we doe not have a view in this design..
7581 onBodyScroll: function()
7583 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7585 this.mainHead.setStyle({
7586 'position' : 'relative',
7587 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7593 var scrollHeight = this.mainBody.dom.scrollHeight;
7595 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7597 var height = this.mainBody.getHeight();
7599 if(scrollHeight - height == scrollTop) {
7601 var total = this.ds.getTotalCount();
7603 if(this.footer.cursor + this.footer.pageSize < total){
7605 this.footer.ds.load({
7607 start : this.footer.cursor + this.footer.pageSize,
7608 limit : this.footer.pageSize
7618 onHeaderChange : function()
7620 var header = this.renderHeader();
7621 var table = this.el.select('table', true).first();
7623 this.mainHead.remove();
7624 this.mainHead = table.createChild(header, this.mainBody, false);
7627 onHiddenChange : function(colModel, colIndex, hidden)
7629 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7630 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7632 this.CSS.updateRule(thSelector, "display", "");
7633 this.CSS.updateRule(tdSelector, "display", "");
7636 this.CSS.updateRule(thSelector, "display", "none");
7637 this.CSS.updateRule(tdSelector, "display", "none");
7640 this.onHeaderChange();
7644 setColumnWidth: function(col_index, width)
7646 // width = "md-2 xs-2..."
7647 if(!this.colModel.config[col_index]) {
7651 var w = width.split(" ");
7653 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7655 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7658 for(var j = 0; j < w.length; j++) {
7664 var size_cls = w[j].split("-");
7666 if(!Number.isInteger(size_cls[1] * 1)) {
7670 if(!this.colModel.config[col_index][size_cls[0]]) {
7674 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7678 h_row[0].classList.replace(
7679 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7680 "col-"+size_cls[0]+"-"+size_cls[1]
7683 for(var i = 0; i < rows.length; i++) {
7685 var size_cls = w[j].split("-");
7687 if(!Number.isInteger(size_cls[1] * 1)) {
7691 if(!this.colModel.config[col_index][size_cls[0]]) {
7695 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7699 rows[i].classList.replace(
7700 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7701 "col-"+size_cls[0]+"-"+size_cls[1]
7705 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7720 * @class Roo.bootstrap.TableCell
7721 * @extends Roo.bootstrap.Component
7722 * Bootstrap TableCell class
7723 * @cfg {String} html cell contain text
7724 * @cfg {String} cls cell class
7725 * @cfg {String} tag cell tag (td|th) default td
7726 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7727 * @cfg {String} align Aligns the content in a cell
7728 * @cfg {String} axis Categorizes cells
7729 * @cfg {String} bgcolor Specifies the background color of a cell
7730 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7731 * @cfg {Number} colspan Specifies the number of columns a cell should span
7732 * @cfg {String} headers Specifies one or more header cells a cell is related to
7733 * @cfg {Number} height Sets the height of a cell
7734 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7735 * @cfg {Number} rowspan Sets the number of rows a cell should span
7736 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7737 * @cfg {String} valign Vertical aligns the content in a cell
7738 * @cfg {Number} width Specifies the width of a cell
7741 * Create a new TableCell
7742 * @param {Object} config The config object
7745 Roo.bootstrap.TableCell = function(config){
7746 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7749 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7769 getAutoCreate : function(){
7770 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7790 cfg.align=this.align
7796 cfg.bgcolor=this.bgcolor
7799 cfg.charoff=this.charoff
7802 cfg.colspan=this.colspan
7805 cfg.headers=this.headers
7808 cfg.height=this.height
7811 cfg.nowrap=this.nowrap
7814 cfg.rowspan=this.rowspan
7817 cfg.scope=this.scope
7820 cfg.valign=this.valign
7823 cfg.width=this.width
7842 * @class Roo.bootstrap.TableRow
7843 * @extends Roo.bootstrap.Component
7844 * Bootstrap TableRow class
7845 * @cfg {String} cls row class
7846 * @cfg {String} align Aligns the content in a table row
7847 * @cfg {String} bgcolor Specifies a background color for a table row
7848 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7849 * @cfg {String} valign Vertical aligns the content in a table row
7852 * Create a new TableRow
7853 * @param {Object} config The config object
7856 Roo.bootstrap.TableRow = function(config){
7857 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7860 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7868 getAutoCreate : function(){
7869 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7879 cfg.align = this.align;
7882 cfg.bgcolor = this.bgcolor;
7885 cfg.charoff = this.charoff;
7888 cfg.valign = this.valign;
7906 * @class Roo.bootstrap.TableBody
7907 * @extends Roo.bootstrap.Component
7908 * Bootstrap TableBody class
7909 * @cfg {String} cls element class
7910 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7911 * @cfg {String} align Aligns the content inside the element
7912 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7913 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7916 * Create a new TableBody
7917 * @param {Object} config The config object
7920 Roo.bootstrap.TableBody = function(config){
7921 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7924 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7932 getAutoCreate : function(){
7933 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7947 cfg.align = this.align;
7950 cfg.charoff = this.charoff;
7953 cfg.valign = this.valign;
7960 // initEvents : function()
7967 // this.store = Roo.factory(this.store, Roo.data);
7968 // this.store.on('load', this.onLoad, this);
7970 // this.store.load();
7974 // onLoad: function ()
7976 // this.fireEvent('load', this);
7986 * Ext JS Library 1.1.1
7987 * Copyright(c) 2006-2007, Ext JS, LLC.
7989 * Originally Released Under LGPL - original licence link has changed is not relivant.
7992 * <script type="text/javascript">
7995 // as we use this in bootstrap.
7996 Roo.namespace('Roo.form');
7998 * @class Roo.form.Action
7999 * Internal Class used to handle form actions
8001 * @param {Roo.form.BasicForm} el The form element or its id
8002 * @param {Object} config Configuration options
8007 // define the action interface
8008 Roo.form.Action = function(form, options){
8010 this.options = options || {};
8013 * Client Validation Failed
8016 Roo.form.Action.CLIENT_INVALID = 'client';
8018 * Server Validation Failed
8021 Roo.form.Action.SERVER_INVALID = 'server';
8023 * Connect to Server Failed
8026 Roo.form.Action.CONNECT_FAILURE = 'connect';
8028 * Reading Data from Server Failed
8031 Roo.form.Action.LOAD_FAILURE = 'load';
8033 Roo.form.Action.prototype = {
8035 failureType : undefined,
8036 response : undefined,
8040 run : function(options){
8045 success : function(response){
8050 handleResponse : function(response){
8054 // default connection failure
8055 failure : function(response){
8057 this.response = response;
8058 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8059 this.form.afterAction(this, false);
8062 processResponse : function(response){
8063 this.response = response;
8064 if(!response.responseText){
8067 this.result = this.handleResponse(response);
8071 // utility functions used internally
8072 getUrl : function(appendParams){
8073 var url = this.options.url || this.form.url || this.form.el.dom.action;
8075 var p = this.getParams();
8077 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
8083 getMethod : function(){
8084 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
8087 getParams : function(){
8088 var bp = this.form.baseParams;
8089 var p = this.options.params;
8091 if(typeof p == "object"){
8092 p = Roo.urlEncode(Roo.applyIf(p, bp));
8093 }else if(typeof p == 'string' && bp){
8094 p += '&' + Roo.urlEncode(bp);
8097 p = Roo.urlEncode(bp);
8102 createCallback : function(){
8104 success: this.success,
8105 failure: this.failure,
8107 timeout: (this.form.timeout*1000),
8108 upload: this.form.fileUpload ? this.success : undefined
8113 Roo.form.Action.Submit = function(form, options){
8114 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
8117 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
8120 haveProgress : false,
8121 uploadComplete : false,
8123 // uploadProgress indicator.
8124 uploadProgress : function()
8126 if (!this.form.progressUrl) {
8130 if (!this.haveProgress) {
8131 Roo.MessageBox.progress("Uploading", "Uploading");
8133 if (this.uploadComplete) {
8134 Roo.MessageBox.hide();
8138 this.haveProgress = true;
8140 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
8142 var c = new Roo.data.Connection();
8144 url : this.form.progressUrl,
8149 success : function(req){
8150 //console.log(data);
8154 rdata = Roo.decode(req.responseText)
8156 Roo.log("Invalid data from server..");
8160 if (!rdata || !rdata.success) {
8162 Roo.MessageBox.alert(Roo.encode(rdata));
8165 var data = rdata.data;
8167 if (this.uploadComplete) {
8168 Roo.MessageBox.hide();
8173 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
8174 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
8177 this.uploadProgress.defer(2000,this);
8180 failure: function(data) {
8181 Roo.log('progress url failed ');
8192 // run get Values on the form, so it syncs any secondary forms.
8193 this.form.getValues();
8195 var o = this.options;
8196 var method = this.getMethod();
8197 var isPost = method == 'POST';
8198 if(o.clientValidation === false || this.form.isValid()){
8200 if (this.form.progressUrl) {
8201 this.form.findField('UPLOAD_IDENTIFIER').setValue(
8202 (new Date() * 1) + '' + Math.random());
8207 Roo.Ajax.request(Roo.apply(this.createCallback(), {
8208 form:this.form.el.dom,
8209 url:this.getUrl(!isPost),
8211 params:isPost ? this.getParams() : null,
8212 isUpload: this.form.fileUpload,
8213 formData : this.form.formData
8216 this.uploadProgress();
8218 }else if (o.clientValidation !== false){ // client validation failed
8219 this.failureType = Roo.form.Action.CLIENT_INVALID;
8220 this.form.afterAction(this, false);
8224 success : function(response)
8226 this.uploadComplete= true;
8227 if (this.haveProgress) {
8228 Roo.MessageBox.hide();
8232 var result = this.processResponse(response);
8233 if(result === true || result.success){
8234 this.form.afterAction(this, true);
8238 this.form.markInvalid(result.errors);
8239 this.failureType = Roo.form.Action.SERVER_INVALID;
8241 this.form.afterAction(this, false);
8243 failure : function(response)
8245 this.uploadComplete= true;
8246 if (this.haveProgress) {
8247 Roo.MessageBox.hide();
8250 this.response = response;
8251 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8252 this.form.afterAction(this, false);
8255 handleResponse : function(response){
8256 if(this.form.errorReader){
8257 var rs = this.form.errorReader.read(response);
8260 for(var i = 0, len = rs.records.length; i < len; i++) {
8261 var r = rs.records[i];
8265 if(errors.length < 1){
8269 success : rs.success,
8275 ret = Roo.decode(response.responseText);
8279 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8289 Roo.form.Action.Load = function(form, options){
8290 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8291 this.reader = this.form.reader;
8294 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8299 Roo.Ajax.request(Roo.apply(
8300 this.createCallback(), {
8301 method:this.getMethod(),
8302 url:this.getUrl(false),
8303 params:this.getParams()
8307 success : function(response){
8309 var result = this.processResponse(response);
8310 if(result === true || !result.success || !result.data){
8311 this.failureType = Roo.form.Action.LOAD_FAILURE;
8312 this.form.afterAction(this, false);
8315 this.form.clearInvalid();
8316 this.form.setValues(result.data);
8317 this.form.afterAction(this, true);
8320 handleResponse : function(response){
8321 if(this.form.reader){
8322 var rs = this.form.reader.read(response);
8323 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8325 success : rs.success,
8329 return Roo.decode(response.responseText);
8333 Roo.form.Action.ACTION_TYPES = {
8334 'load' : Roo.form.Action.Load,
8335 'submit' : Roo.form.Action.Submit
8344 * @class Roo.bootstrap.Form
8345 * @extends Roo.bootstrap.Component
8346 * Bootstrap Form class
8347 * @cfg {String} method GET | POST (default POST)
8348 * @cfg {String} labelAlign top | left (default top)
8349 * @cfg {String} align left | right - for navbars
8350 * @cfg {Boolean} loadMask load mask when submit (default true)
8355 * @param {Object} config The config object
8359 Roo.bootstrap.Form = function(config){
8361 Roo.bootstrap.Form.superclass.constructor.call(this, config);
8363 Roo.bootstrap.Form.popover.apply();
8367 * @event clientvalidation
8368 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8369 * @param {Form} this
8370 * @param {Boolean} valid true if the form has passed client-side validation
8372 clientvalidation: true,
8374 * @event beforeaction
8375 * Fires before any action is performed. Return false to cancel the action.
8376 * @param {Form} this
8377 * @param {Action} action The action to be performed
8381 * @event actionfailed
8382 * Fires when an action fails.
8383 * @param {Form} this
8384 * @param {Action} action The action that failed
8386 actionfailed : true,
8388 * @event actioncomplete
8389 * Fires when an action is completed.
8390 * @param {Form} this
8391 * @param {Action} action The action that completed
8393 actioncomplete : true
8397 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
8400 * @cfg {String} method
8401 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8406 * The URL to use for form actions if one isn't supplied in the action options.
8409 * @cfg {Boolean} fileUpload
8410 * Set to true if this form is a file upload.
8414 * @cfg {Object} baseParams
8415 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8419 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8423 * @cfg {Sting} align (left|right) for navbar forms
8428 activeAction : null,
8431 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8432 * element by passing it or its id or mask the form itself by passing in true.
8435 waitMsgTarget : false,
8440 * @cfg {Boolean} errorMask (true|false) default false
8445 * @cfg {Number} maskOffset Default 100
8450 * @cfg {Boolean} maskBody
8454 getAutoCreate : function(){
8458 method : this.method || 'POST',
8459 id : this.id || Roo.id(),
8462 if (this.parent().xtype.match(/^Nav/)) {
8463 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8467 if (this.labelAlign == 'left' ) {
8468 cfg.cls += ' form-horizontal';
8474 initEvents : function()
8476 this.el.on('submit', this.onSubmit, this);
8477 // this was added as random key presses on the form where triggering form submit.
8478 this.el.on('keypress', function(e) {
8479 if (e.getCharCode() != 13) {
8482 // we might need to allow it for textareas.. and some other items.
8483 // check e.getTarget().
8485 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8489 Roo.log("keypress blocked");
8497 onSubmit : function(e){
8502 * Returns true if client-side validation on the form is successful.
8505 isValid : function(){
8506 var items = this.getItems();
8510 items.each(function(f){
8516 Roo.log('invalid field: ' + f.name);
8520 if(!target && f.el.isVisible(true)){
8526 if(this.errorMask && !valid){
8527 Roo.bootstrap.Form.popover.mask(this, target);
8534 * Returns true if any fields in this form have changed since their original load.
8537 isDirty : function(){
8539 var items = this.getItems();
8540 items.each(function(f){
8550 * Performs a predefined action (submit or load) or custom actions you define on this form.
8551 * @param {String} actionName The name of the action type
8552 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8553 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8554 * accept other config options):
8556 Property Type Description
8557 ---------------- --------------- ----------------------------------------------------------------------------------
8558 url String The url for the action (defaults to the form's url)
8559 method String The form method to use (defaults to the form's method, or POST if not defined)
8560 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8561 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8562 validate the form on the client (defaults to false)
8564 * @return {BasicForm} this
8566 doAction : function(action, options){
8567 if(typeof action == 'string'){
8568 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8570 if(this.fireEvent('beforeaction', this, action) !== false){
8571 this.beforeAction(action);
8572 action.run.defer(100, action);
8578 beforeAction : function(action){
8579 var o = action.options;
8584 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8586 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8589 // not really supported yet.. ??
8591 //if(this.waitMsgTarget === true){
8592 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8593 //}else if(this.waitMsgTarget){
8594 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8595 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8597 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8603 afterAction : function(action, success){
8604 this.activeAction = null;
8605 var o = action.options;
8610 Roo.get(document.body).unmask();
8616 //if(this.waitMsgTarget === true){
8617 // this.el.unmask();
8618 //}else if(this.waitMsgTarget){
8619 // this.waitMsgTarget.unmask();
8621 // Roo.MessageBox.updateProgress(1);
8622 // Roo.MessageBox.hide();
8629 Roo.callback(o.success, o.scope, [this, action]);
8630 this.fireEvent('actioncomplete', this, action);
8634 // failure condition..
8635 // we have a scenario where updates need confirming.
8636 // eg. if a locking scenario exists..
8637 // we look for { errors : { needs_confirm : true }} in the response.
8639 (typeof(action.result) != 'undefined') &&
8640 (typeof(action.result.errors) != 'undefined') &&
8641 (typeof(action.result.errors.needs_confirm) != 'undefined')
8644 Roo.log("not supported yet");
8647 Roo.MessageBox.confirm(
8648 "Change requires confirmation",
8649 action.result.errorMsg,
8654 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8664 Roo.callback(o.failure, o.scope, [this, action]);
8665 // show an error message if no failed handler is set..
8666 if (!this.hasListener('actionfailed')) {
8667 Roo.log("need to add dialog support");
8669 Roo.MessageBox.alert("Error",
8670 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8671 action.result.errorMsg :
8672 "Saving Failed, please check your entries or try again"
8677 this.fireEvent('actionfailed', this, action);
8682 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8683 * @param {String} id The value to search for
8686 findField : function(id){
8687 var items = this.getItems();
8688 var field = items.get(id);
8690 items.each(function(f){
8691 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8698 return field || null;
8701 * Mark fields in this form invalid in bulk.
8702 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8703 * @return {BasicForm} this
8705 markInvalid : function(errors){
8706 if(errors instanceof Array){
8707 for(var i = 0, len = errors.length; i < len; i++){
8708 var fieldError = errors[i];
8709 var f = this.findField(fieldError.id);
8711 f.markInvalid(fieldError.msg);
8717 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8718 field.markInvalid(errors[id]);
8722 //Roo.each(this.childForms || [], function (f) {
8723 // f.markInvalid(errors);
8730 * Set values for fields in this form in bulk.
8731 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8732 * @return {BasicForm} this
8734 setValues : function(values){
8735 if(values instanceof Array){ // array of objects
8736 for(var i = 0, len = values.length; i < len; i++){
8738 var f = this.findField(v.id);
8740 f.setValue(v.value);
8741 if(this.trackResetOnLoad){
8742 f.originalValue = f.getValue();
8746 }else{ // object hash
8749 if(typeof values[id] != 'function' && (field = this.findField(id))){
8751 if (field.setFromData &&
8753 field.displayField &&
8754 // combos' with local stores can
8755 // be queried via setValue()
8756 // to set their value..
8757 (field.store && !field.store.isLocal)
8761 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8762 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8763 field.setFromData(sd);
8765 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8767 field.setFromData(values);
8770 field.setValue(values[id]);
8774 if(this.trackResetOnLoad){
8775 field.originalValue = field.getValue();
8781 //Roo.each(this.childForms || [], function (f) {
8782 // f.setValues(values);
8789 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8790 * they are returned as an array.
8791 * @param {Boolean} asString
8794 getValues : function(asString){
8795 //if (this.childForms) {
8796 // copy values from the child forms
8797 // Roo.each(this.childForms, function (f) {
8798 // this.setValues(f.getValues());
8804 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8805 if(asString === true){
8808 return Roo.urlDecode(fs);
8812 * Returns the fields in this form as an object with key/value pairs.
8813 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8816 getFieldValues : function(with_hidden)
8818 var items = this.getItems();
8820 items.each(function(f){
8826 var v = f.getValue();
8828 if (f.inputType =='radio') {
8829 if (typeof(ret[f.getName()]) == 'undefined') {
8830 ret[f.getName()] = ''; // empty..
8833 if (!f.el.dom.checked) {
8841 if(f.xtype == 'MoneyField'){
8842 ret[f.currencyName] = f.getCurrency();
8845 // not sure if this supported any more..
8846 if ((typeof(v) == 'object') && f.getRawValue) {
8847 v = f.getRawValue() ; // dates..
8849 // combo boxes where name != hiddenName...
8850 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8851 ret[f.name] = f.getRawValue();
8853 ret[f.getName()] = v;
8860 * Clears all invalid messages in this form.
8861 * @return {BasicForm} this
8863 clearInvalid : function(){
8864 var items = this.getItems();
8866 items.each(function(f){
8875 * @return {BasicForm} this
8878 var items = this.getItems();
8879 items.each(function(f){
8883 Roo.each(this.childForms || [], function (f) {
8891 getItems : function()
8893 var r=new Roo.util.MixedCollection(false, function(o){
8894 return o.id || (o.id = Roo.id());
8896 var iter = function(el) {
8903 Roo.each(el.items,function(e) {
8912 hideFields : function(items)
8914 Roo.each(items, function(i){
8916 var f = this.findField(i);
8927 showFields : function(items)
8929 Roo.each(items, function(i){
8931 var f = this.findField(i);
8944 Roo.apply(Roo.bootstrap.Form, {
8971 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8972 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8973 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8974 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8977 this.maskEl.top.enableDisplayMode("block");
8978 this.maskEl.left.enableDisplayMode("block");
8979 this.maskEl.bottom.enableDisplayMode("block");
8980 this.maskEl.right.enableDisplayMode("block");
8982 this.toolTip = new Roo.bootstrap.Tooltip({
8983 cls : 'roo-form-error-popover',
8985 'left' : ['r-l', [-2,0], 'right'],
8986 'right' : ['l-r', [2,0], 'left'],
8987 'bottom' : ['tl-bl', [0,2], 'top'],
8988 'top' : [ 'bl-tl', [0,-2], 'bottom']
8992 this.toolTip.render(Roo.get(document.body));
8994 this.toolTip.el.enableDisplayMode("block");
8996 Roo.get(document.body).on('click', function(){
9000 Roo.get(document.body).on('touchstart', function(){
9004 this.isApplied = true
9007 mask : function(form, target)
9011 this.target = target;
9013 if(!this.form.errorMask || !target.el){
9017 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9019 Roo.log(scrollable);
9021 var ot = this.target.el.calcOffsetsTo(scrollable);
9023 var scrollTo = ot[1] - this.form.maskOffset;
9025 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9027 scrollable.scrollTo('top', scrollTo);
9029 var box = this.target.el.getBox();
9031 var zIndex = Roo.bootstrap.Modal.zIndex++;
9034 this.maskEl.top.setStyle('position', 'absolute');
9035 this.maskEl.top.setStyle('z-index', zIndex);
9036 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9037 this.maskEl.top.setLeft(0);
9038 this.maskEl.top.setTop(0);
9039 this.maskEl.top.show();
9041 this.maskEl.left.setStyle('position', 'absolute');
9042 this.maskEl.left.setStyle('z-index', zIndex);
9043 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9044 this.maskEl.left.setLeft(0);
9045 this.maskEl.left.setTop(box.y - this.padding);
9046 this.maskEl.left.show();
9048 this.maskEl.bottom.setStyle('position', 'absolute');
9049 this.maskEl.bottom.setStyle('z-index', zIndex);
9050 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9051 this.maskEl.bottom.setLeft(0);
9052 this.maskEl.bottom.setTop(box.bottom + this.padding);
9053 this.maskEl.bottom.show();
9055 this.maskEl.right.setStyle('position', 'absolute');
9056 this.maskEl.right.setStyle('z-index', zIndex);
9057 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9058 this.maskEl.right.setLeft(box.right + this.padding);
9059 this.maskEl.right.setTop(box.y - this.padding);
9060 this.maskEl.right.show();
9062 this.toolTip.bindEl = this.target.el;
9064 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9066 var tip = this.target.blankText;
9068 if(this.target.getValue() !== '' ) {
9070 if (this.target.invalidText.length) {
9071 tip = this.target.invalidText;
9072 } else if (this.target.regexText.length){
9073 tip = this.target.regexText;
9077 this.toolTip.show(tip);
9079 this.intervalID = window.setInterval(function() {
9080 Roo.bootstrap.Form.popover.unmask();
9083 window.onwheel = function(){ return false;};
9085 (function(){ this.isMasked = true; }).defer(500, this);
9091 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
9095 this.maskEl.top.setStyle('position', 'absolute');
9096 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
9097 this.maskEl.top.hide();
9099 this.maskEl.left.setStyle('position', 'absolute');
9100 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
9101 this.maskEl.left.hide();
9103 this.maskEl.bottom.setStyle('position', 'absolute');
9104 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
9105 this.maskEl.bottom.hide();
9107 this.maskEl.right.setStyle('position', 'absolute');
9108 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
9109 this.maskEl.right.hide();
9111 this.toolTip.hide();
9113 this.toolTip.el.hide();
9115 window.onwheel = function(){ return true;};
9117 if(this.intervalID){
9118 window.clearInterval(this.intervalID);
9119 this.intervalID = false;
9122 this.isMasked = false;
9132 * Ext JS Library 1.1.1
9133 * Copyright(c) 2006-2007, Ext JS, LLC.
9135 * Originally Released Under LGPL - original licence link has changed is not relivant.
9138 * <script type="text/javascript">
9141 * @class Roo.form.VTypes
9142 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
9145 Roo.form.VTypes = function(){
9146 // closure these in so they are only created once.
9147 var alpha = /^[a-zA-Z_]+$/;
9148 var alphanum = /^[a-zA-Z0-9_]+$/;
9149 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
9150 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
9152 // All these messages and functions are configurable
9155 * The function used to validate email addresses
9156 * @param {String} value The email address
9158 'email' : function(v){
9159 return email.test(v);
9162 * The error text to display when the email validation function returns false
9165 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
9167 * The keystroke filter mask to be applied on email input
9170 'emailMask' : /[a-z0-9_\.\-@]/i,
9173 * The function used to validate URLs
9174 * @param {String} value The URL
9176 'url' : function(v){
9180 * The error text to display when the url validation function returns false
9183 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
9186 * The function used to validate alpha values
9187 * @param {String} value The value
9189 'alpha' : function(v){
9190 return alpha.test(v);
9193 * The error text to display when the alpha validation function returns false
9196 'alphaText' : 'This field should only contain letters and _',
9198 * The keystroke filter mask to be applied on alpha input
9201 'alphaMask' : /[a-z_]/i,
9204 * The function used to validate alphanumeric values
9205 * @param {String} value The value
9207 'alphanum' : function(v){
9208 return alphanum.test(v);
9211 * The error text to display when the alphanumeric validation function returns false
9214 'alphanumText' : 'This field should only contain letters, numbers and _',
9216 * The keystroke filter mask to be applied on alphanumeric input
9219 'alphanumMask' : /[a-z0-9_]/i
9229 * @class Roo.bootstrap.Input
9230 * @extends Roo.bootstrap.Component
9231 * Bootstrap Input class
9232 * @cfg {Boolean} disabled is it disabled
9233 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9234 * @cfg {String} name name of the input
9235 * @cfg {string} fieldLabel - the label associated
9236 * @cfg {string} placeholder - placeholder to put in text.
9237 * @cfg {string} before - input group add on before
9238 * @cfg {string} after - input group add on after
9239 * @cfg {string} size - (lg|sm) or leave empty..
9240 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9241 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9242 * @cfg {Number} md colspan out of 12 for computer-sized screens
9243 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9244 * @cfg {string} value default value of the input
9245 * @cfg {Number} labelWidth set the width of label
9246 * @cfg {Number} labellg set the width of label (1-12)
9247 * @cfg {Number} labelmd set the width of label (1-12)
9248 * @cfg {Number} labelsm set the width of label (1-12)
9249 * @cfg {Number} labelxs set the width of label (1-12)
9250 * @cfg {String} labelAlign (top|left)
9251 * @cfg {Boolean} readOnly Specifies that the field should be read-only
9252 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9253 * @cfg {String} indicatorpos (left|right) default left
9254 * @cfg {String} capture (user|camera) use for file input only. (default empty)
9255 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9257 * @cfg {String} align (left|center|right) Default left
9258 * @cfg {Boolean} forceFeedback (true|false) Default false
9261 * Create a new Input
9262 * @param {Object} config The config object
9265 Roo.bootstrap.Input = function(config){
9267 Roo.bootstrap.Input.superclass.constructor.call(this, config);
9272 * Fires when this field receives input focus.
9273 * @param {Roo.form.Field} this
9278 * Fires when this field loses input focus.
9279 * @param {Roo.form.Field} this
9284 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
9285 * {@link Roo.EventObject#getKey} to determine which key was pressed.
9286 * @param {Roo.form.Field} this
9287 * @param {Roo.EventObject} e The event object
9292 * Fires just before the field blurs if the field value has changed.
9293 * @param {Roo.form.Field} this
9294 * @param {Mixed} newValue The new value
9295 * @param {Mixed} oldValue The original value
9300 * Fires after the field has been marked as invalid.
9301 * @param {Roo.form.Field} this
9302 * @param {String} msg The validation message
9307 * Fires after the field has been validated with no errors.
9308 * @param {Roo.form.Field} this
9313 * Fires after the key up
9314 * @param {Roo.form.Field} this
9315 * @param {Roo.EventObject} e The event Object
9321 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
9323 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9324 automatic validation (defaults to "keyup").
9326 validationEvent : "keyup",
9328 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9330 validateOnBlur : true,
9332 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9334 validationDelay : 250,
9336 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9338 focusClass : "x-form-focus", // not needed???
9342 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9344 invalidClass : "has-warning",
9347 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9349 validClass : "has-success",
9352 * @cfg {Boolean} hasFeedback (true|false) default true
9357 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9359 invalidFeedbackClass : "glyphicon-warning-sign",
9362 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9364 validFeedbackClass : "glyphicon-ok",
9367 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9369 selectOnFocus : false,
9372 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9376 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9381 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9383 disableKeyFilter : false,
9386 * @cfg {Boolean} disabled True to disable the field (defaults to false).
9390 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9394 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9396 blankText : "Please complete this mandatory field",
9399 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9403 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9405 maxLength : Number.MAX_VALUE,
9407 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9409 minLengthText : "The minimum length for this field is {0}",
9411 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9413 maxLengthText : "The maximum length for this field is {0}",
9417 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9418 * If available, this function will be called only after the basic validators all return true, and will be passed the
9419 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9423 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9424 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9425 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9429 * @cfg {String} regexText -- Depricated - use Invalid Text
9434 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9440 autocomplete: false,
9459 formatedValue : false,
9460 forceFeedback : false,
9462 indicatorpos : 'left',
9472 parentLabelAlign : function()
9475 while (parent.parent()) {
9476 parent = parent.parent();
9477 if (typeof(parent.labelAlign) !='undefined') {
9478 return parent.labelAlign;
9485 getAutoCreate : function()
9487 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9493 if(this.inputType != 'hidden'){
9494 cfg.cls = 'form-group' //input-group
9500 type : this.inputType,
9502 cls : 'form-control',
9503 placeholder : this.placeholder || '',
9504 autocomplete : this.autocomplete || 'new-password'
9507 if(this.capture.length){
9508 input.capture = this.capture;
9511 if(this.accept.length){
9512 input.accept = this.accept + "/*";
9516 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9519 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9520 input.maxLength = this.maxLength;
9523 if (this.disabled) {
9524 input.disabled=true;
9527 if (this.readOnly) {
9528 input.readonly=true;
9532 input.name = this.name;
9536 input.cls += ' input-' + this.size;
9540 ['xs','sm','md','lg'].map(function(size){
9541 if (settings[size]) {
9542 cfg.cls += ' col-' + size + '-' + settings[size];
9546 var inputblock = input;
9550 cls: 'glyphicon form-control-feedback'
9553 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9556 cls : 'has-feedback',
9564 if (this.before || this.after) {
9567 cls : 'input-group',
9571 if (this.before && typeof(this.before) == 'string') {
9573 inputblock.cn.push({
9575 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9579 if (this.before && typeof(this.before) == 'object') {
9580 this.before = Roo.factory(this.before);
9582 inputblock.cn.push({
9584 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9585 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9589 inputblock.cn.push(input);
9591 if (this.after && typeof(this.after) == 'string') {
9592 inputblock.cn.push({
9594 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9598 if (this.after && typeof(this.after) == 'object') {
9599 this.after = Roo.factory(this.after);
9601 inputblock.cn.push({
9603 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9604 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9608 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9609 inputblock.cls += ' has-feedback';
9610 inputblock.cn.push(feedback);
9615 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9616 tooltip : 'This field is required'
9618 if (Roo.bootstrap.version == 4) {
9621 style : 'display-none'
9624 if (align ==='left' && this.fieldLabel.length) {
9626 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
9633 cls : 'control-label col-form-label',
9634 html : this.fieldLabel
9645 var labelCfg = cfg.cn[1];
9646 var contentCfg = cfg.cn[2];
9648 if(this.indicatorpos == 'right'){
9653 cls : 'control-label col-form-label',
9657 html : this.fieldLabel
9671 labelCfg = cfg.cn[0];
9672 contentCfg = cfg.cn[1];
9676 if(this.labelWidth > 12){
9677 labelCfg.style = "width: " + this.labelWidth + 'px';
9680 if(this.labelWidth < 13 && this.labelmd == 0){
9681 this.labelmd = this.labelWidth;
9684 if(this.labellg > 0){
9685 labelCfg.cls += ' col-lg-' + this.labellg;
9686 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9689 if(this.labelmd > 0){
9690 labelCfg.cls += ' col-md-' + this.labelmd;
9691 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9694 if(this.labelsm > 0){
9695 labelCfg.cls += ' col-sm-' + this.labelsm;
9696 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9699 if(this.labelxs > 0){
9700 labelCfg.cls += ' col-xs-' + this.labelxs;
9701 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9705 } else if ( this.fieldLabel.length) {
9710 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9711 tooltip : 'This field is required'
9715 //cls : 'input-group-addon',
9716 html : this.fieldLabel
9724 if(this.indicatorpos == 'right'){
9729 //cls : 'input-group-addon',
9730 html : this.fieldLabel
9735 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9736 tooltip : 'This field is required'
9756 if (this.parentType === 'Navbar' && this.parent().bar) {
9757 cfg.cls += ' navbar-form';
9760 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9761 // on BS4 we do this only if not form
9762 cfg.cls += ' navbar-form';
9770 * return the real input element.
9772 inputEl: function ()
9774 return this.el.select('input.form-control',true).first();
9777 tooltipEl : function()
9779 return this.inputEl();
9782 indicatorEl : function()
9784 if (Roo.bootstrap.version == 4) {
9785 return false; // not enabled in v4 yet.
9788 var indicator = this.el.select('i.roo-required-indicator',true).first();
9798 setDisabled : function(v)
9800 var i = this.inputEl().dom;
9802 i.removeAttribute('disabled');
9806 i.setAttribute('disabled','true');
9808 initEvents : function()
9811 this.inputEl().on("keydown" , this.fireKey, this);
9812 this.inputEl().on("focus", this.onFocus, this);
9813 this.inputEl().on("blur", this.onBlur, this);
9815 this.inputEl().relayEvent('keyup', this);
9817 this.indicator = this.indicatorEl();
9820 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9823 // reference to original value for reset
9824 this.originalValue = this.getValue();
9825 //Roo.form.TextField.superclass.initEvents.call(this);
9826 if(this.validationEvent == 'keyup'){
9827 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9828 this.inputEl().on('keyup', this.filterValidation, this);
9830 else if(this.validationEvent !== false){
9831 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9834 if(this.selectOnFocus){
9835 this.on("focus", this.preFocus, this);
9838 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9839 this.inputEl().on("keypress", this.filterKeys, this);
9841 this.inputEl().relayEvent('keypress', this);
9844 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9845 this.el.on("click", this.autoSize, this);
9848 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9849 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9852 if (typeof(this.before) == 'object') {
9853 this.before.render(this.el.select('.roo-input-before',true).first());
9855 if (typeof(this.after) == 'object') {
9856 this.after.render(this.el.select('.roo-input-after',true).first());
9859 this.inputEl().on('change', this.onChange, this);
9862 filterValidation : function(e){
9863 if(!e.isNavKeyPress()){
9864 this.validationTask.delay(this.validationDelay);
9868 * Validates the field value
9869 * @return {Boolean} True if the value is valid, else false
9871 validate : function(){
9872 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9873 if(this.disabled || this.validateValue(this.getRawValue())){
9884 * Validates a value according to the field's validation rules and marks the field as invalid
9885 * if the validation fails
9886 * @param {Mixed} value The value to validate
9887 * @return {Boolean} True if the value is valid, else false
9889 validateValue : function(value)
9891 if(this.getVisibilityEl().hasClass('hidden')){
9895 if(value.length < 1) { // if it's blank
9896 if(this.allowBlank){
9902 if(value.length < this.minLength){
9905 if(value.length > this.maxLength){
9909 var vt = Roo.form.VTypes;
9910 if(!vt[this.vtype](value, this)){
9914 if(typeof this.validator == "function"){
9915 var msg = this.validator(value);
9919 if (typeof(msg) == 'string') {
9920 this.invalidText = msg;
9924 if(this.regex && !this.regex.test(value)){
9932 fireKey : function(e){
9933 //Roo.log('field ' + e.getKey());
9934 if(e.isNavKeyPress()){
9935 this.fireEvent("specialkey", this, e);
9938 focus : function (selectText){
9940 this.inputEl().focus();
9941 if(selectText === true){
9942 this.inputEl().dom.select();
9948 onFocus : function(){
9949 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9950 // this.el.addClass(this.focusClass);
9953 this.hasFocus = true;
9954 this.startValue = this.getValue();
9955 this.fireEvent("focus", this);
9959 beforeBlur : Roo.emptyFn,
9963 onBlur : function(){
9965 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9966 //this.el.removeClass(this.focusClass);
9968 this.hasFocus = false;
9969 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9972 var v = this.getValue();
9973 if(String(v) !== String(this.startValue)){
9974 this.fireEvent('change', this, v, this.startValue);
9976 this.fireEvent("blur", this);
9979 onChange : function(e)
9981 var v = this.getValue();
9982 if(String(v) !== String(this.startValue)){
9983 this.fireEvent('change', this, v, this.startValue);
9989 * Resets the current field value to the originally loaded value and clears any validation messages
9992 this.setValue(this.originalValue);
9996 * Returns the name of the field
9997 * @return {Mixed} name The name field
9999 getName: function(){
10003 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
10004 * @return {Mixed} value The field value
10006 getValue : function(){
10008 var v = this.inputEl().getValue();
10013 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
10014 * @return {Mixed} value The field value
10016 getRawValue : function(){
10017 var v = this.inputEl().getValue();
10023 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
10024 * @param {Mixed} value The value to set
10026 setRawValue : function(v){
10027 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10030 selectText : function(start, end){
10031 var v = this.getRawValue();
10033 start = start === undefined ? 0 : start;
10034 end = end === undefined ? v.length : end;
10035 var d = this.inputEl().dom;
10036 if(d.setSelectionRange){
10037 d.setSelectionRange(start, end);
10038 }else if(d.createTextRange){
10039 var range = d.createTextRange();
10040 range.moveStart("character", start);
10041 range.moveEnd("character", v.length-end);
10048 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
10049 * @param {Mixed} value The value to set
10051 setValue : function(v){
10054 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10060 processValue : function(value){
10061 if(this.stripCharsRe){
10062 var newValue = value.replace(this.stripCharsRe, '');
10063 if(newValue !== value){
10064 this.setRawValue(newValue);
10071 preFocus : function(){
10073 if(this.selectOnFocus){
10074 this.inputEl().dom.select();
10077 filterKeys : function(e){
10078 var k = e.getKey();
10079 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
10082 var c = e.getCharCode(), cc = String.fromCharCode(c);
10083 if(Roo.isIE && (e.isSpecialKey() || !cc)){
10086 if(!this.maskRe.test(cc)){
10091 * Clear any invalid styles/messages for this field
10093 clearInvalid : function(){
10095 if(!this.el || this.preventMark){ // not rendered
10100 this.el.removeClass([this.invalidClass, 'is-invalid']);
10102 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10104 var feedback = this.el.select('.form-control-feedback', true).first();
10107 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10112 if(this.indicator){
10113 this.indicator.removeClass('visible');
10114 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10117 this.fireEvent('valid', this);
10121 * Mark this field as valid
10123 markValid : function()
10125 if(!this.el || this.preventMark){ // not rendered...
10129 this.el.removeClass([this.invalidClass, this.validClass]);
10130 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10132 var feedback = this.el.select('.form-control-feedback', true).first();
10135 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10138 if(this.indicator){
10139 this.indicator.removeClass('visible');
10140 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10147 if(this.allowBlank && !this.getRawValue().length){
10150 if (Roo.bootstrap.version == 3) {
10151 this.el.addClass(this.validClass);
10153 this.inputEl().addClass('is-valid');
10156 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10158 var feedback = this.el.select('.form-control-feedback', true).first();
10161 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10162 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10167 this.fireEvent('valid', this);
10171 * Mark this field as invalid
10172 * @param {String} msg The validation message
10174 markInvalid : function(msg)
10176 if(!this.el || this.preventMark){ // not rendered
10180 this.el.removeClass([this.invalidClass, this.validClass]);
10181 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10183 var feedback = this.el.select('.form-control-feedback', true).first();
10186 this.el.select('.form-control-feedback', true).first().removeClass(
10187 [this.invalidFeedbackClass, this.validFeedbackClass]);
10194 if(this.allowBlank && !this.getRawValue().length){
10198 if(this.indicator){
10199 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10200 this.indicator.addClass('visible');
10202 if (Roo.bootstrap.version == 3) {
10203 this.el.addClass(this.invalidClass);
10205 this.inputEl().addClass('is-invalid');
10210 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10212 var feedback = this.el.select('.form-control-feedback', true).first();
10215 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10217 if(this.getValue().length || this.forceFeedback){
10218 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10225 this.fireEvent('invalid', this, msg);
10228 SafariOnKeyDown : function(event)
10230 // this is a workaround for a password hang bug on chrome/ webkit.
10231 if (this.inputEl().dom.type != 'password') {
10235 var isSelectAll = false;
10237 if(this.inputEl().dom.selectionEnd > 0){
10238 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10240 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10241 event.preventDefault();
10246 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10248 event.preventDefault();
10249 // this is very hacky as keydown always get's upper case.
10251 var cc = String.fromCharCode(event.getCharCode());
10252 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
10256 adjustWidth : function(tag, w){
10257 tag = tag.toLowerCase();
10258 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10259 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10260 if(tag == 'input'){
10263 if(tag == 'textarea'){
10266 }else if(Roo.isOpera){
10267 if(tag == 'input'){
10270 if(tag == 'textarea'){
10278 setFieldLabel : function(v)
10280 if(!this.rendered){
10284 if(this.indicatorEl()){
10285 var ar = this.el.select('label > span',true);
10287 if (ar.elements.length) {
10288 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10289 this.fieldLabel = v;
10293 var br = this.el.select('label',true);
10295 if(br.elements.length) {
10296 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10297 this.fieldLabel = v;
10301 Roo.log('Cannot Found any of label > span || label in input');
10305 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10306 this.fieldLabel = v;
10321 * @class Roo.bootstrap.TextArea
10322 * @extends Roo.bootstrap.Input
10323 * Bootstrap TextArea class
10324 * @cfg {Number} cols Specifies the visible width of a text area
10325 * @cfg {Number} rows Specifies the visible number of lines in a text area
10326 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10327 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10328 * @cfg {string} html text
10331 * Create a new TextArea
10332 * @param {Object} config The config object
10335 Roo.bootstrap.TextArea = function(config){
10336 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10340 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
10350 getAutoCreate : function(){
10352 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10358 if(this.inputType != 'hidden'){
10359 cfg.cls = 'form-group' //input-group
10367 value : this.value || '',
10368 html: this.html || '',
10369 cls : 'form-control',
10370 placeholder : this.placeholder || ''
10374 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10375 input.maxLength = this.maxLength;
10379 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10383 input.cols = this.cols;
10386 if (this.readOnly) {
10387 input.readonly = true;
10391 input.name = this.name;
10395 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10399 ['xs','sm','md','lg'].map(function(size){
10400 if (settings[size]) {
10401 cfg.cls += ' col-' + size + '-' + settings[size];
10405 var inputblock = input;
10407 if(this.hasFeedback && !this.allowBlank){
10411 cls: 'glyphicon form-control-feedback'
10415 cls : 'has-feedback',
10424 if (this.before || this.after) {
10427 cls : 'input-group',
10431 inputblock.cn.push({
10433 cls : 'input-group-addon',
10438 inputblock.cn.push(input);
10440 if(this.hasFeedback && !this.allowBlank){
10441 inputblock.cls += ' has-feedback';
10442 inputblock.cn.push(feedback);
10446 inputblock.cn.push({
10448 cls : 'input-group-addon',
10455 if (align ==='left' && this.fieldLabel.length) {
10460 cls : 'control-label',
10461 html : this.fieldLabel
10472 if(this.labelWidth > 12){
10473 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10476 if(this.labelWidth < 13 && this.labelmd == 0){
10477 this.labelmd = this.labelWidth;
10480 if(this.labellg > 0){
10481 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10482 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10485 if(this.labelmd > 0){
10486 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10487 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10490 if(this.labelsm > 0){
10491 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10492 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10495 if(this.labelxs > 0){
10496 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10497 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10500 } else if ( this.fieldLabel.length) {
10505 //cls : 'input-group-addon',
10506 html : this.fieldLabel
10524 if (this.disabled) {
10525 input.disabled=true;
10532 * return the real textarea element.
10534 inputEl: function ()
10536 return this.el.select('textarea.form-control',true).first();
10540 * Clear any invalid styles/messages for this field
10542 clearInvalid : function()
10545 if(!this.el || this.preventMark){ // not rendered
10549 var label = this.el.select('label', true).first();
10550 var icon = this.el.select('i.fa-star', true).first();
10555 this.el.removeClass( this.validClass);
10556 this.inputEl().removeClass('is-invalid');
10558 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10560 var feedback = this.el.select('.form-control-feedback', true).first();
10563 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10568 this.fireEvent('valid', this);
10572 * Mark this field as valid
10574 markValid : function()
10576 if(!this.el || this.preventMark){ // not rendered
10580 this.el.removeClass([this.invalidClass, this.validClass]);
10581 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10583 var feedback = this.el.select('.form-control-feedback', true).first();
10586 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10589 if(this.disabled || this.allowBlank){
10593 var label = this.el.select('label', true).first();
10594 var icon = this.el.select('i.fa-star', true).first();
10599 if (Roo.bootstrap.version == 3) {
10600 this.el.addClass(this.validClass);
10602 this.inputEl().addClass('is-valid');
10606 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10608 var feedback = this.el.select('.form-control-feedback', true).first();
10611 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10612 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10617 this.fireEvent('valid', this);
10621 * Mark this field as invalid
10622 * @param {String} msg The validation message
10624 markInvalid : function(msg)
10626 if(!this.el || this.preventMark){ // not rendered
10630 this.el.removeClass([this.invalidClass, this.validClass]);
10631 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10633 var feedback = this.el.select('.form-control-feedback', true).first();
10636 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10639 if(this.disabled || this.allowBlank){
10643 var label = this.el.select('label', true).first();
10644 var icon = this.el.select('i.fa-star', true).first();
10646 if(!this.getValue().length && label && !icon){
10647 this.el.createChild({
10649 cls : 'text-danger fa fa-lg fa-star',
10650 tooltip : 'This field is required',
10651 style : 'margin-right:5px;'
10655 if (Roo.bootstrap.version == 3) {
10656 this.el.addClass(this.invalidClass);
10658 this.inputEl().addClass('is-invalid');
10661 // fixme ... this may be depricated need to test..
10662 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10664 var feedback = this.el.select('.form-control-feedback', true).first();
10667 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10669 if(this.getValue().length || this.forceFeedback){
10670 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10677 this.fireEvent('invalid', this, msg);
10685 * trigger field - base class for combo..
10690 * @class Roo.bootstrap.TriggerField
10691 * @extends Roo.bootstrap.Input
10692 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10693 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10694 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10695 * for which you can provide a custom implementation. For example:
10697 var trigger = new Roo.bootstrap.TriggerField();
10698 trigger.onTriggerClick = myTriggerFn;
10699 trigger.applyTo('my-field');
10702 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10703 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10704 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10705 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10706 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10709 * Create a new TriggerField.
10710 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10711 * to the base TextField)
10713 Roo.bootstrap.TriggerField = function(config){
10714 this.mimicing = false;
10715 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10718 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10720 * @cfg {String} triggerClass A CSS class to apply to the trigger
10723 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10728 * @cfg {Boolean} removable (true|false) special filter default false
10732 /** @cfg {Boolean} grow @hide */
10733 /** @cfg {Number} growMin @hide */
10734 /** @cfg {Number} growMax @hide */
10740 autoSize: Roo.emptyFn,
10744 deferHeight : true,
10747 actionMode : 'wrap',
10752 getAutoCreate : function(){
10754 var align = this.labelAlign || this.parentLabelAlign();
10759 cls: 'form-group' //input-group
10766 type : this.inputType,
10767 cls : 'form-control',
10768 autocomplete: 'new-password',
10769 placeholder : this.placeholder || ''
10773 input.name = this.name;
10776 input.cls += ' input-' + this.size;
10779 if (this.disabled) {
10780 input.disabled=true;
10783 var inputblock = input;
10785 if(this.hasFeedback && !this.allowBlank){
10789 cls: 'glyphicon form-control-feedback'
10792 if(this.removable && !this.editable && !this.tickable){
10794 cls : 'has-feedback',
10800 cls : 'roo-combo-removable-btn close'
10807 cls : 'has-feedback',
10816 if(this.removable && !this.editable && !this.tickable){
10818 cls : 'roo-removable',
10824 cls : 'roo-combo-removable-btn close'
10831 if (this.before || this.after) {
10834 cls : 'input-group',
10838 inputblock.cn.push({
10840 cls : 'input-group-addon input-group-prepend input-group-text',
10845 inputblock.cn.push(input);
10847 if(this.hasFeedback && !this.allowBlank){
10848 inputblock.cls += ' has-feedback';
10849 inputblock.cn.push(feedback);
10853 inputblock.cn.push({
10855 cls : 'input-group-addon input-group-append input-group-text',
10864 var ibwrap = inputblock;
10869 cls: 'roo-select2-choices',
10873 cls: 'roo-select2-search-field',
10885 cls: 'roo-select2-container input-group',
10890 cls: 'form-hidden-field'
10896 if(!this.multiple && this.showToggleBtn){
10902 if (this.caret != false) {
10905 cls: 'fa fa-' + this.caret
10912 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10914 Roo.bootstrap.version == 3 ? caret : '',
10917 cls: 'combobox-clear',
10931 combobox.cls += ' roo-select2-container-multi';
10935 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10936 tooltip : 'This field is required'
10938 if (Roo.bootstrap.version == 4) {
10941 style : 'display:none'
10946 if (align ==='left' && this.fieldLabel.length) {
10948 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10955 cls : 'control-label',
10956 html : this.fieldLabel
10968 var labelCfg = cfg.cn[1];
10969 var contentCfg = cfg.cn[2];
10971 if(this.indicatorpos == 'right'){
10976 cls : 'control-label',
10980 html : this.fieldLabel
10994 labelCfg = cfg.cn[0];
10995 contentCfg = cfg.cn[1];
10998 if(this.labelWidth > 12){
10999 labelCfg.style = "width: " + this.labelWidth + 'px';
11002 if(this.labelWidth < 13 && this.labelmd == 0){
11003 this.labelmd = this.labelWidth;
11006 if(this.labellg > 0){
11007 labelCfg.cls += ' col-lg-' + this.labellg;
11008 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11011 if(this.labelmd > 0){
11012 labelCfg.cls += ' col-md-' + this.labelmd;
11013 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11016 if(this.labelsm > 0){
11017 labelCfg.cls += ' col-sm-' + this.labelsm;
11018 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11021 if(this.labelxs > 0){
11022 labelCfg.cls += ' col-xs-' + this.labelxs;
11023 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11026 } else if ( this.fieldLabel.length) {
11027 // Roo.log(" label");
11032 //cls : 'input-group-addon',
11033 html : this.fieldLabel
11041 if(this.indicatorpos == 'right'){
11049 html : this.fieldLabel
11063 // Roo.log(" no label && no align");
11070 ['xs','sm','md','lg'].map(function(size){
11071 if (settings[size]) {
11072 cfg.cls += ' col-' + size + '-' + settings[size];
11083 onResize : function(w, h){
11084 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
11085 // if(typeof w == 'number'){
11086 // var x = w - this.trigger.getWidth();
11087 // this.inputEl().setWidth(this.adjustWidth('input', x));
11088 // this.trigger.setStyle('left', x+'px');
11093 adjustSize : Roo.BoxComponent.prototype.adjustSize,
11096 getResizeEl : function(){
11097 return this.inputEl();
11101 getPositionEl : function(){
11102 return this.inputEl();
11106 alignErrorIcon : function(){
11107 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
11111 initEvents : function(){
11115 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
11116 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
11117 if(!this.multiple && this.showToggleBtn){
11118 this.trigger = this.el.select('span.dropdown-toggle',true).first();
11119 if(this.hideTrigger){
11120 this.trigger.setDisplayed(false);
11122 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
11126 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
11129 if(this.removable && !this.editable && !this.tickable){
11130 var close = this.closeTriggerEl();
11133 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
11134 close.on('click', this.removeBtnClick, this, close);
11138 //this.trigger.addClassOnOver('x-form-trigger-over');
11139 //this.trigger.addClassOnClick('x-form-trigger-click');
11142 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
11146 closeTriggerEl : function()
11148 var close = this.el.select('.roo-combo-removable-btn', true).first();
11149 return close ? close : false;
11152 removeBtnClick : function(e, h, el)
11154 e.preventDefault();
11156 if(this.fireEvent("remove", this) !== false){
11158 this.fireEvent("afterremove", this)
11162 createList : function()
11164 this.list = Roo.get(document.body).createChild({
11165 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
11166 cls: 'typeahead typeahead-long dropdown-menu',
11167 style: 'display:none'
11170 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
11175 initTrigger : function(){
11180 onDestroy : function(){
11182 this.trigger.removeAllListeners();
11183 // this.trigger.remove();
11186 // this.wrap.remove();
11188 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
11192 onFocus : function(){
11193 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
11195 if(!this.mimicing){
11196 this.wrap.addClass('x-trigger-wrap-focus');
11197 this.mimicing = true;
11198 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
11199 if(this.monitorTab){
11200 this.el.on("keydown", this.checkTab, this);
11207 checkTab : function(e){
11208 if(e.getKey() == e.TAB){
11209 this.triggerBlur();
11214 onBlur : function(){
11219 mimicBlur : function(e, t){
11221 if(!this.wrap.contains(t) && this.validateBlur()){
11222 this.triggerBlur();
11228 triggerBlur : function(){
11229 this.mimicing = false;
11230 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
11231 if(this.monitorTab){
11232 this.el.un("keydown", this.checkTab, this);
11234 //this.wrap.removeClass('x-trigger-wrap-focus');
11235 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11239 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11240 validateBlur : function(e, t){
11245 onDisable : function(){
11246 this.inputEl().dom.disabled = true;
11247 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11249 // this.wrap.addClass('x-item-disabled');
11254 onEnable : function(){
11255 this.inputEl().dom.disabled = false;
11256 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11258 // this.el.removeClass('x-item-disabled');
11263 onShow : function(){
11264 var ae = this.getActionEl();
11267 ae.dom.style.display = '';
11268 ae.dom.style.visibility = 'visible';
11274 onHide : function(){
11275 var ae = this.getActionEl();
11276 ae.dom.style.display = 'none';
11280 * The function that should handle the trigger's click event. This method does nothing by default until overridden
11281 * by an implementing function.
11283 * @param {EventObject} e
11285 onTriggerClick : Roo.emptyFn
11289 * Ext JS Library 1.1.1
11290 * Copyright(c) 2006-2007, Ext JS, LLC.
11292 * Originally Released Under LGPL - original licence link has changed is not relivant.
11295 * <script type="text/javascript">
11300 * @class Roo.data.SortTypes
11302 * Defines the default sorting (casting?) comparison functions used when sorting data.
11304 Roo.data.SortTypes = {
11306 * Default sort that does nothing
11307 * @param {Mixed} s The value being converted
11308 * @return {Mixed} The comparison value
11310 none : function(s){
11315 * The regular expression used to strip tags
11319 stripTagsRE : /<\/?[^>]+>/gi,
11322 * Strips all HTML tags to sort on text only
11323 * @param {Mixed} s The value being converted
11324 * @return {String} The comparison value
11326 asText : function(s){
11327 return String(s).replace(this.stripTagsRE, "");
11331 * Strips all HTML tags to sort on text only - Case insensitive
11332 * @param {Mixed} s The value being converted
11333 * @return {String} The comparison value
11335 asUCText : function(s){
11336 return String(s).toUpperCase().replace(this.stripTagsRE, "");
11340 * Case insensitive string
11341 * @param {Mixed} s The value being converted
11342 * @return {String} The comparison value
11344 asUCString : function(s) {
11345 return String(s).toUpperCase();
11350 * @param {Mixed} s The value being converted
11351 * @return {Number} The comparison value
11353 asDate : function(s) {
11357 if(s instanceof Date){
11358 return s.getTime();
11360 return Date.parse(String(s));
11365 * @param {Mixed} s The value being converted
11366 * @return {Float} The comparison value
11368 asFloat : function(s) {
11369 var val = parseFloat(String(s).replace(/,/g, ""));
11378 * @param {Mixed} s The value being converted
11379 * @return {Number} The comparison value
11381 asInt : function(s) {
11382 var val = parseInt(String(s).replace(/,/g, ""));
11390 * Ext JS Library 1.1.1
11391 * Copyright(c) 2006-2007, Ext JS, LLC.
11393 * Originally Released Under LGPL - original licence link has changed is not relivant.
11396 * <script type="text/javascript">
11400 * @class Roo.data.Record
11401 * Instances of this class encapsulate both record <em>definition</em> information, and record
11402 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11403 * to access Records cached in an {@link Roo.data.Store} object.<br>
11405 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11406 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11409 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11411 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11412 * {@link #create}. The parameters are the same.
11413 * @param {Array} data An associative Array of data values keyed by the field name.
11414 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11415 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11416 * not specified an integer id is generated.
11418 Roo.data.Record = function(data, id){
11419 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11424 * Generate a constructor for a specific record layout.
11425 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11426 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11427 * Each field definition object may contain the following properties: <ul>
11428 * <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,
11429 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11430 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11431 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11432 * is being used, then this is a string containing the javascript expression to reference the data relative to
11433 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11434 * to the data item relative to the record element. If the mapping expression is the same as the field name,
11435 * this may be omitted.</p></li>
11436 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11437 * <ul><li>auto (Default, implies no conversion)</li>
11442 * <li>date</li></ul></p></li>
11443 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11444 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11445 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11446 * by the Reader into an object that will be stored in the Record. It is passed the
11447 * following parameters:<ul>
11448 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11450 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11452 * <br>usage:<br><pre><code>
11453 var TopicRecord = Roo.data.Record.create(
11454 {name: 'title', mapping: 'topic_title'},
11455 {name: 'author', mapping: 'username'},
11456 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11457 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11458 {name: 'lastPoster', mapping: 'user2'},
11459 {name: 'excerpt', mapping: 'post_text'}
11462 var myNewRecord = new TopicRecord({
11463 title: 'Do my job please',
11466 lastPost: new Date(),
11467 lastPoster: 'Animal',
11468 excerpt: 'No way dude!'
11470 myStore.add(myNewRecord);
11475 Roo.data.Record.create = function(o){
11476 var f = function(){
11477 f.superclass.constructor.apply(this, arguments);
11479 Roo.extend(f, Roo.data.Record);
11480 var p = f.prototype;
11481 p.fields = new Roo.util.MixedCollection(false, function(field){
11484 for(var i = 0, len = o.length; i < len; i++){
11485 p.fields.add(new Roo.data.Field(o[i]));
11487 f.getField = function(name){
11488 return p.fields.get(name);
11493 Roo.data.Record.AUTO_ID = 1000;
11494 Roo.data.Record.EDIT = 'edit';
11495 Roo.data.Record.REJECT = 'reject';
11496 Roo.data.Record.COMMIT = 'commit';
11498 Roo.data.Record.prototype = {
11500 * Readonly flag - true if this record has been modified.
11509 join : function(store){
11510 this.store = store;
11514 * Set the named field to the specified value.
11515 * @param {String} name The name of the field to set.
11516 * @param {Object} value The value to set the field to.
11518 set : function(name, value){
11519 if(this.data[name] == value){
11523 if(!this.modified){
11524 this.modified = {};
11526 if(typeof this.modified[name] == 'undefined'){
11527 this.modified[name] = this.data[name];
11529 this.data[name] = value;
11530 if(!this.editing && this.store){
11531 this.store.afterEdit(this);
11536 * Get the value of the named field.
11537 * @param {String} name The name of the field to get the value of.
11538 * @return {Object} The value of the field.
11540 get : function(name){
11541 return this.data[name];
11545 beginEdit : function(){
11546 this.editing = true;
11547 this.modified = {};
11551 cancelEdit : function(){
11552 this.editing = false;
11553 delete this.modified;
11557 endEdit : function(){
11558 this.editing = false;
11559 if(this.dirty && this.store){
11560 this.store.afterEdit(this);
11565 * Usually called by the {@link Roo.data.Store} which owns the Record.
11566 * Rejects all changes made to the Record since either creation, or the last commit operation.
11567 * Modified fields are reverted to their original values.
11569 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11570 * of reject operations.
11572 reject : function(){
11573 var m = this.modified;
11575 if(typeof m[n] != "function"){
11576 this.data[n] = m[n];
11579 this.dirty = false;
11580 delete this.modified;
11581 this.editing = false;
11583 this.store.afterReject(this);
11588 * Usually called by the {@link Roo.data.Store} which owns the Record.
11589 * Commits all changes made to the Record since either creation, or the last commit operation.
11591 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11592 * of commit operations.
11594 commit : function(){
11595 this.dirty = false;
11596 delete this.modified;
11597 this.editing = false;
11599 this.store.afterCommit(this);
11604 hasError : function(){
11605 return this.error != null;
11609 clearError : function(){
11614 * Creates a copy of this record.
11615 * @param {String} id (optional) A new record id if you don't want to use this record's id
11618 copy : function(newId) {
11619 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11623 * Ext JS Library 1.1.1
11624 * Copyright(c) 2006-2007, Ext JS, LLC.
11626 * Originally Released Under LGPL - original licence link has changed is not relivant.
11629 * <script type="text/javascript">
11635 * @class Roo.data.Store
11636 * @extends Roo.util.Observable
11637 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11638 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11640 * 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
11641 * has no knowledge of the format of the data returned by the Proxy.<br>
11643 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11644 * instances from the data object. These records are cached and made available through accessor functions.
11646 * Creates a new Store.
11647 * @param {Object} config A config object containing the objects needed for the Store to access data,
11648 * and read the data into Records.
11650 Roo.data.Store = function(config){
11651 this.data = new Roo.util.MixedCollection(false);
11652 this.data.getKey = function(o){
11655 this.baseParams = {};
11657 this.paramNames = {
11662 "multisort" : "_multisort"
11665 if(config && config.data){
11666 this.inlineData = config.data;
11667 delete config.data;
11670 Roo.apply(this, config);
11672 if(this.reader){ // reader passed
11673 this.reader = Roo.factory(this.reader, Roo.data);
11674 this.reader.xmodule = this.xmodule || false;
11675 if(!this.recordType){
11676 this.recordType = this.reader.recordType;
11678 if(this.reader.onMetaChange){
11679 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11683 if(this.recordType){
11684 this.fields = this.recordType.prototype.fields;
11686 this.modified = [];
11690 * @event datachanged
11691 * Fires when the data cache has changed, and a widget which is using this Store
11692 * as a Record cache should refresh its view.
11693 * @param {Store} this
11695 datachanged : true,
11697 * @event metachange
11698 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11699 * @param {Store} this
11700 * @param {Object} meta The JSON metadata
11705 * Fires when Records have been added to the Store
11706 * @param {Store} this
11707 * @param {Roo.data.Record[]} records The array of Records added
11708 * @param {Number} index The index at which the record(s) were added
11713 * Fires when a Record has been removed from the Store
11714 * @param {Store} this
11715 * @param {Roo.data.Record} record The Record that was removed
11716 * @param {Number} index The index at which the record was removed
11721 * Fires when a Record has been updated
11722 * @param {Store} this
11723 * @param {Roo.data.Record} record The Record that was updated
11724 * @param {String} operation The update operation being performed. Value may be one of:
11726 Roo.data.Record.EDIT
11727 Roo.data.Record.REJECT
11728 Roo.data.Record.COMMIT
11734 * Fires when the data cache has been cleared.
11735 * @param {Store} this
11739 * @event beforeload
11740 * Fires before a request is made for a new data object. If the beforeload handler returns false
11741 * the load action will be canceled.
11742 * @param {Store} this
11743 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11747 * @event beforeloadadd
11748 * Fires after a new set of Records has been loaded.
11749 * @param {Store} this
11750 * @param {Roo.data.Record[]} records The Records that were loaded
11751 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11753 beforeloadadd : true,
11756 * Fires after a new set of Records has been loaded, before they are added to the store.
11757 * @param {Store} this
11758 * @param {Roo.data.Record[]} records The Records that were loaded
11759 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11760 * @params {Object} return from reader
11764 * @event loadexception
11765 * Fires if an exception occurs in the Proxy during loading.
11766 * Called with the signature of the Proxy's "loadexception" event.
11767 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11770 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11771 * @param {Object} load options
11772 * @param {Object} jsonData from your request (normally this contains the Exception)
11774 loadexception : true
11778 this.proxy = Roo.factory(this.proxy, Roo.data);
11779 this.proxy.xmodule = this.xmodule || false;
11780 this.relayEvents(this.proxy, ["loadexception"]);
11782 this.sortToggle = {};
11783 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11785 Roo.data.Store.superclass.constructor.call(this);
11787 if(this.inlineData){
11788 this.loadData(this.inlineData);
11789 delete this.inlineData;
11793 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11795 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11796 * without a remote query - used by combo/forms at present.
11800 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11803 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11806 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11807 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11810 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11811 * on any HTTP request
11814 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11817 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11821 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11822 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11824 remoteSort : false,
11827 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11828 * loaded or when a record is removed. (defaults to false).
11830 pruneModifiedRecords : false,
11833 lastOptions : null,
11836 * Add Records to the Store and fires the add event.
11837 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11839 add : function(records){
11840 records = [].concat(records);
11841 for(var i = 0, len = records.length; i < len; i++){
11842 records[i].join(this);
11844 var index = this.data.length;
11845 this.data.addAll(records);
11846 this.fireEvent("add", this, records, index);
11850 * Remove a Record from the Store and fires the remove event.
11851 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11853 remove : function(record){
11854 var index = this.data.indexOf(record);
11855 this.data.removeAt(index);
11857 if(this.pruneModifiedRecords){
11858 this.modified.remove(record);
11860 this.fireEvent("remove", this, record, index);
11864 * Remove all Records from the Store and fires the clear event.
11866 removeAll : function(){
11868 if(this.pruneModifiedRecords){
11869 this.modified = [];
11871 this.fireEvent("clear", this);
11875 * Inserts Records to the Store at the given index and fires the add event.
11876 * @param {Number} index The start index at which to insert the passed Records.
11877 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11879 insert : function(index, records){
11880 records = [].concat(records);
11881 for(var i = 0, len = records.length; i < len; i++){
11882 this.data.insert(index, records[i]);
11883 records[i].join(this);
11885 this.fireEvent("add", this, records, index);
11889 * Get the index within the cache of the passed Record.
11890 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11891 * @return {Number} The index of the passed Record. Returns -1 if not found.
11893 indexOf : function(record){
11894 return this.data.indexOf(record);
11898 * Get the index within the cache of the Record with the passed id.
11899 * @param {String} id The id of the Record to find.
11900 * @return {Number} The index of the Record. Returns -1 if not found.
11902 indexOfId : function(id){
11903 return this.data.indexOfKey(id);
11907 * Get the Record with the specified id.
11908 * @param {String} id The id of the Record to find.
11909 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11911 getById : function(id){
11912 return this.data.key(id);
11916 * Get the Record at the specified index.
11917 * @param {Number} index The index of the Record to find.
11918 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11920 getAt : function(index){
11921 return this.data.itemAt(index);
11925 * Returns a range of Records between specified indices.
11926 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11927 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11928 * @return {Roo.data.Record[]} An array of Records
11930 getRange : function(start, end){
11931 return this.data.getRange(start, end);
11935 storeOptions : function(o){
11936 o = Roo.apply({}, o);
11939 this.lastOptions = o;
11943 * Loads the Record cache from the configured Proxy using the configured Reader.
11945 * If using remote paging, then the first load call must specify the <em>start</em>
11946 * and <em>limit</em> properties in the options.params property to establish the initial
11947 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11949 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11950 * and this call will return before the new data has been loaded. Perform any post-processing
11951 * in a callback function, or in a "load" event handler.</strong>
11953 * @param {Object} options An object containing properties which control loading options:<ul>
11954 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11955 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11956 * passed the following arguments:<ul>
11957 * <li>r : Roo.data.Record[]</li>
11958 * <li>options: Options object from the load call</li>
11959 * <li>success: Boolean success indicator</li></ul></li>
11960 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11961 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11964 load : function(options){
11965 options = options || {};
11966 if(this.fireEvent("beforeload", this, options) !== false){
11967 this.storeOptions(options);
11968 var p = Roo.apply(options.params || {}, this.baseParams);
11969 // if meta was not loaded from remote source.. try requesting it.
11970 if (!this.reader.metaFromRemote) {
11971 p._requestMeta = 1;
11973 if(this.sortInfo && this.remoteSort){
11974 var pn = this.paramNames;
11975 p[pn["sort"]] = this.sortInfo.field;
11976 p[pn["dir"]] = this.sortInfo.direction;
11978 if (this.multiSort) {
11979 var pn = this.paramNames;
11980 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11983 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11988 * Reloads the Record cache from the configured Proxy using the configured Reader and
11989 * the options from the last load operation performed.
11990 * @param {Object} options (optional) An object containing properties which may override the options
11991 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11992 * the most recently used options are reused).
11994 reload : function(options){
11995 this.load(Roo.applyIf(options||{}, this.lastOptions));
11999 // Called as a callback by the Reader during a load operation.
12000 loadRecords : function(o, options, success){
12001 if(!o || success === false){
12002 if(success !== false){
12003 this.fireEvent("load", this, [], options, o);
12005 if(options.callback){
12006 options.callback.call(options.scope || this, [], options, false);
12010 // if data returned failure - throw an exception.
12011 if (o.success === false) {
12012 // show a message if no listener is registered.
12013 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
12014 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
12016 // loadmask wil be hooked into this..
12017 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
12020 var r = o.records, t = o.totalRecords || r.length;
12022 this.fireEvent("beforeloadadd", this, r, options, o);
12024 if(!options || options.add !== true){
12025 if(this.pruneModifiedRecords){
12026 this.modified = [];
12028 for(var i = 0, len = r.length; i < len; i++){
12032 this.data = this.snapshot;
12033 delete this.snapshot;
12036 this.data.addAll(r);
12037 this.totalLength = t;
12039 this.fireEvent("datachanged", this);
12041 this.totalLength = Math.max(t, this.data.length+r.length);
12045 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
12047 var e = new Roo.data.Record({});
12049 e.set(this.parent.displayField, this.parent.emptyTitle);
12050 e.set(this.parent.valueField, '');
12055 this.fireEvent("load", this, r, options, o);
12056 if(options.callback){
12057 options.callback.call(options.scope || this, r, options, true);
12063 * Loads data from a passed data block. A Reader which understands the format of the data
12064 * must have been configured in the constructor.
12065 * @param {Object} data The data block from which to read the Records. The format of the data expected
12066 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
12067 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
12069 loadData : function(o, append){
12070 var r = this.reader.readRecords(o);
12071 this.loadRecords(r, {add: append}, true);
12075 * using 'cn' the nested child reader read the child array into it's child stores.
12076 * @param {Object} rec The record with a 'children array
12078 loadDataFromChildren : function(rec)
12080 this.loadData(this.reader.toLoadData(rec));
12085 * Gets the number of cached records.
12087 * <em>If using paging, this may not be the total size of the dataset. If the data object
12088 * used by the Reader contains the dataset size, then the getTotalCount() function returns
12089 * the data set size</em>
12091 getCount : function(){
12092 return this.data.length || 0;
12096 * Gets the total number of records in the dataset as returned by the server.
12098 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
12099 * the dataset size</em>
12101 getTotalCount : function(){
12102 return this.totalLength || 0;
12106 * Returns the sort state of the Store as an object with two properties:
12108 field {String} The name of the field by which the Records are sorted
12109 direction {String} The sort order, "ASC" or "DESC"
12112 getSortState : function(){
12113 return this.sortInfo;
12117 applySort : function(){
12118 if(this.sortInfo && !this.remoteSort){
12119 var s = this.sortInfo, f = s.field;
12120 var st = this.fields.get(f).sortType;
12121 var fn = function(r1, r2){
12122 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
12123 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12125 this.data.sort(s.direction, fn);
12126 if(this.snapshot && this.snapshot != this.data){
12127 this.snapshot.sort(s.direction, fn);
12133 * Sets the default sort column and order to be used by the next load operation.
12134 * @param {String} fieldName The name of the field to sort by.
12135 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12137 setDefaultSort : function(field, dir){
12138 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
12142 * Sort the Records.
12143 * If remote sorting is used, the sort is performed on the server, and the cache is
12144 * reloaded. If local sorting is used, the cache is sorted internally.
12145 * @param {String} fieldName The name of the field to sort by.
12146 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12148 sort : function(fieldName, dir){
12149 var f = this.fields.get(fieldName);
12151 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
12153 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
12154 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
12159 this.sortToggle[f.name] = dir;
12160 this.sortInfo = {field: f.name, direction: dir};
12161 if(!this.remoteSort){
12163 this.fireEvent("datachanged", this);
12165 this.load(this.lastOptions);
12170 * Calls the specified function for each of the Records in the cache.
12171 * @param {Function} fn The function to call. The Record is passed as the first parameter.
12172 * Returning <em>false</em> aborts and exits the iteration.
12173 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
12175 each : function(fn, scope){
12176 this.data.each(fn, scope);
12180 * Gets all records modified since the last commit. Modified records are persisted across load operations
12181 * (e.g., during paging).
12182 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
12184 getModifiedRecords : function(){
12185 return this.modified;
12189 createFilterFn : function(property, value, anyMatch){
12190 if(!value.exec){ // not a regex
12191 value = String(value);
12192 if(value.length == 0){
12195 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
12197 return function(r){
12198 return value.test(r.data[property]);
12203 * Sums the value of <i>property</i> for each record between start and end and returns the result.
12204 * @param {String} property A field on your records
12205 * @param {Number} start The record index to start at (defaults to 0)
12206 * @param {Number} end The last record index to include (defaults to length - 1)
12207 * @return {Number} The sum
12209 sum : function(property, start, end){
12210 var rs = this.data.items, v = 0;
12211 start = start || 0;
12212 end = (end || end === 0) ? end : rs.length-1;
12214 for(var i = start; i <= end; i++){
12215 v += (rs[i].data[property] || 0);
12221 * Filter the records by a specified property.
12222 * @param {String} field A field on your records
12223 * @param {String/RegExp} value Either a string that the field
12224 * should start with or a RegExp to test against the field
12225 * @param {Boolean} anyMatch True to match any part not just the beginning
12227 filter : function(property, value, anyMatch){
12228 var fn = this.createFilterFn(property, value, anyMatch);
12229 return fn ? this.filterBy(fn) : this.clearFilter();
12233 * Filter by a function. The specified function will be called with each
12234 * record in this data source. If the function returns true the record is included,
12235 * otherwise it is filtered.
12236 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12237 * @param {Object} scope (optional) The scope of the function (defaults to this)
12239 filterBy : function(fn, scope){
12240 this.snapshot = this.snapshot || this.data;
12241 this.data = this.queryBy(fn, scope||this);
12242 this.fireEvent("datachanged", this);
12246 * Query the records by a specified property.
12247 * @param {String} field A field on your records
12248 * @param {String/RegExp} value Either a string that the field
12249 * should start with or a RegExp to test against the field
12250 * @param {Boolean} anyMatch True to match any part not just the beginning
12251 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12253 query : function(property, value, anyMatch){
12254 var fn = this.createFilterFn(property, value, anyMatch);
12255 return fn ? this.queryBy(fn) : this.data.clone();
12259 * Query by a function. The specified function will be called with each
12260 * record in this data source. If the function returns true the record is included
12262 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12263 * @param {Object} scope (optional) The scope of the function (defaults to this)
12264 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12266 queryBy : function(fn, scope){
12267 var data = this.snapshot || this.data;
12268 return data.filterBy(fn, scope||this);
12272 * Collects unique values for a particular dataIndex from this store.
12273 * @param {String} dataIndex The property to collect
12274 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12275 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12276 * @return {Array} An array of the unique values
12278 collect : function(dataIndex, allowNull, bypassFilter){
12279 var d = (bypassFilter === true && this.snapshot) ?
12280 this.snapshot.items : this.data.items;
12281 var v, sv, r = [], l = {};
12282 for(var i = 0, len = d.length; i < len; i++){
12283 v = d[i].data[dataIndex];
12285 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12294 * Revert to a view of the Record cache with no filtering applied.
12295 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12297 clearFilter : function(suppressEvent){
12298 if(this.snapshot && this.snapshot != this.data){
12299 this.data = this.snapshot;
12300 delete this.snapshot;
12301 if(suppressEvent !== true){
12302 this.fireEvent("datachanged", this);
12308 afterEdit : function(record){
12309 if(this.modified.indexOf(record) == -1){
12310 this.modified.push(record);
12312 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12316 afterReject : function(record){
12317 this.modified.remove(record);
12318 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12322 afterCommit : function(record){
12323 this.modified.remove(record);
12324 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12328 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12329 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12331 commitChanges : function(){
12332 var m = this.modified.slice(0);
12333 this.modified = [];
12334 for(var i = 0, len = m.length; i < len; i++){
12340 * Cancel outstanding changes on all changed records.
12342 rejectChanges : function(){
12343 var m = this.modified.slice(0);
12344 this.modified = [];
12345 for(var i = 0, len = m.length; i < len; i++){
12350 onMetaChange : function(meta, rtype, o){
12351 this.recordType = rtype;
12352 this.fields = rtype.prototype.fields;
12353 delete this.snapshot;
12354 this.sortInfo = meta.sortInfo || this.sortInfo;
12355 this.modified = [];
12356 this.fireEvent('metachange', this, this.reader.meta);
12359 moveIndex : function(data, type)
12361 var index = this.indexOf(data);
12363 var newIndex = index + type;
12367 this.insert(newIndex, data);
12372 * Ext JS Library 1.1.1
12373 * Copyright(c) 2006-2007, Ext JS, LLC.
12375 * Originally Released Under LGPL - original licence link has changed is not relivant.
12378 * <script type="text/javascript">
12382 * @class Roo.data.SimpleStore
12383 * @extends Roo.data.Store
12384 * Small helper class to make creating Stores from Array data easier.
12385 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12386 * @cfg {Array} fields An array of field definition objects, or field name strings.
12387 * @cfg {Object} an existing reader (eg. copied from another store)
12388 * @cfg {Array} data The multi-dimensional array of data
12390 * @param {Object} config
12392 Roo.data.SimpleStore = function(config)
12394 Roo.data.SimpleStore.superclass.constructor.call(this, {
12396 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
12399 Roo.data.Record.create(config.fields)
12401 proxy : new Roo.data.MemoryProxy(config.data)
12405 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12407 * Ext JS Library 1.1.1
12408 * Copyright(c) 2006-2007, Ext JS, LLC.
12410 * Originally Released Under LGPL - original licence link has changed is not relivant.
12413 * <script type="text/javascript">
12418 * @extends Roo.data.Store
12419 * @class Roo.data.JsonStore
12420 * Small helper class to make creating Stores for JSON data easier. <br/>
12422 var store = new Roo.data.JsonStore({
12423 url: 'get-images.php',
12425 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12428 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12429 * JsonReader and HttpProxy (unless inline data is provided).</b>
12430 * @cfg {Array} fields An array of field definition objects, or field name strings.
12432 * @param {Object} config
12434 Roo.data.JsonStore = function(c){
12435 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12436 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12437 reader: new Roo.data.JsonReader(c, c.fields)
12440 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12442 * Ext JS Library 1.1.1
12443 * Copyright(c) 2006-2007, Ext JS, LLC.
12445 * Originally Released Under LGPL - original licence link has changed is not relivant.
12448 * <script type="text/javascript">
12452 Roo.data.Field = function(config){
12453 if(typeof config == "string"){
12454 config = {name: config};
12456 Roo.apply(this, config);
12459 this.type = "auto";
12462 var st = Roo.data.SortTypes;
12463 // named sortTypes are supported, here we look them up
12464 if(typeof this.sortType == "string"){
12465 this.sortType = st[this.sortType];
12468 // set default sortType for strings and dates
12469 if(!this.sortType){
12472 this.sortType = st.asUCString;
12475 this.sortType = st.asDate;
12478 this.sortType = st.none;
12483 var stripRe = /[\$,%]/g;
12485 // prebuilt conversion function for this field, instead of
12486 // switching every time we're reading a value
12488 var cv, dateFormat = this.dateFormat;
12493 cv = function(v){ return v; };
12496 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12500 return v !== undefined && v !== null && v !== '' ?
12501 parseInt(String(v).replace(stripRe, ""), 10) : '';
12506 return v !== undefined && v !== null && v !== '' ?
12507 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12512 cv = function(v){ return v === true || v === "true" || v == 1; };
12519 if(v instanceof Date){
12523 if(dateFormat == "timestamp"){
12524 return new Date(v*1000);
12526 return Date.parseDate(v, dateFormat);
12528 var parsed = Date.parse(v);
12529 return parsed ? new Date(parsed) : null;
12538 Roo.data.Field.prototype = {
12546 * Ext JS Library 1.1.1
12547 * Copyright(c) 2006-2007, Ext JS, LLC.
12549 * Originally Released Under LGPL - original licence link has changed is not relivant.
12552 * <script type="text/javascript">
12555 // Base class for reading structured data from a data source. This class is intended to be
12556 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12559 * @class Roo.data.DataReader
12560 * Base class for reading structured data from a data source. This class is intended to be
12561 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12564 Roo.data.DataReader = function(meta, recordType){
12568 this.recordType = recordType instanceof Array ?
12569 Roo.data.Record.create(recordType) : recordType;
12572 Roo.data.DataReader.prototype = {
12575 readerType : 'Data',
12577 * Create an empty record
12578 * @param {Object} data (optional) - overlay some values
12579 * @return {Roo.data.Record} record created.
12581 newRow : function(d) {
12583 this.recordType.prototype.fields.each(function(c) {
12585 case 'int' : da[c.name] = 0; break;
12586 case 'date' : da[c.name] = new Date(); break;
12587 case 'float' : da[c.name] = 0.0; break;
12588 case 'boolean' : da[c.name] = false; break;
12589 default : da[c.name] = ""; break;
12593 return new this.recordType(Roo.apply(da, d));
12599 * Ext JS Library 1.1.1
12600 * Copyright(c) 2006-2007, Ext JS, LLC.
12602 * Originally Released Under LGPL - original licence link has changed is not relivant.
12605 * <script type="text/javascript">
12609 * @class Roo.data.DataProxy
12610 * @extends Roo.data.Observable
12611 * This class is an abstract base class for implementations which provide retrieval of
12612 * unformatted data objects.<br>
12614 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12615 * (of the appropriate type which knows how to parse the data object) to provide a block of
12616 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12618 * Custom implementations must implement the load method as described in
12619 * {@link Roo.data.HttpProxy#load}.
12621 Roo.data.DataProxy = function(){
12624 * @event beforeload
12625 * Fires before a network request is made to retrieve a data object.
12626 * @param {Object} This DataProxy object.
12627 * @param {Object} params The params parameter to the load function.
12632 * Fires before the load method's callback is called.
12633 * @param {Object} This DataProxy object.
12634 * @param {Object} o The data object.
12635 * @param {Object} arg The callback argument object passed to the load function.
12639 * @event loadexception
12640 * Fires if an Exception occurs during data retrieval.
12641 * @param {Object} This DataProxy object.
12642 * @param {Object} o The data object.
12643 * @param {Object} arg The callback argument object passed to the load function.
12644 * @param {Object} e The Exception.
12646 loadexception : true
12648 Roo.data.DataProxy.superclass.constructor.call(this);
12651 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12654 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12658 * Ext JS Library 1.1.1
12659 * Copyright(c) 2006-2007, Ext JS, LLC.
12661 * Originally Released Under LGPL - original licence link has changed is not relivant.
12664 * <script type="text/javascript">
12667 * @class Roo.data.MemoryProxy
12668 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12669 * to the Reader when its load method is called.
12671 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12673 Roo.data.MemoryProxy = function(data){
12677 Roo.data.MemoryProxy.superclass.constructor.call(this);
12681 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12684 * Load data from the requested source (in this case an in-memory
12685 * data object passed to the constructor), read the data object into
12686 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12687 * process that block using the passed callback.
12688 * @param {Object} params This parameter is not used by the MemoryProxy class.
12689 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12690 * object into a block of Roo.data.Records.
12691 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12692 * The function must be passed <ul>
12693 * <li>The Record block object</li>
12694 * <li>The "arg" argument from the load function</li>
12695 * <li>A boolean success indicator</li>
12697 * @param {Object} scope The scope in which to call the callback
12698 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12700 load : function(params, reader, callback, scope, arg){
12701 params = params || {};
12704 result = reader.readRecords(params.data ? params.data :this.data);
12706 this.fireEvent("loadexception", this, arg, null, e);
12707 callback.call(scope, null, arg, false);
12710 callback.call(scope, result, arg, true);
12714 update : function(params, records){
12719 * Ext JS Library 1.1.1
12720 * Copyright(c) 2006-2007, Ext JS, LLC.
12722 * Originally Released Under LGPL - original licence link has changed is not relivant.
12725 * <script type="text/javascript">
12728 * @class Roo.data.HttpProxy
12729 * @extends Roo.data.DataProxy
12730 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12731 * configured to reference a certain URL.<br><br>
12733 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12734 * from which the running page was served.<br><br>
12736 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12738 * Be aware that to enable the browser to parse an XML document, the server must set
12739 * the Content-Type header in the HTTP response to "text/xml".
12741 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12742 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12743 * will be used to make the request.
12745 Roo.data.HttpProxy = function(conn){
12746 Roo.data.HttpProxy.superclass.constructor.call(this);
12747 // is conn a conn config or a real conn?
12749 this.useAjax = !conn || !conn.events;
12753 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12754 // thse are take from connection...
12757 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12760 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12761 * extra parameters to each request made by this object. (defaults to undefined)
12764 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12765 * to each request made by this object. (defaults to undefined)
12768 * @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)
12771 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12774 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12780 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12784 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12785 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12786 * a finer-grained basis than the DataProxy events.
12788 getConnection : function(){
12789 return this.useAjax ? Roo.Ajax : this.conn;
12793 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12794 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12795 * process that block using the passed callback.
12796 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12797 * for the request to the remote server.
12798 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12799 * object into a block of Roo.data.Records.
12800 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12801 * The function must be passed <ul>
12802 * <li>The Record block object</li>
12803 * <li>The "arg" argument from the load function</li>
12804 * <li>A boolean success indicator</li>
12806 * @param {Object} scope The scope in which to call the callback
12807 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12809 load : function(params, reader, callback, scope, arg){
12810 if(this.fireEvent("beforeload", this, params) !== false){
12812 params : params || {},
12814 callback : callback,
12819 callback : this.loadResponse,
12823 Roo.applyIf(o, this.conn);
12824 if(this.activeRequest){
12825 Roo.Ajax.abort(this.activeRequest);
12827 this.activeRequest = Roo.Ajax.request(o);
12829 this.conn.request(o);
12832 callback.call(scope||this, null, arg, false);
12837 loadResponse : function(o, success, response){
12838 delete this.activeRequest;
12840 this.fireEvent("loadexception", this, o, response);
12841 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12846 result = o.reader.read(response);
12848 this.fireEvent("loadexception", this, o, response, e);
12849 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12853 this.fireEvent("load", this, o, o.request.arg);
12854 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12858 update : function(dataSet){
12863 updateResponse : function(dataSet){
12868 * Ext JS Library 1.1.1
12869 * Copyright(c) 2006-2007, Ext JS, LLC.
12871 * Originally Released Under LGPL - original licence link has changed is not relivant.
12874 * <script type="text/javascript">
12878 * @class Roo.data.ScriptTagProxy
12879 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12880 * other than the originating domain of the running page.<br><br>
12882 * <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
12883 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12885 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12886 * source code that is used as the source inside a <script> tag.<br><br>
12888 * In order for the browser to process the returned data, the server must wrap the data object
12889 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12890 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12891 * depending on whether the callback name was passed:
12894 boolean scriptTag = false;
12895 String cb = request.getParameter("callback");
12898 response.setContentType("text/javascript");
12900 response.setContentType("application/x-json");
12902 Writer out = response.getWriter();
12904 out.write(cb + "(");
12906 out.print(dataBlock.toJsonString());
12913 * @param {Object} config A configuration object.
12915 Roo.data.ScriptTagProxy = function(config){
12916 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12917 Roo.apply(this, config);
12918 this.head = document.getElementsByTagName("head")[0];
12921 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12923 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12925 * @cfg {String} url The URL from which to request the data object.
12928 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12932 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12933 * the server the name of the callback function set up by the load call to process the returned data object.
12934 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12935 * javascript output which calls this named function passing the data object as its only parameter.
12937 callbackParam : "callback",
12939 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12940 * name to the request.
12945 * Load data from the configured URL, read the data object into
12946 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12947 * process that block using the passed callback.
12948 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12949 * for the request to the remote server.
12950 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12951 * object into a block of Roo.data.Records.
12952 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12953 * The function must be passed <ul>
12954 * <li>The Record block object</li>
12955 * <li>The "arg" argument from the load function</li>
12956 * <li>A boolean success indicator</li>
12958 * @param {Object} scope The scope in which to call the callback
12959 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12961 load : function(params, reader, callback, scope, arg){
12962 if(this.fireEvent("beforeload", this, params) !== false){
12964 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12966 var url = this.url;
12967 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12969 url += "&_dc=" + (new Date().getTime());
12971 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12974 cb : "stcCallback"+transId,
12975 scriptId : "stcScript"+transId,
12979 callback : callback,
12985 window[trans.cb] = function(o){
12986 conn.handleResponse(o, trans);
12989 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12991 if(this.autoAbort !== false){
12995 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12997 var script = document.createElement("script");
12998 script.setAttribute("src", url);
12999 script.setAttribute("type", "text/javascript");
13000 script.setAttribute("id", trans.scriptId);
13001 this.head.appendChild(script);
13003 this.trans = trans;
13005 callback.call(scope||this, null, arg, false);
13010 isLoading : function(){
13011 return this.trans ? true : false;
13015 * Abort the current server request.
13017 abort : function(){
13018 if(this.isLoading()){
13019 this.destroyTrans(this.trans);
13024 destroyTrans : function(trans, isLoaded){
13025 this.head.removeChild(document.getElementById(trans.scriptId));
13026 clearTimeout(trans.timeoutId);
13028 window[trans.cb] = undefined;
13030 delete window[trans.cb];
13033 // if hasn't been loaded, wait for load to remove it to prevent script error
13034 window[trans.cb] = function(){
13035 window[trans.cb] = undefined;
13037 delete window[trans.cb];
13044 handleResponse : function(o, trans){
13045 this.trans = false;
13046 this.destroyTrans(trans, true);
13049 result = trans.reader.readRecords(o);
13051 this.fireEvent("loadexception", this, o, trans.arg, e);
13052 trans.callback.call(trans.scope||window, null, trans.arg, false);
13055 this.fireEvent("load", this, o, trans.arg);
13056 trans.callback.call(trans.scope||window, result, trans.arg, true);
13060 handleFailure : function(trans){
13061 this.trans = false;
13062 this.destroyTrans(trans, false);
13063 this.fireEvent("loadexception", this, null, trans.arg);
13064 trans.callback.call(trans.scope||window, null, trans.arg, false);
13068 * Ext JS Library 1.1.1
13069 * Copyright(c) 2006-2007, Ext JS, LLC.
13071 * Originally Released Under LGPL - original licence link has changed is not relivant.
13074 * <script type="text/javascript">
13078 * @class Roo.data.JsonReader
13079 * @extends Roo.data.DataReader
13080 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
13081 * based on mappings in a provided Roo.data.Record constructor.
13083 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
13084 * in the reply previously.
13089 var RecordDef = Roo.data.Record.create([
13090 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
13091 {name: 'occupation'} // This field will use "occupation" as the mapping.
13093 var myReader = new Roo.data.JsonReader({
13094 totalProperty: "results", // The property which contains the total dataset size (optional)
13095 root: "rows", // The property which contains an Array of row objects
13096 id: "id" // The property within each row object that provides an ID for the record (optional)
13100 * This would consume a JSON file like this:
13102 { 'results': 2, 'rows': [
13103 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
13104 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
13107 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
13108 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
13109 * paged from the remote server.
13110 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
13111 * @cfg {String} root name of the property which contains the Array of row objects.
13112 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13113 * @cfg {Array} fields Array of field definition objects
13115 * Create a new JsonReader
13116 * @param {Object} meta Metadata configuration options
13117 * @param {Object} recordType Either an Array of field definition objects,
13118 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
13120 Roo.data.JsonReader = function(meta, recordType){
13123 // set some defaults:
13124 Roo.applyIf(meta, {
13125 totalProperty: 'total',
13126 successProperty : 'success',
13131 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13133 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
13135 readerType : 'Json',
13138 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
13139 * Used by Store query builder to append _requestMeta to params.
13142 metaFromRemote : false,
13144 * This method is only used by a DataProxy which has retrieved data from a remote server.
13145 * @param {Object} response The XHR object which contains the JSON data in its responseText.
13146 * @return {Object} data A data block which is used by an Roo.data.Store object as
13147 * a cache of Roo.data.Records.
13149 read : function(response){
13150 var json = response.responseText;
13152 var o = /* eval:var:o */ eval("("+json+")");
13154 throw {message: "JsonReader.read: Json object not found"};
13160 this.metaFromRemote = true;
13161 this.meta = o.metaData;
13162 this.recordType = Roo.data.Record.create(o.metaData.fields);
13163 this.onMetaChange(this.meta, this.recordType, o);
13165 return this.readRecords(o);
13168 // private function a store will implement
13169 onMetaChange : function(meta, recordType, o){
13176 simpleAccess: function(obj, subsc) {
13183 getJsonAccessor: function(){
13185 return function(expr) {
13187 return(re.test(expr))
13188 ? new Function("obj", "return obj." + expr)
13193 return Roo.emptyFn;
13198 * Create a data block containing Roo.data.Records from an XML document.
13199 * @param {Object} o An object which contains an Array of row objects in the property specified
13200 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
13201 * which contains the total size of the dataset.
13202 * @return {Object} data A data block which is used by an Roo.data.Store object as
13203 * a cache of Roo.data.Records.
13205 readRecords : function(o){
13207 * After any data loads, the raw JSON data is available for further custom processing.
13211 var s = this.meta, Record = this.recordType,
13212 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
13214 // Generate extraction functions for the totalProperty, the root, the id, and for each field
13216 if(s.totalProperty) {
13217 this.getTotal = this.getJsonAccessor(s.totalProperty);
13219 if(s.successProperty) {
13220 this.getSuccess = this.getJsonAccessor(s.successProperty);
13222 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
13224 var g = this.getJsonAccessor(s.id);
13225 this.getId = function(rec) {
13227 return (r === undefined || r === "") ? null : r;
13230 this.getId = function(){return null;};
13233 for(var jj = 0; jj < fl; jj++){
13235 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
13236 this.ef[jj] = this.getJsonAccessor(map);
13240 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
13241 if(s.totalProperty){
13242 var vt = parseInt(this.getTotal(o), 10);
13247 if(s.successProperty){
13248 var vs = this.getSuccess(o);
13249 if(vs === false || vs === 'false'){
13254 for(var i = 0; i < c; i++){
13257 var id = this.getId(n);
13258 for(var j = 0; j < fl; j++){
13260 var v = this.ef[j](n);
13262 Roo.log('missing convert for ' + f.name);
13266 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13268 var record = new Record(values, id);
13270 records[i] = record;
13276 totalRecords : totalRecords
13279 // used when loading children.. @see loadDataFromChildren
13280 toLoadData: function(rec)
13282 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13283 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13284 return { data : data, total : data.length };
13289 * Ext JS Library 1.1.1
13290 * Copyright(c) 2006-2007, Ext JS, LLC.
13292 * Originally Released Under LGPL - original licence link has changed is not relivant.
13295 * <script type="text/javascript">
13299 * @class Roo.data.ArrayReader
13300 * @extends Roo.data.DataReader
13301 * Data reader class to create an Array of Roo.data.Record objects from an Array.
13302 * Each element of that Array represents a row of data fields. The
13303 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13304 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13308 var RecordDef = Roo.data.Record.create([
13309 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
13310 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
13312 var myReader = new Roo.data.ArrayReader({
13313 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
13317 * This would consume an Array like this:
13319 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13323 * Create a new JsonReader
13324 * @param {Object} meta Metadata configuration options.
13325 * @param {Object|Array} recordType Either an Array of field definition objects
13327 * @cfg {Array} fields Array of field definition objects
13328 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13329 * as specified to {@link Roo.data.Record#create},
13330 * or an {@link Roo.data.Record} object
13333 * created using {@link Roo.data.Record#create}.
13335 Roo.data.ArrayReader = function(meta, recordType)
13337 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13340 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13343 * Create a data block containing Roo.data.Records from an XML document.
13344 * @param {Object} o An Array of row objects which represents the dataset.
13345 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13346 * a cache of Roo.data.Records.
13348 readRecords : function(o)
13350 var sid = this.meta ? this.meta.id : null;
13351 var recordType = this.recordType, fields = recordType.prototype.fields;
13354 for(var i = 0; i < root.length; i++){
13357 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13358 for(var j = 0, jlen = fields.length; j < jlen; j++){
13359 var f = fields.items[j];
13360 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13361 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13363 values[f.name] = v;
13365 var record = new recordType(values, id);
13367 records[records.length] = record;
13371 totalRecords : records.length
13374 // used when loading children.. @see loadDataFromChildren
13375 toLoadData: function(rec)
13377 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13378 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13389 * @class Roo.bootstrap.ComboBox
13390 * @extends Roo.bootstrap.TriggerField
13391 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13392 * @cfg {Boolean} append (true|false) default false
13393 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13394 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13395 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13396 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13397 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13398 * @cfg {Boolean} animate default true
13399 * @cfg {Boolean} emptyResultText only for touch device
13400 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13401 * @cfg {String} emptyTitle default ''
13403 * Create a new ComboBox.
13404 * @param {Object} config Configuration options
13406 Roo.bootstrap.ComboBox = function(config){
13407 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13411 * Fires when the dropdown list is expanded
13412 * @param {Roo.bootstrap.ComboBox} combo This combo box
13417 * Fires when the dropdown list is collapsed
13418 * @param {Roo.bootstrap.ComboBox} combo This combo box
13422 * @event beforeselect
13423 * Fires before a list item is selected. Return false to cancel the selection.
13424 * @param {Roo.bootstrap.ComboBox} combo This combo box
13425 * @param {Roo.data.Record} record The data record returned from the underlying store
13426 * @param {Number} index The index of the selected item in the dropdown list
13428 'beforeselect' : true,
13431 * Fires when a list item is selected
13432 * @param {Roo.bootstrap.ComboBox} combo This combo box
13433 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13434 * @param {Number} index The index of the selected item in the dropdown list
13438 * @event beforequery
13439 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13440 * The event object passed has these properties:
13441 * @param {Roo.bootstrap.ComboBox} combo This combo box
13442 * @param {String} query The query
13443 * @param {Boolean} forceAll true to force "all" query
13444 * @param {Boolean} cancel true to cancel the query
13445 * @param {Object} e The query event object
13447 'beforequery': true,
13450 * Fires when the 'add' icon is pressed (add a listener to enable add button)
13451 * @param {Roo.bootstrap.ComboBox} combo This combo box
13456 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13457 * @param {Roo.bootstrap.ComboBox} combo This combo box
13458 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13463 * Fires when the remove value from the combobox array
13464 * @param {Roo.bootstrap.ComboBox} combo This combo box
13468 * @event afterremove
13469 * Fires when the remove value from the combobox array
13470 * @param {Roo.bootstrap.ComboBox} combo This combo box
13472 'afterremove' : true,
13474 * @event specialfilter
13475 * Fires when specialfilter
13476 * @param {Roo.bootstrap.ComboBox} combo This combo box
13478 'specialfilter' : true,
13481 * Fires when tick the element
13482 * @param {Roo.bootstrap.ComboBox} combo This combo box
13486 * @event touchviewdisplay
13487 * Fires when touch view require special display (default is using displayField)
13488 * @param {Roo.bootstrap.ComboBox} combo This combo box
13489 * @param {Object} cfg set html .
13491 'touchviewdisplay' : true
13496 this.tickItems = [];
13498 this.selectedIndex = -1;
13499 if(this.mode == 'local'){
13500 if(config.queryDelay === undefined){
13501 this.queryDelay = 10;
13503 if(config.minChars === undefined){
13509 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13512 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13513 * rendering into an Roo.Editor, defaults to false)
13516 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13517 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13520 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13523 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13524 * the dropdown list (defaults to undefined, with no header element)
13528 * @cfg {String/Roo.Template} tpl The template to use to render the output
13532 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13534 listWidth: undefined,
13536 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13537 * mode = 'remote' or 'text' if mode = 'local')
13539 displayField: undefined,
13542 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13543 * mode = 'remote' or 'value' if mode = 'local').
13544 * Note: use of a valueField requires the user make a selection
13545 * in order for a value to be mapped.
13547 valueField: undefined,
13549 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13554 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13555 * field's data value (defaults to the underlying DOM element's name)
13557 hiddenName: undefined,
13559 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13563 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13565 selectedClass: 'active',
13568 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13572 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13573 * anchor positions (defaults to 'tl-bl')
13575 listAlign: 'tl-bl?',
13577 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13581 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13582 * query specified by the allQuery config option (defaults to 'query')
13584 triggerAction: 'query',
13586 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13587 * (defaults to 4, does not apply if editable = false)
13591 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13592 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13596 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13597 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13601 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13602 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13606 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13607 * when editable = true (defaults to false)
13609 selectOnFocus:false,
13611 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13613 queryParam: 'query',
13615 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13616 * when mode = 'remote' (defaults to 'Loading...')
13618 loadingText: 'Loading...',
13620 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13624 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13628 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13629 * traditional select (defaults to true)
13633 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13637 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13641 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13642 * listWidth has a higher value)
13646 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13647 * allow the user to set arbitrary text into the field (defaults to false)
13649 forceSelection:false,
13651 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13652 * if typeAhead = true (defaults to 250)
13654 typeAheadDelay : 250,
13656 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13657 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13659 valueNotFoundText : undefined,
13661 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13663 blockFocus : false,
13666 * @cfg {Boolean} disableClear Disable showing of clear button.
13668 disableClear : false,
13670 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13672 alwaysQuery : false,
13675 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13680 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13682 invalidClass : "has-warning",
13685 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13687 validClass : "has-success",
13690 * @cfg {Boolean} specialFilter (true|false) special filter default false
13692 specialFilter : false,
13695 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13697 mobileTouchView : true,
13700 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13702 useNativeIOS : false,
13705 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13707 mobile_restrict_height : false,
13709 ios_options : false,
13721 btnPosition : 'right',
13722 triggerList : true,
13723 showToggleBtn : true,
13725 emptyResultText: 'Empty',
13726 triggerText : 'Select',
13729 // element that contains real text value.. (when hidden is used..)
13731 getAutoCreate : function()
13736 * Render classic select for iso
13739 if(Roo.isIOS && this.useNativeIOS){
13740 cfg = this.getAutoCreateNativeIOS();
13748 if(Roo.isTouch && this.mobileTouchView){
13749 cfg = this.getAutoCreateTouchView();
13756 if(!this.tickable){
13757 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13762 * ComboBox with tickable selections
13765 var align = this.labelAlign || this.parentLabelAlign();
13768 cls : 'form-group roo-combobox-tickable' //input-group
13771 var btn_text_select = '';
13772 var btn_text_done = '';
13773 var btn_text_cancel = '';
13775 if (this.btn_text_show) {
13776 btn_text_select = 'Select';
13777 btn_text_done = 'Done';
13778 btn_text_cancel = 'Cancel';
13783 cls : 'tickable-buttons',
13788 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13789 //html : this.triggerText
13790 html: btn_text_select
13796 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13798 html: btn_text_done
13804 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13806 html: btn_text_cancel
13812 buttons.cn.unshift({
13814 cls: 'roo-select2-search-field-input'
13820 Roo.each(buttons.cn, function(c){
13822 c.cls += ' btn-' + _this.size;
13825 if (_this.disabled) {
13832 style : 'display: contents',
13837 cls: 'form-hidden-field'
13841 cls: 'roo-select2-choices',
13845 cls: 'roo-select2-search-field',
13856 cls: 'roo-select2-container input-group roo-select2-container-multi',
13862 // cls: 'typeahead typeahead-long dropdown-menu',
13863 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13868 if(this.hasFeedback && !this.allowBlank){
13872 cls: 'glyphicon form-control-feedback'
13875 combobox.cn.push(feedback);
13880 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13881 tooltip : 'This field is required'
13883 if (Roo.bootstrap.version == 4) {
13886 style : 'display:none'
13889 if (align ==='left' && this.fieldLabel.length) {
13891 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13898 cls : 'control-label col-form-label',
13899 html : this.fieldLabel
13911 var labelCfg = cfg.cn[1];
13912 var contentCfg = cfg.cn[2];
13915 if(this.indicatorpos == 'right'){
13921 cls : 'control-label col-form-label',
13925 html : this.fieldLabel
13941 labelCfg = cfg.cn[0];
13942 contentCfg = cfg.cn[1];
13946 if(this.labelWidth > 12){
13947 labelCfg.style = "width: " + this.labelWidth + 'px';
13950 if(this.labelWidth < 13 && this.labelmd == 0){
13951 this.labelmd = this.labelWidth;
13954 if(this.labellg > 0){
13955 labelCfg.cls += ' col-lg-' + this.labellg;
13956 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13959 if(this.labelmd > 0){
13960 labelCfg.cls += ' col-md-' + this.labelmd;
13961 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13964 if(this.labelsm > 0){
13965 labelCfg.cls += ' col-sm-' + this.labelsm;
13966 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13969 if(this.labelxs > 0){
13970 labelCfg.cls += ' col-xs-' + this.labelxs;
13971 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13975 } else if ( this.fieldLabel.length) {
13976 // Roo.log(" label");
13981 //cls : 'input-group-addon',
13982 html : this.fieldLabel
13987 if(this.indicatorpos == 'right'){
13991 //cls : 'input-group-addon',
13992 html : this.fieldLabel
14002 // Roo.log(" no label && no align");
14009 ['xs','sm','md','lg'].map(function(size){
14010 if (settings[size]) {
14011 cfg.cls += ' col-' + size + '-' + settings[size];
14019 _initEventsCalled : false,
14022 initEvents: function()
14024 if (this._initEventsCalled) { // as we call render... prevent looping...
14027 this._initEventsCalled = true;
14030 throw "can not find store for combo";
14033 this.indicator = this.indicatorEl();
14035 this.store = Roo.factory(this.store, Roo.data);
14036 this.store.parent = this;
14038 // if we are building from html. then this element is so complex, that we can not really
14039 // use the rendered HTML.
14040 // so we have to trash and replace the previous code.
14041 if (Roo.XComponent.build_from_html) {
14042 // remove this element....
14043 var e = this.el.dom, k=0;
14044 while (e ) { e = e.previousSibling; ++k;}
14049 this.rendered = false;
14051 this.render(this.parent().getChildContainer(true), k);
14054 if(Roo.isIOS && this.useNativeIOS){
14055 this.initIOSView();
14063 if(Roo.isTouch && this.mobileTouchView){
14064 this.initTouchView();
14069 this.initTickableEvents();
14073 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
14075 if(this.hiddenName){
14077 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14079 this.hiddenField.dom.value =
14080 this.hiddenValue !== undefined ? this.hiddenValue :
14081 this.value !== undefined ? this.value : '';
14083 // prevent input submission
14084 this.el.dom.removeAttribute('name');
14085 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14090 // this.el.dom.setAttribute('autocomplete', 'off');
14093 var cls = 'x-combo-list';
14095 //this.list = new Roo.Layer({
14096 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
14102 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14103 _this.list.setWidth(lw);
14106 this.list.on('mouseover', this.onViewOver, this);
14107 this.list.on('mousemove', this.onViewMove, this);
14108 this.list.on('scroll', this.onViewScroll, this);
14111 this.list.swallowEvent('mousewheel');
14112 this.assetHeight = 0;
14115 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
14116 this.assetHeight += this.header.getHeight();
14119 this.innerList = this.list.createChild({cls:cls+'-inner'});
14120 this.innerList.on('mouseover', this.onViewOver, this);
14121 this.innerList.on('mousemove', this.onViewMove, this);
14122 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14124 if(this.allowBlank && !this.pageSize && !this.disableClear){
14125 this.footer = this.list.createChild({cls:cls+'-ft'});
14126 this.pageTb = new Roo.Toolbar(this.footer);
14130 this.footer = this.list.createChild({cls:cls+'-ft'});
14131 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
14132 {pageSize: this.pageSize});
14136 if (this.pageTb && this.allowBlank && !this.disableClear) {
14138 this.pageTb.add(new Roo.Toolbar.Fill(), {
14139 cls: 'x-btn-icon x-btn-clear',
14141 handler: function()
14144 _this.clearValue();
14145 _this.onSelect(false, -1);
14150 this.assetHeight += this.footer.getHeight();
14155 this.tpl = Roo.bootstrap.version == 4 ?
14156 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
14157 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
14160 this.view = new Roo.View(this.list, this.tpl, {
14161 singleSelect:true, store: this.store, selectedClass: this.selectedClass
14163 //this.view.wrapEl.setDisplayed(false);
14164 this.view.on('click', this.onViewClick, this);
14167 this.store.on('beforeload', this.onBeforeLoad, this);
14168 this.store.on('load', this.onLoad, this);
14169 this.store.on('loadexception', this.onLoadException, this);
14171 if(this.resizable){
14172 this.resizer = new Roo.Resizable(this.list, {
14173 pinned:true, handles:'se'
14175 this.resizer.on('resize', function(r, w, h){
14176 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
14177 this.listWidth = w;
14178 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
14179 this.restrictHeight();
14181 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
14184 if(!this.editable){
14185 this.editable = true;
14186 this.setEditable(false);
14191 if (typeof(this.events.add.listeners) != 'undefined') {
14193 this.addicon = this.wrap.createChild(
14194 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
14196 this.addicon.on('click', function(e) {
14197 this.fireEvent('add', this);
14200 if (typeof(this.events.edit.listeners) != 'undefined') {
14202 this.editicon = this.wrap.createChild(
14203 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
14204 if (this.addicon) {
14205 this.editicon.setStyle('margin-left', '40px');
14207 this.editicon.on('click', function(e) {
14209 // we fire even if inothing is selected..
14210 this.fireEvent('edit', this, this.lastData );
14216 this.keyNav = new Roo.KeyNav(this.inputEl(), {
14217 "up" : function(e){
14218 this.inKeyMode = true;
14222 "down" : function(e){
14223 if(!this.isExpanded()){
14224 this.onTriggerClick();
14226 this.inKeyMode = true;
14231 "enter" : function(e){
14232 // this.onViewClick();
14236 if(this.fireEvent("specialkey", this, e)){
14237 this.onViewClick(false);
14243 "esc" : function(e){
14247 "tab" : function(e){
14250 if(this.fireEvent("specialkey", this, e)){
14251 this.onViewClick(false);
14259 doRelay : function(foo, bar, hname){
14260 if(hname == 'down' || this.scope.isExpanded()){
14261 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14270 this.queryDelay = Math.max(this.queryDelay || 10,
14271 this.mode == 'local' ? 10 : 250);
14274 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14276 if(this.typeAhead){
14277 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14279 if(this.editable !== false){
14280 this.inputEl().on("keyup", this.onKeyUp, this);
14282 if(this.forceSelection){
14283 this.inputEl().on('blur', this.doForce, this);
14287 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14288 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14292 initTickableEvents: function()
14296 if(this.hiddenName){
14298 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14300 this.hiddenField.dom.value =
14301 this.hiddenValue !== undefined ? this.hiddenValue :
14302 this.value !== undefined ? this.value : '';
14304 // prevent input submission
14305 this.el.dom.removeAttribute('name');
14306 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14311 // this.list = this.el.select('ul.dropdown-menu',true).first();
14313 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14314 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14315 if(this.triggerList){
14316 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14319 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14320 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14322 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14323 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14325 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14326 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14328 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14329 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14330 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14333 this.cancelBtn.hide();
14338 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14339 _this.list.setWidth(lw);
14342 this.list.on('mouseover', this.onViewOver, this);
14343 this.list.on('mousemove', this.onViewMove, this);
14345 this.list.on('scroll', this.onViewScroll, this);
14348 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
14349 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14352 this.view = new Roo.View(this.list, this.tpl, {
14357 selectedClass: this.selectedClass
14360 //this.view.wrapEl.setDisplayed(false);
14361 this.view.on('click', this.onViewClick, this);
14365 this.store.on('beforeload', this.onBeforeLoad, this);
14366 this.store.on('load', this.onLoad, this);
14367 this.store.on('loadexception', this.onLoadException, this);
14370 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14371 "up" : function(e){
14372 this.inKeyMode = true;
14376 "down" : function(e){
14377 this.inKeyMode = true;
14381 "enter" : function(e){
14382 if(this.fireEvent("specialkey", this, e)){
14383 this.onViewClick(false);
14389 "esc" : function(e){
14390 this.onTickableFooterButtonClick(e, false, false);
14393 "tab" : function(e){
14394 this.fireEvent("specialkey", this, e);
14396 this.onTickableFooterButtonClick(e, false, false);
14403 doRelay : function(e, fn, key){
14404 if(this.scope.isExpanded()){
14405 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14414 this.queryDelay = Math.max(this.queryDelay || 10,
14415 this.mode == 'local' ? 10 : 250);
14418 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14420 if(this.typeAhead){
14421 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14424 if(this.editable !== false){
14425 this.tickableInputEl().on("keyup", this.onKeyUp, this);
14428 this.indicator = this.indicatorEl();
14430 if(this.indicator){
14431 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14432 this.indicator.hide();
14437 onDestroy : function(){
14439 this.view.setStore(null);
14440 this.view.el.removeAllListeners();
14441 this.view.el.remove();
14442 this.view.purgeListeners();
14445 this.list.dom.innerHTML = '';
14449 this.store.un('beforeload', this.onBeforeLoad, this);
14450 this.store.un('load', this.onLoad, this);
14451 this.store.un('loadexception', this.onLoadException, this);
14453 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14457 fireKey : function(e){
14458 if(e.isNavKeyPress() && !this.list.isVisible()){
14459 this.fireEvent("specialkey", this, e);
14464 onResize: function(w, h){
14465 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14467 // if(typeof w != 'number'){
14468 // // we do not handle it!?!?
14471 // var tw = this.trigger.getWidth();
14472 // // tw += this.addicon ? this.addicon.getWidth() : 0;
14473 // // tw += this.editicon ? this.editicon.getWidth() : 0;
14475 // this.inputEl().setWidth( this.adjustWidth('input', x));
14477 // //this.trigger.setStyle('left', x+'px');
14479 // if(this.list && this.listWidth === undefined){
14480 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14481 // this.list.setWidth(lw);
14482 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14490 * Allow or prevent the user from directly editing the field text. If false is passed,
14491 * the user will only be able to select from the items defined in the dropdown list. This method
14492 * is the runtime equivalent of setting the 'editable' config option at config time.
14493 * @param {Boolean} value True to allow the user to directly edit the field text
14495 setEditable : function(value){
14496 if(value == this.editable){
14499 this.editable = value;
14501 this.inputEl().dom.setAttribute('readOnly', true);
14502 this.inputEl().on('mousedown', this.onTriggerClick, this);
14503 this.inputEl().addClass('x-combo-noedit');
14505 this.inputEl().dom.setAttribute('readOnly', false);
14506 this.inputEl().un('mousedown', this.onTriggerClick, this);
14507 this.inputEl().removeClass('x-combo-noedit');
14513 onBeforeLoad : function(combo,opts){
14514 if(!this.hasFocus){
14518 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14520 this.restrictHeight();
14521 this.selectedIndex = -1;
14525 onLoad : function(){
14527 this.hasQuery = false;
14529 if(!this.hasFocus){
14533 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14534 this.loading.hide();
14537 if(this.store.getCount() > 0){
14540 this.restrictHeight();
14541 if(this.lastQuery == this.allQuery){
14542 if(this.editable && !this.tickable){
14543 this.inputEl().dom.select();
14547 !this.selectByValue(this.value, true) &&
14550 !this.store.lastOptions ||
14551 typeof(this.store.lastOptions.add) == 'undefined' ||
14552 this.store.lastOptions.add != true
14555 this.select(0, true);
14558 if(this.autoFocus){
14561 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14562 this.taTask.delay(this.typeAheadDelay);
14566 this.onEmptyResults();
14572 onLoadException : function()
14574 this.hasQuery = false;
14576 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14577 this.loading.hide();
14580 if(this.tickable && this.editable){
14585 // only causes errors at present
14586 //Roo.log(this.store.reader.jsonData);
14587 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14589 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14595 onTypeAhead : function(){
14596 if(this.store.getCount() > 0){
14597 var r = this.store.getAt(0);
14598 var newValue = r.data[this.displayField];
14599 var len = newValue.length;
14600 var selStart = this.getRawValue().length;
14602 if(selStart != len){
14603 this.setRawValue(newValue);
14604 this.selectText(selStart, newValue.length);
14610 onSelect : function(record, index){
14612 if(this.fireEvent('beforeselect', this, record, index) !== false){
14614 this.setFromData(index > -1 ? record.data : false);
14617 this.fireEvent('select', this, record, index);
14622 * Returns the currently selected field value or empty string if no value is set.
14623 * @return {String} value The selected value
14625 getValue : function()
14627 if(Roo.isIOS && this.useNativeIOS){
14628 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14632 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14635 if(this.valueField){
14636 return typeof this.value != 'undefined' ? this.value : '';
14638 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14642 getRawValue : function()
14644 if(Roo.isIOS && this.useNativeIOS){
14645 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14648 var v = this.inputEl().getValue();
14654 * Clears any text/value currently set in the field
14656 clearValue : function(){
14658 if(this.hiddenField){
14659 this.hiddenField.dom.value = '';
14662 this.setRawValue('');
14663 this.lastSelectionText = '';
14664 this.lastData = false;
14666 var close = this.closeTriggerEl();
14677 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14678 * will be displayed in the field. If the value does not match the data value of an existing item,
14679 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14680 * Otherwise the field will be blank (although the value will still be set).
14681 * @param {String} value The value to match
14683 setValue : function(v)
14685 if(Roo.isIOS && this.useNativeIOS){
14686 this.setIOSValue(v);
14696 if(this.valueField){
14697 var r = this.findRecord(this.valueField, v);
14699 text = r.data[this.displayField];
14700 }else if(this.valueNotFoundText !== undefined){
14701 text = this.valueNotFoundText;
14704 this.lastSelectionText = text;
14705 if(this.hiddenField){
14706 this.hiddenField.dom.value = v;
14708 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14711 var close = this.closeTriggerEl();
14714 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14720 * @property {Object} the last set data for the element
14725 * Sets the value of the field based on a object which is related to the record format for the store.
14726 * @param {Object} value the value to set as. or false on reset?
14728 setFromData : function(o){
14735 var dv = ''; // display value
14736 var vv = ''; // value value..
14738 if (this.displayField) {
14739 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14741 // this is an error condition!!!
14742 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14745 if(this.valueField){
14746 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14749 var close = this.closeTriggerEl();
14752 if(dv.length || vv * 1 > 0){
14754 this.blockFocus=true;
14760 if(this.hiddenField){
14761 this.hiddenField.dom.value = vv;
14763 this.lastSelectionText = dv;
14764 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14768 // no hidden field.. - we store the value in 'value', but still display
14769 // display field!!!!
14770 this.lastSelectionText = dv;
14771 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14778 reset : function(){
14779 // overridden so that last data is reset..
14786 this.setValue(this.originalValue);
14787 //this.clearInvalid();
14788 this.lastData = false;
14790 this.view.clearSelections();
14796 findRecord : function(prop, value){
14798 if(this.store.getCount() > 0){
14799 this.store.each(function(r){
14800 if(r.data[prop] == value){
14810 getName: function()
14812 // returns hidden if it's set..
14813 if (!this.rendered) {return ''};
14814 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14818 onViewMove : function(e, t){
14819 this.inKeyMode = false;
14823 onViewOver : function(e, t){
14824 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14827 var item = this.view.findItemFromChild(t);
14830 var index = this.view.indexOf(item);
14831 this.select(index, false);
14836 onViewClick : function(view, doFocus, el, e)
14838 var index = this.view.getSelectedIndexes()[0];
14840 var r = this.store.getAt(index);
14844 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14851 Roo.each(this.tickItems, function(v,k){
14853 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14855 _this.tickItems.splice(k, 1);
14857 if(typeof(e) == 'undefined' && view == false){
14858 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14870 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14871 this.tickItems.push(r.data);
14874 if(typeof(e) == 'undefined' && view == false){
14875 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14882 this.onSelect(r, index);
14884 if(doFocus !== false && !this.blockFocus){
14885 this.inputEl().focus();
14890 restrictHeight : function(){
14891 //this.innerList.dom.style.height = '';
14892 //var inner = this.innerList.dom;
14893 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14894 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14895 //this.list.beginUpdate();
14896 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14897 this.list.alignTo(this.inputEl(), this.listAlign);
14898 this.list.alignTo(this.inputEl(), this.listAlign);
14899 //this.list.endUpdate();
14903 onEmptyResults : function(){
14905 if(this.tickable && this.editable){
14906 this.hasFocus = false;
14907 this.restrictHeight();
14915 * Returns true if the dropdown list is expanded, else false.
14917 isExpanded : function(){
14918 return this.list.isVisible();
14922 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14923 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14924 * @param {String} value The data value of the item to select
14925 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14926 * selected item if it is not currently in view (defaults to true)
14927 * @return {Boolean} True if the value matched an item in the list, else false
14929 selectByValue : function(v, scrollIntoView){
14930 if(v !== undefined && v !== null){
14931 var r = this.findRecord(this.valueField || this.displayField, v);
14933 this.select(this.store.indexOf(r), scrollIntoView);
14941 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14942 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14943 * @param {Number} index The zero-based index of the list item to select
14944 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14945 * selected item if it is not currently in view (defaults to true)
14947 select : function(index, scrollIntoView){
14948 this.selectedIndex = index;
14949 this.view.select(index);
14950 if(scrollIntoView !== false){
14951 var el = this.view.getNode(index);
14953 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14956 this.list.scrollChildIntoView(el, false);
14962 selectNext : function(){
14963 var ct = this.store.getCount();
14965 if(this.selectedIndex == -1){
14967 }else if(this.selectedIndex < ct-1){
14968 this.select(this.selectedIndex+1);
14974 selectPrev : function(){
14975 var ct = this.store.getCount();
14977 if(this.selectedIndex == -1){
14979 }else if(this.selectedIndex != 0){
14980 this.select(this.selectedIndex-1);
14986 onKeyUp : function(e){
14987 if(this.editable !== false && !e.isSpecialKey()){
14988 this.lastKey = e.getKey();
14989 this.dqTask.delay(this.queryDelay);
14994 validateBlur : function(){
14995 return !this.list || !this.list.isVisible();
14999 initQuery : function(){
15001 var v = this.getRawValue();
15003 if(this.tickable && this.editable){
15004 v = this.tickableInputEl().getValue();
15011 doForce : function(){
15012 if(this.inputEl().dom.value.length > 0){
15013 this.inputEl().dom.value =
15014 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
15020 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
15021 * query allowing the query action to be canceled if needed.
15022 * @param {String} query The SQL query to execute
15023 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
15024 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
15025 * saved in the current store (defaults to false)
15027 doQuery : function(q, forceAll){
15029 if(q === undefined || q === null){
15034 forceAll: forceAll,
15038 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
15043 forceAll = qe.forceAll;
15044 if(forceAll === true || (q.length >= this.minChars)){
15046 this.hasQuery = true;
15048 if(this.lastQuery != q || this.alwaysQuery){
15049 this.lastQuery = q;
15050 if(this.mode == 'local'){
15051 this.selectedIndex = -1;
15053 this.store.clearFilter();
15056 if(this.specialFilter){
15057 this.fireEvent('specialfilter', this);
15062 this.store.filter(this.displayField, q);
15065 this.store.fireEvent("datachanged", this.store);
15072 this.store.baseParams[this.queryParam] = q;
15074 var options = {params : this.getParams(q)};
15077 options.add = true;
15078 options.params.start = this.page * this.pageSize;
15081 this.store.load(options);
15084 * this code will make the page width larger, at the beginning, the list not align correctly,
15085 * we should expand the list on onLoad
15086 * so command out it
15091 this.selectedIndex = -1;
15096 this.loadNext = false;
15100 getParams : function(q){
15102 //p[this.queryParam] = q;
15106 p.limit = this.pageSize;
15112 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
15114 collapse : function(){
15115 if(!this.isExpanded()){
15121 this.hasFocus = false;
15125 this.cancelBtn.hide();
15126 this.trigger.show();
15129 this.tickableInputEl().dom.value = '';
15130 this.tickableInputEl().blur();
15135 Roo.get(document).un('mousedown', this.collapseIf, this);
15136 Roo.get(document).un('mousewheel', this.collapseIf, this);
15137 if (!this.editable) {
15138 Roo.get(document).un('keydown', this.listKeyPress, this);
15140 this.fireEvent('collapse', this);
15146 collapseIf : function(e){
15147 var in_combo = e.within(this.el);
15148 var in_list = e.within(this.list);
15149 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
15151 if (in_combo || in_list || is_list) {
15152 //e.stopPropagation();
15157 this.onTickableFooterButtonClick(e, false, false);
15165 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
15167 expand : function(){
15169 if(this.isExpanded() || !this.hasFocus){
15173 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
15174 this.list.setWidth(lw);
15180 this.restrictHeight();
15184 this.tickItems = Roo.apply([], this.item);
15187 this.cancelBtn.show();
15188 this.trigger.hide();
15191 this.tickableInputEl().focus();
15196 Roo.get(document).on('mousedown', this.collapseIf, this);
15197 Roo.get(document).on('mousewheel', this.collapseIf, this);
15198 if (!this.editable) {
15199 Roo.get(document).on('keydown', this.listKeyPress, this);
15202 this.fireEvent('expand', this);
15206 // Implements the default empty TriggerField.onTriggerClick function
15207 onTriggerClick : function(e)
15209 Roo.log('trigger click');
15211 if(this.disabled || !this.triggerList){
15216 this.loadNext = false;
15218 if(this.isExpanded()){
15220 if (!this.blockFocus) {
15221 this.inputEl().focus();
15225 this.hasFocus = true;
15226 if(this.triggerAction == 'all') {
15227 this.doQuery(this.allQuery, true);
15229 this.doQuery(this.getRawValue());
15231 if (!this.blockFocus) {
15232 this.inputEl().focus();
15237 onTickableTriggerClick : function(e)
15244 this.loadNext = false;
15245 this.hasFocus = true;
15247 if(this.triggerAction == 'all') {
15248 this.doQuery(this.allQuery, true);
15250 this.doQuery(this.getRawValue());
15254 onSearchFieldClick : function(e)
15256 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15257 this.onTickableFooterButtonClick(e, false, false);
15261 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15266 this.loadNext = false;
15267 this.hasFocus = true;
15269 if(this.triggerAction == 'all') {
15270 this.doQuery(this.allQuery, true);
15272 this.doQuery(this.getRawValue());
15276 listKeyPress : function(e)
15278 //Roo.log('listkeypress');
15279 // scroll to first matching element based on key pres..
15280 if (e.isSpecialKey()) {
15283 var k = String.fromCharCode(e.getKey()).toUpperCase();
15286 var csel = this.view.getSelectedNodes();
15287 var cselitem = false;
15289 var ix = this.view.indexOf(csel[0]);
15290 cselitem = this.store.getAt(ix);
15291 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15297 this.store.each(function(v) {
15299 // start at existing selection.
15300 if (cselitem.id == v.id) {
15306 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15307 match = this.store.indexOf(v);
15313 if (match === false) {
15314 return true; // no more action?
15317 this.view.select(match);
15318 var sn = Roo.get(this.view.getSelectedNodes()[0]);
15319 sn.scrollIntoView(sn.dom.parentNode, false);
15322 onViewScroll : function(e, t){
15324 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){
15328 this.hasQuery = true;
15330 this.loading = this.list.select('.loading', true).first();
15332 if(this.loading === null){
15333 this.list.createChild({
15335 cls: 'loading roo-select2-more-results roo-select2-active',
15336 html: 'Loading more results...'
15339 this.loading = this.list.select('.loading', true).first();
15341 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15343 this.loading.hide();
15346 this.loading.show();
15351 this.loadNext = true;
15353 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15358 addItem : function(o)
15360 var dv = ''; // display value
15362 if (this.displayField) {
15363 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15365 // this is an error condition!!!
15366 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
15373 var choice = this.choices.createChild({
15375 cls: 'roo-select2-search-choice',
15384 cls: 'roo-select2-search-choice-close fa fa-times',
15389 }, this.searchField);
15391 var close = choice.select('a.roo-select2-search-choice-close', true).first();
15393 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15401 this.inputEl().dom.value = '';
15406 onRemoveItem : function(e, _self, o)
15408 e.preventDefault();
15410 this.lastItem = Roo.apply([], this.item);
15412 var index = this.item.indexOf(o.data) * 1;
15415 Roo.log('not this item?!');
15419 this.item.splice(index, 1);
15424 this.fireEvent('remove', this, e);
15430 syncValue : function()
15432 if(!this.item.length){
15439 Roo.each(this.item, function(i){
15440 if(_this.valueField){
15441 value.push(i[_this.valueField]);
15448 this.value = value.join(',');
15450 if(this.hiddenField){
15451 this.hiddenField.dom.value = this.value;
15454 this.store.fireEvent("datachanged", this.store);
15459 clearItem : function()
15461 if(!this.multiple){
15467 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15475 if(this.tickable && !Roo.isTouch){
15476 this.view.refresh();
15480 inputEl: function ()
15482 if(Roo.isIOS && this.useNativeIOS){
15483 return this.el.select('select.roo-ios-select', true).first();
15486 if(Roo.isTouch && this.mobileTouchView){
15487 return this.el.select('input.form-control',true).first();
15491 return this.searchField;
15494 return this.el.select('input.form-control',true).first();
15497 onTickableFooterButtonClick : function(e, btn, el)
15499 e.preventDefault();
15501 this.lastItem = Roo.apply([], this.item);
15503 if(btn && btn.name == 'cancel'){
15504 this.tickItems = Roo.apply([], this.item);
15513 Roo.each(this.tickItems, function(o){
15521 validate : function()
15523 if(this.getVisibilityEl().hasClass('hidden')){
15527 var v = this.getRawValue();
15530 v = this.getValue();
15533 if(this.disabled || this.allowBlank || v.length){
15538 this.markInvalid();
15542 tickableInputEl : function()
15544 if(!this.tickable || !this.editable){
15545 return this.inputEl();
15548 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15552 getAutoCreateTouchView : function()
15557 cls: 'form-group' //input-group
15563 type : this.inputType,
15564 cls : 'form-control x-combo-noedit',
15565 autocomplete: 'new-password',
15566 placeholder : this.placeholder || '',
15571 input.name = this.name;
15575 input.cls += ' input-' + this.size;
15578 if (this.disabled) {
15579 input.disabled = true;
15590 inputblock.cls += ' input-group';
15592 inputblock.cn.unshift({
15594 cls : 'input-group-addon input-group-prepend input-group-text',
15599 if(this.removable && !this.multiple){
15600 inputblock.cls += ' roo-removable';
15602 inputblock.cn.push({
15605 cls : 'roo-combo-removable-btn close'
15609 if(this.hasFeedback && !this.allowBlank){
15611 inputblock.cls += ' has-feedback';
15613 inputblock.cn.push({
15615 cls: 'glyphicon form-control-feedback'
15622 inputblock.cls += (this.before) ? '' : ' input-group';
15624 inputblock.cn.push({
15626 cls : 'input-group-addon input-group-append input-group-text',
15632 var ibwrap = inputblock;
15637 cls: 'roo-select2-choices',
15641 cls: 'roo-select2-search-field',
15654 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15659 cls: 'form-hidden-field'
15665 if(!this.multiple && this.showToggleBtn){
15671 if (this.caret != false) {
15674 cls: 'fa fa-' + this.caret
15681 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15683 Roo.bootstrap.version == 3 ? caret : '',
15686 cls: 'combobox-clear',
15700 combobox.cls += ' roo-select2-container-multi';
15703 var align = this.labelAlign || this.parentLabelAlign();
15705 if (align ==='left' && this.fieldLabel.length) {
15710 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15711 tooltip : 'This field is required'
15715 cls : 'control-label col-form-label',
15716 html : this.fieldLabel
15727 var labelCfg = cfg.cn[1];
15728 var contentCfg = cfg.cn[2];
15731 if(this.indicatorpos == 'right'){
15736 cls : 'control-label col-form-label',
15740 html : this.fieldLabel
15744 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15745 tooltip : 'This field is required'
15758 labelCfg = cfg.cn[0];
15759 contentCfg = cfg.cn[1];
15764 if(this.labelWidth > 12){
15765 labelCfg.style = "width: " + this.labelWidth + 'px';
15768 if(this.labelWidth < 13 && this.labelmd == 0){
15769 this.labelmd = this.labelWidth;
15772 if(this.labellg > 0){
15773 labelCfg.cls += ' col-lg-' + this.labellg;
15774 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15777 if(this.labelmd > 0){
15778 labelCfg.cls += ' col-md-' + this.labelmd;
15779 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15782 if(this.labelsm > 0){
15783 labelCfg.cls += ' col-sm-' + this.labelsm;
15784 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15787 if(this.labelxs > 0){
15788 labelCfg.cls += ' col-xs-' + this.labelxs;
15789 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15793 } else if ( this.fieldLabel.length) {
15797 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15798 tooltip : 'This field is required'
15802 cls : 'control-label',
15803 html : this.fieldLabel
15814 if(this.indicatorpos == 'right'){
15818 cls : 'control-label',
15819 html : this.fieldLabel,
15823 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15824 tooltip : 'This field is required'
15841 var settings = this;
15843 ['xs','sm','md','lg'].map(function(size){
15844 if (settings[size]) {
15845 cfg.cls += ' col-' + size + '-' + settings[size];
15852 initTouchView : function()
15854 this.renderTouchView();
15856 this.touchViewEl.on('scroll', function(){
15857 this.el.dom.scrollTop = 0;
15860 this.originalValue = this.getValue();
15862 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15864 this.inputEl().on("click", this.showTouchView, this);
15865 if (this.triggerEl) {
15866 this.triggerEl.on("click", this.showTouchView, this);
15870 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15871 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15873 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15875 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15876 this.store.on('load', this.onTouchViewLoad, this);
15877 this.store.on('loadexception', this.onTouchViewLoadException, this);
15879 if(this.hiddenName){
15881 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15883 this.hiddenField.dom.value =
15884 this.hiddenValue !== undefined ? this.hiddenValue :
15885 this.value !== undefined ? this.value : '';
15887 this.el.dom.removeAttribute('name');
15888 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15892 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15893 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15896 if(this.removable && !this.multiple){
15897 var close = this.closeTriggerEl();
15899 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15900 close.on('click', this.removeBtnClick, this, close);
15904 * fix the bug in Safari iOS8
15906 this.inputEl().on("focus", function(e){
15907 document.activeElement.blur();
15910 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15917 renderTouchView : function()
15919 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15920 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15922 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15923 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15925 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15926 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15927 this.touchViewBodyEl.setStyle('overflow', 'auto');
15929 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15930 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15932 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15933 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15937 showTouchView : function()
15943 this.touchViewHeaderEl.hide();
15945 if(this.modalTitle.length){
15946 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15947 this.touchViewHeaderEl.show();
15950 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15951 this.touchViewEl.show();
15953 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15955 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15956 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15958 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15960 if(this.modalTitle.length){
15961 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15964 this.touchViewBodyEl.setHeight(bodyHeight);
15968 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15970 this.touchViewEl.addClass('in');
15973 if(this._touchViewMask){
15974 Roo.get(document.body).addClass("x-body-masked");
15975 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15976 this._touchViewMask.setStyle('z-index', 10000);
15977 this._touchViewMask.addClass('show');
15980 this.doTouchViewQuery();
15984 hideTouchView : function()
15986 this.touchViewEl.removeClass('in');
15990 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15992 this.touchViewEl.setStyle('display', 'none');
15995 if(this._touchViewMask){
15996 this._touchViewMask.removeClass('show');
15997 Roo.get(document.body).removeClass("x-body-masked");
16001 setTouchViewValue : function()
16008 Roo.each(this.tickItems, function(o){
16013 this.hideTouchView();
16016 doTouchViewQuery : function()
16025 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
16029 if(!this.alwaysQuery || this.mode == 'local'){
16030 this.onTouchViewLoad();
16037 onTouchViewBeforeLoad : function(combo,opts)
16043 onTouchViewLoad : function()
16045 if(this.store.getCount() < 1){
16046 this.onTouchViewEmptyResults();
16050 this.clearTouchView();
16052 var rawValue = this.getRawValue();
16054 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
16056 this.tickItems = [];
16058 this.store.data.each(function(d, rowIndex){
16059 var row = this.touchViewListGroup.createChild(template);
16061 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
16062 row.addClass(d.data.cls);
16065 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16068 html : d.data[this.displayField]
16071 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
16072 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
16075 row.removeClass('selected');
16076 if(!this.multiple && this.valueField &&
16077 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
16080 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16081 row.addClass('selected');
16084 if(this.multiple && this.valueField &&
16085 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
16089 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16090 this.tickItems.push(d.data);
16093 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
16097 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
16099 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16101 if(this.modalTitle.length){
16102 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16105 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
16107 if(this.mobile_restrict_height && listHeight < bodyHeight){
16108 this.touchViewBodyEl.setHeight(listHeight);
16113 if(firstChecked && listHeight > bodyHeight){
16114 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
16119 onTouchViewLoadException : function()
16121 this.hideTouchView();
16124 onTouchViewEmptyResults : function()
16126 this.clearTouchView();
16128 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
16130 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
16134 clearTouchView : function()
16136 this.touchViewListGroup.dom.innerHTML = '';
16139 onTouchViewClick : function(e, el, o)
16141 e.preventDefault();
16144 var rowIndex = o.rowIndex;
16146 var r = this.store.getAt(rowIndex);
16148 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
16150 if(!this.multiple){
16151 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
16152 c.dom.removeAttribute('checked');
16155 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16157 this.setFromData(r.data);
16159 var close = this.closeTriggerEl();
16165 this.hideTouchView();
16167 this.fireEvent('select', this, r, rowIndex);
16172 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
16173 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
16174 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
16178 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16179 this.addItem(r.data);
16180 this.tickItems.push(r.data);
16184 getAutoCreateNativeIOS : function()
16187 cls: 'form-group' //input-group,
16192 cls : 'roo-ios-select'
16196 combobox.name = this.name;
16199 if (this.disabled) {
16200 combobox.disabled = true;
16203 var settings = this;
16205 ['xs','sm','md','lg'].map(function(size){
16206 if (settings[size]) {
16207 cfg.cls += ' col-' + size + '-' + settings[size];
16217 initIOSView : function()
16219 this.store.on('load', this.onIOSViewLoad, this);
16224 onIOSViewLoad : function()
16226 if(this.store.getCount() < 1){
16230 this.clearIOSView();
16232 if(this.allowBlank) {
16234 var default_text = '-- SELECT --';
16236 if(this.placeholder.length){
16237 default_text = this.placeholder;
16240 if(this.emptyTitle.length){
16241 default_text += ' - ' + this.emptyTitle + ' -';
16244 var opt = this.inputEl().createChild({
16247 html : default_text
16251 o[this.valueField] = 0;
16252 o[this.displayField] = default_text;
16254 this.ios_options.push({
16261 this.store.data.each(function(d, rowIndex){
16265 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16266 html = d.data[this.displayField];
16271 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16272 value = d.data[this.valueField];
16281 if(this.value == d.data[this.valueField]){
16282 option['selected'] = true;
16285 var opt = this.inputEl().createChild(option);
16287 this.ios_options.push({
16294 this.inputEl().on('change', function(){
16295 this.fireEvent('select', this);
16300 clearIOSView: function()
16302 this.inputEl().dom.innerHTML = '';
16304 this.ios_options = [];
16307 setIOSValue: function(v)
16311 if(!this.ios_options){
16315 Roo.each(this.ios_options, function(opts){
16317 opts.el.dom.removeAttribute('selected');
16319 if(opts.data[this.valueField] != v){
16323 opts.el.dom.setAttribute('selected', true);
16329 * @cfg {Boolean} grow
16333 * @cfg {Number} growMin
16337 * @cfg {Number} growMax
16346 Roo.apply(Roo.bootstrap.ComboBox, {
16350 cls: 'modal-header',
16372 cls: 'list-group-item',
16376 cls: 'roo-combobox-list-group-item-value'
16380 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16394 listItemCheckbox : {
16396 cls: 'list-group-item',
16400 cls: 'roo-combobox-list-group-item-value'
16404 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16420 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16425 cls: 'modal-footer',
16433 cls: 'col-xs-6 text-left',
16436 cls: 'btn btn-danger roo-touch-view-cancel',
16442 cls: 'col-xs-6 text-right',
16445 cls: 'btn btn-success roo-touch-view-ok',
16456 Roo.apply(Roo.bootstrap.ComboBox, {
16458 touchViewTemplate : {
16460 cls: 'modal fade roo-combobox-touch-view',
16464 cls: 'modal-dialog',
16465 style : 'position:fixed', // we have to fix position....
16469 cls: 'modal-content',
16471 Roo.bootstrap.ComboBox.header,
16472 Roo.bootstrap.ComboBox.body,
16473 Roo.bootstrap.ComboBox.footer
16482 * Ext JS Library 1.1.1
16483 * Copyright(c) 2006-2007, Ext JS, LLC.
16485 * Originally Released Under LGPL - original licence link has changed is not relivant.
16488 * <script type="text/javascript">
16493 * @extends Roo.util.Observable
16494 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16495 * This class also supports single and multi selection modes. <br>
16496 * Create a data model bound view:
16498 var store = new Roo.data.Store(...);
16500 var view = new Roo.View({
16502 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16504 singleSelect: true,
16505 selectedClass: "ydataview-selected",
16509 // listen for node click?
16510 view.on("click", function(vw, index, node, e){
16511 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16515 dataModel.load("foobar.xml");
16517 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16519 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16520 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16522 * Note: old style constructor is still suported (container, template, config)
16525 * Create a new View
16526 * @param {Object} config The config object
16529 Roo.View = function(config, depreciated_tpl, depreciated_config){
16531 this.parent = false;
16533 if (typeof(depreciated_tpl) == 'undefined') {
16534 // new way.. - universal constructor.
16535 Roo.apply(this, config);
16536 this.el = Roo.get(this.el);
16539 this.el = Roo.get(config);
16540 this.tpl = depreciated_tpl;
16541 Roo.apply(this, depreciated_config);
16543 this.wrapEl = this.el.wrap().wrap();
16544 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16547 if(typeof(this.tpl) == "string"){
16548 this.tpl = new Roo.Template(this.tpl);
16550 // support xtype ctors..
16551 this.tpl = new Roo.factory(this.tpl, Roo);
16555 this.tpl.compile();
16560 * @event beforeclick
16561 * Fires before a click is processed. Returns false to cancel the default action.
16562 * @param {Roo.View} this
16563 * @param {Number} index The index of the target node
16564 * @param {HTMLElement} node The target node
16565 * @param {Roo.EventObject} e The raw event object
16567 "beforeclick" : true,
16570 * Fires when a template node is clicked.
16571 * @param {Roo.View} this
16572 * @param {Number} index The index of the target node
16573 * @param {HTMLElement} node The target node
16574 * @param {Roo.EventObject} e The raw event object
16579 * Fires when a template node is double clicked.
16580 * @param {Roo.View} this
16581 * @param {Number} index The index of the target node
16582 * @param {HTMLElement} node The target node
16583 * @param {Roo.EventObject} e The raw event object
16587 * @event contextmenu
16588 * Fires when a template node is right clicked.
16589 * @param {Roo.View} this
16590 * @param {Number} index The index of the target node
16591 * @param {HTMLElement} node The target node
16592 * @param {Roo.EventObject} e The raw event object
16594 "contextmenu" : true,
16596 * @event selectionchange
16597 * Fires when the selected nodes change.
16598 * @param {Roo.View} this
16599 * @param {Array} selections Array of the selected nodes
16601 "selectionchange" : true,
16604 * @event beforeselect
16605 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16606 * @param {Roo.View} this
16607 * @param {HTMLElement} node The node to be selected
16608 * @param {Array} selections Array of currently selected nodes
16610 "beforeselect" : true,
16612 * @event preparedata
16613 * Fires on every row to render, to allow you to change the data.
16614 * @param {Roo.View} this
16615 * @param {Object} data to be rendered (change this)
16617 "preparedata" : true
16625 "click": this.onClick,
16626 "dblclick": this.onDblClick,
16627 "contextmenu": this.onContextMenu,
16631 this.selections = [];
16633 this.cmp = new Roo.CompositeElementLite([]);
16635 this.store = Roo.factory(this.store, Roo.data);
16636 this.setStore(this.store, true);
16639 if ( this.footer && this.footer.xtype) {
16641 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16643 this.footer.dataSource = this.store;
16644 this.footer.container = fctr;
16645 this.footer = Roo.factory(this.footer, Roo);
16646 fctr.insertFirst(this.el);
16648 // this is a bit insane - as the paging toolbar seems to detach the el..
16649 // dom.parentNode.parentNode.parentNode
16650 // they get detached?
16654 Roo.View.superclass.constructor.call(this);
16659 Roo.extend(Roo.View, Roo.util.Observable, {
16662 * @cfg {Roo.data.Store} store Data store to load data from.
16667 * @cfg {String|Roo.Element} el The container element.
16672 * @cfg {String|Roo.Template} tpl The template used by this View
16676 * @cfg {String} dataName the named area of the template to use as the data area
16677 * Works with domtemplates roo-name="name"
16681 * @cfg {String} selectedClass The css class to add to selected nodes
16683 selectedClass : "x-view-selected",
16685 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16690 * @cfg {String} text to display on mask (default Loading)
16694 * @cfg {Boolean} multiSelect Allow multiple selection
16696 multiSelect : false,
16698 * @cfg {Boolean} singleSelect Allow single selection
16700 singleSelect: false,
16703 * @cfg {Boolean} toggleSelect - selecting
16705 toggleSelect : false,
16708 * @cfg {Boolean} tickable - selecting
16713 * Returns the element this view is bound to.
16714 * @return {Roo.Element}
16716 getEl : function(){
16717 return this.wrapEl;
16723 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16725 refresh : function(){
16726 //Roo.log('refresh');
16729 // if we are using something like 'domtemplate', then
16730 // the what gets used is:
16731 // t.applySubtemplate(NAME, data, wrapping data..)
16732 // the outer template then get' applied with
16733 // the store 'extra data'
16734 // and the body get's added to the
16735 // roo-name="data" node?
16736 // <span class='roo-tpl-{name}'></span> ?????
16740 this.clearSelections();
16741 this.el.update("");
16743 var records = this.store.getRange();
16744 if(records.length < 1) {
16746 // is this valid?? = should it render a template??
16748 this.el.update(this.emptyText);
16752 if (this.dataName) {
16753 this.el.update(t.apply(this.store.meta)); //????
16754 el = this.el.child('.roo-tpl-' + this.dataName);
16757 for(var i = 0, len = records.length; i < len; i++){
16758 var data = this.prepareData(records[i].data, i, records[i]);
16759 this.fireEvent("preparedata", this, data, i, records[i]);
16761 var d = Roo.apply({}, data);
16764 Roo.apply(d, {'roo-id' : Roo.id()});
16768 Roo.each(this.parent.item, function(item){
16769 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16772 Roo.apply(d, {'roo-data-checked' : 'checked'});
16776 html[html.length] = Roo.util.Format.trim(
16778 t.applySubtemplate(this.dataName, d, this.store.meta) :
16785 el.update(html.join(""));
16786 this.nodes = el.dom.childNodes;
16787 this.updateIndexes(0);
16792 * Function to override to reformat the data that is sent to
16793 * the template for each node.
16794 * DEPRICATED - use the preparedata event handler.
16795 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16796 * a JSON object for an UpdateManager bound view).
16798 prepareData : function(data, index, record)
16800 this.fireEvent("preparedata", this, data, index, record);
16804 onUpdate : function(ds, record){
16805 // Roo.log('on update');
16806 this.clearSelections();
16807 var index = this.store.indexOf(record);
16808 var n = this.nodes[index];
16809 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16810 n.parentNode.removeChild(n);
16811 this.updateIndexes(index, index);
16817 onAdd : function(ds, records, index)
16819 //Roo.log(['on Add', ds, records, index] );
16820 this.clearSelections();
16821 if(this.nodes.length == 0){
16825 var n = this.nodes[index];
16826 for(var i = 0, len = records.length; i < len; i++){
16827 var d = this.prepareData(records[i].data, i, records[i]);
16829 this.tpl.insertBefore(n, d);
16832 this.tpl.append(this.el, d);
16835 this.updateIndexes(index);
16838 onRemove : function(ds, record, index){
16839 // Roo.log('onRemove');
16840 this.clearSelections();
16841 var el = this.dataName ?
16842 this.el.child('.roo-tpl-' + this.dataName) :
16845 el.dom.removeChild(this.nodes[index]);
16846 this.updateIndexes(index);
16850 * Refresh an individual node.
16851 * @param {Number} index
16853 refreshNode : function(index){
16854 this.onUpdate(this.store, this.store.getAt(index));
16857 updateIndexes : function(startIndex, endIndex){
16858 var ns = this.nodes;
16859 startIndex = startIndex || 0;
16860 endIndex = endIndex || ns.length - 1;
16861 for(var i = startIndex; i <= endIndex; i++){
16862 ns[i].nodeIndex = i;
16867 * Changes the data store this view uses and refresh the view.
16868 * @param {Store} store
16870 setStore : function(store, initial){
16871 if(!initial && this.store){
16872 this.store.un("datachanged", this.refresh);
16873 this.store.un("add", this.onAdd);
16874 this.store.un("remove", this.onRemove);
16875 this.store.un("update", this.onUpdate);
16876 this.store.un("clear", this.refresh);
16877 this.store.un("beforeload", this.onBeforeLoad);
16878 this.store.un("load", this.onLoad);
16879 this.store.un("loadexception", this.onLoad);
16883 store.on("datachanged", this.refresh, this);
16884 store.on("add", this.onAdd, this);
16885 store.on("remove", this.onRemove, this);
16886 store.on("update", this.onUpdate, this);
16887 store.on("clear", this.refresh, this);
16888 store.on("beforeload", this.onBeforeLoad, this);
16889 store.on("load", this.onLoad, this);
16890 store.on("loadexception", this.onLoad, this);
16898 * onbeforeLoad - masks the loading area.
16901 onBeforeLoad : function(store,opts)
16903 //Roo.log('onBeforeLoad');
16905 this.el.update("");
16907 this.el.mask(this.mask ? this.mask : "Loading" );
16909 onLoad : function ()
16916 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16917 * @param {HTMLElement} node
16918 * @return {HTMLElement} The template node
16920 findItemFromChild : function(node){
16921 var el = this.dataName ?
16922 this.el.child('.roo-tpl-' + this.dataName,true) :
16925 if(!node || node.parentNode == el){
16928 var p = node.parentNode;
16929 while(p && p != el){
16930 if(p.parentNode == el){
16939 onClick : function(e){
16940 var item = this.findItemFromChild(e.getTarget());
16942 var index = this.indexOf(item);
16943 if(this.onItemClick(item, index, e) !== false){
16944 this.fireEvent("click", this, index, item, e);
16947 this.clearSelections();
16952 onContextMenu : function(e){
16953 var item = this.findItemFromChild(e.getTarget());
16955 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16960 onDblClick : function(e){
16961 var item = this.findItemFromChild(e.getTarget());
16963 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16967 onItemClick : function(item, index, e)
16969 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16972 if (this.toggleSelect) {
16973 var m = this.isSelected(item) ? 'unselect' : 'select';
16976 _t[m](item, true, false);
16979 if(this.multiSelect || this.singleSelect){
16980 if(this.multiSelect && e.shiftKey && this.lastSelection){
16981 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16983 this.select(item, this.multiSelect && e.ctrlKey);
16984 this.lastSelection = item;
16987 if(!this.tickable){
16988 e.preventDefault();
16996 * Get the number of selected nodes.
16999 getSelectionCount : function(){
17000 return this.selections.length;
17004 * Get the currently selected nodes.
17005 * @return {Array} An array of HTMLElements
17007 getSelectedNodes : function(){
17008 return this.selections;
17012 * Get the indexes of the selected nodes.
17015 getSelectedIndexes : function(){
17016 var indexes = [], s = this.selections;
17017 for(var i = 0, len = s.length; i < len; i++){
17018 indexes.push(s[i].nodeIndex);
17024 * Clear all selections
17025 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
17027 clearSelections : function(suppressEvent){
17028 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
17029 this.cmp.elements = this.selections;
17030 this.cmp.removeClass(this.selectedClass);
17031 this.selections = [];
17032 if(!suppressEvent){
17033 this.fireEvent("selectionchange", this, this.selections);
17039 * Returns true if the passed node is selected
17040 * @param {HTMLElement/Number} node The node or node index
17041 * @return {Boolean}
17043 isSelected : function(node){
17044 var s = this.selections;
17048 node = this.getNode(node);
17049 return s.indexOf(node) !== -1;
17054 * @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
17055 * @param {Boolean} keepExisting (optional) true to keep existing selections
17056 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17058 select : function(nodeInfo, keepExisting, suppressEvent){
17059 if(nodeInfo instanceof Array){
17061 this.clearSelections(true);
17063 for(var i = 0, len = nodeInfo.length; i < len; i++){
17064 this.select(nodeInfo[i], true, true);
17068 var node = this.getNode(nodeInfo);
17069 if(!node || this.isSelected(node)){
17070 return; // already selected.
17073 this.clearSelections(true);
17076 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
17077 Roo.fly(node).addClass(this.selectedClass);
17078 this.selections.push(node);
17079 if(!suppressEvent){
17080 this.fireEvent("selectionchange", this, this.selections);
17088 * @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
17089 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
17090 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17092 unselect : function(nodeInfo, keepExisting, suppressEvent)
17094 if(nodeInfo instanceof Array){
17095 Roo.each(this.selections, function(s) {
17096 this.unselect(s, nodeInfo);
17100 var node = this.getNode(nodeInfo);
17101 if(!node || !this.isSelected(node)){
17102 //Roo.log("not selected");
17103 return; // not selected.
17107 Roo.each(this.selections, function(s) {
17109 Roo.fly(node).removeClass(this.selectedClass);
17116 this.selections= ns;
17117 this.fireEvent("selectionchange", this, this.selections);
17121 * Gets a template node.
17122 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17123 * @return {HTMLElement} The node or null if it wasn't found
17125 getNode : function(nodeInfo){
17126 if(typeof nodeInfo == "string"){
17127 return document.getElementById(nodeInfo);
17128 }else if(typeof nodeInfo == "number"){
17129 return this.nodes[nodeInfo];
17135 * Gets a range template nodes.
17136 * @param {Number} startIndex
17137 * @param {Number} endIndex
17138 * @return {Array} An array of nodes
17140 getNodes : function(start, end){
17141 var ns = this.nodes;
17142 start = start || 0;
17143 end = typeof end == "undefined" ? ns.length - 1 : end;
17146 for(var i = start; i <= end; i++){
17150 for(var i = start; i >= end; i--){
17158 * Finds the index of the passed node
17159 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17160 * @return {Number} The index of the node or -1
17162 indexOf : function(node){
17163 node = this.getNode(node);
17164 if(typeof node.nodeIndex == "number"){
17165 return node.nodeIndex;
17167 var ns = this.nodes;
17168 for(var i = 0, len = ns.length; i < len; i++){
17179 * based on jquery fullcalendar
17183 Roo.bootstrap = Roo.bootstrap || {};
17185 * @class Roo.bootstrap.Calendar
17186 * @extends Roo.bootstrap.Component
17187 * Bootstrap Calendar class
17188 * @cfg {Boolean} loadMask (true|false) default false
17189 * @cfg {Object} header generate the user specific header of the calendar, default false
17192 * Create a new Container
17193 * @param {Object} config The config object
17198 Roo.bootstrap.Calendar = function(config){
17199 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
17203 * Fires when a date is selected
17204 * @param {DatePicker} this
17205 * @param {Date} date The selected date
17209 * @event monthchange
17210 * Fires when the displayed month changes
17211 * @param {DatePicker} this
17212 * @param {Date} date The selected month
17214 'monthchange': true,
17216 * @event evententer
17217 * Fires when mouse over an event
17218 * @param {Calendar} this
17219 * @param {event} Event
17221 'evententer': true,
17223 * @event eventleave
17224 * Fires when the mouse leaves an
17225 * @param {Calendar} this
17228 'eventleave': true,
17230 * @event eventclick
17231 * Fires when the mouse click an
17232 * @param {Calendar} this
17241 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
17244 * @cfg {Number} startDay
17245 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17253 getAutoCreate : function(){
17256 var fc_button = function(name, corner, style, content ) {
17257 return Roo.apply({},{
17259 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
17261 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17264 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17275 style : 'width:100%',
17282 cls : 'fc-header-left',
17284 fc_button('prev', 'left', 'arrow', '‹' ),
17285 fc_button('next', 'right', 'arrow', '›' ),
17286 { tag: 'span', cls: 'fc-header-space' },
17287 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
17295 cls : 'fc-header-center',
17299 cls: 'fc-header-title',
17302 html : 'month / year'
17310 cls : 'fc-header-right',
17312 /* fc_button('month', 'left', '', 'month' ),
17313 fc_button('week', '', '', 'week' ),
17314 fc_button('day', 'right', '', 'day' )
17326 header = this.header;
17329 var cal_heads = function() {
17331 // fixme - handle this.
17333 for (var i =0; i < Date.dayNames.length; i++) {
17334 var d = Date.dayNames[i];
17337 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17338 html : d.substring(0,3)
17342 ret[0].cls += ' fc-first';
17343 ret[6].cls += ' fc-last';
17346 var cal_cell = function(n) {
17349 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17354 cls: 'fc-day-number',
17358 cls: 'fc-day-content',
17362 style: 'position: relative;' // height: 17px;
17374 var cal_rows = function() {
17377 for (var r = 0; r < 6; r++) {
17384 for (var i =0; i < Date.dayNames.length; i++) {
17385 var d = Date.dayNames[i];
17386 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17389 row.cn[0].cls+=' fc-first';
17390 row.cn[0].cn[0].style = 'min-height:90px';
17391 row.cn[6].cls+=' fc-last';
17395 ret[0].cls += ' fc-first';
17396 ret[4].cls += ' fc-prev-last';
17397 ret[5].cls += ' fc-last';
17404 cls: 'fc-border-separate',
17405 style : 'width:100%',
17413 cls : 'fc-first fc-last',
17431 cls : 'fc-content',
17432 style : "position: relative;",
17435 cls : 'fc-view fc-view-month fc-grid',
17436 style : 'position: relative',
17437 unselectable : 'on',
17440 cls : 'fc-event-container',
17441 style : 'position:absolute;z-index:8;top:0;left:0;'
17459 initEvents : function()
17462 throw "can not find store for calendar";
17468 style: "text-align:center",
17472 style: "background-color:white;width:50%;margin:250 auto",
17476 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17487 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17489 var size = this.el.select('.fc-content', true).first().getSize();
17490 this.maskEl.setSize(size.width, size.height);
17491 this.maskEl.enableDisplayMode("block");
17492 if(!this.loadMask){
17493 this.maskEl.hide();
17496 this.store = Roo.factory(this.store, Roo.data);
17497 this.store.on('load', this.onLoad, this);
17498 this.store.on('beforeload', this.onBeforeLoad, this);
17502 this.cells = this.el.select('.fc-day',true);
17503 //Roo.log(this.cells);
17504 this.textNodes = this.el.query('.fc-day-number');
17505 this.cells.addClassOnOver('fc-state-hover');
17507 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17508 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17509 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17510 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17512 this.on('monthchange', this.onMonthChange, this);
17514 this.update(new Date().clearTime());
17517 resize : function() {
17518 var sz = this.el.getSize();
17520 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17521 this.el.select('.fc-day-content div',true).setHeight(34);
17526 showPrevMonth : function(e){
17527 this.update(this.activeDate.add("mo", -1));
17529 showToday : function(e){
17530 this.update(new Date().clearTime());
17533 showNextMonth : function(e){
17534 this.update(this.activeDate.add("mo", 1));
17538 showPrevYear : function(){
17539 this.update(this.activeDate.add("y", -1));
17543 showNextYear : function(){
17544 this.update(this.activeDate.add("y", 1));
17549 update : function(date)
17551 var vd = this.activeDate;
17552 this.activeDate = date;
17553 // if(vd && this.el){
17554 // var t = date.getTime();
17555 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17556 // Roo.log('using add remove');
17558 // this.fireEvent('monthchange', this, date);
17560 // this.cells.removeClass("fc-state-highlight");
17561 // this.cells.each(function(c){
17562 // if(c.dateValue == t){
17563 // c.addClass("fc-state-highlight");
17564 // setTimeout(function(){
17565 // try{c.dom.firstChild.focus();}catch(e){}
17575 var days = date.getDaysInMonth();
17577 var firstOfMonth = date.getFirstDateOfMonth();
17578 var startingPos = firstOfMonth.getDay()-this.startDay;
17580 if(startingPos < this.startDay){
17584 var pm = date.add(Date.MONTH, -1);
17585 var prevStart = pm.getDaysInMonth()-startingPos;
17587 this.cells = this.el.select('.fc-day',true);
17588 this.textNodes = this.el.query('.fc-day-number');
17589 this.cells.addClassOnOver('fc-state-hover');
17591 var cells = this.cells.elements;
17592 var textEls = this.textNodes;
17594 Roo.each(cells, function(cell){
17595 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17598 days += startingPos;
17600 // convert everything to numbers so it's fast
17601 var day = 86400000;
17602 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17605 //Roo.log(prevStart);
17607 var today = new Date().clearTime().getTime();
17608 var sel = date.clearTime().getTime();
17609 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17610 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17611 var ddMatch = this.disabledDatesRE;
17612 var ddText = this.disabledDatesText;
17613 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17614 var ddaysText = this.disabledDaysText;
17615 var format = this.format;
17617 var setCellClass = function(cal, cell){
17621 //Roo.log('set Cell Class');
17623 var t = d.getTime();
17627 cell.dateValue = t;
17629 cell.className += " fc-today";
17630 cell.className += " fc-state-highlight";
17631 cell.title = cal.todayText;
17634 // disable highlight in other month..
17635 //cell.className += " fc-state-highlight";
17640 cell.className = " fc-state-disabled";
17641 cell.title = cal.minText;
17645 cell.className = " fc-state-disabled";
17646 cell.title = cal.maxText;
17650 if(ddays.indexOf(d.getDay()) != -1){
17651 cell.title = ddaysText;
17652 cell.className = " fc-state-disabled";
17655 if(ddMatch && format){
17656 var fvalue = d.dateFormat(format);
17657 if(ddMatch.test(fvalue)){
17658 cell.title = ddText.replace("%0", fvalue);
17659 cell.className = " fc-state-disabled";
17663 if (!cell.initialClassName) {
17664 cell.initialClassName = cell.dom.className;
17667 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17672 for(; i < startingPos; i++) {
17673 textEls[i].innerHTML = (++prevStart);
17674 d.setDate(d.getDate()+1);
17676 cells[i].className = "fc-past fc-other-month";
17677 setCellClass(this, cells[i]);
17682 for(; i < days; i++){
17683 intDay = i - startingPos + 1;
17684 textEls[i].innerHTML = (intDay);
17685 d.setDate(d.getDate()+1);
17687 cells[i].className = ''; // "x-date-active";
17688 setCellClass(this, cells[i]);
17692 for(; i < 42; i++) {
17693 textEls[i].innerHTML = (++extraDays);
17694 d.setDate(d.getDate()+1);
17696 cells[i].className = "fc-future fc-other-month";
17697 setCellClass(this, cells[i]);
17700 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17702 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17704 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17705 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17707 if(totalRows != 6){
17708 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17709 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17712 this.fireEvent('monthchange', this, date);
17716 if(!this.internalRender){
17717 var main = this.el.dom.firstChild;
17718 var w = main.offsetWidth;
17719 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17720 Roo.fly(main).setWidth(w);
17721 this.internalRender = true;
17722 // opera does not respect the auto grow header center column
17723 // then, after it gets a width opera refuses to recalculate
17724 // without a second pass
17725 if(Roo.isOpera && !this.secondPass){
17726 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17727 this.secondPass = true;
17728 this.update.defer(10, this, [date]);
17735 findCell : function(dt) {
17736 dt = dt.clearTime().getTime();
17738 this.cells.each(function(c){
17739 //Roo.log("check " +c.dateValue + '?=' + dt);
17740 if(c.dateValue == dt){
17750 findCells : function(ev) {
17751 var s = ev.start.clone().clearTime().getTime();
17753 var e= ev.end.clone().clearTime().getTime();
17756 this.cells.each(function(c){
17757 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17759 if(c.dateValue > e){
17762 if(c.dateValue < s){
17771 // findBestRow: function(cells)
17775 // for (var i =0 ; i < cells.length;i++) {
17776 // ret = Math.max(cells[i].rows || 0,ret);
17783 addItem : function(ev)
17785 // look for vertical location slot in
17786 var cells = this.findCells(ev);
17788 // ev.row = this.findBestRow(cells);
17790 // work out the location.
17794 for(var i =0; i < cells.length; i++) {
17796 cells[i].row = cells[0].row;
17799 cells[i].row = cells[i].row + 1;
17809 if (crow.start.getY() == cells[i].getY()) {
17811 crow.end = cells[i];
17828 cells[0].events.push(ev);
17830 this.calevents.push(ev);
17833 clearEvents: function() {
17835 if(!this.calevents){
17839 Roo.each(this.cells.elements, function(c){
17845 Roo.each(this.calevents, function(e) {
17846 Roo.each(e.els, function(el) {
17847 el.un('mouseenter' ,this.onEventEnter, this);
17848 el.un('mouseleave' ,this.onEventLeave, this);
17853 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17859 renderEvents: function()
17863 this.cells.each(function(c) {
17872 if(c.row != c.events.length){
17873 r = 4 - (4 - (c.row - c.events.length));
17876 c.events = ev.slice(0, r);
17877 c.more = ev.slice(r);
17879 if(c.more.length && c.more.length == 1){
17880 c.events.push(c.more.pop());
17883 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17887 this.cells.each(function(c) {
17889 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17892 for (var e = 0; e < c.events.length; e++){
17893 var ev = c.events[e];
17894 var rows = ev.rows;
17896 for(var i = 0; i < rows.length; i++) {
17898 // how many rows should it span..
17901 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17902 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17904 unselectable : "on",
17907 cls: 'fc-event-inner',
17911 // cls: 'fc-event-time',
17912 // html : cells.length > 1 ? '' : ev.time
17916 cls: 'fc-event-title',
17917 html : String.format('{0}', ev.title)
17924 cls: 'ui-resizable-handle ui-resizable-e',
17925 html : '  '
17932 cfg.cls += ' fc-event-start';
17934 if ((i+1) == rows.length) {
17935 cfg.cls += ' fc-event-end';
17938 var ctr = _this.el.select('.fc-event-container',true).first();
17939 var cg = ctr.createChild(cfg);
17941 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17942 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17944 var r = (c.more.length) ? 1 : 0;
17945 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17946 cg.setWidth(ebox.right - sbox.x -2);
17948 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17949 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17950 cg.on('click', _this.onEventClick, _this, ev);
17961 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17962 style : 'position: absolute',
17963 unselectable : "on",
17966 cls: 'fc-event-inner',
17970 cls: 'fc-event-title',
17978 cls: 'ui-resizable-handle ui-resizable-e',
17979 html : '  '
17985 var ctr = _this.el.select('.fc-event-container',true).first();
17986 var cg = ctr.createChild(cfg);
17988 var sbox = c.select('.fc-day-content',true).first().getBox();
17989 var ebox = c.select('.fc-day-content',true).first().getBox();
17991 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17992 cg.setWidth(ebox.right - sbox.x -2);
17994 cg.on('click', _this.onMoreEventClick, _this, c.more);
18004 onEventEnter: function (e, el,event,d) {
18005 this.fireEvent('evententer', this, el, event);
18008 onEventLeave: function (e, el,event,d) {
18009 this.fireEvent('eventleave', this, el, event);
18012 onEventClick: function (e, el,event,d) {
18013 this.fireEvent('eventclick', this, el, event);
18016 onMonthChange: function () {
18020 onMoreEventClick: function(e, el, more)
18024 this.calpopover.placement = 'right';
18025 this.calpopover.setTitle('More');
18027 this.calpopover.setContent('');
18029 var ctr = this.calpopover.el.select('.popover-content', true).first();
18031 Roo.each(more, function(m){
18033 cls : 'fc-event-hori fc-event-draggable',
18036 var cg = ctr.createChild(cfg);
18038 cg.on('click', _this.onEventClick, _this, m);
18041 this.calpopover.show(el);
18046 onLoad: function ()
18048 this.calevents = [];
18051 if(this.store.getCount() > 0){
18052 this.store.data.each(function(d){
18055 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
18056 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
18057 time : d.data.start_time,
18058 title : d.data.title,
18059 description : d.data.description,
18060 venue : d.data.venue
18065 this.renderEvents();
18067 if(this.calevents.length && this.loadMask){
18068 this.maskEl.hide();
18072 onBeforeLoad: function()
18074 this.clearEvents();
18076 this.maskEl.show();
18090 * @class Roo.bootstrap.Popover
18091 * @extends Roo.bootstrap.Component
18092 * Bootstrap Popover class
18093 * @cfg {String} html contents of the popover (or false to use children..)
18094 * @cfg {String} title of popover (or false to hide)
18095 * @cfg {String} placement how it is placed
18096 * @cfg {String} trigger click || hover (or false to trigger manually)
18097 * @cfg {String} over what (parent or false to trigger manually.)
18098 * @cfg {Number} delay - delay before showing
18101 * Create a new Popover
18102 * @param {Object} config The config object
18105 Roo.bootstrap.Popover = function(config){
18106 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
18112 * After the popover show
18114 * @param {Roo.bootstrap.Popover} this
18119 * After the popover hide
18121 * @param {Roo.bootstrap.Popover} this
18127 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
18129 title: 'Fill in a title',
18132 placement : 'right',
18133 trigger : 'hover', // hover
18139 can_build_overlaid : false,
18141 getChildContainer : function()
18143 return this.el.select('.popover-content',true).first();
18146 getAutoCreate : function(){
18149 cls : 'popover roo-dynamic',
18150 style: 'display:block',
18156 cls : 'popover-inner',
18160 cls: 'popover-title popover-header',
18164 cls : 'popover-content popover-body',
18175 setTitle: function(str)
18178 this.el.select('.popover-title',true).first().dom.innerHTML = str;
18180 setContent: function(str)
18183 this.el.select('.popover-content',true).first().dom.innerHTML = str;
18185 // as it get's added to the bottom of the page.
18186 onRender : function(ct, position)
18188 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18190 var cfg = Roo.apply({}, this.getAutoCreate());
18194 cfg.cls += ' ' + this.cls;
18197 cfg.style = this.style;
18199 //Roo.log("adding to ");
18200 this.el = Roo.get(document.body).createChild(cfg, position);
18201 // Roo.log(this.el);
18206 initEvents : function()
18208 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
18209 this.el.enableDisplayMode('block');
18211 if (this.over === false) {
18214 if (this.triggers === false) {
18217 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18218 var triggers = this.trigger ? this.trigger.split(' ') : [];
18219 Roo.each(triggers, function(trigger) {
18221 if (trigger == 'click') {
18222 on_el.on('click', this.toggle, this);
18223 } else if (trigger != 'manual') {
18224 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
18225 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
18227 on_el.on(eventIn ,this.enter, this);
18228 on_el.on(eventOut, this.leave, this);
18239 toggle : function () {
18240 this.hoverState == 'in' ? this.leave() : this.enter();
18243 enter : function () {
18245 clearTimeout(this.timeout);
18247 this.hoverState = 'in';
18249 if (!this.delay || !this.delay.show) {
18254 this.timeout = setTimeout(function () {
18255 if (_t.hoverState == 'in') {
18258 }, this.delay.show)
18261 leave : function() {
18262 clearTimeout(this.timeout);
18264 this.hoverState = 'out';
18266 if (!this.delay || !this.delay.hide) {
18271 this.timeout = setTimeout(function () {
18272 if (_t.hoverState == 'out') {
18275 }, this.delay.hide)
18278 show : function (on_el)
18281 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18285 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18286 if (this.html !== false) {
18287 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18289 this.el.removeClass([
18290 'fade','top','bottom', 'left', 'right','in',
18291 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18293 if (!this.title.length) {
18294 this.el.select('.popover-title',true).hide();
18297 var placement = typeof this.placement == 'function' ?
18298 this.placement.call(this, this.el, on_el) :
18301 var autoToken = /\s?auto?\s?/i;
18302 var autoPlace = autoToken.test(placement);
18304 placement = placement.replace(autoToken, '') || 'top';
18308 //this.el.setXY([0,0]);
18310 this.el.dom.style.display='block';
18311 this.el.addClass(placement);
18313 //this.el.appendTo(on_el);
18315 var p = this.getPosition();
18316 var box = this.el.getBox();
18321 var align = Roo.bootstrap.Popover.alignment[placement];
18324 this.el.alignTo(on_el, align[0],align[1]);
18325 //var arrow = this.el.select('.arrow',true).first();
18326 //arrow.set(align[2],
18328 this.el.addClass('in');
18331 if (this.el.hasClass('fade')) {
18335 this.hoverState = 'in';
18337 this.fireEvent('show', this);
18342 this.el.setXY([0,0]);
18343 this.el.removeClass('in');
18345 this.hoverState = null;
18347 this.fireEvent('hide', this);
18352 Roo.bootstrap.Popover.alignment = {
18353 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18354 'right' : ['l-r', [10,0], 'left bs-popover-left'],
18355 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18356 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18367 * @class Roo.bootstrap.Progress
18368 * @extends Roo.bootstrap.Component
18369 * Bootstrap Progress class
18370 * @cfg {Boolean} striped striped of the progress bar
18371 * @cfg {Boolean} active animated of the progress bar
18375 * Create a new Progress
18376 * @param {Object} config The config object
18379 Roo.bootstrap.Progress = function(config){
18380 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18383 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
18388 getAutoCreate : function(){
18396 cfg.cls += ' progress-striped';
18400 cfg.cls += ' active';
18419 * @class Roo.bootstrap.ProgressBar
18420 * @extends Roo.bootstrap.Component
18421 * Bootstrap ProgressBar class
18422 * @cfg {Number} aria_valuenow aria-value now
18423 * @cfg {Number} aria_valuemin aria-value min
18424 * @cfg {Number} aria_valuemax aria-value max
18425 * @cfg {String} label label for the progress bar
18426 * @cfg {String} panel (success | info | warning | danger )
18427 * @cfg {String} role role of the progress bar
18428 * @cfg {String} sr_only text
18432 * Create a new ProgressBar
18433 * @param {Object} config The config object
18436 Roo.bootstrap.ProgressBar = function(config){
18437 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18440 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
18444 aria_valuemax : 100,
18450 getAutoCreate : function()
18455 cls: 'progress-bar',
18456 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18468 cfg.role = this.role;
18471 if(this.aria_valuenow){
18472 cfg['aria-valuenow'] = this.aria_valuenow;
18475 if(this.aria_valuemin){
18476 cfg['aria-valuemin'] = this.aria_valuemin;
18479 if(this.aria_valuemax){
18480 cfg['aria-valuemax'] = this.aria_valuemax;
18483 if(this.label && !this.sr_only){
18484 cfg.html = this.label;
18488 cfg.cls += ' progress-bar-' + this.panel;
18494 update : function(aria_valuenow)
18496 this.aria_valuenow = aria_valuenow;
18498 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18513 * @class Roo.bootstrap.TabGroup
18514 * @extends Roo.bootstrap.Column
18515 * Bootstrap Column class
18516 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18517 * @cfg {Boolean} carousel true to make the group behave like a carousel
18518 * @cfg {Boolean} bullets show bullets for the panels
18519 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18520 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18521 * @cfg {Boolean} showarrow (true|false) show arrow default true
18524 * Create a new TabGroup
18525 * @param {Object} config The config object
18528 Roo.bootstrap.TabGroup = function(config){
18529 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18531 this.navId = Roo.id();
18534 Roo.bootstrap.TabGroup.register(this);
18538 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18541 transition : false,
18546 slideOnTouch : false,
18549 getAutoCreate : function()
18551 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18553 cfg.cls += ' tab-content';
18555 if (this.carousel) {
18556 cfg.cls += ' carousel slide';
18559 cls : 'carousel-inner',
18563 if(this.bullets && !Roo.isTouch){
18566 cls : 'carousel-bullets',
18570 if(this.bullets_cls){
18571 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18578 cfg.cn[0].cn.push(bullets);
18581 if(this.showarrow){
18582 cfg.cn[0].cn.push({
18584 class : 'carousel-arrow',
18588 class : 'carousel-prev',
18592 class : 'fa fa-chevron-left'
18598 class : 'carousel-next',
18602 class : 'fa fa-chevron-right'
18615 initEvents: function()
18617 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18618 // this.el.on("touchstart", this.onTouchStart, this);
18621 if(this.autoslide){
18624 this.slideFn = window.setInterval(function() {
18625 _this.showPanelNext();
18629 if(this.showarrow){
18630 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18631 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18637 // onTouchStart : function(e, el, o)
18639 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18643 // this.showPanelNext();
18647 getChildContainer : function()
18649 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18653 * register a Navigation item
18654 * @param {Roo.bootstrap.NavItem} the navitem to add
18656 register : function(item)
18658 this.tabs.push( item);
18659 item.navId = this.navId; // not really needed..
18664 getActivePanel : function()
18667 Roo.each(this.tabs, function(t) {
18677 getPanelByName : function(n)
18680 Roo.each(this.tabs, function(t) {
18681 if (t.tabId == n) {
18689 indexOfPanel : function(p)
18692 Roo.each(this.tabs, function(t,i) {
18693 if (t.tabId == p.tabId) {
18702 * show a specific panel
18703 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18704 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18706 showPanel : function (pan)
18708 if(this.transition || typeof(pan) == 'undefined'){
18709 Roo.log("waiting for the transitionend");
18713 if (typeof(pan) == 'number') {
18714 pan = this.tabs[pan];
18717 if (typeof(pan) == 'string') {
18718 pan = this.getPanelByName(pan);
18721 var cur = this.getActivePanel();
18724 Roo.log('pan or acitve pan is undefined');
18728 if (pan.tabId == this.getActivePanel().tabId) {
18732 if (false === cur.fireEvent('beforedeactivate')) {
18736 if(this.bullets > 0 && !Roo.isTouch){
18737 this.setActiveBullet(this.indexOfPanel(pan));
18740 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18742 //class="carousel-item carousel-item-next carousel-item-left"
18744 this.transition = true;
18745 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18746 var lr = dir == 'next' ? 'left' : 'right';
18747 pan.el.addClass(dir); // or prev
18748 pan.el.addClass('carousel-item-' + dir); // or prev
18749 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18750 cur.el.addClass(lr); // or right
18751 pan.el.addClass(lr);
18752 cur.el.addClass('carousel-item-' +lr); // or right
18753 pan.el.addClass('carousel-item-' +lr);
18757 cur.el.on('transitionend', function() {
18758 Roo.log("trans end?");
18760 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18761 pan.setActive(true);
18763 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18764 cur.setActive(false);
18766 _this.transition = false;
18768 }, this, { single: true } );
18773 cur.setActive(false);
18774 pan.setActive(true);
18779 showPanelNext : function()
18781 var i = this.indexOfPanel(this.getActivePanel());
18783 if (i >= this.tabs.length - 1 && !this.autoslide) {
18787 if (i >= this.tabs.length - 1 && this.autoslide) {
18791 this.showPanel(this.tabs[i+1]);
18794 showPanelPrev : function()
18796 var i = this.indexOfPanel(this.getActivePanel());
18798 if (i < 1 && !this.autoslide) {
18802 if (i < 1 && this.autoslide) {
18803 i = this.tabs.length;
18806 this.showPanel(this.tabs[i-1]);
18810 addBullet: function()
18812 if(!this.bullets || Roo.isTouch){
18815 var ctr = this.el.select('.carousel-bullets',true).first();
18816 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18817 var bullet = ctr.createChild({
18818 cls : 'bullet bullet-' + i
18819 },ctr.dom.lastChild);
18824 bullet.on('click', (function(e, el, o, ii, t){
18826 e.preventDefault();
18828 this.showPanel(ii);
18830 if(this.autoslide && this.slideFn){
18831 clearInterval(this.slideFn);
18832 this.slideFn = window.setInterval(function() {
18833 _this.showPanelNext();
18837 }).createDelegate(this, [i, bullet], true));
18842 setActiveBullet : function(i)
18848 Roo.each(this.el.select('.bullet', true).elements, function(el){
18849 el.removeClass('selected');
18852 var bullet = this.el.select('.bullet-' + i, true).first();
18858 bullet.addClass('selected');
18869 Roo.apply(Roo.bootstrap.TabGroup, {
18873 * register a Navigation Group
18874 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18876 register : function(navgrp)
18878 this.groups[navgrp.navId] = navgrp;
18882 * fetch a Navigation Group based on the navigation ID
18883 * if one does not exist , it will get created.
18884 * @param {string} the navgroup to add
18885 * @returns {Roo.bootstrap.NavGroup} the navgroup
18887 get: function(navId) {
18888 if (typeof(this.groups[navId]) == 'undefined') {
18889 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18891 return this.groups[navId] ;
18906 * @class Roo.bootstrap.TabPanel
18907 * @extends Roo.bootstrap.Component
18908 * Bootstrap TabPanel class
18909 * @cfg {Boolean} active panel active
18910 * @cfg {String} html panel content
18911 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18912 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18913 * @cfg {String} href click to link..
18917 * Create a new TabPanel
18918 * @param {Object} config The config object
18921 Roo.bootstrap.TabPanel = function(config){
18922 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18926 * Fires when the active status changes
18927 * @param {Roo.bootstrap.TabPanel} this
18928 * @param {Boolean} state the new state
18933 * @event beforedeactivate
18934 * Fires before a tab is de-activated - can be used to do validation on a form.
18935 * @param {Roo.bootstrap.TabPanel} this
18936 * @return {Boolean} false if there is an error
18939 'beforedeactivate': true
18942 this.tabId = this.tabId || Roo.id();
18946 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18954 getAutoCreate : function(){
18959 // item is needed for carousel - not sure if it has any effect otherwise
18960 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18961 html: this.html || ''
18965 cfg.cls += ' active';
18969 cfg.tabId = this.tabId;
18977 initEvents: function()
18979 var p = this.parent();
18981 this.navId = this.navId || p.navId;
18983 if (typeof(this.navId) != 'undefined') {
18984 // not really needed.. but just in case.. parent should be a NavGroup.
18985 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18989 var i = tg.tabs.length - 1;
18991 if(this.active && tg.bullets > 0 && i < tg.bullets){
18992 tg.setActiveBullet(i);
18996 this.el.on('click', this.onClick, this);
18999 this.el.on("touchstart", this.onTouchStart, this);
19000 this.el.on("touchmove", this.onTouchMove, this);
19001 this.el.on("touchend", this.onTouchEnd, this);
19006 onRender : function(ct, position)
19008 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
19011 setActive : function(state)
19013 Roo.log("panel - set active " + this.tabId + "=" + state);
19015 this.active = state;
19017 this.el.removeClass('active');
19019 } else if (!this.el.hasClass('active')) {
19020 this.el.addClass('active');
19023 this.fireEvent('changed', this, state);
19026 onClick : function(e)
19028 e.preventDefault();
19030 if(!this.href.length){
19034 window.location.href = this.href;
19043 onTouchStart : function(e)
19045 this.swiping = false;
19047 this.startX = e.browserEvent.touches[0].clientX;
19048 this.startY = e.browserEvent.touches[0].clientY;
19051 onTouchMove : function(e)
19053 this.swiping = true;
19055 this.endX = e.browserEvent.touches[0].clientX;
19056 this.endY = e.browserEvent.touches[0].clientY;
19059 onTouchEnd : function(e)
19066 var tabGroup = this.parent();
19068 if(this.endX > this.startX){ // swiping right
19069 tabGroup.showPanelPrev();
19073 if(this.startX > this.endX){ // swiping left
19074 tabGroup.showPanelNext();
19093 * @class Roo.bootstrap.DateField
19094 * @extends Roo.bootstrap.Input
19095 * Bootstrap DateField class
19096 * @cfg {Number} weekStart default 0
19097 * @cfg {String} viewMode default empty, (months|years)
19098 * @cfg {String} minViewMode default empty, (months|years)
19099 * @cfg {Number} startDate default -Infinity
19100 * @cfg {Number} endDate default Infinity
19101 * @cfg {Boolean} todayHighlight default false
19102 * @cfg {Boolean} todayBtn default false
19103 * @cfg {Boolean} calendarWeeks default false
19104 * @cfg {Object} daysOfWeekDisabled default empty
19105 * @cfg {Boolean} singleMode default false (true | false)
19107 * @cfg {Boolean} keyboardNavigation default true
19108 * @cfg {String} language default en
19111 * Create a new DateField
19112 * @param {Object} config The config object
19115 Roo.bootstrap.DateField = function(config){
19116 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
19120 * Fires when this field show.
19121 * @param {Roo.bootstrap.DateField} this
19122 * @param {Mixed} date The date value
19127 * Fires when this field hide.
19128 * @param {Roo.bootstrap.DateField} this
19129 * @param {Mixed} date The date value
19134 * Fires when select a date.
19135 * @param {Roo.bootstrap.DateField} this
19136 * @param {Mixed} date The date value
19140 * @event beforeselect
19141 * Fires when before select a date.
19142 * @param {Roo.bootstrap.DateField} this
19143 * @param {Mixed} date The date value
19145 beforeselect : true
19149 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
19152 * @cfg {String} format
19153 * The default date format string which can be overriden for localization support. The format must be
19154 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
19158 * @cfg {String} altFormats
19159 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
19160 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
19162 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
19170 todayHighlight : false,
19176 keyboardNavigation: true,
19178 calendarWeeks: false,
19180 startDate: -Infinity,
19184 daysOfWeekDisabled: [],
19188 singleMode : false,
19190 UTCDate: function()
19192 return new Date(Date.UTC.apply(Date, arguments));
19195 UTCToday: function()
19197 var today = new Date();
19198 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
19201 getDate: function() {
19202 var d = this.getUTCDate();
19203 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
19206 getUTCDate: function() {
19210 setDate: function(d) {
19211 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
19214 setUTCDate: function(d) {
19216 this.setValue(this.formatDate(this.date));
19219 onRender: function(ct, position)
19222 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
19224 this.language = this.language || 'en';
19225 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
19226 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
19228 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
19229 this.format = this.format || 'm/d/y';
19230 this.isInline = false;
19231 this.isInput = true;
19232 this.component = this.el.select('.add-on', true).first() || false;
19233 this.component = (this.component && this.component.length === 0) ? false : this.component;
19234 this.hasInput = this.component && this.inputEl().length;
19236 if (typeof(this.minViewMode === 'string')) {
19237 switch (this.minViewMode) {
19239 this.minViewMode = 1;
19242 this.minViewMode = 2;
19245 this.minViewMode = 0;
19250 if (typeof(this.viewMode === 'string')) {
19251 switch (this.viewMode) {
19264 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19266 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19268 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19270 this.picker().on('mousedown', this.onMousedown, this);
19271 this.picker().on('click', this.onClick, this);
19273 this.picker().addClass('datepicker-dropdown');
19275 this.startViewMode = this.viewMode;
19277 if(this.singleMode){
19278 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19279 v.setVisibilityMode(Roo.Element.DISPLAY);
19283 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19284 v.setStyle('width', '189px');
19288 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19289 if(!this.calendarWeeks){
19294 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19295 v.attr('colspan', function(i, val){
19296 return parseInt(val) + 1;
19301 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19303 this.setStartDate(this.startDate);
19304 this.setEndDate(this.endDate);
19306 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19313 if(this.isInline) {
19318 picker : function()
19320 return this.pickerEl;
19321 // return this.el.select('.datepicker', true).first();
19324 fillDow: function()
19326 var dowCnt = this.weekStart;
19335 if(this.calendarWeeks){
19343 while (dowCnt < this.weekStart + 7) {
19347 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19351 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19354 fillMonths: function()
19357 var months = this.picker().select('>.datepicker-months td', true).first();
19359 months.dom.innerHTML = '';
19365 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19368 months.createChild(month);
19375 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;
19377 if (this.date < this.startDate) {
19378 this.viewDate = new Date(this.startDate);
19379 } else if (this.date > this.endDate) {
19380 this.viewDate = new Date(this.endDate);
19382 this.viewDate = new Date(this.date);
19390 var d = new Date(this.viewDate),
19391 year = d.getUTCFullYear(),
19392 month = d.getUTCMonth(),
19393 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19394 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19395 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19396 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19397 currentDate = this.date && this.date.valueOf(),
19398 today = this.UTCToday();
19400 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19402 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19404 // this.picker.select('>tfoot th.today').
19405 // .text(dates[this.language].today)
19406 // .toggle(this.todayBtn !== false);
19408 this.updateNavArrows();
19411 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19413 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19415 prevMonth.setUTCDate(day);
19417 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19419 var nextMonth = new Date(prevMonth);
19421 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19423 nextMonth = nextMonth.valueOf();
19425 var fillMonths = false;
19427 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19429 while(prevMonth.valueOf() <= nextMonth) {
19432 if (prevMonth.getUTCDay() === this.weekStart) {
19434 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19442 if(this.calendarWeeks){
19443 // ISO 8601: First week contains first thursday.
19444 // ISO also states week starts on Monday, but we can be more abstract here.
19446 // Start of current week: based on weekstart/current date
19447 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19448 // Thursday of this week
19449 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19450 // First Thursday of year, year from thursday
19451 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19452 // Calendar week: ms between thursdays, div ms per day, div 7 days
19453 calWeek = (th - yth) / 864e5 / 7 + 1;
19455 fillMonths.cn.push({
19463 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19465 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19468 if (this.todayHighlight &&
19469 prevMonth.getUTCFullYear() == today.getFullYear() &&
19470 prevMonth.getUTCMonth() == today.getMonth() &&
19471 prevMonth.getUTCDate() == today.getDate()) {
19472 clsName += ' today';
19475 if (currentDate && prevMonth.valueOf() === currentDate) {
19476 clsName += ' active';
19479 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19480 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19481 clsName += ' disabled';
19484 fillMonths.cn.push({
19486 cls: 'day ' + clsName,
19487 html: prevMonth.getDate()
19490 prevMonth.setDate(prevMonth.getDate()+1);
19493 var currentYear = this.date && this.date.getUTCFullYear();
19494 var currentMonth = this.date && this.date.getUTCMonth();
19496 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19498 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19499 v.removeClass('active');
19501 if(currentYear === year && k === currentMonth){
19502 v.addClass('active');
19505 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19506 v.addClass('disabled');
19512 year = parseInt(year/10, 10) * 10;
19514 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19516 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19519 for (var i = -1; i < 11; i++) {
19520 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19522 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19530 showMode: function(dir)
19533 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19536 Roo.each(this.picker().select('>div',true).elements, function(v){
19537 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19540 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19545 if(this.isInline) {
19549 this.picker().removeClass(['bottom', 'top']);
19551 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19553 * place to the top of element!
19557 this.picker().addClass('top');
19558 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19563 this.picker().addClass('bottom');
19565 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19568 parseDate : function(value)
19570 if(!value || value instanceof Date){
19573 var v = Date.parseDate(value, this.format);
19574 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19575 v = Date.parseDate(value, 'Y-m-d');
19577 if(!v && this.altFormats){
19578 if(!this.altFormatsArray){
19579 this.altFormatsArray = this.altFormats.split("|");
19581 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19582 v = Date.parseDate(value, this.altFormatsArray[i]);
19588 formatDate : function(date, fmt)
19590 return (!date || !(date instanceof Date)) ?
19591 date : date.dateFormat(fmt || this.format);
19594 onFocus : function()
19596 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19600 onBlur : function()
19602 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19604 var d = this.inputEl().getValue();
19611 showPopup : function()
19613 this.picker().show();
19617 this.fireEvent('showpopup', this, this.date);
19620 hidePopup : function()
19622 if(this.isInline) {
19625 this.picker().hide();
19626 this.viewMode = this.startViewMode;
19629 this.fireEvent('hidepopup', this, this.date);
19633 onMousedown: function(e)
19635 e.stopPropagation();
19636 e.preventDefault();
19641 Roo.bootstrap.DateField.superclass.keyup.call(this);
19645 setValue: function(v)
19647 if(this.fireEvent('beforeselect', this, v) !== false){
19648 var d = new Date(this.parseDate(v) ).clearTime();
19650 if(isNaN(d.getTime())){
19651 this.date = this.viewDate = '';
19652 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19656 v = this.formatDate(d);
19658 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19660 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19664 this.fireEvent('select', this, this.date);
19668 getValue: function()
19670 return this.formatDate(this.date);
19673 fireKey: function(e)
19675 if (!this.picker().isVisible()){
19676 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19682 var dateChanged = false,
19684 newDate, newViewDate;
19689 e.preventDefault();
19693 if (!this.keyboardNavigation) {
19696 dir = e.keyCode == 37 ? -1 : 1;
19699 newDate = this.moveYear(this.date, dir);
19700 newViewDate = this.moveYear(this.viewDate, dir);
19701 } else if (e.shiftKey){
19702 newDate = this.moveMonth(this.date, dir);
19703 newViewDate = this.moveMonth(this.viewDate, dir);
19705 newDate = new Date(this.date);
19706 newDate.setUTCDate(this.date.getUTCDate() + dir);
19707 newViewDate = new Date(this.viewDate);
19708 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19710 if (this.dateWithinRange(newDate)){
19711 this.date = newDate;
19712 this.viewDate = newViewDate;
19713 this.setValue(this.formatDate(this.date));
19715 e.preventDefault();
19716 dateChanged = true;
19721 if (!this.keyboardNavigation) {
19724 dir = e.keyCode == 38 ? -1 : 1;
19726 newDate = this.moveYear(this.date, dir);
19727 newViewDate = this.moveYear(this.viewDate, dir);
19728 } else if (e.shiftKey){
19729 newDate = this.moveMonth(this.date, dir);
19730 newViewDate = this.moveMonth(this.viewDate, dir);
19732 newDate = new Date(this.date);
19733 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19734 newViewDate = new Date(this.viewDate);
19735 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19737 if (this.dateWithinRange(newDate)){
19738 this.date = newDate;
19739 this.viewDate = newViewDate;
19740 this.setValue(this.formatDate(this.date));
19742 e.preventDefault();
19743 dateChanged = true;
19747 this.setValue(this.formatDate(this.date));
19749 e.preventDefault();
19752 this.setValue(this.formatDate(this.date));
19766 onClick: function(e)
19768 e.stopPropagation();
19769 e.preventDefault();
19771 var target = e.getTarget();
19773 if(target.nodeName.toLowerCase() === 'i'){
19774 target = Roo.get(target).dom.parentNode;
19777 var nodeName = target.nodeName;
19778 var className = target.className;
19779 var html = target.innerHTML;
19780 //Roo.log(nodeName);
19782 switch(nodeName.toLowerCase()) {
19784 switch(className) {
19790 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19791 switch(this.viewMode){
19793 this.viewDate = this.moveMonth(this.viewDate, dir);
19797 this.viewDate = this.moveYear(this.viewDate, dir);
19803 var date = new Date();
19804 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19806 this.setValue(this.formatDate(this.date));
19813 if (className.indexOf('disabled') < 0) {
19814 this.viewDate.setUTCDate(1);
19815 if (className.indexOf('month') > -1) {
19816 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19818 var year = parseInt(html, 10) || 0;
19819 this.viewDate.setUTCFullYear(year);
19823 if(this.singleMode){
19824 this.setValue(this.formatDate(this.viewDate));
19835 //Roo.log(className);
19836 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19837 var day = parseInt(html, 10) || 1;
19838 var year = this.viewDate.getUTCFullYear(),
19839 month = this.viewDate.getUTCMonth();
19841 if (className.indexOf('old') > -1) {
19848 } else if (className.indexOf('new') > -1) {
19856 //Roo.log([year,month,day]);
19857 this.date = this.UTCDate(year, month, day,0,0,0,0);
19858 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19860 //Roo.log(this.formatDate(this.date));
19861 this.setValue(this.formatDate(this.date));
19868 setStartDate: function(startDate)
19870 this.startDate = startDate || -Infinity;
19871 if (this.startDate !== -Infinity) {
19872 this.startDate = this.parseDate(this.startDate);
19875 this.updateNavArrows();
19878 setEndDate: function(endDate)
19880 this.endDate = endDate || Infinity;
19881 if (this.endDate !== Infinity) {
19882 this.endDate = this.parseDate(this.endDate);
19885 this.updateNavArrows();
19888 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19890 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19891 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19892 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19894 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19895 return parseInt(d, 10);
19898 this.updateNavArrows();
19901 updateNavArrows: function()
19903 if(this.singleMode){
19907 var d = new Date(this.viewDate),
19908 year = d.getUTCFullYear(),
19909 month = d.getUTCMonth();
19911 Roo.each(this.picker().select('.prev', true).elements, function(v){
19913 switch (this.viewMode) {
19916 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19922 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19929 Roo.each(this.picker().select('.next', true).elements, function(v){
19931 switch (this.viewMode) {
19934 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19940 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19948 moveMonth: function(date, dir)
19953 var new_date = new Date(date.valueOf()),
19954 day = new_date.getUTCDate(),
19955 month = new_date.getUTCMonth(),
19956 mag = Math.abs(dir),
19958 dir = dir > 0 ? 1 : -1;
19961 // If going back one month, make sure month is not current month
19962 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19964 return new_date.getUTCMonth() == month;
19966 // If going forward one month, make sure month is as expected
19967 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19969 return new_date.getUTCMonth() != new_month;
19971 new_month = month + dir;
19972 new_date.setUTCMonth(new_month);
19973 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19974 if (new_month < 0 || new_month > 11) {
19975 new_month = (new_month + 12) % 12;
19978 // For magnitudes >1, move one month at a time...
19979 for (var i=0; i<mag; i++) {
19980 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19981 new_date = this.moveMonth(new_date, dir);
19983 // ...then reset the day, keeping it in the new month
19984 new_month = new_date.getUTCMonth();
19985 new_date.setUTCDate(day);
19987 return new_month != new_date.getUTCMonth();
19990 // Common date-resetting loop -- if date is beyond end of month, make it
19993 new_date.setUTCDate(--day);
19994 new_date.setUTCMonth(new_month);
19999 moveYear: function(date, dir)
20001 return this.moveMonth(date, dir*12);
20004 dateWithinRange: function(date)
20006 return date >= this.startDate && date <= this.endDate;
20012 this.picker().remove();
20015 validateValue : function(value)
20017 if(this.getVisibilityEl().hasClass('hidden')){
20021 if(value.length < 1) {
20022 if(this.allowBlank){
20028 if(value.length < this.minLength){
20031 if(value.length > this.maxLength){
20035 var vt = Roo.form.VTypes;
20036 if(!vt[this.vtype](value, this)){
20040 if(typeof this.validator == "function"){
20041 var msg = this.validator(value);
20047 if(this.regex && !this.regex.test(value)){
20051 if(typeof(this.parseDate(value)) == 'undefined'){
20055 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
20059 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
20069 this.date = this.viewDate = '';
20071 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20076 Roo.apply(Roo.bootstrap.DateField, {
20087 html: '<i class="fa fa-arrow-left"/>'
20097 html: '<i class="fa fa-arrow-right"/>'
20139 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
20140 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
20141 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
20142 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20143 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
20156 navFnc: 'FullYear',
20161 navFnc: 'FullYear',
20166 Roo.apply(Roo.bootstrap.DateField, {
20170 cls: 'datepicker dropdown-menu roo-dynamic',
20174 cls: 'datepicker-days',
20178 cls: 'table-condensed',
20180 Roo.bootstrap.DateField.head,
20184 Roo.bootstrap.DateField.footer
20191 cls: 'datepicker-months',
20195 cls: 'table-condensed',
20197 Roo.bootstrap.DateField.head,
20198 Roo.bootstrap.DateField.content,
20199 Roo.bootstrap.DateField.footer
20206 cls: 'datepicker-years',
20210 cls: 'table-condensed',
20212 Roo.bootstrap.DateField.head,
20213 Roo.bootstrap.DateField.content,
20214 Roo.bootstrap.DateField.footer
20233 * @class Roo.bootstrap.TimeField
20234 * @extends Roo.bootstrap.Input
20235 * Bootstrap DateField class
20239 * Create a new TimeField
20240 * @param {Object} config The config object
20243 Roo.bootstrap.TimeField = function(config){
20244 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20248 * Fires when this field show.
20249 * @param {Roo.bootstrap.DateField} thisthis
20250 * @param {Mixed} date The date value
20255 * Fires when this field hide.
20256 * @param {Roo.bootstrap.DateField} this
20257 * @param {Mixed} date The date value
20262 * Fires when select a date.
20263 * @param {Roo.bootstrap.DateField} this
20264 * @param {Mixed} date The date value
20270 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
20273 * @cfg {String} format
20274 * The default time format string which can be overriden for localization support. The format must be
20275 * valid according to {@link Date#parseDate} (defaults to 'H:i').
20279 onRender: function(ct, position)
20282 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20284 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20286 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20288 this.pop = this.picker().select('>.datepicker-time',true).first();
20289 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20291 this.picker().on('mousedown', this.onMousedown, this);
20292 this.picker().on('click', this.onClick, this);
20294 this.picker().addClass('datepicker-dropdown');
20299 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20300 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20301 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20302 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20303 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20304 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20308 fireKey: function(e){
20309 if (!this.picker().isVisible()){
20310 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20316 e.preventDefault();
20324 this.onTogglePeriod();
20327 this.onIncrementMinutes();
20330 this.onDecrementMinutes();
20339 onClick: function(e) {
20340 e.stopPropagation();
20341 e.preventDefault();
20344 picker : function()
20346 return this.el.select('.datepicker', true).first();
20349 fillTime: function()
20351 var time = this.pop.select('tbody', true).first();
20353 time.dom.innerHTML = '';
20368 cls: 'hours-up glyphicon glyphicon-chevron-up'
20388 cls: 'minutes-up glyphicon glyphicon-chevron-up'
20409 cls: 'timepicker-hour',
20424 cls: 'timepicker-minute',
20439 cls: 'btn btn-primary period',
20461 cls: 'hours-down glyphicon glyphicon-chevron-down'
20481 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20499 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20506 var hours = this.time.getHours();
20507 var minutes = this.time.getMinutes();
20520 hours = hours - 12;
20524 hours = '0' + hours;
20528 minutes = '0' + minutes;
20531 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20532 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20533 this.pop.select('button', true).first().dom.innerHTML = period;
20539 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20541 var cls = ['bottom'];
20543 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20550 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20555 this.picker().addClass(cls.join('-'));
20559 Roo.each(cls, function(c){
20561 _this.picker().setTop(_this.inputEl().getHeight());
20565 _this.picker().setTop(0 - _this.picker().getHeight());
20570 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20574 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20581 onFocus : function()
20583 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20587 onBlur : function()
20589 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20595 this.picker().show();
20600 this.fireEvent('show', this, this.date);
20605 this.picker().hide();
20608 this.fireEvent('hide', this, this.date);
20611 setTime : function()
20614 this.setValue(this.time.format(this.format));
20616 this.fireEvent('select', this, this.date);
20621 onMousedown: function(e){
20622 e.stopPropagation();
20623 e.preventDefault();
20626 onIncrementHours: function()
20628 Roo.log('onIncrementHours');
20629 this.time = this.time.add(Date.HOUR, 1);
20634 onDecrementHours: function()
20636 Roo.log('onDecrementHours');
20637 this.time = this.time.add(Date.HOUR, -1);
20641 onIncrementMinutes: function()
20643 Roo.log('onIncrementMinutes');
20644 this.time = this.time.add(Date.MINUTE, 1);
20648 onDecrementMinutes: function()
20650 Roo.log('onDecrementMinutes');
20651 this.time = this.time.add(Date.MINUTE, -1);
20655 onTogglePeriod: function()
20657 Roo.log('onTogglePeriod');
20658 this.time = this.time.add(Date.HOUR, 12);
20665 Roo.apply(Roo.bootstrap.TimeField, {
20695 cls: 'btn btn-info ok',
20707 Roo.apply(Roo.bootstrap.TimeField, {
20711 cls: 'datepicker dropdown-menu',
20715 cls: 'datepicker-time',
20719 cls: 'table-condensed',
20721 Roo.bootstrap.TimeField.content,
20722 Roo.bootstrap.TimeField.footer
20741 * @class Roo.bootstrap.MonthField
20742 * @extends Roo.bootstrap.Input
20743 * Bootstrap MonthField class
20745 * @cfg {String} language default en
20748 * Create a new MonthField
20749 * @param {Object} config The config object
20752 Roo.bootstrap.MonthField = function(config){
20753 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20758 * Fires when this field show.
20759 * @param {Roo.bootstrap.MonthField} this
20760 * @param {Mixed} date The date value
20765 * Fires when this field hide.
20766 * @param {Roo.bootstrap.MonthField} this
20767 * @param {Mixed} date The date value
20772 * Fires when select a date.
20773 * @param {Roo.bootstrap.MonthField} this
20774 * @param {String} oldvalue The old value
20775 * @param {String} newvalue The new value
20781 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20783 onRender: function(ct, position)
20786 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20788 this.language = this.language || 'en';
20789 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20790 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20792 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20793 this.isInline = false;
20794 this.isInput = true;
20795 this.component = this.el.select('.add-on', true).first() || false;
20796 this.component = (this.component && this.component.length === 0) ? false : this.component;
20797 this.hasInput = this.component && this.inputEL().length;
20799 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20801 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20803 this.picker().on('mousedown', this.onMousedown, this);
20804 this.picker().on('click', this.onClick, this);
20806 this.picker().addClass('datepicker-dropdown');
20808 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20809 v.setStyle('width', '189px');
20816 if(this.isInline) {
20822 setValue: function(v, suppressEvent)
20824 var o = this.getValue();
20826 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20830 if(suppressEvent !== true){
20831 this.fireEvent('select', this, o, v);
20836 getValue: function()
20841 onClick: function(e)
20843 e.stopPropagation();
20844 e.preventDefault();
20846 var target = e.getTarget();
20848 if(target.nodeName.toLowerCase() === 'i'){
20849 target = Roo.get(target).dom.parentNode;
20852 var nodeName = target.nodeName;
20853 var className = target.className;
20854 var html = target.innerHTML;
20856 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20860 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20862 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20868 picker : function()
20870 return this.pickerEl;
20873 fillMonths: function()
20876 var months = this.picker().select('>.datepicker-months td', true).first();
20878 months.dom.innerHTML = '';
20884 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20887 months.createChild(month);
20896 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20897 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20900 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20901 e.removeClass('active');
20903 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20904 e.addClass('active');
20911 if(this.isInline) {
20915 this.picker().removeClass(['bottom', 'top']);
20917 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20919 * place to the top of element!
20923 this.picker().addClass('top');
20924 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20929 this.picker().addClass('bottom');
20931 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20934 onFocus : function()
20936 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20940 onBlur : function()
20942 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20944 var d = this.inputEl().getValue();
20953 this.picker().show();
20954 this.picker().select('>.datepicker-months', true).first().show();
20958 this.fireEvent('show', this, this.date);
20963 if(this.isInline) {
20966 this.picker().hide();
20967 this.fireEvent('hide', this, this.date);
20971 onMousedown: function(e)
20973 e.stopPropagation();
20974 e.preventDefault();
20979 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20983 fireKey: function(e)
20985 if (!this.picker().isVisible()){
20986 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20997 e.preventDefault();
21001 dir = e.keyCode == 37 ? -1 : 1;
21003 this.vIndex = this.vIndex + dir;
21005 if(this.vIndex < 0){
21009 if(this.vIndex > 11){
21013 if(isNaN(this.vIndex)){
21017 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21023 dir = e.keyCode == 38 ? -1 : 1;
21025 this.vIndex = this.vIndex + dir * 4;
21027 if(this.vIndex < 0){
21031 if(this.vIndex > 11){
21035 if(isNaN(this.vIndex)){
21039 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21044 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21045 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21049 e.preventDefault();
21052 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21053 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21069 this.picker().remove();
21074 Roo.apply(Roo.bootstrap.MonthField, {
21093 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21094 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
21099 Roo.apply(Roo.bootstrap.MonthField, {
21103 cls: 'datepicker dropdown-menu roo-dynamic',
21107 cls: 'datepicker-months',
21111 cls: 'table-condensed',
21113 Roo.bootstrap.DateField.content
21133 * @class Roo.bootstrap.CheckBox
21134 * @extends Roo.bootstrap.Input
21135 * Bootstrap CheckBox class
21137 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
21138 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
21139 * @cfg {String} boxLabel The text that appears beside the checkbox
21140 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
21141 * @cfg {Boolean} checked initnal the element
21142 * @cfg {Boolean} inline inline the element (default false)
21143 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
21144 * @cfg {String} tooltip label tooltip
21147 * Create a new CheckBox
21148 * @param {Object} config The config object
21151 Roo.bootstrap.CheckBox = function(config){
21152 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
21157 * Fires when the element is checked or unchecked.
21158 * @param {Roo.bootstrap.CheckBox} this This input
21159 * @param {Boolean} checked The new checked value
21164 * Fires when the element is click.
21165 * @param {Roo.bootstrap.CheckBox} this This input
21172 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
21174 inputType: 'checkbox',
21183 // checkbox success does not make any sense really..
21188 getAutoCreate : function()
21190 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
21196 cfg.cls = 'form-group ' + this.inputType; //input-group
21199 cfg.cls += ' ' + this.inputType + '-inline';
21205 type : this.inputType,
21206 value : this.inputValue,
21207 cls : 'roo-' + this.inputType, //'form-box',
21208 placeholder : this.placeholder || ''
21212 if(this.inputType != 'radio'){
21216 cls : 'roo-hidden-value',
21217 value : this.checked ? this.inputValue : this.valueOff
21222 if (this.weight) { // Validity check?
21223 cfg.cls += " " + this.inputType + "-" + this.weight;
21226 if (this.disabled) {
21227 input.disabled=true;
21231 input.checked = this.checked;
21236 input.name = this.name;
21238 if(this.inputType != 'radio'){
21239 hidden.name = this.name;
21240 input.name = '_hidden_' + this.name;
21245 input.cls += ' input-' + this.size;
21250 ['xs','sm','md','lg'].map(function(size){
21251 if (settings[size]) {
21252 cfg.cls += ' col-' + size + '-' + settings[size];
21256 var inputblock = input;
21258 if (this.before || this.after) {
21261 cls : 'input-group',
21266 inputblock.cn.push({
21268 cls : 'input-group-addon',
21273 inputblock.cn.push(input);
21275 if(this.inputType != 'radio'){
21276 inputblock.cn.push(hidden);
21280 inputblock.cn.push({
21282 cls : 'input-group-addon',
21288 var boxLabelCfg = false;
21294 //'for': id, // box label is handled by onclick - so no for...
21296 html: this.boxLabel
21299 boxLabelCfg.tooltip = this.tooltip;
21305 if (align ==='left' && this.fieldLabel.length) {
21306 // Roo.log("left and has label");
21311 cls : 'control-label',
21312 html : this.fieldLabel
21323 cfg.cn[1].cn.push(boxLabelCfg);
21326 if(this.labelWidth > 12){
21327 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21330 if(this.labelWidth < 13 && this.labelmd == 0){
21331 this.labelmd = this.labelWidth;
21334 if(this.labellg > 0){
21335 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21336 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21339 if(this.labelmd > 0){
21340 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21341 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21344 if(this.labelsm > 0){
21345 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21346 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21349 if(this.labelxs > 0){
21350 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21351 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21354 } else if ( this.fieldLabel.length) {
21355 // Roo.log(" label");
21359 tag: this.boxLabel ? 'span' : 'label',
21361 cls: 'control-label box-input-label',
21362 //cls : 'input-group-addon',
21363 html : this.fieldLabel
21370 cfg.cn.push(boxLabelCfg);
21375 // Roo.log(" no label && no align");
21376 cfg.cn = [ inputblock ] ;
21378 cfg.cn.push(boxLabelCfg);
21386 if(this.inputType != 'radio'){
21387 cfg.cn.push(hidden);
21395 * return the real input element.
21397 inputEl: function ()
21399 return this.el.select('input.roo-' + this.inputType,true).first();
21401 hiddenEl: function ()
21403 return this.el.select('input.roo-hidden-value',true).first();
21406 labelEl: function()
21408 return this.el.select('label.control-label',true).first();
21410 /* depricated... */
21414 return this.labelEl();
21417 boxLabelEl: function()
21419 return this.el.select('label.box-label',true).first();
21422 initEvents : function()
21424 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21426 this.inputEl().on('click', this.onClick, this);
21428 if (this.boxLabel) {
21429 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
21432 this.startValue = this.getValue();
21435 Roo.bootstrap.CheckBox.register(this);
21439 onClick : function(e)
21441 if(this.fireEvent('click', this, e) !== false){
21442 this.setChecked(!this.checked);
21447 setChecked : function(state,suppressEvent)
21449 this.startValue = this.getValue();
21451 if(this.inputType == 'radio'){
21453 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21454 e.dom.checked = false;
21457 this.inputEl().dom.checked = true;
21459 this.inputEl().dom.value = this.inputValue;
21461 if(suppressEvent !== true){
21462 this.fireEvent('check', this, true);
21470 this.checked = state;
21472 this.inputEl().dom.checked = state;
21475 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21477 if(suppressEvent !== true){
21478 this.fireEvent('check', this, state);
21484 getValue : function()
21486 if(this.inputType == 'radio'){
21487 return this.getGroupValue();
21490 return this.hiddenEl().dom.value;
21494 getGroupValue : function()
21496 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21500 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21503 setValue : function(v,suppressEvent)
21505 if(this.inputType == 'radio'){
21506 this.setGroupValue(v, suppressEvent);
21510 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21515 setGroupValue : function(v, suppressEvent)
21517 this.startValue = this.getValue();
21519 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21520 e.dom.checked = false;
21522 if(e.dom.value == v){
21523 e.dom.checked = true;
21527 if(suppressEvent !== true){
21528 this.fireEvent('check', this, true);
21536 validate : function()
21538 if(this.getVisibilityEl().hasClass('hidden')){
21544 (this.inputType == 'radio' && this.validateRadio()) ||
21545 (this.inputType == 'checkbox' && this.validateCheckbox())
21551 this.markInvalid();
21555 validateRadio : function()
21557 if(this.getVisibilityEl().hasClass('hidden')){
21561 if(this.allowBlank){
21567 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21568 if(!e.dom.checked){
21580 validateCheckbox : function()
21583 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21584 //return (this.getValue() == this.inputValue) ? true : false;
21587 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21595 for(var i in group){
21596 if(group[i].el.isVisible(true)){
21604 for(var i in group){
21609 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21616 * Mark this field as valid
21618 markValid : function()
21622 this.fireEvent('valid', this);
21624 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21627 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21634 if(this.inputType == 'radio'){
21635 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21636 var fg = e.findParent('.form-group', false, true);
21637 if (Roo.bootstrap.version == 3) {
21638 fg.removeClass([_this.invalidClass, _this.validClass]);
21639 fg.addClass(_this.validClass);
21641 fg.removeClass(['is-valid', 'is-invalid']);
21642 fg.addClass('is-valid');
21650 var fg = this.el.findParent('.form-group', false, true);
21651 if (Roo.bootstrap.version == 3) {
21652 fg.removeClass([this.invalidClass, this.validClass]);
21653 fg.addClass(this.validClass);
21655 fg.removeClass(['is-valid', 'is-invalid']);
21656 fg.addClass('is-valid');
21661 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21667 for(var i in group){
21668 var fg = group[i].el.findParent('.form-group', false, true);
21669 if (Roo.bootstrap.version == 3) {
21670 fg.removeClass([this.invalidClass, this.validClass]);
21671 fg.addClass(this.validClass);
21673 fg.removeClass(['is-valid', 'is-invalid']);
21674 fg.addClass('is-valid');
21680 * Mark this field as invalid
21681 * @param {String} msg The validation message
21683 markInvalid : function(msg)
21685 if(this.allowBlank){
21691 this.fireEvent('invalid', this, msg);
21693 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21696 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21700 label.markInvalid();
21703 if(this.inputType == 'radio'){
21705 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21706 var fg = e.findParent('.form-group', false, true);
21707 if (Roo.bootstrap.version == 3) {
21708 fg.removeClass([_this.invalidClass, _this.validClass]);
21709 fg.addClass(_this.invalidClass);
21711 fg.removeClass(['is-invalid', 'is-valid']);
21712 fg.addClass('is-invalid');
21720 var fg = this.el.findParent('.form-group', false, true);
21721 if (Roo.bootstrap.version == 3) {
21722 fg.removeClass([_this.invalidClass, _this.validClass]);
21723 fg.addClass(_this.invalidClass);
21725 fg.removeClass(['is-invalid', 'is-valid']);
21726 fg.addClass('is-invalid');
21731 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21737 for(var i in group){
21738 var fg = group[i].el.findParent('.form-group', false, true);
21739 if (Roo.bootstrap.version == 3) {
21740 fg.removeClass([_this.invalidClass, _this.validClass]);
21741 fg.addClass(_this.invalidClass);
21743 fg.removeClass(['is-invalid', 'is-valid']);
21744 fg.addClass('is-invalid');
21750 clearInvalid : function()
21752 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21754 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21756 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21758 if (label && label.iconEl) {
21759 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21760 label.iconEl.removeClass(['is-invalid', 'is-valid']);
21764 disable : function()
21766 if(this.inputType != 'radio'){
21767 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21774 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21775 _this.getActionEl().addClass(this.disabledClass);
21776 e.dom.disabled = true;
21780 this.disabled = true;
21781 this.fireEvent("disable", this);
21785 enable : function()
21787 if(this.inputType != 'radio'){
21788 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21795 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21796 _this.getActionEl().removeClass(this.disabledClass);
21797 e.dom.disabled = false;
21801 this.disabled = false;
21802 this.fireEvent("enable", this);
21806 setBoxLabel : function(v)
21811 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21817 Roo.apply(Roo.bootstrap.CheckBox, {
21822 * register a CheckBox Group
21823 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21825 register : function(checkbox)
21827 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21828 this.groups[checkbox.groupId] = {};
21831 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21835 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21839 * fetch a CheckBox Group based on the group ID
21840 * @param {string} the group ID
21841 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21843 get: function(groupId) {
21844 if (typeof(this.groups[groupId]) == 'undefined') {
21848 return this.groups[groupId] ;
21861 * @class Roo.bootstrap.Radio
21862 * @extends Roo.bootstrap.Component
21863 * Bootstrap Radio class
21864 * @cfg {String} boxLabel - the label associated
21865 * @cfg {String} value - the value of radio
21868 * Create a new Radio
21869 * @param {Object} config The config object
21871 Roo.bootstrap.Radio = function(config){
21872 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21876 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21882 getAutoCreate : function()
21886 cls : 'form-group radio',
21891 html : this.boxLabel
21899 initEvents : function()
21901 this.parent().register(this);
21903 this.el.on('click', this.onClick, this);
21907 onClick : function(e)
21909 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21910 this.setChecked(true);
21914 setChecked : function(state, suppressEvent)
21916 this.parent().setValue(this.value, suppressEvent);
21920 setBoxLabel : function(v)
21925 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21940 * @class Roo.bootstrap.SecurePass
21941 * @extends Roo.bootstrap.Input
21942 * Bootstrap SecurePass class
21946 * Create a new SecurePass
21947 * @param {Object} config The config object
21950 Roo.bootstrap.SecurePass = function (config) {
21951 // these go here, so the translation tool can replace them..
21953 PwdEmpty: "Please type a password, and then retype it to confirm.",
21954 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21955 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21956 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21957 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21958 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21959 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21960 TooWeak: "Your password is Too Weak."
21962 this.meterLabel = "Password strength:";
21963 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21964 this.meterClass = [
21965 "roo-password-meter-tooweak",
21966 "roo-password-meter-weak",
21967 "roo-password-meter-medium",
21968 "roo-password-meter-strong",
21969 "roo-password-meter-grey"
21974 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21977 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21979 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21981 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21982 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21983 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21984 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21985 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21986 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21987 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21997 * @cfg {String/Object} Label for the strength meter (defaults to
21998 * 'Password strength:')
22003 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
22004 * ['Weak', 'Medium', 'Strong'])
22007 pwdStrengths: false,
22020 initEvents: function ()
22022 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
22024 if (this.el.is('input[type=password]') && Roo.isSafari) {
22025 this.el.on('keydown', this.SafariOnKeyDown, this);
22028 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
22031 onRender: function (ct, position)
22033 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
22034 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
22035 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
22037 this.trigger.createChild({
22042 cls: 'roo-password-meter-grey col-xs-12',
22045 //width: this.meterWidth + 'px'
22049 cls: 'roo-password-meter-text'
22055 if (this.hideTrigger) {
22056 this.trigger.setDisplayed(false);
22058 this.setSize(this.width || '', this.height || '');
22061 onDestroy: function ()
22063 if (this.trigger) {
22064 this.trigger.removeAllListeners();
22065 this.trigger.remove();
22068 this.wrap.remove();
22070 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
22073 checkStrength: function ()
22075 var pwd = this.inputEl().getValue();
22076 if (pwd == this._lastPwd) {
22081 if (this.ClientSideStrongPassword(pwd)) {
22083 } else if (this.ClientSideMediumPassword(pwd)) {
22085 } else if (this.ClientSideWeakPassword(pwd)) {
22091 Roo.log('strength1: ' + strength);
22093 //var pm = this.trigger.child('div/div/div').dom;
22094 var pm = this.trigger.child('div/div');
22095 pm.removeClass(this.meterClass);
22096 pm.addClass(this.meterClass[strength]);
22099 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22101 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
22103 this._lastPwd = pwd;
22107 Roo.bootstrap.SecurePass.superclass.reset.call(this);
22109 this._lastPwd = '';
22111 var pm = this.trigger.child('div/div');
22112 pm.removeClass(this.meterClass);
22113 pm.addClass('roo-password-meter-grey');
22116 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22119 this.inputEl().dom.type='password';
22122 validateValue: function (value)
22125 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
22128 if (value.length == 0) {
22129 if (this.allowBlank) {
22130 this.clearInvalid();
22134 this.markInvalid(this.errors.PwdEmpty);
22135 this.errorMsg = this.errors.PwdEmpty;
22143 if ('[\x21-\x7e]*'.match(value)) {
22144 this.markInvalid(this.errors.PwdBadChar);
22145 this.errorMsg = this.errors.PwdBadChar;
22148 if (value.length < 6) {
22149 this.markInvalid(this.errors.PwdShort);
22150 this.errorMsg = this.errors.PwdShort;
22153 if (value.length > 16) {
22154 this.markInvalid(this.errors.PwdLong);
22155 this.errorMsg = this.errors.PwdLong;
22159 if (this.ClientSideStrongPassword(value)) {
22161 } else if (this.ClientSideMediumPassword(value)) {
22163 } else if (this.ClientSideWeakPassword(value)) {
22170 if (strength < 2) {
22171 //this.markInvalid(this.errors.TooWeak);
22172 this.errorMsg = this.errors.TooWeak;
22177 console.log('strength2: ' + strength);
22179 //var pm = this.trigger.child('div/div/div').dom;
22181 var pm = this.trigger.child('div/div');
22182 pm.removeClass(this.meterClass);
22183 pm.addClass(this.meterClass[strength]);
22185 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22187 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
22189 this.errorMsg = '';
22193 CharacterSetChecks: function (type)
22196 this.fResult = false;
22199 isctype: function (character, type)
22202 case this.kCapitalLetter:
22203 if (character >= 'A' && character <= 'Z') {
22208 case this.kSmallLetter:
22209 if (character >= 'a' && character <= 'z') {
22215 if (character >= '0' && character <= '9') {
22220 case this.kPunctuation:
22221 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
22232 IsLongEnough: function (pwd, size)
22234 return !(pwd == null || isNaN(size) || pwd.length < size);
22237 SpansEnoughCharacterSets: function (word, nb)
22239 if (!this.IsLongEnough(word, nb))
22244 var characterSetChecks = new Array(
22245 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22246 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22249 for (var index = 0; index < word.length; ++index) {
22250 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22251 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22252 characterSetChecks[nCharSet].fResult = true;
22259 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22260 if (characterSetChecks[nCharSet].fResult) {
22265 if (nCharSets < nb) {
22271 ClientSideStrongPassword: function (pwd)
22273 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22276 ClientSideMediumPassword: function (pwd)
22278 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22281 ClientSideWeakPassword: function (pwd)
22283 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22286 })//<script type="text/javascript">
22289 * Based Ext JS Library 1.1.1
22290 * Copyright(c) 2006-2007, Ext JS, LLC.
22296 * @class Roo.HtmlEditorCore
22297 * @extends Roo.Component
22298 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22300 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22303 Roo.HtmlEditorCore = function(config){
22306 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22311 * @event initialize
22312 * Fires when the editor is fully initialized (including the iframe)
22313 * @param {Roo.HtmlEditorCore} this
22318 * Fires when the editor is first receives the focus. Any insertion must wait
22319 * until after this event.
22320 * @param {Roo.HtmlEditorCore} this
22324 * @event beforesync
22325 * Fires before the textarea is updated with content from the editor iframe. Return false
22326 * to cancel the sync.
22327 * @param {Roo.HtmlEditorCore} this
22328 * @param {String} html
22332 * @event beforepush
22333 * Fires before the iframe editor is updated with content from the textarea. Return false
22334 * to cancel the push.
22335 * @param {Roo.HtmlEditorCore} this
22336 * @param {String} html
22341 * Fires when the textarea is updated with content from the editor iframe.
22342 * @param {Roo.HtmlEditorCore} this
22343 * @param {String} html
22348 * Fires when the iframe editor is updated with content from the textarea.
22349 * @param {Roo.HtmlEditorCore} this
22350 * @param {String} html
22355 * @event editorevent
22356 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22357 * @param {Roo.HtmlEditorCore} this
22363 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22365 // defaults : white / black...
22366 this.applyBlacklists();
22373 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
22377 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
22383 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22388 * @cfg {Number} height (in pixels)
22392 * @cfg {Number} width (in pixels)
22397 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22400 stylesheets: false,
22405 // private properties
22406 validationEvent : false,
22408 initialized : false,
22410 sourceEditMode : false,
22411 onFocus : Roo.emptyFn,
22413 hideMode:'offsets',
22417 // blacklist + whitelisted elements..
22424 * Protected method that will not generally be called directly. It
22425 * is called when the editor initializes the iframe with HTML contents. Override this method if you
22426 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22428 getDocMarkup : function(){
22432 // inherit styels from page...??
22433 if (this.stylesheets === false) {
22435 Roo.get(document.head).select('style').each(function(node) {
22436 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22439 Roo.get(document.head).select('link').each(function(node) {
22440 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22443 } else if (!this.stylesheets.length) {
22445 st = '<style type="text/css">' +
22446 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22449 st = '<style type="text/css">' +
22454 st += '<style type="text/css">' +
22455 'IMG { cursor: pointer } ' +
22458 var cls = 'roo-htmleditor-body';
22460 if(this.bodyCls.length){
22461 cls += ' ' + this.bodyCls;
22464 return '<html><head>' + st +
22465 //<style type="text/css">' +
22466 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22468 ' </head><body class="' + cls + '"></body></html>';
22472 onRender : function(ct, position)
22475 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22476 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22479 this.el.dom.style.border = '0 none';
22480 this.el.dom.setAttribute('tabIndex', -1);
22481 this.el.addClass('x-hidden hide');
22485 if(Roo.isIE){ // fix IE 1px bogus margin
22486 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22490 this.frameId = Roo.id();
22494 var iframe = this.owner.wrap.createChild({
22496 cls: 'form-control', // bootstrap..
22498 name: this.frameId,
22499 frameBorder : 'no',
22500 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
22505 this.iframe = iframe.dom;
22507 this.assignDocWin();
22509 this.doc.designMode = 'on';
22512 this.doc.write(this.getDocMarkup());
22516 var task = { // must defer to wait for browser to be ready
22518 //console.log("run task?" + this.doc.readyState);
22519 this.assignDocWin();
22520 if(this.doc.body || this.doc.readyState == 'complete'){
22522 this.doc.designMode="on";
22526 Roo.TaskMgr.stop(task);
22527 this.initEditor.defer(10, this);
22534 Roo.TaskMgr.start(task);
22539 onResize : function(w, h)
22541 Roo.log('resize: ' +w + ',' + h );
22542 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22546 if(typeof w == 'number'){
22548 this.iframe.style.width = w + 'px';
22550 if(typeof h == 'number'){
22552 this.iframe.style.height = h + 'px';
22554 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22561 * Toggles the editor between standard and source edit mode.
22562 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22564 toggleSourceEdit : function(sourceEditMode){
22566 this.sourceEditMode = sourceEditMode === true;
22568 if(this.sourceEditMode){
22570 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22573 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22574 //this.iframe.className = '';
22577 //this.setSize(this.owner.wrap.getSize());
22578 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22585 * Protected method that will not generally be called directly. If you need/want
22586 * custom HTML cleanup, this is the method you should override.
22587 * @param {String} html The HTML to be cleaned
22588 * return {String} The cleaned HTML
22590 cleanHtml : function(html){
22591 html = String(html);
22592 if(html.length > 5){
22593 if(Roo.isSafari){ // strip safari nonsense
22594 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22597 if(html == ' '){
22604 * HTML Editor -> Textarea
22605 * Protected method that will not generally be called directly. Syncs the contents
22606 * of the editor iframe with the textarea.
22608 syncValue : function(){
22609 if(this.initialized){
22610 var bd = (this.doc.body || this.doc.documentElement);
22611 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22612 var html = bd.innerHTML;
22614 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22615 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22617 html = '<div style="'+m[0]+'">' + html + '</div>';
22620 html = this.cleanHtml(html);
22621 // fix up the special chars.. normaly like back quotes in word...
22622 // however we do not want to do this with chinese..
22623 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
22625 var cc = match.charCodeAt();
22627 // Get the character value, handling surrogate pairs
22628 if (match.length == 2) {
22629 // It's a surrogate pair, calculate the Unicode code point
22630 var high = match.charCodeAt(0) - 0xD800;
22631 var low = match.charCodeAt(1) - 0xDC00;
22632 cc = (high * 0x400) + low + 0x10000;
22634 (cc >= 0x4E00 && cc < 0xA000 ) ||
22635 (cc >= 0x3400 && cc < 0x4E00 ) ||
22636 (cc >= 0xf900 && cc < 0xfb00 )
22641 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
22642 return "&#" + cc + ";";
22649 if(this.owner.fireEvent('beforesync', this, html) !== false){
22650 this.el.dom.value = html;
22651 this.owner.fireEvent('sync', this, html);
22657 * Protected method that will not generally be called directly. Pushes the value of the textarea
22658 * into the iframe editor.
22660 pushValue : function(){
22661 if(this.initialized){
22662 var v = this.el.dom.value.trim();
22664 // if(v.length < 1){
22668 if(this.owner.fireEvent('beforepush', this, v) !== false){
22669 var d = (this.doc.body || this.doc.documentElement);
22671 this.cleanUpPaste();
22672 this.el.dom.value = d.innerHTML;
22673 this.owner.fireEvent('push', this, v);
22679 deferFocus : function(){
22680 this.focus.defer(10, this);
22684 focus : function(){
22685 if(this.win && !this.sourceEditMode){
22692 assignDocWin: function()
22694 var iframe = this.iframe;
22697 this.doc = iframe.contentWindow.document;
22698 this.win = iframe.contentWindow;
22700 // if (!Roo.get(this.frameId)) {
22703 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22704 // this.win = Roo.get(this.frameId).dom.contentWindow;
22706 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22710 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22711 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22716 initEditor : function(){
22717 //console.log("INIT EDITOR");
22718 this.assignDocWin();
22722 this.doc.designMode="on";
22724 this.doc.write(this.getDocMarkup());
22727 var dbody = (this.doc.body || this.doc.documentElement);
22728 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22729 // this copies styles from the containing element into thsi one..
22730 // not sure why we need all of this..
22731 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22733 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22734 //ss['background-attachment'] = 'fixed'; // w3c
22735 dbody.bgProperties = 'fixed'; // ie
22736 //Roo.DomHelper.applyStyles(dbody, ss);
22737 Roo.EventManager.on(this.doc, {
22738 //'mousedown': this.onEditorEvent,
22739 'mouseup': this.onEditorEvent,
22740 'dblclick': this.onEditorEvent,
22741 'click': this.onEditorEvent,
22742 'keyup': this.onEditorEvent,
22747 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22749 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22750 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22752 this.initialized = true;
22754 this.owner.fireEvent('initialize', this);
22759 onDestroy : function(){
22765 //for (var i =0; i < this.toolbars.length;i++) {
22766 // // fixme - ask toolbars for heights?
22767 // this.toolbars[i].onDestroy();
22770 //this.wrap.dom.innerHTML = '';
22771 //this.wrap.remove();
22776 onFirstFocus : function(){
22778 this.assignDocWin();
22781 this.activated = true;
22784 if(Roo.isGecko){ // prevent silly gecko errors
22786 var s = this.win.getSelection();
22787 if(!s.focusNode || s.focusNode.nodeType != 3){
22788 var r = s.getRangeAt(0);
22789 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22794 this.execCmd('useCSS', true);
22795 this.execCmd('styleWithCSS', false);
22798 this.owner.fireEvent('activate', this);
22802 adjustFont: function(btn){
22803 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22804 //if(Roo.isSafari){ // safari
22807 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22808 if(Roo.isSafari){ // safari
22809 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22810 v = (v < 10) ? 10 : v;
22811 v = (v > 48) ? 48 : v;
22812 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22817 v = Math.max(1, v+adjust);
22819 this.execCmd('FontSize', v );
22822 onEditorEvent : function(e)
22824 this.owner.fireEvent('editorevent', this, e);
22825 // this.updateToolbar();
22826 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22829 insertTag : function(tg)
22831 // could be a bit smarter... -> wrap the current selected tRoo..
22832 if (tg.toLowerCase() == 'span' ||
22833 tg.toLowerCase() == 'code' ||
22834 tg.toLowerCase() == 'sup' ||
22835 tg.toLowerCase() == 'sub'
22838 range = this.createRange(this.getSelection());
22839 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22840 wrappingNode.appendChild(range.extractContents());
22841 range.insertNode(wrappingNode);
22848 this.execCmd("formatblock", tg);
22852 insertText : function(txt)
22856 var range = this.createRange();
22857 range.deleteContents();
22858 //alert(Sender.getAttribute('label'));
22860 range.insertNode(this.doc.createTextNode(txt));
22866 * Executes a Midas editor command on the editor document and performs necessary focus and
22867 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22868 * @param {String} cmd The Midas command
22869 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22871 relayCmd : function(cmd, value){
22873 this.execCmd(cmd, value);
22874 this.owner.fireEvent('editorevent', this);
22875 //this.updateToolbar();
22876 this.owner.deferFocus();
22880 * Executes a Midas editor command directly on the editor document.
22881 * For visual commands, you should use {@link #relayCmd} instead.
22882 * <b>This should only be called after the editor is initialized.</b>
22883 * @param {String} cmd The Midas command
22884 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22886 execCmd : function(cmd, value){
22887 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22894 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22896 * @param {String} text | dom node..
22898 insertAtCursor : function(text)
22901 if(!this.activated){
22907 var r = this.doc.selection.createRange();
22918 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22922 // from jquery ui (MIT licenced)
22924 var win = this.win;
22926 if (win.getSelection && win.getSelection().getRangeAt) {
22927 range = win.getSelection().getRangeAt(0);
22928 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22929 range.insertNode(node);
22930 } else if (win.document.selection && win.document.selection.createRange) {
22931 // no firefox support
22932 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22933 win.document.selection.createRange().pasteHTML(txt);
22935 // no firefox support
22936 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22937 this.execCmd('InsertHTML', txt);
22946 mozKeyPress : function(e){
22948 var c = e.getCharCode(), cmd;
22951 c = String.fromCharCode(c).toLowerCase();
22965 this.cleanUpPaste.defer(100, this);
22973 e.preventDefault();
22981 fixKeys : function(){ // load time branching for fastest keydown performance
22983 return function(e){
22984 var k = e.getKey(), r;
22987 r = this.doc.selection.createRange();
22990 r.pasteHTML('    ');
22997 r = this.doc.selection.createRange();
22999 var target = r.parentElement();
23000 if(!target || target.tagName.toLowerCase() != 'li'){
23002 r.pasteHTML('<br />');
23008 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23009 this.cleanUpPaste.defer(100, this);
23015 }else if(Roo.isOpera){
23016 return function(e){
23017 var k = e.getKey();
23021 this.execCmd('InsertHTML','    ');
23024 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23025 this.cleanUpPaste.defer(100, this);
23030 }else if(Roo.isSafari){
23031 return function(e){
23032 var k = e.getKey();
23036 this.execCmd('InsertText','\t');
23040 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23041 this.cleanUpPaste.defer(100, this);
23049 getAllAncestors: function()
23051 var p = this.getSelectedNode();
23054 a.push(p); // push blank onto stack..
23055 p = this.getParentElement();
23059 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
23063 a.push(this.doc.body);
23067 lastSelNode : false,
23070 getSelection : function()
23072 this.assignDocWin();
23073 return Roo.isIE ? this.doc.selection : this.win.getSelection();
23076 getSelectedNode: function()
23078 // this may only work on Gecko!!!
23080 // should we cache this!!!!
23085 var range = this.createRange(this.getSelection()).cloneRange();
23088 var parent = range.parentElement();
23090 var testRange = range.duplicate();
23091 testRange.moveToElementText(parent);
23092 if (testRange.inRange(range)) {
23095 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
23098 parent = parent.parentElement;
23103 // is ancestor a text element.
23104 var ac = range.commonAncestorContainer;
23105 if (ac.nodeType == 3) {
23106 ac = ac.parentNode;
23109 var ar = ac.childNodes;
23112 var other_nodes = [];
23113 var has_other_nodes = false;
23114 for (var i=0;i<ar.length;i++) {
23115 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
23118 // fullly contained node.
23120 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
23125 // probably selected..
23126 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
23127 other_nodes.push(ar[i]);
23131 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
23136 has_other_nodes = true;
23138 if (!nodes.length && other_nodes.length) {
23139 nodes= other_nodes;
23141 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
23147 createRange: function(sel)
23149 // this has strange effects when using with
23150 // top toolbar - not sure if it's a great idea.
23151 //this.editor.contentWindow.focus();
23152 if (typeof sel != "undefined") {
23154 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
23156 return this.doc.createRange();
23159 return this.doc.createRange();
23162 getParentElement: function()
23165 this.assignDocWin();
23166 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
23168 var range = this.createRange(sel);
23171 var p = range.commonAncestorContainer;
23172 while (p.nodeType == 3) { // text node
23183 * Range intersection.. the hard stuff...
23187 * [ -- selected range --- ]
23191 * if end is before start or hits it. fail.
23192 * if start is after end or hits it fail.
23194 * if either hits (but other is outside. - then it's not
23200 // @see http://www.thismuchiknow.co.uk/?p=64.
23201 rangeIntersectsNode : function(range, node)
23203 var nodeRange = node.ownerDocument.createRange();
23205 nodeRange.selectNode(node);
23207 nodeRange.selectNodeContents(node);
23210 var rangeStartRange = range.cloneRange();
23211 rangeStartRange.collapse(true);
23213 var rangeEndRange = range.cloneRange();
23214 rangeEndRange.collapse(false);
23216 var nodeStartRange = nodeRange.cloneRange();
23217 nodeStartRange.collapse(true);
23219 var nodeEndRange = nodeRange.cloneRange();
23220 nodeEndRange.collapse(false);
23222 return rangeStartRange.compareBoundaryPoints(
23223 Range.START_TO_START, nodeEndRange) == -1 &&
23224 rangeEndRange.compareBoundaryPoints(
23225 Range.START_TO_START, nodeStartRange) == 1;
23229 rangeCompareNode : function(range, node)
23231 var nodeRange = node.ownerDocument.createRange();
23233 nodeRange.selectNode(node);
23235 nodeRange.selectNodeContents(node);
23239 range.collapse(true);
23241 nodeRange.collapse(true);
23243 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23244 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
23246 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23248 var nodeIsBefore = ss == 1;
23249 var nodeIsAfter = ee == -1;
23251 if (nodeIsBefore && nodeIsAfter) {
23254 if (!nodeIsBefore && nodeIsAfter) {
23255 return 1; //right trailed.
23258 if (nodeIsBefore && !nodeIsAfter) {
23259 return 2; // left trailed.
23265 // private? - in a new class?
23266 cleanUpPaste : function()
23268 // cleans up the whole document..
23269 Roo.log('cleanuppaste');
23271 this.cleanUpChildren(this.doc.body);
23272 var clean = this.cleanWordChars(this.doc.body.innerHTML);
23273 if (clean != this.doc.body.innerHTML) {
23274 this.doc.body.innerHTML = clean;
23279 cleanWordChars : function(input) {// change the chars to hex code
23280 var he = Roo.HtmlEditorCore;
23282 var output = input;
23283 Roo.each(he.swapCodes, function(sw) {
23284 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23286 output = output.replace(swapper, sw[1]);
23293 cleanUpChildren : function (n)
23295 if (!n.childNodes.length) {
23298 for (var i = n.childNodes.length-1; i > -1 ; i--) {
23299 this.cleanUpChild(n.childNodes[i]);
23306 cleanUpChild : function (node)
23309 //console.log(node);
23310 if (node.nodeName == "#text") {
23311 // clean up silly Windows -- stuff?
23314 if (node.nodeName == "#comment") {
23315 node.parentNode.removeChild(node);
23316 // clean up silly Windows -- stuff?
23319 var lcname = node.tagName.toLowerCase();
23320 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23321 // whitelist of tags..
23323 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23325 node.parentNode.removeChild(node);
23330 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23332 // spans with no attributes - just remove them..
23333 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
23334 remove_keep_children = true;
23337 // remove <a name=....> as rendering on yahoo mailer is borked with this.
23338 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23340 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23341 // remove_keep_children = true;
23344 if (remove_keep_children) {
23345 this.cleanUpChildren(node);
23346 // inserts everything just before this node...
23347 while (node.childNodes.length) {
23348 var cn = node.childNodes[0];
23349 node.removeChild(cn);
23350 node.parentNode.insertBefore(cn, node);
23352 node.parentNode.removeChild(node);
23356 if (!node.attributes || !node.attributes.length) {
23361 this.cleanUpChildren(node);
23365 function cleanAttr(n,v)
23368 if (v.match(/^\./) || v.match(/^\//)) {
23371 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23374 if (v.match(/^#/)) {
23377 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23378 node.removeAttribute(n);
23382 var cwhite = this.cwhite;
23383 var cblack = this.cblack;
23385 function cleanStyle(n,v)
23387 if (v.match(/expression/)) { //XSS?? should we even bother..
23388 node.removeAttribute(n);
23392 var parts = v.split(/;/);
23395 Roo.each(parts, function(p) {
23396 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23400 var l = p.split(':').shift().replace(/\s+/g,'');
23401 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23403 if ( cwhite.length && cblack.indexOf(l) > -1) {
23404 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23405 //node.removeAttribute(n);
23409 // only allow 'c whitelisted system attributes'
23410 if ( cwhite.length && cwhite.indexOf(l) < 0) {
23411 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23412 //node.removeAttribute(n);
23422 if (clean.length) {
23423 node.setAttribute(n, clean.join(';'));
23425 node.removeAttribute(n);
23431 for (var i = node.attributes.length-1; i > -1 ; i--) {
23432 var a = node.attributes[i];
23435 if (a.name.toLowerCase().substr(0,2)=='on') {
23436 node.removeAttribute(a.name);
23439 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23440 node.removeAttribute(a.name);
23443 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23444 cleanAttr(a.name,a.value); // fixme..
23447 if (a.name == 'style') {
23448 cleanStyle(a.name,a.value);
23451 /// clean up MS crap..
23452 // tecnically this should be a list of valid class'es..
23455 if (a.name == 'class') {
23456 if (a.value.match(/^Mso/)) {
23457 node.removeAttribute('class');
23460 if (a.value.match(/^body$/)) {
23461 node.removeAttribute('class');
23472 this.cleanUpChildren(node);
23478 * Clean up MS wordisms...
23480 cleanWord : function(node)
23483 this.cleanWord(this.doc.body);
23488 node.nodeName == 'SPAN' &&
23489 !node.hasAttributes() &&
23490 node.childNodes.length == 1 &&
23491 node.firstChild.nodeName == "#text"
23493 var textNode = node.firstChild;
23494 node.removeChild(textNode);
23495 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
23496 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
23498 node.parentNode.insertBefore(textNode, node);
23499 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
23500 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
23502 node.parentNode.removeChild(node);
23505 if (node.nodeName == "#text") {
23506 // clean up silly Windows -- stuff?
23509 if (node.nodeName == "#comment") {
23510 node.parentNode.removeChild(node);
23511 // clean up silly Windows -- stuff?
23515 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23516 node.parentNode.removeChild(node);
23519 //Roo.log(node.tagName);
23520 // remove - but keep children..
23521 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
23522 //Roo.log('-- removed');
23523 while (node.childNodes.length) {
23524 var cn = node.childNodes[0];
23525 node.removeChild(cn);
23526 node.parentNode.insertBefore(cn, node);
23527 // move node to parent - and clean it..
23528 this.cleanWord(cn);
23530 node.parentNode.removeChild(node);
23531 /// no need to iterate chidlren = it's got none..
23532 //this.iterateChildren(node, this.cleanWord);
23536 if (node.className.length) {
23538 var cn = node.className.split(/\W+/);
23540 Roo.each(cn, function(cls) {
23541 if (cls.match(/Mso[a-zA-Z]+/)) {
23546 node.className = cna.length ? cna.join(' ') : '';
23548 node.removeAttribute("class");
23552 if (node.hasAttribute("lang")) {
23553 node.removeAttribute("lang");
23556 if (node.hasAttribute("style")) {
23558 var styles = node.getAttribute("style").split(";");
23560 Roo.each(styles, function(s) {
23561 if (!s.match(/:/)) {
23564 var kv = s.split(":");
23565 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23568 // what ever is left... we allow.
23571 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23572 if (!nstyle.length) {
23573 node.removeAttribute('style');
23576 this.iterateChildren(node, this.cleanWord);
23582 * iterateChildren of a Node, calling fn each time, using this as the scole..
23583 * @param {DomNode} node node to iterate children of.
23584 * @param {Function} fn method of this class to call on each item.
23586 iterateChildren : function(node, fn)
23588 if (!node.childNodes.length) {
23591 for (var i = node.childNodes.length-1; i > -1 ; i--) {
23592 fn.call(this, node.childNodes[i])
23598 * cleanTableWidths.
23600 * Quite often pasting from word etc.. results in tables with column and widths.
23601 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23604 cleanTableWidths : function(node)
23609 this.cleanTableWidths(this.doc.body);
23614 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23617 Roo.log(node.tagName);
23618 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23619 this.iterateChildren(node, this.cleanTableWidths);
23622 if (node.hasAttribute('width')) {
23623 node.removeAttribute('width');
23627 if (node.hasAttribute("style")) {
23630 var styles = node.getAttribute("style").split(";");
23632 Roo.each(styles, function(s) {
23633 if (!s.match(/:/)) {
23636 var kv = s.split(":");
23637 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23640 // what ever is left... we allow.
23643 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23644 if (!nstyle.length) {
23645 node.removeAttribute('style');
23649 this.iterateChildren(node, this.cleanTableWidths);
23657 domToHTML : function(currentElement, depth, nopadtext) {
23659 depth = depth || 0;
23660 nopadtext = nopadtext || false;
23662 if (!currentElement) {
23663 return this.domToHTML(this.doc.body);
23666 //Roo.log(currentElement);
23668 var allText = false;
23669 var nodeName = currentElement.nodeName;
23670 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23672 if (nodeName == '#text') {
23674 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23679 if (nodeName != 'BODY') {
23682 // Prints the node tagName, such as <A>, <IMG>, etc
23685 for(i = 0; i < currentElement.attributes.length;i++) {
23687 var aname = currentElement.attributes.item(i).name;
23688 if (!currentElement.attributes.item(i).value.length) {
23691 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23694 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23703 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23706 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23711 // Traverse the tree
23713 var currentElementChild = currentElement.childNodes.item(i);
23714 var allText = true;
23715 var innerHTML = '';
23717 while (currentElementChild) {
23718 // Formatting code (indent the tree so it looks nice on the screen)
23719 var nopad = nopadtext;
23720 if (lastnode == 'SPAN') {
23724 if (currentElementChild.nodeName == '#text') {
23725 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23726 toadd = nopadtext ? toadd : toadd.trim();
23727 if (!nopad && toadd.length > 80) {
23728 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23730 innerHTML += toadd;
23733 currentElementChild = currentElement.childNodes.item(i);
23739 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23741 // Recursively traverse the tree structure of the child node
23742 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23743 lastnode = currentElementChild.nodeName;
23745 currentElementChild=currentElement.childNodes.item(i);
23751 // The remaining code is mostly for formatting the tree
23752 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23757 ret+= "</"+tagName+">";
23763 applyBlacklists : function()
23765 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23766 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23770 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23771 if (b.indexOf(tag) > -1) {
23774 this.white.push(tag);
23778 Roo.each(w, function(tag) {
23779 if (b.indexOf(tag) > -1) {
23782 if (this.white.indexOf(tag) > -1) {
23785 this.white.push(tag);
23790 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23791 if (w.indexOf(tag) > -1) {
23794 this.black.push(tag);
23798 Roo.each(b, function(tag) {
23799 if (w.indexOf(tag) > -1) {
23802 if (this.black.indexOf(tag) > -1) {
23805 this.black.push(tag);
23810 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23811 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23815 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23816 if (b.indexOf(tag) > -1) {
23819 this.cwhite.push(tag);
23823 Roo.each(w, function(tag) {
23824 if (b.indexOf(tag) > -1) {
23827 if (this.cwhite.indexOf(tag) > -1) {
23830 this.cwhite.push(tag);
23835 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23836 if (w.indexOf(tag) > -1) {
23839 this.cblack.push(tag);
23843 Roo.each(b, function(tag) {
23844 if (w.indexOf(tag) > -1) {
23847 if (this.cblack.indexOf(tag) > -1) {
23850 this.cblack.push(tag);
23855 setStylesheets : function(stylesheets)
23857 if(typeof(stylesheets) == 'string'){
23858 Roo.get(this.iframe.contentDocument.head).createChild({
23860 rel : 'stylesheet',
23869 Roo.each(stylesheets, function(s) {
23874 Roo.get(_this.iframe.contentDocument.head).createChild({
23876 rel : 'stylesheet',
23885 removeStylesheets : function()
23889 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23894 setStyle : function(style)
23896 Roo.get(this.iframe.contentDocument.head).createChild({
23905 // hide stuff that is not compatible
23919 * @event specialkey
23923 * @cfg {String} fieldClass @hide
23926 * @cfg {String} focusClass @hide
23929 * @cfg {String} autoCreate @hide
23932 * @cfg {String} inputType @hide
23935 * @cfg {String} invalidClass @hide
23938 * @cfg {String} invalidText @hide
23941 * @cfg {String} msgFx @hide
23944 * @cfg {String} validateOnBlur @hide
23948 Roo.HtmlEditorCore.white = [
23949 'area', 'br', 'img', 'input', 'hr', 'wbr',
23951 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23952 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23953 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23954 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23955 'table', 'ul', 'xmp',
23957 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23960 'dir', 'menu', 'ol', 'ul', 'dl',
23966 Roo.HtmlEditorCore.black = [
23967 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23969 'base', 'basefont', 'bgsound', 'blink', 'body',
23970 'frame', 'frameset', 'head', 'html', 'ilayer',
23971 'iframe', 'layer', 'link', 'meta', 'object',
23972 'script', 'style' ,'title', 'xml' // clean later..
23974 Roo.HtmlEditorCore.clean = [
23975 'script', 'style', 'title', 'xml'
23977 Roo.HtmlEditorCore.remove = [
23982 Roo.HtmlEditorCore.ablack = [
23986 Roo.HtmlEditorCore.aclean = [
23987 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23991 Roo.HtmlEditorCore.pwhite= [
23992 'http', 'https', 'mailto'
23995 // white listed style attributes.
23996 Roo.HtmlEditorCore.cwhite= [
23997 // 'text-align', /// default is to allow most things..
24003 // black listed style attributes.
24004 Roo.HtmlEditorCore.cblack= [
24005 // 'font-size' -- this can be set by the project
24009 Roo.HtmlEditorCore.swapCodes =[
24028 * @class Roo.bootstrap.HtmlEditor
24029 * @extends Roo.bootstrap.TextArea
24030 * Bootstrap HtmlEditor class
24033 * Create a new HtmlEditor
24034 * @param {Object} config The config object
24037 Roo.bootstrap.HtmlEditor = function(config){
24038 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
24039 if (!this.toolbars) {
24040 this.toolbars = [];
24043 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
24046 * @event initialize
24047 * Fires when the editor is fully initialized (including the iframe)
24048 * @param {HtmlEditor} this
24053 * Fires when the editor is first receives the focus. Any insertion must wait
24054 * until after this event.
24055 * @param {HtmlEditor} this
24059 * @event beforesync
24060 * Fires before the textarea is updated with content from the editor iframe. Return false
24061 * to cancel the sync.
24062 * @param {HtmlEditor} this
24063 * @param {String} html
24067 * @event beforepush
24068 * Fires before the iframe editor is updated with content from the textarea. Return false
24069 * to cancel the push.
24070 * @param {HtmlEditor} this
24071 * @param {String} html
24076 * Fires when the textarea is updated with content from the editor iframe.
24077 * @param {HtmlEditor} this
24078 * @param {String} html
24083 * Fires when the iframe editor is updated with content from the textarea.
24084 * @param {HtmlEditor} this
24085 * @param {String} html
24089 * @event editmodechange
24090 * Fires when the editor switches edit modes
24091 * @param {HtmlEditor} this
24092 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24094 editmodechange: true,
24096 * @event editorevent
24097 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24098 * @param {HtmlEditor} this
24102 * @event firstfocus
24103 * Fires when on first focus - needed by toolbars..
24104 * @param {HtmlEditor} this
24109 * Auto save the htmlEditor value as a file into Events
24110 * @param {HtmlEditor} this
24114 * @event savedpreview
24115 * preview the saved version of htmlEditor
24116 * @param {HtmlEditor} this
24123 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
24127 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24132 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
24137 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24142 * @cfg {Number} height (in pixels)
24146 * @cfg {Number} width (in pixels)
24151 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24154 stylesheets: false,
24159 // private properties
24160 validationEvent : false,
24162 initialized : false,
24165 onFocus : Roo.emptyFn,
24167 hideMode:'offsets',
24169 tbContainer : false,
24173 toolbarContainer :function() {
24174 return this.wrap.select('.x-html-editor-tb',true).first();
24178 * Protected method that will not generally be called directly. It
24179 * is called when the editor creates its toolbar. Override this method if you need to
24180 * add custom toolbar buttons.
24181 * @param {HtmlEditor} editor
24183 createToolbar : function(){
24184 Roo.log('renewing');
24185 Roo.log("create toolbars");
24187 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
24188 this.toolbars[0].render(this.toolbarContainer());
24192 // if (!editor.toolbars || !editor.toolbars.length) {
24193 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
24196 // for (var i =0 ; i < editor.toolbars.length;i++) {
24197 // editor.toolbars[i] = Roo.factory(
24198 // typeof(editor.toolbars[i]) == 'string' ?
24199 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
24200 // Roo.bootstrap.HtmlEditor);
24201 // editor.toolbars[i].init(editor);
24207 onRender : function(ct, position)
24209 // Roo.log("Call onRender: " + this.xtype);
24211 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
24213 this.wrap = this.inputEl().wrap({
24214 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24217 this.editorcore.onRender(ct, position);
24219 if (this.resizable) {
24220 this.resizeEl = new Roo.Resizable(this.wrap, {
24224 minHeight : this.height,
24225 height: this.height,
24226 handles : this.resizable,
24229 resize : function(r, w, h) {
24230 _t.onResize(w,h); // -something
24236 this.createToolbar(this);
24239 if(!this.width && this.resizable){
24240 this.setSize(this.wrap.getSize());
24242 if (this.resizeEl) {
24243 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24244 // should trigger onReize..
24250 onResize : function(w, h)
24252 Roo.log('resize: ' +w + ',' + h );
24253 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
24257 if(this.inputEl() ){
24258 if(typeof w == 'number'){
24259 var aw = w - this.wrap.getFrameWidth('lr');
24260 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24263 if(typeof h == 'number'){
24264 var tbh = -11; // fixme it needs to tool bar size!
24265 for (var i =0; i < this.toolbars.length;i++) {
24266 // fixme - ask toolbars for heights?
24267 tbh += this.toolbars[i].el.getHeight();
24268 //if (this.toolbars[i].footer) {
24269 // tbh += this.toolbars[i].footer.el.getHeight();
24277 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24278 ah -= 5; // knock a few pixes off for look..
24279 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24283 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24284 this.editorcore.onResize(ew,eh);
24289 * Toggles the editor between standard and source edit mode.
24290 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24292 toggleSourceEdit : function(sourceEditMode)
24294 this.editorcore.toggleSourceEdit(sourceEditMode);
24296 if(this.editorcore.sourceEditMode){
24297 Roo.log('editor - showing textarea');
24300 // Roo.log(this.syncValue());
24302 this.inputEl().removeClass(['hide', 'x-hidden']);
24303 this.inputEl().dom.removeAttribute('tabIndex');
24304 this.inputEl().focus();
24306 Roo.log('editor - hiding textarea');
24308 // Roo.log(this.pushValue());
24311 this.inputEl().addClass(['hide', 'x-hidden']);
24312 this.inputEl().dom.setAttribute('tabIndex', -1);
24313 //this.deferFocus();
24316 if(this.resizable){
24317 this.setSize(this.wrap.getSize());
24320 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24323 // private (for BoxComponent)
24324 adjustSize : Roo.BoxComponent.prototype.adjustSize,
24326 // private (for BoxComponent)
24327 getResizeEl : function(){
24331 // private (for BoxComponent)
24332 getPositionEl : function(){
24337 initEvents : function(){
24338 this.originalValue = this.getValue();
24342 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24345 // markInvalid : Roo.emptyFn,
24347 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24350 // clearInvalid : Roo.emptyFn,
24352 setValue : function(v){
24353 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24354 this.editorcore.pushValue();
24359 deferFocus : function(){
24360 this.focus.defer(10, this);
24364 focus : function(){
24365 this.editorcore.focus();
24371 onDestroy : function(){
24377 for (var i =0; i < this.toolbars.length;i++) {
24378 // fixme - ask toolbars for heights?
24379 this.toolbars[i].onDestroy();
24382 this.wrap.dom.innerHTML = '';
24383 this.wrap.remove();
24388 onFirstFocus : function(){
24389 //Roo.log("onFirstFocus");
24390 this.editorcore.onFirstFocus();
24391 for (var i =0; i < this.toolbars.length;i++) {
24392 this.toolbars[i].onFirstFocus();
24398 syncValue : function()
24400 this.editorcore.syncValue();
24403 pushValue : function()
24405 this.editorcore.pushValue();
24409 // hide stuff that is not compatible
24423 * @event specialkey
24427 * @cfg {String} fieldClass @hide
24430 * @cfg {String} focusClass @hide
24433 * @cfg {String} autoCreate @hide
24436 * @cfg {String} inputType @hide
24440 * @cfg {String} invalidText @hide
24443 * @cfg {String} msgFx @hide
24446 * @cfg {String} validateOnBlur @hide
24455 Roo.namespace('Roo.bootstrap.htmleditor');
24457 * @class Roo.bootstrap.HtmlEditorToolbar1
24463 new Roo.bootstrap.HtmlEditor({
24466 new Roo.bootstrap.HtmlEditorToolbar1({
24467 disable : { fonts: 1 , format: 1, ..., ... , ...],
24473 * @cfg {Object} disable List of elements to disable..
24474 * @cfg {Array} btns List of additional buttons.
24478 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24481 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24484 Roo.apply(this, config);
24486 // default disabled, based on 'good practice'..
24487 this.disable = this.disable || {};
24488 Roo.applyIf(this.disable, {
24491 specialElements : true
24493 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24495 this.editor = config.editor;
24496 this.editorcore = config.editor.editorcore;
24498 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24500 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24501 // dont call parent... till later.
24503 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
24508 editorcore : false,
24513 "h1","h2","h3","h4","h5","h6",
24515 "abbr", "acronym", "address", "cite", "samp", "var",
24519 onRender : function(ct, position)
24521 // Roo.log("Call onRender: " + this.xtype);
24523 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24525 this.el.dom.style.marginBottom = '0';
24527 var editorcore = this.editorcore;
24528 var editor= this.editor;
24531 var btn = function(id,cmd , toggle, handler, html){
24533 var event = toggle ? 'toggle' : 'click';
24538 xns: Roo.bootstrap,
24542 enableToggle:toggle !== false,
24544 pressed : toggle ? false : null,
24547 a.listeners[toggle ? 'toggle' : 'click'] = function() {
24548 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
24554 // var cb_box = function...
24559 xns: Roo.bootstrap,
24564 xns: Roo.bootstrap,
24568 Roo.each(this.formats, function(f) {
24569 style.menu.items.push({
24571 xns: Roo.bootstrap,
24572 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24577 editorcore.insertTag(this.tagname);
24584 children.push(style);
24586 btn('bold',false,true);
24587 btn('italic',false,true);
24588 btn('align-left', 'justifyleft',true);
24589 btn('align-center', 'justifycenter',true);
24590 btn('align-right' , 'justifyright',true);
24591 btn('link', false, false, function(btn) {
24592 //Roo.log("create link?");
24593 var url = prompt(this.createLinkText, this.defaultLinkValue);
24594 if(url && url != 'http:/'+'/'){
24595 this.editorcore.relayCmd('createlink', url);
24598 btn('list','insertunorderedlist',true);
24599 btn('pencil', false,true, function(btn){
24601 this.toggleSourceEdit(btn.pressed);
24604 if (this.editor.btns.length > 0) {
24605 for (var i = 0; i<this.editor.btns.length; i++) {
24606 children.push(this.editor.btns[i]);
24614 xns: Roo.bootstrap,
24619 xns: Roo.bootstrap,
24624 cog.menu.items.push({
24626 xns: Roo.bootstrap,
24627 html : Clean styles,
24632 editorcore.insertTag(this.tagname);
24641 this.xtype = 'NavSimplebar';
24643 for(var i=0;i< children.length;i++) {
24645 this.buttons.add(this.addxtypeChild(children[i]));
24649 editor.on('editorevent', this.updateToolbar, this);
24651 onBtnClick : function(id)
24653 this.editorcore.relayCmd(id);
24654 this.editorcore.focus();
24658 * Protected method that will not generally be called directly. It triggers
24659 * a toolbar update by reading the markup state of the current selection in the editor.
24661 updateToolbar: function(){
24663 if(!this.editorcore.activated){
24664 this.editor.onFirstFocus(); // is this neeed?
24668 var btns = this.buttons;
24669 var doc = this.editorcore.doc;
24670 btns.get('bold').setActive(doc.queryCommandState('bold'));
24671 btns.get('italic').setActive(doc.queryCommandState('italic'));
24672 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24674 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24675 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24676 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24678 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24679 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24682 var ans = this.editorcore.getAllAncestors();
24683 if (this.formatCombo) {
24686 var store = this.formatCombo.store;
24687 this.formatCombo.setValue("");
24688 for (var i =0; i < ans.length;i++) {
24689 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24691 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24699 // hides menus... - so this cant be on a menu...
24700 Roo.bootstrap.MenuMgr.hideAll();
24702 Roo.bootstrap.MenuMgr.hideAll();
24703 //this.editorsyncValue();
24705 onFirstFocus: function() {
24706 this.buttons.each(function(item){
24710 toggleSourceEdit : function(sourceEditMode){
24713 if(sourceEditMode){
24714 Roo.log("disabling buttons");
24715 this.buttons.each( function(item){
24716 if(item.cmd != 'pencil'){
24722 Roo.log("enabling buttons");
24723 if(this.editorcore.initialized){
24724 this.buttons.each( function(item){
24730 Roo.log("calling toggole on editor");
24731 // tell the editor that it's been pressed..
24732 this.editor.toggleSourceEdit(sourceEditMode);
24742 * @class Roo.bootstrap.Table.AbstractSelectionModel
24743 * @extends Roo.util.Observable
24744 * Abstract base class for grid SelectionModels. It provides the interface that should be
24745 * implemented by descendant classes. This class should not be directly instantiated.
24748 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24749 this.locked = false;
24750 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24754 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24755 /** @ignore Called by the grid automatically. Do not call directly. */
24756 init : function(grid){
24762 * Locks the selections.
24765 this.locked = true;
24769 * Unlocks the selections.
24771 unlock : function(){
24772 this.locked = false;
24776 * Returns true if the selections are locked.
24777 * @return {Boolean}
24779 isLocked : function(){
24780 return this.locked;
24784 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24785 * @class Roo.bootstrap.Table.RowSelectionModel
24786 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24787 * It supports multiple selections and keyboard selection/navigation.
24789 * @param {Object} config
24792 Roo.bootstrap.Table.RowSelectionModel = function(config){
24793 Roo.apply(this, config);
24794 this.selections = new Roo.util.MixedCollection(false, function(o){
24799 this.lastActive = false;
24803 * @event selectionchange
24804 * Fires when the selection changes
24805 * @param {SelectionModel} this
24807 "selectionchange" : true,
24809 * @event afterselectionchange
24810 * Fires after the selection changes (eg. by key press or clicking)
24811 * @param {SelectionModel} this
24813 "afterselectionchange" : true,
24815 * @event beforerowselect
24816 * Fires when a row is selected being selected, return false to cancel.
24817 * @param {SelectionModel} this
24818 * @param {Number} rowIndex The selected index
24819 * @param {Boolean} keepExisting False if other selections will be cleared
24821 "beforerowselect" : true,
24824 * Fires when a row is selected.
24825 * @param {SelectionModel} this
24826 * @param {Number} rowIndex The selected index
24827 * @param {Roo.data.Record} r The record
24829 "rowselect" : true,
24831 * @event rowdeselect
24832 * Fires when a row is deselected.
24833 * @param {SelectionModel} this
24834 * @param {Number} rowIndex The selected index
24836 "rowdeselect" : true
24838 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24839 this.locked = false;
24842 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24844 * @cfg {Boolean} singleSelect
24845 * True to allow selection of only one row at a time (defaults to false)
24847 singleSelect : false,
24850 initEvents : function()
24853 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24854 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24855 //}else{ // allow click to work like normal
24856 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24858 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24859 this.grid.on("rowclick", this.handleMouseDown, this);
24861 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24862 "up" : function(e){
24864 this.selectPrevious(e.shiftKey);
24865 }else if(this.last !== false && this.lastActive !== false){
24866 var last = this.last;
24867 this.selectRange(this.last, this.lastActive-1);
24868 this.grid.getView().focusRow(this.lastActive);
24869 if(last !== false){
24873 this.selectFirstRow();
24875 this.fireEvent("afterselectionchange", this);
24877 "down" : function(e){
24879 this.selectNext(e.shiftKey);
24880 }else if(this.last !== false && this.lastActive !== false){
24881 var last = this.last;
24882 this.selectRange(this.last, this.lastActive+1);
24883 this.grid.getView().focusRow(this.lastActive);
24884 if(last !== false){
24888 this.selectFirstRow();
24890 this.fireEvent("afterselectionchange", this);
24894 this.grid.store.on('load', function(){
24895 this.selections.clear();
24898 var view = this.grid.view;
24899 view.on("refresh", this.onRefresh, this);
24900 view.on("rowupdated", this.onRowUpdated, this);
24901 view.on("rowremoved", this.onRemove, this);
24906 onRefresh : function()
24908 var ds = this.grid.store, i, v = this.grid.view;
24909 var s = this.selections;
24910 s.each(function(r){
24911 if((i = ds.indexOfId(r.id)) != -1){
24920 onRemove : function(v, index, r){
24921 this.selections.remove(r);
24925 onRowUpdated : function(v, index, r){
24926 if(this.isSelected(r)){
24927 v.onRowSelect(index);
24933 * @param {Array} records The records to select
24934 * @param {Boolean} keepExisting (optional) True to keep existing selections
24936 selectRecords : function(records, keepExisting)
24939 this.clearSelections();
24941 var ds = this.grid.store;
24942 for(var i = 0, len = records.length; i < len; i++){
24943 this.selectRow(ds.indexOf(records[i]), true);
24948 * Gets the number of selected rows.
24951 getCount : function(){
24952 return this.selections.length;
24956 * Selects the first row in the grid.
24958 selectFirstRow : function(){
24963 * Select the last row.
24964 * @param {Boolean} keepExisting (optional) True to keep existing selections
24966 selectLastRow : function(keepExisting){
24967 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24968 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24972 * Selects the row immediately following the last selected row.
24973 * @param {Boolean} keepExisting (optional) True to keep existing selections
24975 selectNext : function(keepExisting)
24977 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24978 this.selectRow(this.last+1, keepExisting);
24979 this.grid.getView().focusRow(this.last);
24984 * Selects the row that precedes the last selected row.
24985 * @param {Boolean} keepExisting (optional) True to keep existing selections
24987 selectPrevious : function(keepExisting){
24989 this.selectRow(this.last-1, keepExisting);
24990 this.grid.getView().focusRow(this.last);
24995 * Returns the selected records
24996 * @return {Array} Array of selected records
24998 getSelections : function(){
24999 return [].concat(this.selections.items);
25003 * Returns the first selected record.
25006 getSelected : function(){
25007 return this.selections.itemAt(0);
25012 * Clears all selections.
25014 clearSelections : function(fast)
25020 var ds = this.grid.store;
25021 var s = this.selections;
25022 s.each(function(r){
25023 this.deselectRow(ds.indexOfId(r.id));
25027 this.selections.clear();
25034 * Selects all rows.
25036 selectAll : function(){
25040 this.selections.clear();
25041 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
25042 this.selectRow(i, true);
25047 * Returns True if there is a selection.
25048 * @return {Boolean}
25050 hasSelection : function(){
25051 return this.selections.length > 0;
25055 * Returns True if the specified row is selected.
25056 * @param {Number/Record} record The record or index of the record to check
25057 * @return {Boolean}
25059 isSelected : function(index){
25060 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
25061 return (r && this.selections.key(r.id) ? true : false);
25065 * Returns True if the specified record id is selected.
25066 * @param {String} id The id of record to check
25067 * @return {Boolean}
25069 isIdSelected : function(id){
25070 return (this.selections.key(id) ? true : false);
25075 handleMouseDBClick : function(e, t){
25079 handleMouseDown : function(e, t)
25081 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
25082 if(this.isLocked() || rowIndex < 0 ){
25085 if(e.shiftKey && this.last !== false){
25086 var last = this.last;
25087 this.selectRange(last, rowIndex, e.ctrlKey);
25088 this.last = last; // reset the last
25092 var isSelected = this.isSelected(rowIndex);
25093 //Roo.log("select row:" + rowIndex);
25095 this.deselectRow(rowIndex);
25097 this.selectRow(rowIndex, true);
25101 if(e.button !== 0 && isSelected){
25102 alert('rowIndex 2: ' + rowIndex);
25103 view.focusRow(rowIndex);
25104 }else if(e.ctrlKey && isSelected){
25105 this.deselectRow(rowIndex);
25106 }else if(!isSelected){
25107 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
25108 view.focusRow(rowIndex);
25112 this.fireEvent("afterselectionchange", this);
25115 handleDragableRowClick : function(grid, rowIndex, e)
25117 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
25118 this.selectRow(rowIndex, false);
25119 grid.view.focusRow(rowIndex);
25120 this.fireEvent("afterselectionchange", this);
25125 * Selects multiple rows.
25126 * @param {Array} rows Array of the indexes of the row to select
25127 * @param {Boolean} keepExisting (optional) True to keep existing selections
25129 selectRows : function(rows, keepExisting){
25131 this.clearSelections();
25133 for(var i = 0, len = rows.length; i < len; i++){
25134 this.selectRow(rows[i], true);
25139 * Selects a range of rows. All rows in between startRow and endRow are also selected.
25140 * @param {Number} startRow The index of the first row in the range
25141 * @param {Number} endRow The index of the last row in the range
25142 * @param {Boolean} keepExisting (optional) True to retain existing selections
25144 selectRange : function(startRow, endRow, keepExisting){
25149 this.clearSelections();
25151 if(startRow <= endRow){
25152 for(var i = startRow; i <= endRow; i++){
25153 this.selectRow(i, true);
25156 for(var i = startRow; i >= endRow; i--){
25157 this.selectRow(i, true);
25163 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
25164 * @param {Number} startRow The index of the first row in the range
25165 * @param {Number} endRow The index of the last row in the range
25167 deselectRange : function(startRow, endRow, preventViewNotify){
25171 for(var i = startRow; i <= endRow; i++){
25172 this.deselectRow(i, preventViewNotify);
25178 * @param {Number} row The index of the row to select
25179 * @param {Boolean} keepExisting (optional) True to keep existing selections
25181 selectRow : function(index, keepExisting, preventViewNotify)
25183 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
25186 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
25187 if(!keepExisting || this.singleSelect){
25188 this.clearSelections();
25191 var r = this.grid.store.getAt(index);
25192 //console.log('selectRow - record id :' + r.id);
25194 this.selections.add(r);
25195 this.last = this.lastActive = index;
25196 if(!preventViewNotify){
25197 var proxy = new Roo.Element(
25198 this.grid.getRowDom(index)
25200 proxy.addClass('bg-info info');
25202 this.fireEvent("rowselect", this, index, r);
25203 this.fireEvent("selectionchange", this);
25209 * @param {Number} row The index of the row to deselect
25211 deselectRow : function(index, preventViewNotify)
25216 if(this.last == index){
25219 if(this.lastActive == index){
25220 this.lastActive = false;
25223 var r = this.grid.store.getAt(index);
25228 this.selections.remove(r);
25229 //.console.log('deselectRow - record id :' + r.id);
25230 if(!preventViewNotify){
25232 var proxy = new Roo.Element(
25233 this.grid.getRowDom(index)
25235 proxy.removeClass('bg-info info');
25237 this.fireEvent("rowdeselect", this, index);
25238 this.fireEvent("selectionchange", this);
25242 restoreLast : function(){
25244 this.last = this._last;
25249 acceptsNav : function(row, col, cm){
25250 return !cm.isHidden(col) && cm.isCellEditable(col, row);
25254 onEditorKey : function(field, e){
25255 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25260 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25262 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25264 }else if(k == e.ENTER && !e.ctrlKey){
25268 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25270 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25272 }else if(k == e.ESC){
25276 g.startEditing(newCell[0], newCell[1]);
25282 * Ext JS Library 1.1.1
25283 * Copyright(c) 2006-2007, Ext JS, LLC.
25285 * Originally Released Under LGPL - original licence link has changed is not relivant.
25288 * <script type="text/javascript">
25292 * @class Roo.bootstrap.PagingToolbar
25293 * @extends Roo.bootstrap.NavSimplebar
25294 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25296 * Create a new PagingToolbar
25297 * @param {Object} config The config object
25298 * @param {Roo.data.Store} store
25300 Roo.bootstrap.PagingToolbar = function(config)
25302 // old args format still supported... - xtype is prefered..
25303 // created from xtype...
25305 this.ds = config.dataSource;
25307 if (config.store && !this.ds) {
25308 this.store= Roo.factory(config.store, Roo.data);
25309 this.ds = this.store;
25310 this.ds.xmodule = this.xmodule || false;
25313 this.toolbarItems = [];
25314 if (config.items) {
25315 this.toolbarItems = config.items;
25318 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25323 this.bind(this.ds);
25326 if (Roo.bootstrap.version == 4) {
25327 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25329 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25334 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25336 * @cfg {Roo.data.Store} dataSource
25337 * The underlying data store providing the paged data
25340 * @cfg {String/HTMLElement/Element} container
25341 * container The id or element that will contain the toolbar
25344 * @cfg {Boolean} displayInfo
25345 * True to display the displayMsg (defaults to false)
25348 * @cfg {Number} pageSize
25349 * The number of records to display per page (defaults to 20)
25353 * @cfg {String} displayMsg
25354 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25356 displayMsg : 'Displaying {0} - {1} of {2}',
25358 * @cfg {String} emptyMsg
25359 * The message to display when no records are found (defaults to "No data to display")
25361 emptyMsg : 'No data to display',
25363 * Customizable piece of the default paging text (defaults to "Page")
25366 beforePageText : "Page",
25368 * Customizable piece of the default paging text (defaults to "of %0")
25371 afterPageText : "of {0}",
25373 * Customizable piece of the default paging text (defaults to "First Page")
25376 firstText : "First Page",
25378 * Customizable piece of the default paging text (defaults to "Previous Page")
25381 prevText : "Previous Page",
25383 * Customizable piece of the default paging text (defaults to "Next Page")
25386 nextText : "Next Page",
25388 * Customizable piece of the default paging text (defaults to "Last Page")
25391 lastText : "Last Page",
25393 * Customizable piece of the default paging text (defaults to "Refresh")
25396 refreshText : "Refresh",
25400 onRender : function(ct, position)
25402 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25403 this.navgroup.parentId = this.id;
25404 this.navgroup.onRender(this.el, null);
25405 // add the buttons to the navgroup
25407 if(this.displayInfo){
25408 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25409 this.displayEl = this.el.select('.x-paging-info', true).first();
25410 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25411 // this.displayEl = navel.el.select('span',true).first();
25417 Roo.each(_this.buttons, function(e){ // this might need to use render????
25418 Roo.factory(e).render(_this.el);
25422 Roo.each(_this.toolbarItems, function(e) {
25423 _this.navgroup.addItem(e);
25427 this.first = this.navgroup.addItem({
25428 tooltip: this.firstText,
25429 cls: "prev btn-outline-secondary",
25430 html : ' <i class="fa fa-step-backward"></i>',
25432 preventDefault: true,
25433 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25436 this.prev = this.navgroup.addItem({
25437 tooltip: this.prevText,
25438 cls: "prev btn-outline-secondary",
25439 html : ' <i class="fa fa-backward"></i>',
25441 preventDefault: true,
25442 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
25444 //this.addSeparator();
25447 var field = this.navgroup.addItem( {
25449 cls : 'x-paging-position btn-outline-secondary',
25451 html : this.beforePageText +
25452 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25453 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
25456 this.field = field.el.select('input', true).first();
25457 this.field.on("keydown", this.onPagingKeydown, this);
25458 this.field.on("focus", function(){this.dom.select();});
25461 this.afterTextEl = field.el.select('.x-paging-after',true).first();
25462 //this.field.setHeight(18);
25463 //this.addSeparator();
25464 this.next = this.navgroup.addItem({
25465 tooltip: this.nextText,
25466 cls: "next btn-outline-secondary",
25467 html : ' <i class="fa fa-forward"></i>',
25469 preventDefault: true,
25470 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
25472 this.last = this.navgroup.addItem({
25473 tooltip: this.lastText,
25474 html : ' <i class="fa fa-step-forward"></i>',
25475 cls: "next btn-outline-secondary",
25477 preventDefault: true,
25478 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
25480 //this.addSeparator();
25481 this.loading = this.navgroup.addItem({
25482 tooltip: this.refreshText,
25483 cls: "btn-outline-secondary",
25484 html : ' <i class="fa fa-refresh"></i>',
25485 preventDefault: true,
25486 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25492 updateInfo : function(){
25493 if(this.displayEl){
25494 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25495 var msg = count == 0 ?
25499 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
25501 this.displayEl.update(msg);
25506 onLoad : function(ds, r, o)
25508 this.cursor = o.params.start ? o.params.start : 0;
25510 var d = this.getPageData(),
25515 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25516 this.field.dom.value = ap;
25517 this.first.setDisabled(ap == 1);
25518 this.prev.setDisabled(ap == 1);
25519 this.next.setDisabled(ap == ps);
25520 this.last.setDisabled(ap == ps);
25521 this.loading.enable();
25526 getPageData : function(){
25527 var total = this.ds.getTotalCount();
25530 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25531 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25536 onLoadError : function(){
25537 this.loading.enable();
25541 onPagingKeydown : function(e){
25542 var k = e.getKey();
25543 var d = this.getPageData();
25545 var v = this.field.dom.value, pageNum;
25546 if(!v || isNaN(pageNum = parseInt(v, 10))){
25547 this.field.dom.value = d.activePage;
25550 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25551 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25554 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))
25556 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25557 this.field.dom.value = pageNum;
25558 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25561 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25563 var v = this.field.dom.value, pageNum;
25564 var increment = (e.shiftKey) ? 10 : 1;
25565 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25568 if(!v || isNaN(pageNum = parseInt(v, 10))) {
25569 this.field.dom.value = d.activePage;
25572 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25574 this.field.dom.value = parseInt(v, 10) + increment;
25575 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25576 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25583 beforeLoad : function(){
25585 this.loading.disable();
25590 onClick : function(which){
25599 ds.load({params:{start: 0, limit: this.pageSize}});
25602 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25605 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25608 var total = ds.getTotalCount();
25609 var extra = total % this.pageSize;
25610 var lastStart = extra ? (total - extra) : total-this.pageSize;
25611 ds.load({params:{start: lastStart, limit: this.pageSize}});
25614 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25620 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25621 * @param {Roo.data.Store} store The data store to unbind
25623 unbind : function(ds){
25624 ds.un("beforeload", this.beforeLoad, this);
25625 ds.un("load", this.onLoad, this);
25626 ds.un("loadexception", this.onLoadError, this);
25627 ds.un("remove", this.updateInfo, this);
25628 ds.un("add", this.updateInfo, this);
25629 this.ds = undefined;
25633 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25634 * @param {Roo.data.Store} store The data store to bind
25636 bind : function(ds){
25637 ds.on("beforeload", this.beforeLoad, this);
25638 ds.on("load", this.onLoad, this);
25639 ds.on("loadexception", this.onLoadError, this);
25640 ds.on("remove", this.updateInfo, this);
25641 ds.on("add", this.updateInfo, this);
25652 * @class Roo.bootstrap.MessageBar
25653 * @extends Roo.bootstrap.Component
25654 * Bootstrap MessageBar class
25655 * @cfg {String} html contents of the MessageBar
25656 * @cfg {String} weight (info | success | warning | danger) default info
25657 * @cfg {String} beforeClass insert the bar before the given class
25658 * @cfg {Boolean} closable (true | false) default false
25659 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25662 * Create a new Element
25663 * @param {Object} config The config object
25666 Roo.bootstrap.MessageBar = function(config){
25667 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25670 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25676 beforeClass: 'bootstrap-sticky-wrap',
25678 getAutoCreate : function(){
25682 cls: 'alert alert-dismissable alert-' + this.weight,
25687 html: this.html || ''
25693 cfg.cls += ' alert-messages-fixed';
25707 onRender : function(ct, position)
25709 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25712 var cfg = Roo.apply({}, this.getAutoCreate());
25716 cfg.cls += ' ' + this.cls;
25719 cfg.style = this.style;
25721 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25723 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25726 this.el.select('>button.close').on('click', this.hide, this);
25732 if (!this.rendered) {
25738 this.fireEvent('show', this);
25744 if (!this.rendered) {
25750 this.fireEvent('hide', this);
25753 update : function()
25755 // var e = this.el.dom.firstChild;
25757 // if(this.closable){
25758 // e = e.nextSibling;
25761 // e.data = this.html || '';
25763 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25779 * @class Roo.bootstrap.Graph
25780 * @extends Roo.bootstrap.Component
25781 * Bootstrap Graph class
25785 @cfg {String} graphtype bar | vbar | pie
25786 @cfg {number} g_x coodinator | centre x (pie)
25787 @cfg {number} g_y coodinator | centre y (pie)
25788 @cfg {number} g_r radius (pie)
25789 @cfg {number} g_height height of the chart (respected by all elements in the set)
25790 @cfg {number} g_width width of the chart (respected by all elements in the set)
25791 @cfg {Object} title The title of the chart
25794 -opts (object) options for the chart
25796 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25797 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25799 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.
25800 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25802 o stretch (boolean)
25804 -opts (object) options for the pie
25807 o startAngle (number)
25808 o endAngle (number)
25812 * Create a new Input
25813 * @param {Object} config The config object
25816 Roo.bootstrap.Graph = function(config){
25817 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25823 * The img click event for the img.
25824 * @param {Roo.EventObject} e
25830 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25841 //g_colors: this.colors,
25848 getAutoCreate : function(){
25859 onRender : function(ct,position){
25862 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25864 if (typeof(Raphael) == 'undefined') {
25865 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25869 this.raphael = Raphael(this.el.dom);
25871 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25872 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25873 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25874 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25876 r.text(160, 10, "Single Series Chart").attr(txtattr);
25877 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25878 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25879 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25881 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25882 r.barchart(330, 10, 300, 220, data1);
25883 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25884 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25887 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25888 // r.barchart(30, 30, 560, 250, xdata, {
25889 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25890 // axis : "0 0 1 1",
25891 // axisxlabels : xdata
25892 // //yvalues : cols,
25895 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25897 // this.load(null,xdata,{
25898 // axis : "0 0 1 1",
25899 // axisxlabels : xdata
25904 load : function(graphtype,xdata,opts)
25906 this.raphael.clear();
25908 graphtype = this.graphtype;
25913 var r = this.raphael,
25914 fin = function () {
25915 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25917 fout = function () {
25918 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25920 pfin = function() {
25921 this.sector.stop();
25922 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25925 this.label[0].stop();
25926 this.label[0].attr({ r: 7.5 });
25927 this.label[1].attr({ "font-weight": 800 });
25930 pfout = function() {
25931 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25934 this.label[0].animate({ r: 5 }, 500, "bounce");
25935 this.label[1].attr({ "font-weight": 400 });
25941 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25944 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25947 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25948 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25950 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25957 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25962 setTitle: function(o)
25967 initEvents: function() {
25970 this.el.on('click', this.onClick, this);
25974 onClick : function(e)
25976 Roo.log('img onclick');
25977 this.fireEvent('click', this, e);
25989 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25992 * @class Roo.bootstrap.dash.NumberBox
25993 * @extends Roo.bootstrap.Component
25994 * Bootstrap NumberBox class
25995 * @cfg {String} headline Box headline
25996 * @cfg {String} content Box content
25997 * @cfg {String} icon Box icon
25998 * @cfg {String} footer Footer text
25999 * @cfg {String} fhref Footer href
26002 * Create a new NumberBox
26003 * @param {Object} config The config object
26007 Roo.bootstrap.dash.NumberBox = function(config){
26008 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
26012 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
26021 getAutoCreate : function(){
26025 cls : 'small-box ',
26033 cls : 'roo-headline',
26034 html : this.headline
26038 cls : 'roo-content',
26039 html : this.content
26053 cls : 'ion ' + this.icon
26062 cls : 'small-box-footer',
26063 href : this.fhref || '#',
26067 cfg.cn.push(footer);
26074 onRender : function(ct,position){
26075 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
26082 setHeadline: function (value)
26084 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
26087 setFooter: function (value, href)
26089 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
26092 this.el.select('a.small-box-footer',true).first().attr('href', href);
26097 setContent: function (value)
26099 this.el.select('.roo-content',true).first().dom.innerHTML = value;
26102 initEvents: function()
26116 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26119 * @class Roo.bootstrap.dash.TabBox
26120 * @extends Roo.bootstrap.Component
26121 * Bootstrap TabBox class
26122 * @cfg {String} title Title of the TabBox
26123 * @cfg {String} icon Icon of the TabBox
26124 * @cfg {Boolean} showtabs (true|false) show the tabs default true
26125 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
26128 * Create a new TabBox
26129 * @param {Object} config The config object
26133 Roo.bootstrap.dash.TabBox = function(config){
26134 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
26139 * When a pane is added
26140 * @param {Roo.bootstrap.dash.TabPane} pane
26144 * @event activatepane
26145 * When a pane is activated
26146 * @param {Roo.bootstrap.dash.TabPane} pane
26148 "activatepane" : true
26156 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
26161 tabScrollable : false,
26163 getChildContainer : function()
26165 return this.el.select('.tab-content', true).first();
26168 getAutoCreate : function(){
26172 cls: 'pull-left header',
26180 cls: 'fa ' + this.icon
26186 cls: 'nav nav-tabs pull-right',
26192 if(this.tabScrollable){
26199 cls: 'nav nav-tabs pull-right',
26210 cls: 'nav-tabs-custom',
26215 cls: 'tab-content no-padding',
26223 initEvents : function()
26225 //Roo.log('add add pane handler');
26226 this.on('addpane', this.onAddPane, this);
26229 * Updates the box title
26230 * @param {String} html to set the title to.
26232 setTitle : function(value)
26234 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
26236 onAddPane : function(pane)
26238 this.panes.push(pane);
26239 //Roo.log('addpane');
26241 // tabs are rendere left to right..
26242 if(!this.showtabs){
26246 var ctr = this.el.select('.nav-tabs', true).first();
26249 var existing = ctr.select('.nav-tab',true);
26250 var qty = existing.getCount();;
26253 var tab = ctr.createChild({
26255 cls : 'nav-tab' + (qty ? '' : ' active'),
26263 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26266 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26268 pane.el.addClass('active');
26273 onTabClick : function(ev,un,ob,pane)
26275 //Roo.log('tab - prev default');
26276 ev.preventDefault();
26279 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26280 pane.tab.addClass('active');
26281 //Roo.log(pane.title);
26282 this.getChildContainer().select('.tab-pane',true).removeClass('active');
26283 // technically we should have a deactivate event.. but maybe add later.
26284 // and it should not de-activate the selected tab...
26285 this.fireEvent('activatepane', pane);
26286 pane.el.addClass('active');
26287 pane.fireEvent('activate');
26292 getActivePane : function()
26295 Roo.each(this.panes, function(p) {
26296 if(p.el.hasClass('active')){
26317 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26319 * @class Roo.bootstrap.TabPane
26320 * @extends Roo.bootstrap.Component
26321 * Bootstrap TabPane class
26322 * @cfg {Boolean} active (false | true) Default false
26323 * @cfg {String} title title of panel
26327 * Create a new TabPane
26328 * @param {Object} config The config object
26331 Roo.bootstrap.dash.TabPane = function(config){
26332 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26338 * When a pane is activated
26339 * @param {Roo.bootstrap.dash.TabPane} pane
26346 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
26351 // the tabBox that this is attached to.
26354 getAutoCreate : function()
26362 cfg.cls += ' active';
26367 initEvents : function()
26369 //Roo.log('trigger add pane handler');
26370 this.parent().fireEvent('addpane', this)
26374 * Updates the tab title
26375 * @param {String} html to set the title to.
26377 setTitle: function(str)
26383 this.tab.select('a', true).first().dom.innerHTML = str;
26400 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26403 * @class Roo.bootstrap.menu.Menu
26404 * @extends Roo.bootstrap.Component
26405 * Bootstrap Menu class - container for Menu
26406 * @cfg {String} html Text of the menu
26407 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26408 * @cfg {String} icon Font awesome icon
26409 * @cfg {String} pos Menu align to (top | bottom) default bottom
26413 * Create a new Menu
26414 * @param {Object} config The config object
26418 Roo.bootstrap.menu.Menu = function(config){
26419 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26423 * @event beforeshow
26424 * Fires before this menu is displayed
26425 * @param {Roo.bootstrap.menu.Menu} this
26429 * @event beforehide
26430 * Fires before this menu is hidden
26431 * @param {Roo.bootstrap.menu.Menu} this
26436 * Fires after this menu is displayed
26437 * @param {Roo.bootstrap.menu.Menu} this
26442 * Fires after this menu is hidden
26443 * @param {Roo.bootstrap.menu.Menu} this
26448 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26449 * @param {Roo.bootstrap.menu.Menu} this
26450 * @param {Roo.EventObject} e
26457 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
26461 weight : 'default',
26466 getChildContainer : function() {
26467 if(this.isSubMenu){
26471 return this.el.select('ul.dropdown-menu', true).first();
26474 getAutoCreate : function()
26479 cls : 'roo-menu-text',
26487 cls : 'fa ' + this.icon
26498 cls : 'dropdown-button btn btn-' + this.weight,
26503 cls : 'dropdown-toggle btn btn-' + this.weight,
26513 cls : 'dropdown-menu'
26519 if(this.pos == 'top'){
26520 cfg.cls += ' dropup';
26523 if(this.isSubMenu){
26526 cls : 'dropdown-menu'
26533 onRender : function(ct, position)
26535 this.isSubMenu = ct.hasClass('dropdown-submenu');
26537 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26540 initEvents : function()
26542 if(this.isSubMenu){
26546 this.hidden = true;
26548 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26549 this.triggerEl.on('click', this.onTriggerPress, this);
26551 this.buttonEl = this.el.select('button.dropdown-button', true).first();
26552 this.buttonEl.on('click', this.onClick, this);
26558 if(this.isSubMenu){
26562 return this.el.select('ul.dropdown-menu', true).first();
26565 onClick : function(e)
26567 this.fireEvent("click", this, e);
26570 onTriggerPress : function(e)
26572 if (this.isVisible()) {
26579 isVisible : function(){
26580 return !this.hidden;
26585 this.fireEvent("beforeshow", this);
26587 this.hidden = false;
26588 this.el.addClass('open');
26590 Roo.get(document).on("mouseup", this.onMouseUp, this);
26592 this.fireEvent("show", this);
26599 this.fireEvent("beforehide", this);
26601 this.hidden = true;
26602 this.el.removeClass('open');
26604 Roo.get(document).un("mouseup", this.onMouseUp);
26606 this.fireEvent("hide", this);
26609 onMouseUp : function()
26623 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26626 * @class Roo.bootstrap.menu.Item
26627 * @extends Roo.bootstrap.Component
26628 * Bootstrap MenuItem class
26629 * @cfg {Boolean} submenu (true | false) default false
26630 * @cfg {String} html text of the item
26631 * @cfg {String} href the link
26632 * @cfg {Boolean} disable (true | false) default false
26633 * @cfg {Boolean} preventDefault (true | false) default true
26634 * @cfg {String} icon Font awesome icon
26635 * @cfg {String} pos Submenu align to (left | right) default right
26639 * Create a new Item
26640 * @param {Object} config The config object
26644 Roo.bootstrap.menu.Item = function(config){
26645 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26649 * Fires when the mouse is hovering over this menu
26650 * @param {Roo.bootstrap.menu.Item} this
26651 * @param {Roo.EventObject} e
26656 * Fires when the mouse exits this menu
26657 * @param {Roo.bootstrap.menu.Item} this
26658 * @param {Roo.EventObject} e
26664 * The raw click event for the entire grid.
26665 * @param {Roo.EventObject} e
26671 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26676 preventDefault: true,
26681 getAutoCreate : function()
26686 cls : 'roo-menu-item-text',
26694 cls : 'fa ' + this.icon
26703 href : this.href || '#',
26710 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26714 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26716 if(this.pos == 'left'){
26717 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26724 initEvents : function()
26726 this.el.on('mouseover', this.onMouseOver, this);
26727 this.el.on('mouseout', this.onMouseOut, this);
26729 this.el.select('a', true).first().on('click', this.onClick, this);
26733 onClick : function(e)
26735 if(this.preventDefault){
26736 e.preventDefault();
26739 this.fireEvent("click", this, e);
26742 onMouseOver : function(e)
26744 if(this.submenu && this.pos == 'left'){
26745 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26748 this.fireEvent("mouseover", this, e);
26751 onMouseOut : function(e)
26753 this.fireEvent("mouseout", this, e);
26765 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26768 * @class Roo.bootstrap.menu.Separator
26769 * @extends Roo.bootstrap.Component
26770 * Bootstrap Separator class
26773 * Create a new Separator
26774 * @param {Object} config The config object
26778 Roo.bootstrap.menu.Separator = function(config){
26779 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26782 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26784 getAutoCreate : function(){
26805 * @class Roo.bootstrap.Tooltip
26806 * Bootstrap Tooltip class
26807 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26808 * to determine which dom element triggers the tooltip.
26810 * It needs to add support for additional attributes like tooltip-position
26813 * Create a new Toolti
26814 * @param {Object} config The config object
26817 Roo.bootstrap.Tooltip = function(config){
26818 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26820 this.alignment = Roo.bootstrap.Tooltip.alignment;
26822 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26823 this.alignment = config.alignment;
26828 Roo.apply(Roo.bootstrap.Tooltip, {
26830 * @function init initialize tooltip monitoring.
26834 currentTip : false,
26835 currentRegion : false,
26841 Roo.get(document).on('mouseover', this.enter ,this);
26842 Roo.get(document).on('mouseout', this.leave, this);
26845 this.currentTip = new Roo.bootstrap.Tooltip();
26848 enter : function(ev)
26850 var dom = ev.getTarget();
26852 //Roo.log(['enter',dom]);
26853 var el = Roo.fly(dom);
26854 if (this.currentEl) {
26856 //Roo.log(this.currentEl);
26857 //Roo.log(this.currentEl.contains(dom));
26858 if (this.currentEl == el) {
26861 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26867 if (this.currentTip.el) {
26868 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26872 if(!el || el.dom == document){
26878 // you can not look for children, as if el is the body.. then everythign is the child..
26879 if (!el.attr('tooltip')) { //
26880 if (!el.select("[tooltip]").elements.length) {
26883 // is the mouse over this child...?
26884 bindEl = el.select("[tooltip]").first();
26885 var xy = ev.getXY();
26886 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26887 //Roo.log("not in region.");
26890 //Roo.log("child element over..");
26893 this.currentEl = bindEl;
26894 this.currentTip.bind(bindEl);
26895 this.currentRegion = Roo.lib.Region.getRegion(dom);
26896 this.currentTip.enter();
26899 leave : function(ev)
26901 var dom = ev.getTarget();
26902 //Roo.log(['leave',dom]);
26903 if (!this.currentEl) {
26908 if (dom != this.currentEl.dom) {
26911 var xy = ev.getXY();
26912 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26915 // only activate leave if mouse cursor is outside... bounding box..
26920 if (this.currentTip) {
26921 this.currentTip.leave();
26923 //Roo.log('clear currentEl');
26924 this.currentEl = false;
26929 'left' : ['r-l', [-2,0], 'right'],
26930 'right' : ['l-r', [2,0], 'left'],
26931 'bottom' : ['t-b', [0,2], 'top'],
26932 'top' : [ 'b-t', [0,-2], 'bottom']
26938 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26943 delay : null, // can be { show : 300 , hide: 500}
26947 hoverState : null, //???
26949 placement : 'bottom',
26953 getAutoCreate : function(){
26960 cls : 'tooltip-arrow'
26963 cls : 'tooltip-inner'
26970 bind : function(el)
26976 enter : function () {
26978 if (this.timeout != null) {
26979 clearTimeout(this.timeout);
26982 this.hoverState = 'in';
26983 //Roo.log("enter - show");
26984 if (!this.delay || !this.delay.show) {
26989 this.timeout = setTimeout(function () {
26990 if (_t.hoverState == 'in') {
26993 }, this.delay.show);
26997 clearTimeout(this.timeout);
26999 this.hoverState = 'out';
27000 if (!this.delay || !this.delay.hide) {
27006 this.timeout = setTimeout(function () {
27007 //Roo.log("leave - timeout");
27009 if (_t.hoverState == 'out') {
27011 Roo.bootstrap.Tooltip.currentEl = false;
27016 show : function (msg)
27019 this.render(document.body);
27022 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
27024 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
27026 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
27028 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
27030 var placement = typeof this.placement == 'function' ?
27031 this.placement.call(this, this.el, on_el) :
27034 var autoToken = /\s?auto?\s?/i;
27035 var autoPlace = autoToken.test(placement);
27037 placement = placement.replace(autoToken, '') || 'top';
27041 //this.el.setXY([0,0]);
27043 //this.el.dom.style.display='block';
27045 //this.el.appendTo(on_el);
27047 var p = this.getPosition();
27048 var box = this.el.getBox();
27054 var align = this.alignment[placement];
27056 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
27058 if(placement == 'top' || placement == 'bottom'){
27060 placement = 'right';
27063 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
27064 placement = 'left';
27067 var scroll = Roo.select('body', true).first().getScroll();
27069 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
27073 align = this.alignment[placement];
27076 this.el.alignTo(this.bindEl, align[0],align[1]);
27077 //var arrow = this.el.select('.arrow',true).first();
27078 //arrow.set(align[2],
27080 this.el.addClass(placement);
27082 this.el.addClass('in fade');
27084 this.hoverState = null;
27086 if (this.el.hasClass('fade')) {
27097 //this.el.setXY([0,0]);
27098 this.el.removeClass('in');
27114 * @class Roo.bootstrap.LocationPicker
27115 * @extends Roo.bootstrap.Component
27116 * Bootstrap LocationPicker class
27117 * @cfg {Number} latitude Position when init default 0
27118 * @cfg {Number} longitude Position when init default 0
27119 * @cfg {Number} zoom default 15
27120 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
27121 * @cfg {Boolean} mapTypeControl default false
27122 * @cfg {Boolean} disableDoubleClickZoom default false
27123 * @cfg {Boolean} scrollwheel default true
27124 * @cfg {Boolean} streetViewControl default false
27125 * @cfg {Number} radius default 0
27126 * @cfg {String} locationName
27127 * @cfg {Boolean} draggable default true
27128 * @cfg {Boolean} enableAutocomplete default false
27129 * @cfg {Boolean} enableReverseGeocode default true
27130 * @cfg {String} markerTitle
27133 * Create a new LocationPicker
27134 * @param {Object} config The config object
27138 Roo.bootstrap.LocationPicker = function(config){
27140 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
27145 * Fires when the picker initialized.
27146 * @param {Roo.bootstrap.LocationPicker} this
27147 * @param {Google Location} location
27151 * @event positionchanged
27152 * Fires when the picker position changed.
27153 * @param {Roo.bootstrap.LocationPicker} this
27154 * @param {Google Location} location
27156 positionchanged : true,
27159 * Fires when the map resize.
27160 * @param {Roo.bootstrap.LocationPicker} this
27165 * Fires when the map show.
27166 * @param {Roo.bootstrap.LocationPicker} this
27171 * Fires when the map hide.
27172 * @param {Roo.bootstrap.LocationPicker} this
27177 * Fires when click the map.
27178 * @param {Roo.bootstrap.LocationPicker} this
27179 * @param {Map event} e
27183 * @event mapRightClick
27184 * Fires when right click the map.
27185 * @param {Roo.bootstrap.LocationPicker} this
27186 * @param {Map event} e
27188 mapRightClick : true,
27190 * @event markerClick
27191 * Fires when click the marker.
27192 * @param {Roo.bootstrap.LocationPicker} this
27193 * @param {Map event} e
27195 markerClick : true,
27197 * @event markerRightClick
27198 * Fires when right click the marker.
27199 * @param {Roo.bootstrap.LocationPicker} this
27200 * @param {Map event} e
27202 markerRightClick : true,
27204 * @event OverlayViewDraw
27205 * Fires when OverlayView Draw
27206 * @param {Roo.bootstrap.LocationPicker} this
27208 OverlayViewDraw : true,
27210 * @event OverlayViewOnAdd
27211 * Fires when OverlayView Draw
27212 * @param {Roo.bootstrap.LocationPicker} this
27214 OverlayViewOnAdd : true,
27216 * @event OverlayViewOnRemove
27217 * Fires when OverlayView Draw
27218 * @param {Roo.bootstrap.LocationPicker} this
27220 OverlayViewOnRemove : true,
27222 * @event OverlayViewShow
27223 * Fires when OverlayView Draw
27224 * @param {Roo.bootstrap.LocationPicker} this
27225 * @param {Pixel} cpx
27227 OverlayViewShow : true,
27229 * @event OverlayViewHide
27230 * Fires when OverlayView Draw
27231 * @param {Roo.bootstrap.LocationPicker} this
27233 OverlayViewHide : true,
27235 * @event loadexception
27236 * Fires when load google lib failed.
27237 * @param {Roo.bootstrap.LocationPicker} this
27239 loadexception : true
27244 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
27246 gMapContext: false,
27252 mapTypeControl: false,
27253 disableDoubleClickZoom: false,
27255 streetViewControl: false,
27259 enableAutocomplete: false,
27260 enableReverseGeocode: true,
27263 getAutoCreate: function()
27268 cls: 'roo-location-picker'
27274 initEvents: function(ct, position)
27276 if(!this.el.getWidth() || this.isApplied()){
27280 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27285 initial: function()
27287 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27288 this.fireEvent('loadexception', this);
27292 if(!this.mapTypeId){
27293 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27296 this.gMapContext = this.GMapContext();
27298 this.initOverlayView();
27300 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27304 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27305 _this.setPosition(_this.gMapContext.marker.position);
27308 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27309 _this.fireEvent('mapClick', this, event);
27313 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27314 _this.fireEvent('mapRightClick', this, event);
27318 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27319 _this.fireEvent('markerClick', this, event);
27323 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27324 _this.fireEvent('markerRightClick', this, event);
27328 this.setPosition(this.gMapContext.location);
27330 this.fireEvent('initial', this, this.gMapContext.location);
27333 initOverlayView: function()
27337 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27341 _this.fireEvent('OverlayViewDraw', _this);
27346 _this.fireEvent('OverlayViewOnAdd', _this);
27349 onRemove: function()
27351 _this.fireEvent('OverlayViewOnRemove', _this);
27354 show: function(cpx)
27356 _this.fireEvent('OverlayViewShow', _this, cpx);
27361 _this.fireEvent('OverlayViewHide', _this);
27367 fromLatLngToContainerPixel: function(event)
27369 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27372 isApplied: function()
27374 return this.getGmapContext() == false ? false : true;
27377 getGmapContext: function()
27379 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27382 GMapContext: function()
27384 var position = new google.maps.LatLng(this.latitude, this.longitude);
27386 var _map = new google.maps.Map(this.el.dom, {
27389 mapTypeId: this.mapTypeId,
27390 mapTypeControl: this.mapTypeControl,
27391 disableDoubleClickZoom: this.disableDoubleClickZoom,
27392 scrollwheel: this.scrollwheel,
27393 streetViewControl: this.streetViewControl,
27394 locationName: this.locationName,
27395 draggable: this.draggable,
27396 enableAutocomplete: this.enableAutocomplete,
27397 enableReverseGeocode: this.enableReverseGeocode
27400 var _marker = new google.maps.Marker({
27401 position: position,
27403 title: this.markerTitle,
27404 draggable: this.draggable
27411 location: position,
27412 radius: this.radius,
27413 locationName: this.locationName,
27414 addressComponents: {
27415 formatted_address: null,
27416 addressLine1: null,
27417 addressLine2: null,
27419 streetNumber: null,
27423 stateOrProvince: null
27426 domContainer: this.el.dom,
27427 geodecoder: new google.maps.Geocoder()
27431 drawCircle: function(center, radius, options)
27433 if (this.gMapContext.circle != null) {
27434 this.gMapContext.circle.setMap(null);
27438 options = Roo.apply({}, options, {
27439 strokeColor: "#0000FF",
27440 strokeOpacity: .35,
27442 fillColor: "#0000FF",
27446 options.map = this.gMapContext.map;
27447 options.radius = radius;
27448 options.center = center;
27449 this.gMapContext.circle = new google.maps.Circle(options);
27450 return this.gMapContext.circle;
27456 setPosition: function(location)
27458 this.gMapContext.location = location;
27459 this.gMapContext.marker.setPosition(location);
27460 this.gMapContext.map.panTo(location);
27461 this.drawCircle(location, this.gMapContext.radius, {});
27465 if (this.gMapContext.settings.enableReverseGeocode) {
27466 this.gMapContext.geodecoder.geocode({
27467 latLng: this.gMapContext.location
27468 }, function(results, status) {
27470 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27471 _this.gMapContext.locationName = results[0].formatted_address;
27472 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27474 _this.fireEvent('positionchanged', this, location);
27481 this.fireEvent('positionchanged', this, location);
27486 google.maps.event.trigger(this.gMapContext.map, "resize");
27488 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27490 this.fireEvent('resize', this);
27493 setPositionByLatLng: function(latitude, longitude)
27495 this.setPosition(new google.maps.LatLng(latitude, longitude));
27498 getCurrentPosition: function()
27501 latitude: this.gMapContext.location.lat(),
27502 longitude: this.gMapContext.location.lng()
27506 getAddressName: function()
27508 return this.gMapContext.locationName;
27511 getAddressComponents: function()
27513 return this.gMapContext.addressComponents;
27516 address_component_from_google_geocode: function(address_components)
27520 for (var i = 0; i < address_components.length; i++) {
27521 var component = address_components[i];
27522 if (component.types.indexOf("postal_code") >= 0) {
27523 result.postalCode = component.short_name;
27524 } else if (component.types.indexOf("street_number") >= 0) {
27525 result.streetNumber = component.short_name;
27526 } else if (component.types.indexOf("route") >= 0) {
27527 result.streetName = component.short_name;
27528 } else if (component.types.indexOf("neighborhood") >= 0) {
27529 result.city = component.short_name;
27530 } else if (component.types.indexOf("locality") >= 0) {
27531 result.city = component.short_name;
27532 } else if (component.types.indexOf("sublocality") >= 0) {
27533 result.district = component.short_name;
27534 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27535 result.stateOrProvince = component.short_name;
27536 } else if (component.types.indexOf("country") >= 0) {
27537 result.country = component.short_name;
27541 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27542 result.addressLine2 = "";
27546 setZoomLevel: function(zoom)
27548 this.gMapContext.map.setZoom(zoom);
27561 this.fireEvent('show', this);
27572 this.fireEvent('hide', this);
27577 Roo.apply(Roo.bootstrap.LocationPicker, {
27579 OverlayView : function(map, options)
27581 options = options || {};
27588 * @class Roo.bootstrap.Alert
27589 * @extends Roo.bootstrap.Component
27590 * Bootstrap Alert class - shows an alert area box
27592 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27593 Enter a valid email address
27596 * @cfg {String} title The title of alert
27597 * @cfg {String} html The content of alert
27598 * @cfg {String} weight ( success | info | warning | danger )
27599 * @cfg {String} faicon font-awesomeicon
27602 * Create a new alert
27603 * @param {Object} config The config object
27607 Roo.bootstrap.Alert = function(config){
27608 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27612 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27619 getAutoCreate : function()
27628 cls : 'roo-alert-icon'
27633 cls : 'roo-alert-title',
27638 cls : 'roo-alert-text',
27645 cfg.cn[0].cls += ' fa ' + this.faicon;
27649 cfg.cls += ' alert-' + this.weight;
27655 initEvents: function()
27657 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27660 setTitle : function(str)
27662 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27665 setText : function(str)
27667 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27670 setWeight : function(weight)
27673 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27676 this.weight = weight;
27678 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27681 setIcon : function(icon)
27684 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27687 this.faicon = icon;
27689 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27710 * @class Roo.bootstrap.UploadCropbox
27711 * @extends Roo.bootstrap.Component
27712 * Bootstrap UploadCropbox class
27713 * @cfg {String} emptyText show when image has been loaded
27714 * @cfg {String} rotateNotify show when image too small to rotate
27715 * @cfg {Number} errorTimeout default 3000
27716 * @cfg {Number} minWidth default 300
27717 * @cfg {Number} minHeight default 300
27718 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27719 * @cfg {Boolean} isDocument (true|false) default false
27720 * @cfg {String} url action url
27721 * @cfg {String} paramName default 'imageUpload'
27722 * @cfg {String} method default POST
27723 * @cfg {Boolean} loadMask (true|false) default true
27724 * @cfg {Boolean} loadingText default 'Loading...'
27727 * Create a new UploadCropbox
27728 * @param {Object} config The config object
27731 Roo.bootstrap.UploadCropbox = function(config){
27732 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27736 * @event beforeselectfile
27737 * Fire before select file
27738 * @param {Roo.bootstrap.UploadCropbox} this
27740 "beforeselectfile" : true,
27743 * Fire after initEvent
27744 * @param {Roo.bootstrap.UploadCropbox} this
27749 * Fire after initEvent
27750 * @param {Roo.bootstrap.UploadCropbox} this
27751 * @param {String} data
27756 * Fire when preparing the file data
27757 * @param {Roo.bootstrap.UploadCropbox} this
27758 * @param {Object} file
27763 * Fire when get exception
27764 * @param {Roo.bootstrap.UploadCropbox} this
27765 * @param {XMLHttpRequest} xhr
27767 "exception" : true,
27769 * @event beforeloadcanvas
27770 * Fire before load the canvas
27771 * @param {Roo.bootstrap.UploadCropbox} this
27772 * @param {String} src
27774 "beforeloadcanvas" : true,
27777 * Fire when trash image
27778 * @param {Roo.bootstrap.UploadCropbox} this
27783 * Fire when download the image
27784 * @param {Roo.bootstrap.UploadCropbox} this
27788 * @event footerbuttonclick
27789 * Fire when footerbuttonclick
27790 * @param {Roo.bootstrap.UploadCropbox} this
27791 * @param {String} type
27793 "footerbuttonclick" : true,
27797 * @param {Roo.bootstrap.UploadCropbox} this
27802 * Fire when rotate the image
27803 * @param {Roo.bootstrap.UploadCropbox} this
27804 * @param {String} pos
27809 * Fire when inspect the file
27810 * @param {Roo.bootstrap.UploadCropbox} this
27811 * @param {Object} file
27816 * Fire when xhr upload the file
27817 * @param {Roo.bootstrap.UploadCropbox} this
27818 * @param {Object} data
27823 * Fire when arrange the file data
27824 * @param {Roo.bootstrap.UploadCropbox} this
27825 * @param {Object} formData
27830 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27833 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27835 emptyText : 'Click to upload image',
27836 rotateNotify : 'Image is too small to rotate',
27837 errorTimeout : 3000,
27851 cropType : 'image/jpeg',
27853 canvasLoaded : false,
27854 isDocument : false,
27856 paramName : 'imageUpload',
27858 loadingText : 'Loading...',
27861 getAutoCreate : function()
27865 cls : 'roo-upload-cropbox',
27869 cls : 'roo-upload-cropbox-selector',
27874 cls : 'roo-upload-cropbox-body',
27875 style : 'cursor:pointer',
27879 cls : 'roo-upload-cropbox-preview'
27883 cls : 'roo-upload-cropbox-thumb'
27887 cls : 'roo-upload-cropbox-empty-notify',
27888 html : this.emptyText
27892 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27893 html : this.rotateNotify
27899 cls : 'roo-upload-cropbox-footer',
27902 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27912 onRender : function(ct, position)
27914 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27916 if (this.buttons.length) {
27918 Roo.each(this.buttons, function(bb) {
27920 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27922 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27928 this.maskEl = this.el;
27932 initEvents : function()
27934 this.urlAPI = (window.createObjectURL && window) ||
27935 (window.URL && URL.revokeObjectURL && URL) ||
27936 (window.webkitURL && webkitURL);
27938 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27939 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27941 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27942 this.selectorEl.hide();
27944 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27945 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27947 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27948 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27949 this.thumbEl.hide();
27951 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27952 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27954 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27955 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27956 this.errorEl.hide();
27958 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27959 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27960 this.footerEl.hide();
27962 this.setThumbBoxSize();
27968 this.fireEvent('initial', this);
27975 window.addEventListener("resize", function() { _this.resize(); } );
27977 this.bodyEl.on('click', this.beforeSelectFile, this);
27980 this.bodyEl.on('touchstart', this.onTouchStart, this);
27981 this.bodyEl.on('touchmove', this.onTouchMove, this);
27982 this.bodyEl.on('touchend', this.onTouchEnd, this);
27986 this.bodyEl.on('mousedown', this.onMouseDown, this);
27987 this.bodyEl.on('mousemove', this.onMouseMove, this);
27988 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27989 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27990 Roo.get(document).on('mouseup', this.onMouseUp, this);
27993 this.selectorEl.on('change', this.onFileSelected, this);
27999 this.baseScale = 1;
28001 this.baseRotate = 1;
28002 this.dragable = false;
28003 this.pinching = false;
28006 this.cropData = false;
28007 this.notifyEl.dom.innerHTML = this.emptyText;
28009 this.selectorEl.dom.value = '';
28013 resize : function()
28015 if(this.fireEvent('resize', this) != false){
28016 this.setThumbBoxPosition();
28017 this.setCanvasPosition();
28021 onFooterButtonClick : function(e, el, o, type)
28024 case 'rotate-left' :
28025 this.onRotateLeft(e);
28027 case 'rotate-right' :
28028 this.onRotateRight(e);
28031 this.beforeSelectFile(e);
28046 this.fireEvent('footerbuttonclick', this, type);
28049 beforeSelectFile : function(e)
28051 e.preventDefault();
28053 if(this.fireEvent('beforeselectfile', this) != false){
28054 this.selectorEl.dom.click();
28058 onFileSelected : function(e)
28060 e.preventDefault();
28062 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28066 var file = this.selectorEl.dom.files[0];
28068 if(this.fireEvent('inspect', this, file) != false){
28069 this.prepare(file);
28074 trash : function(e)
28076 this.fireEvent('trash', this);
28079 download : function(e)
28081 this.fireEvent('download', this);
28084 loadCanvas : function(src)
28086 if(this.fireEvent('beforeloadcanvas', this, src) != false){
28090 this.imageEl = document.createElement('img');
28094 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
28096 this.imageEl.src = src;
28100 onLoadCanvas : function()
28102 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
28103 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
28105 this.bodyEl.un('click', this.beforeSelectFile, this);
28107 this.notifyEl.hide();
28108 this.thumbEl.show();
28109 this.footerEl.show();
28111 this.baseRotateLevel();
28113 if(this.isDocument){
28114 this.setThumbBoxSize();
28117 this.setThumbBoxPosition();
28119 this.baseScaleLevel();
28125 this.canvasLoaded = true;
28128 this.maskEl.unmask();
28133 setCanvasPosition : function()
28135 if(!this.canvasEl){
28139 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
28140 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
28142 this.previewEl.setLeft(pw);
28143 this.previewEl.setTop(ph);
28147 onMouseDown : function(e)
28151 this.dragable = true;
28152 this.pinching = false;
28154 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
28155 this.dragable = false;
28159 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28160 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28164 onMouseMove : function(e)
28168 if(!this.canvasLoaded){
28172 if (!this.dragable){
28176 var minX = Math.ceil(this.thumbEl.getLeft(true));
28177 var minY = Math.ceil(this.thumbEl.getTop(true));
28179 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
28180 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
28182 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28183 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28185 x = x - this.mouseX;
28186 y = y - this.mouseY;
28188 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
28189 var bgY = Math.ceil(y + this.previewEl.getTop(true));
28191 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
28192 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
28194 this.previewEl.setLeft(bgX);
28195 this.previewEl.setTop(bgY);
28197 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28198 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28201 onMouseUp : function(e)
28205 this.dragable = false;
28208 onMouseWheel : function(e)
28212 this.startScale = this.scale;
28214 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
28216 if(!this.zoomable()){
28217 this.scale = this.startScale;
28226 zoomable : function()
28228 var minScale = this.thumbEl.getWidth() / this.minWidth;
28230 if(this.minWidth < this.minHeight){
28231 minScale = this.thumbEl.getHeight() / this.minHeight;
28234 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
28235 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
28239 (this.rotate == 0 || this.rotate == 180) &&
28241 width > this.imageEl.OriginWidth ||
28242 height > this.imageEl.OriginHeight ||
28243 (width < this.minWidth && height < this.minHeight)
28251 (this.rotate == 90 || this.rotate == 270) &&
28253 width > this.imageEl.OriginWidth ||
28254 height > this.imageEl.OriginHeight ||
28255 (width < this.minHeight && height < this.minWidth)
28262 !this.isDocument &&
28263 (this.rotate == 0 || this.rotate == 180) &&
28265 width < this.minWidth ||
28266 width > this.imageEl.OriginWidth ||
28267 height < this.minHeight ||
28268 height > this.imageEl.OriginHeight
28275 !this.isDocument &&
28276 (this.rotate == 90 || this.rotate == 270) &&
28278 width < this.minHeight ||
28279 width > this.imageEl.OriginWidth ||
28280 height < this.minWidth ||
28281 height > this.imageEl.OriginHeight
28291 onRotateLeft : function(e)
28293 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28295 var minScale = this.thumbEl.getWidth() / this.minWidth;
28297 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28298 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28300 this.startScale = this.scale;
28302 while (this.getScaleLevel() < minScale){
28304 this.scale = this.scale + 1;
28306 if(!this.zoomable()){
28311 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28312 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28317 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28324 this.scale = this.startScale;
28326 this.onRotateFail();
28331 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28333 if(this.isDocument){
28334 this.setThumbBoxSize();
28335 this.setThumbBoxPosition();
28336 this.setCanvasPosition();
28341 this.fireEvent('rotate', this, 'left');
28345 onRotateRight : function(e)
28347 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28349 var minScale = this.thumbEl.getWidth() / this.minWidth;
28351 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28352 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28354 this.startScale = this.scale;
28356 while (this.getScaleLevel() < minScale){
28358 this.scale = this.scale + 1;
28360 if(!this.zoomable()){
28365 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28366 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28371 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28378 this.scale = this.startScale;
28380 this.onRotateFail();
28385 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28387 if(this.isDocument){
28388 this.setThumbBoxSize();
28389 this.setThumbBoxPosition();
28390 this.setCanvasPosition();
28395 this.fireEvent('rotate', this, 'right');
28398 onRotateFail : function()
28400 this.errorEl.show(true);
28404 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28409 this.previewEl.dom.innerHTML = '';
28411 var canvasEl = document.createElement("canvas");
28413 var contextEl = canvasEl.getContext("2d");
28415 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28416 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28417 var center = this.imageEl.OriginWidth / 2;
28419 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28420 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28421 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28422 center = this.imageEl.OriginHeight / 2;
28425 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28427 contextEl.translate(center, center);
28428 contextEl.rotate(this.rotate * Math.PI / 180);
28430 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28432 this.canvasEl = document.createElement("canvas");
28434 this.contextEl = this.canvasEl.getContext("2d");
28436 switch (this.rotate) {
28439 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28440 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28442 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28447 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28448 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28450 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28451 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);
28455 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28460 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28461 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28463 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28464 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);
28468 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);
28473 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28474 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28476 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28477 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28481 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);
28488 this.previewEl.appendChild(this.canvasEl);
28490 this.setCanvasPosition();
28495 if(!this.canvasLoaded){
28499 var imageCanvas = document.createElement("canvas");
28501 var imageContext = imageCanvas.getContext("2d");
28503 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28504 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28506 var center = imageCanvas.width / 2;
28508 imageContext.translate(center, center);
28510 imageContext.rotate(this.rotate * Math.PI / 180);
28512 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28514 var canvas = document.createElement("canvas");
28516 var context = canvas.getContext("2d");
28518 canvas.width = this.minWidth;
28519 canvas.height = this.minHeight;
28521 switch (this.rotate) {
28524 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28525 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28527 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28528 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28530 var targetWidth = this.minWidth - 2 * x;
28531 var targetHeight = this.minHeight - 2 * y;
28535 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28536 scale = targetWidth / width;
28539 if(x > 0 && y == 0){
28540 scale = targetHeight / height;
28543 if(x > 0 && y > 0){
28544 scale = targetWidth / width;
28546 if(width < height){
28547 scale = targetHeight / height;
28551 context.scale(scale, scale);
28553 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28554 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28556 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28557 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28559 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28564 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28565 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28567 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28568 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28570 var targetWidth = this.minWidth - 2 * x;
28571 var targetHeight = this.minHeight - 2 * y;
28575 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28576 scale = targetWidth / width;
28579 if(x > 0 && y == 0){
28580 scale = targetHeight / height;
28583 if(x > 0 && y > 0){
28584 scale = targetWidth / width;
28586 if(width < height){
28587 scale = targetHeight / height;
28591 context.scale(scale, scale);
28593 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28594 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28596 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28597 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28599 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28601 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28606 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28607 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28609 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28610 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28612 var targetWidth = this.minWidth - 2 * x;
28613 var targetHeight = this.minHeight - 2 * y;
28617 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28618 scale = targetWidth / width;
28621 if(x > 0 && y == 0){
28622 scale = targetHeight / height;
28625 if(x > 0 && y > 0){
28626 scale = targetWidth / width;
28628 if(width < height){
28629 scale = targetHeight / height;
28633 context.scale(scale, scale);
28635 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28636 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28638 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28639 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28641 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28642 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28644 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28649 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28650 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28652 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28653 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28655 var targetWidth = this.minWidth - 2 * x;
28656 var targetHeight = this.minHeight - 2 * y;
28660 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28661 scale = targetWidth / width;
28664 if(x > 0 && y == 0){
28665 scale = targetHeight / height;
28668 if(x > 0 && y > 0){
28669 scale = targetWidth / width;
28671 if(width < height){
28672 scale = targetHeight / height;
28676 context.scale(scale, scale);
28678 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28679 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28681 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28682 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28684 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28686 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28693 this.cropData = canvas.toDataURL(this.cropType);
28695 if(this.fireEvent('crop', this, this.cropData) !== false){
28696 this.process(this.file, this.cropData);
28703 setThumbBoxSize : function()
28707 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28708 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28709 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28711 this.minWidth = width;
28712 this.minHeight = height;
28714 if(this.rotate == 90 || this.rotate == 270){
28715 this.minWidth = height;
28716 this.minHeight = width;
28721 width = Math.ceil(this.minWidth * height / this.minHeight);
28723 if(this.minWidth > this.minHeight){
28725 height = Math.ceil(this.minHeight * width / this.minWidth);
28728 this.thumbEl.setStyle({
28729 width : width + 'px',
28730 height : height + 'px'
28737 setThumbBoxPosition : function()
28739 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28740 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28742 this.thumbEl.setLeft(x);
28743 this.thumbEl.setTop(y);
28747 baseRotateLevel : function()
28749 this.baseRotate = 1;
28752 typeof(this.exif) != 'undefined' &&
28753 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28754 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28756 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28759 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28763 baseScaleLevel : function()
28767 if(this.isDocument){
28769 if(this.baseRotate == 6 || this.baseRotate == 8){
28771 height = this.thumbEl.getHeight();
28772 this.baseScale = height / this.imageEl.OriginWidth;
28774 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28775 width = this.thumbEl.getWidth();
28776 this.baseScale = width / this.imageEl.OriginHeight;
28782 height = this.thumbEl.getHeight();
28783 this.baseScale = height / this.imageEl.OriginHeight;
28785 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28786 width = this.thumbEl.getWidth();
28787 this.baseScale = width / this.imageEl.OriginWidth;
28793 if(this.baseRotate == 6 || this.baseRotate == 8){
28795 width = this.thumbEl.getHeight();
28796 this.baseScale = width / this.imageEl.OriginHeight;
28798 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28799 height = this.thumbEl.getWidth();
28800 this.baseScale = height / this.imageEl.OriginHeight;
28803 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28804 height = this.thumbEl.getWidth();
28805 this.baseScale = height / this.imageEl.OriginHeight;
28807 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28808 width = this.thumbEl.getHeight();
28809 this.baseScale = width / this.imageEl.OriginWidth;
28816 width = this.thumbEl.getWidth();
28817 this.baseScale = width / this.imageEl.OriginWidth;
28819 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28820 height = this.thumbEl.getHeight();
28821 this.baseScale = height / this.imageEl.OriginHeight;
28824 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28826 height = this.thumbEl.getHeight();
28827 this.baseScale = height / this.imageEl.OriginHeight;
28829 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28830 width = this.thumbEl.getWidth();
28831 this.baseScale = width / this.imageEl.OriginWidth;
28839 getScaleLevel : function()
28841 return this.baseScale * Math.pow(1.1, this.scale);
28844 onTouchStart : function(e)
28846 if(!this.canvasLoaded){
28847 this.beforeSelectFile(e);
28851 var touches = e.browserEvent.touches;
28857 if(touches.length == 1){
28858 this.onMouseDown(e);
28862 if(touches.length != 2){
28868 for(var i = 0, finger; finger = touches[i]; i++){
28869 coords.push(finger.pageX, finger.pageY);
28872 var x = Math.pow(coords[0] - coords[2], 2);
28873 var y = Math.pow(coords[1] - coords[3], 2);
28875 this.startDistance = Math.sqrt(x + y);
28877 this.startScale = this.scale;
28879 this.pinching = true;
28880 this.dragable = false;
28884 onTouchMove : function(e)
28886 if(!this.pinching && !this.dragable){
28890 var touches = e.browserEvent.touches;
28897 this.onMouseMove(e);
28903 for(var i = 0, finger; finger = touches[i]; i++){
28904 coords.push(finger.pageX, finger.pageY);
28907 var x = Math.pow(coords[0] - coords[2], 2);
28908 var y = Math.pow(coords[1] - coords[3], 2);
28910 this.endDistance = Math.sqrt(x + y);
28912 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28914 if(!this.zoomable()){
28915 this.scale = this.startScale;
28923 onTouchEnd : function(e)
28925 this.pinching = false;
28926 this.dragable = false;
28930 process : function(file, crop)
28933 this.maskEl.mask(this.loadingText);
28936 this.xhr = new XMLHttpRequest();
28938 file.xhr = this.xhr;
28940 this.xhr.open(this.method, this.url, true);
28943 "Accept": "application/json",
28944 "Cache-Control": "no-cache",
28945 "X-Requested-With": "XMLHttpRequest"
28948 for (var headerName in headers) {
28949 var headerValue = headers[headerName];
28951 this.xhr.setRequestHeader(headerName, headerValue);
28957 this.xhr.onload = function()
28959 _this.xhrOnLoad(_this.xhr);
28962 this.xhr.onerror = function()
28964 _this.xhrOnError(_this.xhr);
28967 var formData = new FormData();
28969 formData.append('returnHTML', 'NO');
28972 formData.append('crop', crop);
28975 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28976 formData.append(this.paramName, file, file.name);
28979 if(typeof(file.filename) != 'undefined'){
28980 formData.append('filename', file.filename);
28983 if(typeof(file.mimetype) != 'undefined'){
28984 formData.append('mimetype', file.mimetype);
28987 if(this.fireEvent('arrange', this, formData) != false){
28988 this.xhr.send(formData);
28992 xhrOnLoad : function(xhr)
28995 this.maskEl.unmask();
28998 if (xhr.readyState !== 4) {
28999 this.fireEvent('exception', this, xhr);
29003 var response = Roo.decode(xhr.responseText);
29005 if(!response.success){
29006 this.fireEvent('exception', this, xhr);
29010 var response = Roo.decode(xhr.responseText);
29012 this.fireEvent('upload', this, response);
29016 xhrOnError : function()
29019 this.maskEl.unmask();
29022 Roo.log('xhr on error');
29024 var response = Roo.decode(xhr.responseText);
29030 prepare : function(file)
29033 this.maskEl.mask(this.loadingText);
29039 if(typeof(file) === 'string'){
29040 this.loadCanvas(file);
29044 if(!file || !this.urlAPI){
29049 this.cropType = file.type;
29053 if(this.fireEvent('prepare', this, this.file) != false){
29055 var reader = new FileReader();
29057 reader.onload = function (e) {
29058 if (e.target.error) {
29059 Roo.log(e.target.error);
29063 var buffer = e.target.result,
29064 dataView = new DataView(buffer),
29066 maxOffset = dataView.byteLength - 4,
29070 if (dataView.getUint16(0) === 0xffd8) {
29071 while (offset < maxOffset) {
29072 markerBytes = dataView.getUint16(offset);
29074 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
29075 markerLength = dataView.getUint16(offset + 2) + 2;
29076 if (offset + markerLength > dataView.byteLength) {
29077 Roo.log('Invalid meta data: Invalid segment size.');
29081 if(markerBytes == 0xffe1){
29082 _this.parseExifData(
29089 offset += markerLength;
29099 var url = _this.urlAPI.createObjectURL(_this.file);
29101 _this.loadCanvas(url);
29106 reader.readAsArrayBuffer(this.file);
29112 parseExifData : function(dataView, offset, length)
29114 var tiffOffset = offset + 10,
29118 if (dataView.getUint32(offset + 4) !== 0x45786966) {
29119 // No Exif data, might be XMP data instead
29123 // Check for the ASCII code for "Exif" (0x45786966):
29124 if (dataView.getUint32(offset + 4) !== 0x45786966) {
29125 // No Exif data, might be XMP data instead
29128 if (tiffOffset + 8 > dataView.byteLength) {
29129 Roo.log('Invalid Exif data: Invalid segment size.');
29132 // Check for the two null bytes:
29133 if (dataView.getUint16(offset + 8) !== 0x0000) {
29134 Roo.log('Invalid Exif data: Missing byte alignment offset.');
29137 // Check the byte alignment:
29138 switch (dataView.getUint16(tiffOffset)) {
29140 littleEndian = true;
29143 littleEndian = false;
29146 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
29149 // Check for the TIFF tag marker (0x002A):
29150 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
29151 Roo.log('Invalid Exif data: Missing TIFF marker.');
29154 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
29155 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
29157 this.parseExifTags(
29160 tiffOffset + dirOffset,
29165 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
29170 if (dirOffset + 6 > dataView.byteLength) {
29171 Roo.log('Invalid Exif data: Invalid directory offset.');
29174 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
29175 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
29176 if (dirEndOffset + 4 > dataView.byteLength) {
29177 Roo.log('Invalid Exif data: Invalid directory size.');
29180 for (i = 0; i < tagsNumber; i += 1) {
29184 dirOffset + 2 + 12 * i, // tag offset
29188 // Return the offset to the next directory:
29189 return dataView.getUint32(dirEndOffset, littleEndian);
29192 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
29194 var tag = dataView.getUint16(offset, littleEndian);
29196 this.exif[tag] = this.getExifValue(
29200 dataView.getUint16(offset + 2, littleEndian), // tag type
29201 dataView.getUint32(offset + 4, littleEndian), // tag length
29206 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
29208 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
29217 Roo.log('Invalid Exif data: Invalid tag type.');
29221 tagSize = tagType.size * length;
29222 // Determine if the value is contained in the dataOffset bytes,
29223 // or if the value at the dataOffset is a pointer to the actual data:
29224 dataOffset = tagSize > 4 ?
29225 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
29226 if (dataOffset + tagSize > dataView.byteLength) {
29227 Roo.log('Invalid Exif data: Invalid data offset.');
29230 if (length === 1) {
29231 return tagType.getValue(dataView, dataOffset, littleEndian);
29234 for (i = 0; i < length; i += 1) {
29235 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
29238 if (tagType.ascii) {
29240 // Concatenate the chars:
29241 for (i = 0; i < values.length; i += 1) {
29243 // Ignore the terminating NULL byte(s):
29244 if (c === '\u0000') {
29256 Roo.apply(Roo.bootstrap.UploadCropbox, {
29258 'Orientation': 0x0112
29262 1: 0, //'top-left',
29264 3: 180, //'bottom-right',
29265 // 4: 'bottom-left',
29267 6: 90, //'right-top',
29268 // 7: 'right-bottom',
29269 8: 270 //'left-bottom'
29273 // byte, 8-bit unsigned int:
29275 getValue: function (dataView, dataOffset) {
29276 return dataView.getUint8(dataOffset);
29280 // ascii, 8-bit byte:
29282 getValue: function (dataView, dataOffset) {
29283 return String.fromCharCode(dataView.getUint8(dataOffset));
29288 // short, 16 bit int:
29290 getValue: function (dataView, dataOffset, littleEndian) {
29291 return dataView.getUint16(dataOffset, littleEndian);
29295 // long, 32 bit int:
29297 getValue: function (dataView, dataOffset, littleEndian) {
29298 return dataView.getUint32(dataOffset, littleEndian);
29302 // rational = two long values, first is numerator, second is denominator:
29304 getValue: function (dataView, dataOffset, littleEndian) {
29305 return dataView.getUint32(dataOffset, littleEndian) /
29306 dataView.getUint32(dataOffset + 4, littleEndian);
29310 // slong, 32 bit signed int:
29312 getValue: function (dataView, dataOffset, littleEndian) {
29313 return dataView.getInt32(dataOffset, littleEndian);
29317 // srational, two slongs, first is numerator, second is denominator:
29319 getValue: function (dataView, dataOffset, littleEndian) {
29320 return dataView.getInt32(dataOffset, littleEndian) /
29321 dataView.getInt32(dataOffset + 4, littleEndian);
29331 cls : 'btn-group roo-upload-cropbox-rotate-left',
29332 action : 'rotate-left',
29336 cls : 'btn btn-default',
29337 html : '<i class="fa fa-undo"></i>'
29343 cls : 'btn-group roo-upload-cropbox-picture',
29344 action : 'picture',
29348 cls : 'btn btn-default',
29349 html : '<i class="fa fa-picture-o"></i>'
29355 cls : 'btn-group roo-upload-cropbox-rotate-right',
29356 action : 'rotate-right',
29360 cls : 'btn btn-default',
29361 html : '<i class="fa fa-repeat"></i>'
29369 cls : 'btn-group roo-upload-cropbox-rotate-left',
29370 action : 'rotate-left',
29374 cls : 'btn btn-default',
29375 html : '<i class="fa fa-undo"></i>'
29381 cls : 'btn-group roo-upload-cropbox-download',
29382 action : 'download',
29386 cls : 'btn btn-default',
29387 html : '<i class="fa fa-download"></i>'
29393 cls : 'btn-group roo-upload-cropbox-crop',
29398 cls : 'btn btn-default',
29399 html : '<i class="fa fa-crop"></i>'
29405 cls : 'btn-group roo-upload-cropbox-trash',
29410 cls : 'btn btn-default',
29411 html : '<i class="fa fa-trash"></i>'
29417 cls : 'btn-group roo-upload-cropbox-rotate-right',
29418 action : 'rotate-right',
29422 cls : 'btn btn-default',
29423 html : '<i class="fa fa-repeat"></i>'
29431 cls : 'btn-group roo-upload-cropbox-rotate-left',
29432 action : 'rotate-left',
29436 cls : 'btn btn-default',
29437 html : '<i class="fa fa-undo"></i>'
29443 cls : 'btn-group roo-upload-cropbox-rotate-right',
29444 action : 'rotate-right',
29448 cls : 'btn btn-default',
29449 html : '<i class="fa fa-repeat"></i>'
29462 * @class Roo.bootstrap.DocumentManager
29463 * @extends Roo.bootstrap.Component
29464 * Bootstrap DocumentManager class
29465 * @cfg {String} paramName default 'imageUpload'
29466 * @cfg {String} toolTipName default 'filename'
29467 * @cfg {String} method default POST
29468 * @cfg {String} url action url
29469 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29470 * @cfg {Boolean} multiple multiple upload default true
29471 * @cfg {Number} thumbSize default 300
29472 * @cfg {String} fieldLabel
29473 * @cfg {Number} labelWidth default 4
29474 * @cfg {String} labelAlign (left|top) default left
29475 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29476 * @cfg {Number} labellg set the width of label (1-12)
29477 * @cfg {Number} labelmd set the width of label (1-12)
29478 * @cfg {Number} labelsm set the width of label (1-12)
29479 * @cfg {Number} labelxs set the width of label (1-12)
29482 * Create a new DocumentManager
29483 * @param {Object} config The config object
29486 Roo.bootstrap.DocumentManager = function(config){
29487 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29490 this.delegates = [];
29495 * Fire when initial the DocumentManager
29496 * @param {Roo.bootstrap.DocumentManager} this
29501 * inspect selected file
29502 * @param {Roo.bootstrap.DocumentManager} this
29503 * @param {File} file
29508 * Fire when xhr load exception
29509 * @param {Roo.bootstrap.DocumentManager} this
29510 * @param {XMLHttpRequest} xhr
29512 "exception" : true,
29514 * @event afterupload
29515 * Fire when xhr load exception
29516 * @param {Roo.bootstrap.DocumentManager} this
29517 * @param {XMLHttpRequest} xhr
29519 "afterupload" : true,
29522 * prepare the form data
29523 * @param {Roo.bootstrap.DocumentManager} this
29524 * @param {Object} formData
29529 * Fire when remove the file
29530 * @param {Roo.bootstrap.DocumentManager} this
29531 * @param {Object} file
29536 * Fire after refresh the file
29537 * @param {Roo.bootstrap.DocumentManager} this
29542 * Fire after click the image
29543 * @param {Roo.bootstrap.DocumentManager} this
29544 * @param {Object} file
29549 * Fire when upload a image and editable set to true
29550 * @param {Roo.bootstrap.DocumentManager} this
29551 * @param {Object} file
29555 * @event beforeselectfile
29556 * Fire before select file
29557 * @param {Roo.bootstrap.DocumentManager} this
29559 "beforeselectfile" : true,
29562 * Fire before process file
29563 * @param {Roo.bootstrap.DocumentManager} this
29564 * @param {Object} file
29568 * @event previewrendered
29569 * Fire when preview rendered
29570 * @param {Roo.bootstrap.DocumentManager} this
29571 * @param {Object} file
29573 "previewrendered" : true,
29576 "previewResize" : true
29581 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
29590 paramName : 'imageUpload',
29591 toolTipName : 'filename',
29594 labelAlign : 'left',
29604 getAutoCreate : function()
29606 var managerWidget = {
29608 cls : 'roo-document-manager',
29612 cls : 'roo-document-manager-selector',
29617 cls : 'roo-document-manager-uploader',
29621 cls : 'roo-document-manager-upload-btn',
29622 html : '<i class="fa fa-plus"></i>'
29633 cls : 'column col-md-12',
29638 if(this.fieldLabel.length){
29643 cls : 'column col-md-12',
29644 html : this.fieldLabel
29648 cls : 'column col-md-12',
29653 if(this.labelAlign == 'left'){
29658 html : this.fieldLabel
29667 if(this.labelWidth > 12){
29668 content[0].style = "width: " + this.labelWidth + 'px';
29671 if(this.labelWidth < 13 && this.labelmd == 0){
29672 this.labelmd = this.labelWidth;
29675 if(this.labellg > 0){
29676 content[0].cls += ' col-lg-' + this.labellg;
29677 content[1].cls += ' col-lg-' + (12 - this.labellg);
29680 if(this.labelmd > 0){
29681 content[0].cls += ' col-md-' + this.labelmd;
29682 content[1].cls += ' col-md-' + (12 - this.labelmd);
29685 if(this.labelsm > 0){
29686 content[0].cls += ' col-sm-' + this.labelsm;
29687 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29690 if(this.labelxs > 0){
29691 content[0].cls += ' col-xs-' + this.labelxs;
29692 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29700 cls : 'row clearfix',
29708 initEvents : function()
29710 this.managerEl = this.el.select('.roo-document-manager', true).first();
29711 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29713 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29714 this.selectorEl.hide();
29717 this.selectorEl.attr('multiple', 'multiple');
29720 this.selectorEl.on('change', this.onFileSelected, this);
29722 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29723 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29725 this.uploader.on('click', this.onUploaderClick, this);
29727 this.renderProgressDialog();
29731 window.addEventListener("resize", function() { _this.refresh(); } );
29733 this.fireEvent('initial', this);
29736 renderProgressDialog : function()
29740 this.progressDialog = new Roo.bootstrap.Modal({
29741 cls : 'roo-document-manager-progress-dialog',
29742 allow_close : false,
29753 btnclick : function() {
29754 _this.uploadCancel();
29760 this.progressDialog.render(Roo.get(document.body));
29762 this.progress = new Roo.bootstrap.Progress({
29763 cls : 'roo-document-manager-progress',
29768 this.progress.render(this.progressDialog.getChildContainer());
29770 this.progressBar = new Roo.bootstrap.ProgressBar({
29771 cls : 'roo-document-manager-progress-bar',
29774 aria_valuemax : 12,
29778 this.progressBar.render(this.progress.getChildContainer());
29781 onUploaderClick : function(e)
29783 e.preventDefault();
29785 if(this.fireEvent('beforeselectfile', this) != false){
29786 this.selectorEl.dom.click();
29791 onFileSelected : function(e)
29793 e.preventDefault();
29795 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29799 Roo.each(this.selectorEl.dom.files, function(file){
29800 if(this.fireEvent('inspect', this, file) != false){
29801 this.files.push(file);
29811 this.selectorEl.dom.value = '';
29813 if(!this.files || !this.files.length){
29817 if(this.boxes > 0 && this.files.length > this.boxes){
29818 this.files = this.files.slice(0, this.boxes);
29821 this.uploader.show();
29823 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29824 this.uploader.hide();
29833 Roo.each(this.files, function(file){
29835 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29836 var f = this.renderPreview(file);
29841 if(file.type.indexOf('image') != -1){
29842 this.delegates.push(
29844 _this.process(file);
29845 }).createDelegate(this)
29853 _this.process(file);
29854 }).createDelegate(this)
29859 this.files = files;
29861 this.delegates = this.delegates.concat(docs);
29863 if(!this.delegates.length){
29868 this.progressBar.aria_valuemax = this.delegates.length;
29875 arrange : function()
29877 if(!this.delegates.length){
29878 this.progressDialog.hide();
29883 var delegate = this.delegates.shift();
29885 this.progressDialog.show();
29887 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29889 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29894 refresh : function()
29896 this.uploader.show();
29898 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29899 this.uploader.hide();
29902 Roo.isTouch ? this.closable(false) : this.closable(true);
29904 this.fireEvent('refresh', this);
29907 onRemove : function(e, el, o)
29909 e.preventDefault();
29911 this.fireEvent('remove', this, o);
29915 remove : function(o)
29919 Roo.each(this.files, function(file){
29920 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29929 this.files = files;
29936 Roo.each(this.files, function(file){
29941 file.target.remove();
29950 onClick : function(e, el, o)
29952 e.preventDefault();
29954 this.fireEvent('click', this, o);
29958 closable : function(closable)
29960 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29962 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29974 xhrOnLoad : function(xhr)
29976 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29980 if (xhr.readyState !== 4) {
29982 this.fireEvent('exception', this, xhr);
29986 var response = Roo.decode(xhr.responseText);
29988 if(!response.success){
29990 this.fireEvent('exception', this, xhr);
29994 var file = this.renderPreview(response.data);
29996 this.files.push(file);
30000 this.fireEvent('afterupload', this, xhr);
30004 xhrOnError : function(xhr)
30006 Roo.log('xhr on error');
30008 var response = Roo.decode(xhr.responseText);
30015 process : function(file)
30017 if(this.fireEvent('process', this, file) !== false){
30018 if(this.editable && file.type.indexOf('image') != -1){
30019 this.fireEvent('edit', this, file);
30023 this.uploadStart(file, false);
30030 uploadStart : function(file, crop)
30032 this.xhr = new XMLHttpRequest();
30034 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30039 file.xhr = this.xhr;
30041 this.managerEl.createChild({
30043 cls : 'roo-document-manager-loading',
30047 tooltip : file.name,
30048 cls : 'roo-document-manager-thumb',
30049 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30055 this.xhr.open(this.method, this.url, true);
30058 "Accept": "application/json",
30059 "Cache-Control": "no-cache",
30060 "X-Requested-With": "XMLHttpRequest"
30063 for (var headerName in headers) {
30064 var headerValue = headers[headerName];
30066 this.xhr.setRequestHeader(headerName, headerValue);
30072 this.xhr.onload = function()
30074 _this.xhrOnLoad(_this.xhr);
30077 this.xhr.onerror = function()
30079 _this.xhrOnError(_this.xhr);
30082 var formData = new FormData();
30084 formData.append('returnHTML', 'NO');
30087 formData.append('crop', crop);
30090 formData.append(this.paramName, file, file.name);
30097 if(this.fireEvent('prepare', this, formData, options) != false){
30099 if(options.manually){
30103 this.xhr.send(formData);
30107 this.uploadCancel();
30110 uploadCancel : function()
30116 this.delegates = [];
30118 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30125 renderPreview : function(file)
30127 if(typeof(file.target) != 'undefined' && file.target){
30131 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
30133 var previewEl = this.managerEl.createChild({
30135 cls : 'roo-document-manager-preview',
30139 tooltip : file[this.toolTipName],
30140 cls : 'roo-document-manager-thumb',
30141 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
30146 html : '<i class="fa fa-times-circle"></i>'
30151 var close = previewEl.select('button.close', true).first();
30153 close.on('click', this.onRemove, this, file);
30155 file.target = previewEl;
30157 var image = previewEl.select('img', true).first();
30161 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
30163 image.on('click', this.onClick, this, file);
30165 this.fireEvent('previewrendered', this, file);
30171 onPreviewLoad : function(file, image)
30173 if(typeof(file.target) == 'undefined' || !file.target){
30177 var width = image.dom.naturalWidth || image.dom.width;
30178 var height = image.dom.naturalHeight || image.dom.height;
30180 if(!this.previewResize) {
30184 if(width > height){
30185 file.target.addClass('wide');
30189 file.target.addClass('tall');
30194 uploadFromSource : function(file, crop)
30196 this.xhr = new XMLHttpRequest();
30198 this.managerEl.createChild({
30200 cls : 'roo-document-manager-loading',
30204 tooltip : file.name,
30205 cls : 'roo-document-manager-thumb',
30206 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30212 this.xhr.open(this.method, this.url, true);
30215 "Accept": "application/json",
30216 "Cache-Control": "no-cache",
30217 "X-Requested-With": "XMLHttpRequest"
30220 for (var headerName in headers) {
30221 var headerValue = headers[headerName];
30223 this.xhr.setRequestHeader(headerName, headerValue);
30229 this.xhr.onload = function()
30231 _this.xhrOnLoad(_this.xhr);
30234 this.xhr.onerror = function()
30236 _this.xhrOnError(_this.xhr);
30239 var formData = new FormData();
30241 formData.append('returnHTML', 'NO');
30243 formData.append('crop', crop);
30245 if(typeof(file.filename) != 'undefined'){
30246 formData.append('filename', file.filename);
30249 if(typeof(file.mimetype) != 'undefined'){
30250 formData.append('mimetype', file.mimetype);
30255 if(this.fireEvent('prepare', this, formData) != false){
30256 this.xhr.send(formData);
30266 * @class Roo.bootstrap.DocumentViewer
30267 * @extends Roo.bootstrap.Component
30268 * Bootstrap DocumentViewer class
30269 * @cfg {Boolean} showDownload (true|false) show download button (default true)
30270 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30273 * Create a new DocumentViewer
30274 * @param {Object} config The config object
30277 Roo.bootstrap.DocumentViewer = function(config){
30278 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30283 * Fire after initEvent
30284 * @param {Roo.bootstrap.DocumentViewer} this
30290 * @param {Roo.bootstrap.DocumentViewer} this
30295 * Fire after download button
30296 * @param {Roo.bootstrap.DocumentViewer} this
30301 * Fire after trash button
30302 * @param {Roo.bootstrap.DocumentViewer} this
30309 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
30311 showDownload : true,
30315 getAutoCreate : function()
30319 cls : 'roo-document-viewer',
30323 cls : 'roo-document-viewer-body',
30327 cls : 'roo-document-viewer-thumb',
30331 cls : 'roo-document-viewer-image'
30339 cls : 'roo-document-viewer-footer',
30342 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30346 cls : 'btn-group roo-document-viewer-download',
30350 cls : 'btn btn-default',
30351 html : '<i class="fa fa-download"></i>'
30357 cls : 'btn-group roo-document-viewer-trash',
30361 cls : 'btn btn-default',
30362 html : '<i class="fa fa-trash"></i>'
30375 initEvents : function()
30377 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30378 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30380 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30381 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30383 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30384 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30386 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30387 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30389 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30390 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30392 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30393 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30395 this.bodyEl.on('click', this.onClick, this);
30396 this.downloadBtn.on('click', this.onDownload, this);
30397 this.trashBtn.on('click', this.onTrash, this);
30399 this.downloadBtn.hide();
30400 this.trashBtn.hide();
30402 if(this.showDownload){
30403 this.downloadBtn.show();
30406 if(this.showTrash){
30407 this.trashBtn.show();
30410 if(!this.showDownload && !this.showTrash) {
30411 this.footerEl.hide();
30416 initial : function()
30418 this.fireEvent('initial', this);
30422 onClick : function(e)
30424 e.preventDefault();
30426 this.fireEvent('click', this);
30429 onDownload : function(e)
30431 e.preventDefault();
30433 this.fireEvent('download', this);
30436 onTrash : function(e)
30438 e.preventDefault();
30440 this.fireEvent('trash', this);
30452 * @class Roo.bootstrap.NavProgressBar
30453 * @extends Roo.bootstrap.Component
30454 * Bootstrap NavProgressBar class
30457 * Create a new nav progress bar
30458 * @param {Object} config The config object
30461 Roo.bootstrap.NavProgressBar = function(config){
30462 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30464 this.bullets = this.bullets || [];
30466 // Roo.bootstrap.NavProgressBar.register(this);
30470 * Fires when the active item changes
30471 * @param {Roo.bootstrap.NavProgressBar} this
30472 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30473 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
30480 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
30485 getAutoCreate : function()
30487 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30491 cls : 'roo-navigation-bar-group',
30495 cls : 'roo-navigation-top-bar'
30499 cls : 'roo-navigation-bullets-bar',
30503 cls : 'roo-navigation-bar'
30510 cls : 'roo-navigation-bottom-bar'
30520 initEvents: function()
30525 onRender : function(ct, position)
30527 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30529 if(this.bullets.length){
30530 Roo.each(this.bullets, function(b){
30539 addItem : function(cfg)
30541 var item = new Roo.bootstrap.NavProgressItem(cfg);
30543 item.parentId = this.id;
30544 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30547 var top = new Roo.bootstrap.Element({
30549 cls : 'roo-navigation-bar-text'
30552 var bottom = new Roo.bootstrap.Element({
30554 cls : 'roo-navigation-bar-text'
30557 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30558 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30560 var topText = new Roo.bootstrap.Element({
30562 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30565 var bottomText = new Roo.bootstrap.Element({
30567 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30570 topText.onRender(top.el, null);
30571 bottomText.onRender(bottom.el, null);
30574 item.bottomEl = bottom;
30577 this.barItems.push(item);
30582 getActive : function()
30584 var active = false;
30586 Roo.each(this.barItems, function(v){
30588 if (!v.isActive()) {
30600 setActiveItem : function(item)
30604 Roo.each(this.barItems, function(v){
30605 if (v.rid == item.rid) {
30609 if (v.isActive()) {
30610 v.setActive(false);
30615 item.setActive(true);
30617 this.fireEvent('changed', this, item, prev);
30620 getBarItem: function(rid)
30624 Roo.each(this.barItems, function(e) {
30625 if (e.rid != rid) {
30636 indexOfItem : function(item)
30640 Roo.each(this.barItems, function(v, i){
30642 if (v.rid != item.rid) {
30653 setActiveNext : function()
30655 var i = this.indexOfItem(this.getActive());
30657 if (i > this.barItems.length) {
30661 this.setActiveItem(this.barItems[i+1]);
30664 setActivePrev : function()
30666 var i = this.indexOfItem(this.getActive());
30672 this.setActiveItem(this.barItems[i-1]);
30675 format : function()
30677 if(!this.barItems.length){
30681 var width = 100 / this.barItems.length;
30683 Roo.each(this.barItems, function(i){
30684 i.el.setStyle('width', width + '%');
30685 i.topEl.el.setStyle('width', width + '%');
30686 i.bottomEl.el.setStyle('width', width + '%');
30695 * Nav Progress Item
30700 * @class Roo.bootstrap.NavProgressItem
30701 * @extends Roo.bootstrap.Component
30702 * Bootstrap NavProgressItem class
30703 * @cfg {String} rid the reference id
30704 * @cfg {Boolean} active (true|false) Is item active default false
30705 * @cfg {Boolean} disabled (true|false) Is item active default false
30706 * @cfg {String} html
30707 * @cfg {String} position (top|bottom) text position default bottom
30708 * @cfg {String} icon show icon instead of number
30711 * Create a new NavProgressItem
30712 * @param {Object} config The config object
30714 Roo.bootstrap.NavProgressItem = function(config){
30715 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30720 * The raw click event for the entire grid.
30721 * @param {Roo.bootstrap.NavProgressItem} this
30722 * @param {Roo.EventObject} e
30729 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30735 position : 'bottom',
30738 getAutoCreate : function()
30740 var iconCls = 'roo-navigation-bar-item-icon';
30742 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30746 cls: 'roo-navigation-bar-item',
30756 cfg.cls += ' active';
30759 cfg.cls += ' disabled';
30765 disable : function()
30767 this.setDisabled(true);
30770 enable : function()
30772 this.setDisabled(false);
30775 initEvents: function()
30777 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30779 this.iconEl.on('click', this.onClick, this);
30782 onClick : function(e)
30784 e.preventDefault();
30790 if(this.fireEvent('click', this, e) === false){
30794 this.parent().setActiveItem(this);
30797 isActive: function ()
30799 return this.active;
30802 setActive : function(state)
30804 if(this.active == state){
30808 this.active = state;
30811 this.el.addClass('active');
30815 this.el.removeClass('active');
30820 setDisabled : function(state)
30822 if(this.disabled == state){
30826 this.disabled = state;
30829 this.el.addClass('disabled');
30833 this.el.removeClass('disabled');
30836 tooltipEl : function()
30838 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30851 * @class Roo.bootstrap.FieldLabel
30852 * @extends Roo.bootstrap.Component
30853 * Bootstrap FieldLabel class
30854 * @cfg {String} html contents of the element
30855 * @cfg {String} tag tag of the element default label
30856 * @cfg {String} cls class of the element
30857 * @cfg {String} target label target
30858 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30859 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30860 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30861 * @cfg {String} iconTooltip default "This field is required"
30862 * @cfg {String} indicatorpos (left|right) default left
30865 * Create a new FieldLabel
30866 * @param {Object} config The config object
30869 Roo.bootstrap.FieldLabel = function(config){
30870 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30875 * Fires after the field has been marked as invalid.
30876 * @param {Roo.form.FieldLabel} this
30877 * @param {String} msg The validation message
30882 * Fires after the field has been validated with no errors.
30883 * @param {Roo.form.FieldLabel} this
30889 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30896 invalidClass : 'has-warning',
30897 validClass : 'has-success',
30898 iconTooltip : 'This field is required',
30899 indicatorpos : 'left',
30901 getAutoCreate : function(){
30904 if (!this.allowBlank) {
30910 cls : 'roo-bootstrap-field-label ' + this.cls,
30915 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30916 tooltip : this.iconTooltip
30925 if(this.indicatorpos == 'right'){
30928 cls : 'roo-bootstrap-field-label ' + this.cls,
30937 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30938 tooltip : this.iconTooltip
30947 initEvents: function()
30949 Roo.bootstrap.Element.superclass.initEvents.call(this);
30951 this.indicator = this.indicatorEl();
30953 if(this.indicator){
30954 this.indicator.removeClass('visible');
30955 this.indicator.addClass('invisible');
30958 Roo.bootstrap.FieldLabel.register(this);
30961 indicatorEl : function()
30963 var indicator = this.el.select('i.roo-required-indicator',true).first();
30974 * Mark this field as valid
30976 markValid : function()
30978 if(this.indicator){
30979 this.indicator.removeClass('visible');
30980 this.indicator.addClass('invisible');
30982 if (Roo.bootstrap.version == 3) {
30983 this.el.removeClass(this.invalidClass);
30984 this.el.addClass(this.validClass);
30986 this.el.removeClass('is-invalid');
30987 this.el.addClass('is-valid');
30991 this.fireEvent('valid', this);
30995 * Mark this field as invalid
30996 * @param {String} msg The validation message
30998 markInvalid : function(msg)
31000 if(this.indicator){
31001 this.indicator.removeClass('invisible');
31002 this.indicator.addClass('visible');
31004 if (Roo.bootstrap.version == 3) {
31005 this.el.removeClass(this.validClass);
31006 this.el.addClass(this.invalidClass);
31008 this.el.removeClass('is-valid');
31009 this.el.addClass('is-invalid');
31013 this.fireEvent('invalid', this, msg);
31019 Roo.apply(Roo.bootstrap.FieldLabel, {
31024 * register a FieldLabel Group
31025 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
31027 register : function(label)
31029 if(this.groups.hasOwnProperty(label.target)){
31033 this.groups[label.target] = label;
31037 * fetch a FieldLabel Group based on the target
31038 * @param {string} target
31039 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
31041 get: function(target) {
31042 if (typeof(this.groups[target]) == 'undefined') {
31046 return this.groups[target] ;
31055 * page DateSplitField.
31061 * @class Roo.bootstrap.DateSplitField
31062 * @extends Roo.bootstrap.Component
31063 * Bootstrap DateSplitField class
31064 * @cfg {string} fieldLabel - the label associated
31065 * @cfg {Number} labelWidth set the width of label (0-12)
31066 * @cfg {String} labelAlign (top|left)
31067 * @cfg {Boolean} dayAllowBlank (true|false) default false
31068 * @cfg {Boolean} monthAllowBlank (true|false) default false
31069 * @cfg {Boolean} yearAllowBlank (true|false) default false
31070 * @cfg {string} dayPlaceholder
31071 * @cfg {string} monthPlaceholder
31072 * @cfg {string} yearPlaceholder
31073 * @cfg {string} dayFormat default 'd'
31074 * @cfg {string} monthFormat default 'm'
31075 * @cfg {string} yearFormat default 'Y'
31076 * @cfg {Number} labellg set the width of label (1-12)
31077 * @cfg {Number} labelmd set the width of label (1-12)
31078 * @cfg {Number} labelsm set the width of label (1-12)
31079 * @cfg {Number} labelxs set the width of label (1-12)
31083 * Create a new DateSplitField
31084 * @param {Object} config The config object
31087 Roo.bootstrap.DateSplitField = function(config){
31088 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
31094 * getting the data of years
31095 * @param {Roo.bootstrap.DateSplitField} this
31096 * @param {Object} years
31101 * getting the data of days
31102 * @param {Roo.bootstrap.DateSplitField} this
31103 * @param {Object} days
31108 * Fires after the field has been marked as invalid.
31109 * @param {Roo.form.Field} this
31110 * @param {String} msg The validation message
31115 * Fires after the field has been validated with no errors.
31116 * @param {Roo.form.Field} this
31122 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
31125 labelAlign : 'top',
31127 dayAllowBlank : false,
31128 monthAllowBlank : false,
31129 yearAllowBlank : false,
31130 dayPlaceholder : '',
31131 monthPlaceholder : '',
31132 yearPlaceholder : '',
31136 isFormField : true,
31142 getAutoCreate : function()
31146 cls : 'row roo-date-split-field-group',
31151 cls : 'form-hidden-field roo-date-split-field-group-value',
31157 var labelCls = 'col-md-12';
31158 var contentCls = 'col-md-4';
31160 if(this.fieldLabel){
31164 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
31168 html : this.fieldLabel
31173 if(this.labelAlign == 'left'){
31175 if(this.labelWidth > 12){
31176 label.style = "width: " + this.labelWidth + 'px';
31179 if(this.labelWidth < 13 && this.labelmd == 0){
31180 this.labelmd = this.labelWidth;
31183 if(this.labellg > 0){
31184 labelCls = ' col-lg-' + this.labellg;
31185 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
31188 if(this.labelmd > 0){
31189 labelCls = ' col-md-' + this.labelmd;
31190 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
31193 if(this.labelsm > 0){
31194 labelCls = ' col-sm-' + this.labelsm;
31195 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
31198 if(this.labelxs > 0){
31199 labelCls = ' col-xs-' + this.labelxs;
31200 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
31204 label.cls += ' ' + labelCls;
31206 cfg.cn.push(label);
31209 Roo.each(['day', 'month', 'year'], function(t){
31212 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
31219 inputEl: function ()
31221 return this.el.select('.roo-date-split-field-group-value', true).first();
31224 onRender : function(ct, position)
31228 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31230 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
31232 this.dayField = new Roo.bootstrap.ComboBox({
31233 allowBlank : this.dayAllowBlank,
31234 alwaysQuery : true,
31235 displayField : 'value',
31238 forceSelection : true,
31240 placeholder : this.dayPlaceholder,
31241 selectOnFocus : true,
31242 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31243 triggerAction : 'all',
31245 valueField : 'value',
31246 store : new Roo.data.SimpleStore({
31247 data : (function() {
31249 _this.fireEvent('days', _this, days);
31252 fields : [ 'value' ]
31255 select : function (_self, record, index)
31257 _this.setValue(_this.getValue());
31262 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31264 this.monthField = new Roo.bootstrap.MonthField({
31265 after : '<i class=\"fa fa-calendar\"></i>',
31266 allowBlank : this.monthAllowBlank,
31267 placeholder : this.monthPlaceholder,
31270 render : function (_self)
31272 this.el.select('span.input-group-addon', true).first().on('click', function(e){
31273 e.preventDefault();
31277 select : function (_self, oldvalue, newvalue)
31279 _this.setValue(_this.getValue());
31284 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31286 this.yearField = new Roo.bootstrap.ComboBox({
31287 allowBlank : this.yearAllowBlank,
31288 alwaysQuery : true,
31289 displayField : 'value',
31292 forceSelection : true,
31294 placeholder : this.yearPlaceholder,
31295 selectOnFocus : true,
31296 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31297 triggerAction : 'all',
31299 valueField : 'value',
31300 store : new Roo.data.SimpleStore({
31301 data : (function() {
31303 _this.fireEvent('years', _this, years);
31306 fields : [ 'value' ]
31309 select : function (_self, record, index)
31311 _this.setValue(_this.getValue());
31316 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31319 setValue : function(v, format)
31321 this.inputEl.dom.value = v;
31323 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31325 var d = Date.parseDate(v, f);
31332 this.setDay(d.format(this.dayFormat));
31333 this.setMonth(d.format(this.monthFormat));
31334 this.setYear(d.format(this.yearFormat));
31341 setDay : function(v)
31343 this.dayField.setValue(v);
31344 this.inputEl.dom.value = this.getValue();
31349 setMonth : function(v)
31351 this.monthField.setValue(v, true);
31352 this.inputEl.dom.value = this.getValue();
31357 setYear : function(v)
31359 this.yearField.setValue(v);
31360 this.inputEl.dom.value = this.getValue();
31365 getDay : function()
31367 return this.dayField.getValue();
31370 getMonth : function()
31372 return this.monthField.getValue();
31375 getYear : function()
31377 return this.yearField.getValue();
31380 getValue : function()
31382 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31384 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31394 this.inputEl.dom.value = '';
31399 validate : function()
31401 var d = this.dayField.validate();
31402 var m = this.monthField.validate();
31403 var y = this.yearField.validate();
31408 (!this.dayAllowBlank && !d) ||
31409 (!this.monthAllowBlank && !m) ||
31410 (!this.yearAllowBlank && !y)
31415 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31424 this.markInvalid();
31429 markValid : function()
31432 var label = this.el.select('label', true).first();
31433 var icon = this.el.select('i.fa-star', true).first();
31439 this.fireEvent('valid', this);
31443 * Mark this field as invalid
31444 * @param {String} msg The validation message
31446 markInvalid : function(msg)
31449 var label = this.el.select('label', true).first();
31450 var icon = this.el.select('i.fa-star', true).first();
31452 if(label && !icon){
31453 this.el.select('.roo-date-split-field-label', true).createChild({
31455 cls : 'text-danger fa fa-lg fa-star',
31456 tooltip : 'This field is required',
31457 style : 'margin-right:5px;'
31461 this.fireEvent('invalid', this, msg);
31464 clearInvalid : function()
31466 var label = this.el.select('label', true).first();
31467 var icon = this.el.select('i.fa-star', true).first();
31473 this.fireEvent('valid', this);
31476 getName: function()
31486 * http://masonry.desandro.com
31488 * The idea is to render all the bricks based on vertical width...
31490 * The original code extends 'outlayer' - we might need to use that....
31496 * @class Roo.bootstrap.LayoutMasonry
31497 * @extends Roo.bootstrap.Component
31498 * Bootstrap Layout Masonry class
31501 * Create a new Element
31502 * @param {Object} config The config object
31505 Roo.bootstrap.LayoutMasonry = function(config){
31507 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31511 Roo.bootstrap.LayoutMasonry.register(this);
31517 * Fire after layout the items
31518 * @param {Roo.bootstrap.LayoutMasonry} this
31519 * @param {Roo.EventObject} e
31526 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
31529 * @cfg {Boolean} isLayoutInstant = no animation?
31531 isLayoutInstant : false, // needed?
31534 * @cfg {Number} boxWidth width of the columns
31539 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
31544 * @cfg {Number} padWidth padding below box..
31549 * @cfg {Number} gutter gutter width..
31554 * @cfg {Number} maxCols maximum number of columns
31560 * @cfg {Boolean} isAutoInitial defalut true
31562 isAutoInitial : true,
31567 * @cfg {Boolean} isHorizontal defalut false
31569 isHorizontal : false,
31571 currentSize : null,
31577 bricks: null, //CompositeElement
31581 _isLayoutInited : false,
31583 // isAlternative : false, // only use for vertical layout...
31586 * @cfg {Number} alternativePadWidth padding below box..
31588 alternativePadWidth : 50,
31590 selectedBrick : [],
31592 getAutoCreate : function(){
31594 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31598 cls: 'blog-masonary-wrapper ' + this.cls,
31600 cls : 'mas-boxes masonary'
31607 getChildContainer: function( )
31609 if (this.boxesEl) {
31610 return this.boxesEl;
31613 this.boxesEl = this.el.select('.mas-boxes').first();
31615 return this.boxesEl;
31619 initEvents : function()
31623 if(this.isAutoInitial){
31624 Roo.log('hook children rendered');
31625 this.on('childrenrendered', function() {
31626 Roo.log('children rendered');
31632 initial : function()
31634 this.selectedBrick = [];
31636 this.currentSize = this.el.getBox(true);
31638 Roo.EventManager.onWindowResize(this.resize, this);
31640 if(!this.isAutoInitial){
31648 //this.layout.defer(500,this);
31652 resize : function()
31654 var cs = this.el.getBox(true);
31657 this.currentSize.width == cs.width &&
31658 this.currentSize.x == cs.x &&
31659 this.currentSize.height == cs.height &&
31660 this.currentSize.y == cs.y
31662 Roo.log("no change in with or X or Y");
31666 this.currentSize = cs;
31672 layout : function()
31674 this._resetLayout();
31676 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31678 this.layoutItems( isInstant );
31680 this._isLayoutInited = true;
31682 this.fireEvent('layout', this);
31686 _resetLayout : function()
31688 if(this.isHorizontal){
31689 this.horizontalMeasureColumns();
31693 this.verticalMeasureColumns();
31697 verticalMeasureColumns : function()
31699 this.getContainerWidth();
31701 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31702 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31706 var boxWidth = this.boxWidth + this.padWidth;
31708 if(this.containerWidth < this.boxWidth){
31709 boxWidth = this.containerWidth
31712 var containerWidth = this.containerWidth;
31714 var cols = Math.floor(containerWidth / boxWidth);
31716 this.cols = Math.max( cols, 1 );
31718 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31720 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31722 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31724 this.colWidth = boxWidth + avail - this.padWidth;
31726 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31727 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31730 horizontalMeasureColumns : function()
31732 this.getContainerWidth();
31734 var boxWidth = this.boxWidth;
31736 if(this.containerWidth < boxWidth){
31737 boxWidth = this.containerWidth;
31740 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31742 this.el.setHeight(boxWidth);
31746 getContainerWidth : function()
31748 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31751 layoutItems : function( isInstant )
31753 Roo.log(this.bricks);
31755 var items = Roo.apply([], this.bricks);
31757 if(this.isHorizontal){
31758 this._horizontalLayoutItems( items , isInstant );
31762 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31763 // this._verticalAlternativeLayoutItems( items , isInstant );
31767 this._verticalLayoutItems( items , isInstant );
31771 _verticalLayoutItems : function ( items , isInstant)
31773 if ( !items || !items.length ) {
31778 ['xs', 'xs', 'xs', 'tall'],
31779 ['xs', 'xs', 'tall'],
31780 ['xs', 'xs', 'sm'],
31781 ['xs', 'xs', 'xs'],
31787 ['sm', 'xs', 'xs'],
31791 ['tall', 'xs', 'xs', 'xs'],
31792 ['tall', 'xs', 'xs'],
31804 Roo.each(items, function(item, k){
31806 switch (item.size) {
31807 // these layouts take up a full box,
31818 boxes.push([item]);
31841 var filterPattern = function(box, length)
31849 var pattern = box.slice(0, length);
31853 Roo.each(pattern, function(i){
31854 format.push(i.size);
31857 Roo.each(standard, function(s){
31859 if(String(s) != String(format)){
31868 if(!match && length == 1){
31873 filterPattern(box, length - 1);
31877 queue.push(pattern);
31879 box = box.slice(length, box.length);
31881 filterPattern(box, 4);
31887 Roo.each(boxes, function(box, k){
31893 if(box.length == 1){
31898 filterPattern(box, 4);
31902 this._processVerticalLayoutQueue( queue, isInstant );
31906 // _verticalAlternativeLayoutItems : function( items , isInstant )
31908 // if ( !items || !items.length ) {
31912 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31916 _horizontalLayoutItems : function ( items , isInstant)
31918 if ( !items || !items.length || items.length < 3) {
31924 var eItems = items.slice(0, 3);
31926 items = items.slice(3, items.length);
31929 ['xs', 'xs', 'xs', 'wide'],
31930 ['xs', 'xs', 'wide'],
31931 ['xs', 'xs', 'sm'],
31932 ['xs', 'xs', 'xs'],
31938 ['sm', 'xs', 'xs'],
31942 ['wide', 'xs', 'xs', 'xs'],
31943 ['wide', 'xs', 'xs'],
31956 Roo.each(items, function(item, k){
31958 switch (item.size) {
31969 boxes.push([item]);
31993 var filterPattern = function(box, length)
32001 var pattern = box.slice(0, length);
32005 Roo.each(pattern, function(i){
32006 format.push(i.size);
32009 Roo.each(standard, function(s){
32011 if(String(s) != String(format)){
32020 if(!match && length == 1){
32025 filterPattern(box, length - 1);
32029 queue.push(pattern);
32031 box = box.slice(length, box.length);
32033 filterPattern(box, 4);
32039 Roo.each(boxes, function(box, k){
32045 if(box.length == 1){
32050 filterPattern(box, 4);
32057 var pos = this.el.getBox(true);
32061 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32063 var hit_end = false;
32065 Roo.each(queue, function(box){
32069 Roo.each(box, function(b){
32071 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32081 Roo.each(box, function(b){
32083 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32086 mx = Math.max(mx, b.x);
32090 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
32094 Roo.each(box, function(b){
32096 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32110 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
32113 /** Sets position of item in DOM
32114 * @param {Element} item
32115 * @param {Number} x - horizontal position
32116 * @param {Number} y - vertical position
32117 * @param {Boolean} isInstant - disables transitions
32119 _processVerticalLayoutQueue : function( queue, isInstant )
32121 var pos = this.el.getBox(true);
32126 for (var i = 0; i < this.cols; i++){
32130 Roo.each(queue, function(box, k){
32132 var col = k % this.cols;
32134 Roo.each(box, function(b,kk){
32136 b.el.position('absolute');
32138 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32139 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32141 if(b.size == 'md-left' || b.size == 'md-right'){
32142 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32143 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32146 b.el.setWidth(width);
32147 b.el.setHeight(height);
32149 b.el.select('iframe',true).setSize(width,height);
32153 for (var i = 0; i < this.cols; i++){
32155 if(maxY[i] < maxY[col]){
32160 col = Math.min(col, i);
32164 x = pos.x + col * (this.colWidth + this.padWidth);
32168 var positions = [];
32170 switch (box.length){
32172 positions = this.getVerticalOneBoxColPositions(x, y, box);
32175 positions = this.getVerticalTwoBoxColPositions(x, y, box);
32178 positions = this.getVerticalThreeBoxColPositions(x, y, box);
32181 positions = this.getVerticalFourBoxColPositions(x, y, box);
32187 Roo.each(box, function(b,kk){
32189 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32191 var sz = b.el.getSize();
32193 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
32201 for (var i = 0; i < this.cols; i++){
32202 mY = Math.max(mY, maxY[i]);
32205 this.el.setHeight(mY - pos.y);
32209 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
32211 // var pos = this.el.getBox(true);
32214 // var maxX = pos.right;
32216 // var maxHeight = 0;
32218 // Roo.each(items, function(item, k){
32222 // item.el.position('absolute');
32224 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
32226 // item.el.setWidth(width);
32228 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
32230 // item.el.setHeight(height);
32233 // item.el.setXY([x, y], isInstant ? false : true);
32235 // item.el.setXY([maxX - width, y], isInstant ? false : true);
32238 // y = y + height + this.alternativePadWidth;
32240 // maxHeight = maxHeight + height + this.alternativePadWidth;
32244 // this.el.setHeight(maxHeight);
32248 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32250 var pos = this.el.getBox(true);
32255 var maxX = pos.right;
32257 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32259 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32261 Roo.each(queue, function(box, k){
32263 Roo.each(box, function(b, kk){
32265 b.el.position('absolute');
32267 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32268 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32270 if(b.size == 'md-left' || b.size == 'md-right'){
32271 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32272 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32275 b.el.setWidth(width);
32276 b.el.setHeight(height);
32284 var positions = [];
32286 switch (box.length){
32288 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32291 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32294 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32297 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32303 Roo.each(box, function(b,kk){
32305 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32307 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32315 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32317 Roo.each(eItems, function(b,k){
32319 b.size = (k == 0) ? 'sm' : 'xs';
32320 b.x = (k == 0) ? 2 : 1;
32321 b.y = (k == 0) ? 2 : 1;
32323 b.el.position('absolute');
32325 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32327 b.el.setWidth(width);
32329 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32331 b.el.setHeight(height);
32335 var positions = [];
32338 x : maxX - this.unitWidth * 2 - this.gutter,
32343 x : maxX - this.unitWidth,
32344 y : minY + (this.unitWidth + this.gutter) * 2
32348 x : maxX - this.unitWidth * 3 - this.gutter * 2,
32352 Roo.each(eItems, function(b,k){
32354 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32360 getVerticalOneBoxColPositions : function(x, y, box)
32364 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32366 if(box[0].size == 'md-left'){
32370 if(box[0].size == 'md-right'){
32375 x : x + (this.unitWidth + this.gutter) * rand,
32382 getVerticalTwoBoxColPositions : function(x, y, box)
32386 if(box[0].size == 'xs'){
32390 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32394 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32408 x : x + (this.unitWidth + this.gutter) * 2,
32409 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32416 getVerticalThreeBoxColPositions : function(x, y, box)
32420 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32428 x : x + (this.unitWidth + this.gutter) * 1,
32433 x : x + (this.unitWidth + this.gutter) * 2,
32441 if(box[0].size == 'xs' && box[1].size == 'xs'){
32450 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32454 x : x + (this.unitWidth + this.gutter) * 1,
32468 x : x + (this.unitWidth + this.gutter) * 2,
32473 x : x + (this.unitWidth + this.gutter) * 2,
32474 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32481 getVerticalFourBoxColPositions : function(x, y, box)
32485 if(box[0].size == 'xs'){
32494 y : y + (this.unitHeight + this.gutter) * 1
32499 y : y + (this.unitHeight + this.gutter) * 2
32503 x : x + (this.unitWidth + this.gutter) * 1,
32517 x : x + (this.unitWidth + this.gutter) * 2,
32522 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32523 y : y + (this.unitHeight + this.gutter) * 1
32527 x : x + (this.unitWidth + this.gutter) * 2,
32528 y : y + (this.unitWidth + this.gutter) * 2
32535 getHorizontalOneBoxColPositions : function(maxX, minY, box)
32539 if(box[0].size == 'md-left'){
32541 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32548 if(box[0].size == 'md-right'){
32550 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32551 y : minY + (this.unitWidth + this.gutter) * 1
32557 var rand = Math.floor(Math.random() * (4 - box[0].y));
32560 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32561 y : minY + (this.unitWidth + this.gutter) * rand
32568 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32572 if(box[0].size == 'xs'){
32575 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32580 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32581 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32589 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32594 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32595 y : minY + (this.unitWidth + this.gutter) * 2
32602 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32606 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32609 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32614 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32615 y : minY + (this.unitWidth + this.gutter) * 1
32619 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32620 y : minY + (this.unitWidth + this.gutter) * 2
32627 if(box[0].size == 'xs' && box[1].size == 'xs'){
32630 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32635 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32640 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32641 y : minY + (this.unitWidth + this.gutter) * 1
32649 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32654 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32655 y : minY + (this.unitWidth + this.gutter) * 2
32659 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32660 y : minY + (this.unitWidth + this.gutter) * 2
32667 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32671 if(box[0].size == 'xs'){
32674 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32679 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32684 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),
32689 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32690 y : minY + (this.unitWidth + this.gutter) * 1
32698 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32703 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32704 y : minY + (this.unitWidth + this.gutter) * 2
32708 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32709 y : minY + (this.unitWidth + this.gutter) * 2
32713 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),
32714 y : minY + (this.unitWidth + this.gutter) * 2
32722 * remove a Masonry Brick
32723 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32725 removeBrick : function(brick_id)
32731 for (var i = 0; i<this.bricks.length; i++) {
32732 if (this.bricks[i].id == brick_id) {
32733 this.bricks.splice(i,1);
32734 this.el.dom.removeChild(Roo.get(brick_id).dom);
32741 * adds a Masonry Brick
32742 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32744 addBrick : function(cfg)
32746 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32747 //this.register(cn);
32748 cn.parentId = this.id;
32749 cn.render(this.el);
32754 * register a Masonry Brick
32755 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32758 register : function(brick)
32760 this.bricks.push(brick);
32761 brick.masonryId = this.id;
32765 * clear all the Masonry Brick
32767 clearAll : function()
32770 //this.getChildContainer().dom.innerHTML = "";
32771 this.el.dom.innerHTML = '';
32774 getSelected : function()
32776 if (!this.selectedBrick) {
32780 return this.selectedBrick;
32784 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32788 * register a Masonry Layout
32789 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32792 register : function(layout)
32794 this.groups[layout.id] = layout;
32797 * fetch a Masonry Layout based on the masonry layout ID
32798 * @param {string} the masonry layout to add
32799 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32802 get: function(layout_id) {
32803 if (typeof(this.groups[layout_id]) == 'undefined') {
32806 return this.groups[layout_id] ;
32818 * http://masonry.desandro.com
32820 * The idea is to render all the bricks based on vertical width...
32822 * The original code extends 'outlayer' - we might need to use that....
32828 * @class Roo.bootstrap.LayoutMasonryAuto
32829 * @extends Roo.bootstrap.Component
32830 * Bootstrap Layout Masonry class
32833 * Create a new Element
32834 * @param {Object} config The config object
32837 Roo.bootstrap.LayoutMasonryAuto = function(config){
32838 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32841 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32844 * @cfg {Boolean} isFitWidth - resize the width..
32846 isFitWidth : false, // options..
32848 * @cfg {Boolean} isOriginLeft = left align?
32850 isOriginLeft : true,
32852 * @cfg {Boolean} isOriginTop = top align?
32854 isOriginTop : false,
32856 * @cfg {Boolean} isLayoutInstant = no animation?
32858 isLayoutInstant : false, // needed?
32860 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32862 isResizingContainer : true,
32864 * @cfg {Number} columnWidth width of the columns
32870 * @cfg {Number} maxCols maximum number of columns
32875 * @cfg {Number} padHeight padding below box..
32881 * @cfg {Boolean} isAutoInitial defalut true
32884 isAutoInitial : true,
32890 initialColumnWidth : 0,
32891 currentSize : null,
32893 colYs : null, // array.
32900 bricks: null, //CompositeElement
32901 cols : 0, // array?
32902 // element : null, // wrapped now this.el
32903 _isLayoutInited : null,
32906 getAutoCreate : function(){
32910 cls: 'blog-masonary-wrapper ' + this.cls,
32912 cls : 'mas-boxes masonary'
32919 getChildContainer: function( )
32921 if (this.boxesEl) {
32922 return this.boxesEl;
32925 this.boxesEl = this.el.select('.mas-boxes').first();
32927 return this.boxesEl;
32931 initEvents : function()
32935 if(this.isAutoInitial){
32936 Roo.log('hook children rendered');
32937 this.on('childrenrendered', function() {
32938 Roo.log('children rendered');
32945 initial : function()
32947 this.reloadItems();
32949 this.currentSize = this.el.getBox(true);
32951 /// was window resize... - let's see if this works..
32952 Roo.EventManager.onWindowResize(this.resize, this);
32954 if(!this.isAutoInitial){
32959 this.layout.defer(500,this);
32962 reloadItems: function()
32964 this.bricks = this.el.select('.masonry-brick', true);
32966 this.bricks.each(function(b) {
32967 //Roo.log(b.getSize());
32968 if (!b.attr('originalwidth')) {
32969 b.attr('originalwidth', b.getSize().width);
32974 Roo.log(this.bricks.elements.length);
32977 resize : function()
32980 var cs = this.el.getBox(true);
32982 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32983 Roo.log("no change in with or X");
32986 this.currentSize = cs;
32990 layout : function()
32993 this._resetLayout();
32994 //this._manageStamps();
32996 // don't animate first layout
32997 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32998 this.layoutItems( isInstant );
33000 // flag for initalized
33001 this._isLayoutInited = true;
33004 layoutItems : function( isInstant )
33006 //var items = this._getItemsForLayout( this.items );
33007 // original code supports filtering layout items.. we just ignore it..
33009 this._layoutItems( this.bricks , isInstant );
33011 this._postLayout();
33013 _layoutItems : function ( items , isInstant)
33015 //this.fireEvent( 'layout', this, items );
33018 if ( !items || !items.elements.length ) {
33019 // no items, emit event with empty array
33024 items.each(function(item) {
33025 Roo.log("layout item");
33027 // get x/y object from method
33028 var position = this._getItemLayoutPosition( item );
33030 position.item = item;
33031 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
33032 queue.push( position );
33035 this._processLayoutQueue( queue );
33037 /** Sets position of item in DOM
33038 * @param {Element} item
33039 * @param {Number} x - horizontal position
33040 * @param {Number} y - vertical position
33041 * @param {Boolean} isInstant - disables transitions
33043 _processLayoutQueue : function( queue )
33045 for ( var i=0, len = queue.length; i < len; i++ ) {
33046 var obj = queue[i];
33047 obj.item.position('absolute');
33048 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
33054 * Any logic you want to do after each layout,
33055 * i.e. size the container
33057 _postLayout : function()
33059 this.resizeContainer();
33062 resizeContainer : function()
33064 if ( !this.isResizingContainer ) {
33067 var size = this._getContainerSize();
33069 this.el.setSize(size.width,size.height);
33070 this.boxesEl.setSize(size.width,size.height);
33076 _resetLayout : function()
33078 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
33079 this.colWidth = this.el.getWidth();
33080 //this.gutter = this.el.getWidth();
33082 this.measureColumns();
33088 this.colYs.push( 0 );
33094 measureColumns : function()
33096 this.getContainerWidth();
33097 // if columnWidth is 0, default to outerWidth of first item
33098 if ( !this.columnWidth ) {
33099 var firstItem = this.bricks.first();
33100 Roo.log(firstItem);
33101 this.columnWidth = this.containerWidth;
33102 if (firstItem && firstItem.attr('originalwidth') ) {
33103 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
33105 // columnWidth fall back to item of first element
33106 Roo.log("set column width?");
33107 this.initialColumnWidth = this.columnWidth ;
33109 // if first elem has no width, default to size of container
33114 if (this.initialColumnWidth) {
33115 this.columnWidth = this.initialColumnWidth;
33120 // column width is fixed at the top - however if container width get's smaller we should
33123 // this bit calcs how man columns..
33125 var columnWidth = this.columnWidth += this.gutter;
33127 // calculate columns
33128 var containerWidth = this.containerWidth + this.gutter;
33130 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
33131 // fix rounding errors, typically with gutters
33132 var excess = columnWidth - containerWidth % columnWidth;
33135 // if overshoot is less than a pixel, round up, otherwise floor it
33136 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
33137 cols = Math[ mathMethod ]( cols );
33138 this.cols = Math.max( cols, 1 );
33139 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33141 // padding positioning..
33142 var totalColWidth = this.cols * this.columnWidth;
33143 var padavail = this.containerWidth - totalColWidth;
33144 // so for 2 columns - we need 3 'pads'
33146 var padNeeded = (1+this.cols) * this.padWidth;
33148 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
33150 this.columnWidth += padExtra
33151 //this.padWidth = Math.floor(padavail / ( this.cols));
33153 // adjust colum width so that padding is fixed??
33155 // we have 3 columns ... total = width * 3
33156 // we have X left over... that should be used by
33158 //if (this.expandC) {
33166 getContainerWidth : function()
33168 /* // container is parent if fit width
33169 var container = this.isFitWidth ? this.element.parentNode : this.element;
33170 // check that this.size and size are there
33171 // IE8 triggers resize on body size change, so they might not be
33173 var size = getSize( container ); //FIXME
33174 this.containerWidth = size && size.innerWidth; //FIXME
33177 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33181 _getItemLayoutPosition : function( item ) // what is item?
33183 // we resize the item to our columnWidth..
33185 item.setWidth(this.columnWidth);
33186 item.autoBoxAdjust = false;
33188 var sz = item.getSize();
33190 // how many columns does this brick span
33191 var remainder = this.containerWidth % this.columnWidth;
33193 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
33194 // round if off by 1 pixel, otherwise use ceil
33195 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
33196 colSpan = Math.min( colSpan, this.cols );
33198 // normally this should be '1' as we dont' currently allow multi width columns..
33200 var colGroup = this._getColGroup( colSpan );
33201 // get the minimum Y value from the columns
33202 var minimumY = Math.min.apply( Math, colGroup );
33203 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
33205 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
33207 // position the brick
33209 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
33210 y: this.currentSize.y + minimumY + this.padHeight
33214 // apply setHeight to necessary columns
33215 var setHeight = minimumY + sz.height + this.padHeight;
33216 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
33218 var setSpan = this.cols + 1 - colGroup.length;
33219 for ( var i = 0; i < setSpan; i++ ) {
33220 this.colYs[ shortColIndex + i ] = setHeight ;
33227 * @param {Number} colSpan - number of columns the element spans
33228 * @returns {Array} colGroup
33230 _getColGroup : function( colSpan )
33232 if ( colSpan < 2 ) {
33233 // if brick spans only one column, use all the column Ys
33238 // how many different places could this brick fit horizontally
33239 var groupCount = this.cols + 1 - colSpan;
33240 // for each group potential horizontal position
33241 for ( var i = 0; i < groupCount; i++ ) {
33242 // make an array of colY values for that one group
33243 var groupColYs = this.colYs.slice( i, i + colSpan );
33244 // and get the max value of the array
33245 colGroup[i] = Math.max.apply( Math, groupColYs );
33250 _manageStamp : function( stamp )
33252 var stampSize = stamp.getSize();
33253 var offset = stamp.getBox();
33254 // get the columns that this stamp affects
33255 var firstX = this.isOriginLeft ? offset.x : offset.right;
33256 var lastX = firstX + stampSize.width;
33257 var firstCol = Math.floor( firstX / this.columnWidth );
33258 firstCol = Math.max( 0, firstCol );
33260 var lastCol = Math.floor( lastX / this.columnWidth );
33261 // lastCol should not go over if multiple of columnWidth #425
33262 lastCol -= lastX % this.columnWidth ? 0 : 1;
33263 lastCol = Math.min( this.cols - 1, lastCol );
33265 // set colYs to bottom of the stamp
33266 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33269 for ( var i = firstCol; i <= lastCol; i++ ) {
33270 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33275 _getContainerSize : function()
33277 this.maxY = Math.max.apply( Math, this.colYs );
33282 if ( this.isFitWidth ) {
33283 size.width = this._getContainerFitWidth();
33289 _getContainerFitWidth : function()
33291 var unusedCols = 0;
33292 // count unused columns
33295 if ( this.colYs[i] !== 0 ) {
33300 // fit container to columns that have been used
33301 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33304 needsResizeLayout : function()
33306 var previousWidth = this.containerWidth;
33307 this.getContainerWidth();
33308 return previousWidth !== this.containerWidth;
33323 * @class Roo.bootstrap.MasonryBrick
33324 * @extends Roo.bootstrap.Component
33325 * Bootstrap MasonryBrick class
33328 * Create a new MasonryBrick
33329 * @param {Object} config The config object
33332 Roo.bootstrap.MasonryBrick = function(config){
33334 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33336 Roo.bootstrap.MasonryBrick.register(this);
33342 * When a MasonryBrick is clcik
33343 * @param {Roo.bootstrap.MasonryBrick} this
33344 * @param {Roo.EventObject} e
33350 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
33353 * @cfg {String} title
33357 * @cfg {String} html
33361 * @cfg {String} bgimage
33365 * @cfg {String} videourl
33369 * @cfg {String} cls
33373 * @cfg {String} href
33377 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33382 * @cfg {String} placetitle (center|bottom)
33387 * @cfg {Boolean} isFitContainer defalut true
33389 isFitContainer : true,
33392 * @cfg {Boolean} preventDefault defalut false
33394 preventDefault : false,
33397 * @cfg {Boolean} inverse defalut false
33399 maskInverse : false,
33401 getAutoCreate : function()
33403 if(!this.isFitContainer){
33404 return this.getSplitAutoCreate();
33407 var cls = 'masonry-brick masonry-brick-full';
33409 if(this.href.length){
33410 cls += ' masonry-brick-link';
33413 if(this.bgimage.length){
33414 cls += ' masonry-brick-image';
33417 if(this.maskInverse){
33418 cls += ' mask-inverse';
33421 if(!this.html.length && !this.maskInverse && !this.videourl.length){
33422 cls += ' enable-mask';
33426 cls += ' masonry-' + this.size + '-brick';
33429 if(this.placetitle.length){
33431 switch (this.placetitle) {
33433 cls += ' masonry-center-title';
33436 cls += ' masonry-bottom-title';
33443 if(!this.html.length && !this.bgimage.length){
33444 cls += ' masonry-center-title';
33447 if(!this.html.length && this.bgimage.length){
33448 cls += ' masonry-bottom-title';
33453 cls += ' ' + this.cls;
33457 tag: (this.href.length) ? 'a' : 'div',
33462 cls: 'masonry-brick-mask'
33466 cls: 'masonry-brick-paragraph',
33472 if(this.href.length){
33473 cfg.href = this.href;
33476 var cn = cfg.cn[1].cn;
33478 if(this.title.length){
33481 cls: 'masonry-brick-title',
33486 if(this.html.length){
33489 cls: 'masonry-brick-text',
33494 if (!this.title.length && !this.html.length) {
33495 cfg.cn[1].cls += ' hide';
33498 if(this.bgimage.length){
33501 cls: 'masonry-brick-image-view',
33506 if(this.videourl.length){
33507 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33508 // youtube support only?
33511 cls: 'masonry-brick-image-view',
33514 allowfullscreen : true
33522 getSplitAutoCreate : function()
33524 var cls = 'masonry-brick masonry-brick-split';
33526 if(this.href.length){
33527 cls += ' masonry-brick-link';
33530 if(this.bgimage.length){
33531 cls += ' masonry-brick-image';
33535 cls += ' masonry-' + this.size + '-brick';
33538 switch (this.placetitle) {
33540 cls += ' masonry-center-title';
33543 cls += ' masonry-bottom-title';
33546 if(!this.bgimage.length){
33547 cls += ' masonry-center-title';
33550 if(this.bgimage.length){
33551 cls += ' masonry-bottom-title';
33557 cls += ' ' + this.cls;
33561 tag: (this.href.length) ? 'a' : 'div',
33566 cls: 'masonry-brick-split-head',
33570 cls: 'masonry-brick-paragraph',
33577 cls: 'masonry-brick-split-body',
33583 if(this.href.length){
33584 cfg.href = this.href;
33587 if(this.title.length){
33588 cfg.cn[0].cn[0].cn.push({
33590 cls: 'masonry-brick-title',
33595 if(this.html.length){
33596 cfg.cn[1].cn.push({
33598 cls: 'masonry-brick-text',
33603 if(this.bgimage.length){
33604 cfg.cn[0].cn.push({
33606 cls: 'masonry-brick-image-view',
33611 if(this.videourl.length){
33612 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33613 // youtube support only?
33614 cfg.cn[0].cn.cn.push({
33616 cls: 'masonry-brick-image-view',
33619 allowfullscreen : true
33626 initEvents: function()
33628 switch (this.size) {
33661 this.el.on('touchstart', this.onTouchStart, this);
33662 this.el.on('touchmove', this.onTouchMove, this);
33663 this.el.on('touchend', this.onTouchEnd, this);
33664 this.el.on('contextmenu', this.onContextMenu, this);
33666 this.el.on('mouseenter' ,this.enter, this);
33667 this.el.on('mouseleave', this.leave, this);
33668 this.el.on('click', this.onClick, this);
33671 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33672 this.parent().bricks.push(this);
33677 onClick: function(e, el)
33679 var time = this.endTimer - this.startTimer;
33680 // Roo.log(e.preventDefault());
33683 e.preventDefault();
33688 if(!this.preventDefault){
33692 e.preventDefault();
33694 if (this.activeClass != '') {
33695 this.selectBrick();
33698 this.fireEvent('click', this, e);
33701 enter: function(e, el)
33703 e.preventDefault();
33705 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33709 if(this.bgimage.length && this.html.length){
33710 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33714 leave: function(e, el)
33716 e.preventDefault();
33718 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33722 if(this.bgimage.length && this.html.length){
33723 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33727 onTouchStart: function(e, el)
33729 // e.preventDefault();
33731 this.touchmoved = false;
33733 if(!this.isFitContainer){
33737 if(!this.bgimage.length || !this.html.length){
33741 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33743 this.timer = new Date().getTime();
33747 onTouchMove: function(e, el)
33749 this.touchmoved = true;
33752 onContextMenu : function(e,el)
33754 e.preventDefault();
33755 e.stopPropagation();
33759 onTouchEnd: function(e, el)
33761 // e.preventDefault();
33763 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33770 if(!this.bgimage.length || !this.html.length){
33772 if(this.href.length){
33773 window.location.href = this.href;
33779 if(!this.isFitContainer){
33783 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33785 window.location.href = this.href;
33788 //selection on single brick only
33789 selectBrick : function() {
33791 if (!this.parentId) {
33795 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33796 var index = m.selectedBrick.indexOf(this.id);
33799 m.selectedBrick.splice(index,1);
33800 this.el.removeClass(this.activeClass);
33804 for(var i = 0; i < m.selectedBrick.length; i++) {
33805 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33806 b.el.removeClass(b.activeClass);
33809 m.selectedBrick = [];
33811 m.selectedBrick.push(this.id);
33812 this.el.addClass(this.activeClass);
33816 isSelected : function(){
33817 return this.el.hasClass(this.activeClass);
33822 Roo.apply(Roo.bootstrap.MasonryBrick, {
33825 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33827 * register a Masonry Brick
33828 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33831 register : function(brick)
33833 //this.groups[brick.id] = brick;
33834 this.groups.add(brick.id, brick);
33837 * fetch a masonry brick based on the masonry brick ID
33838 * @param {string} the masonry brick to add
33839 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33842 get: function(brick_id)
33844 // if (typeof(this.groups[brick_id]) == 'undefined') {
33847 // return this.groups[brick_id] ;
33849 if(this.groups.key(brick_id)) {
33850 return this.groups.key(brick_id);
33868 * @class Roo.bootstrap.Brick
33869 * @extends Roo.bootstrap.Component
33870 * Bootstrap Brick class
33873 * Create a new Brick
33874 * @param {Object} config The config object
33877 Roo.bootstrap.Brick = function(config){
33878 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33884 * When a Brick is click
33885 * @param {Roo.bootstrap.Brick} this
33886 * @param {Roo.EventObject} e
33892 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33895 * @cfg {String} title
33899 * @cfg {String} html
33903 * @cfg {String} bgimage
33907 * @cfg {String} cls
33911 * @cfg {String} href
33915 * @cfg {String} video
33919 * @cfg {Boolean} square
33923 getAutoCreate : function()
33925 var cls = 'roo-brick';
33927 if(this.href.length){
33928 cls += ' roo-brick-link';
33931 if(this.bgimage.length){
33932 cls += ' roo-brick-image';
33935 if(!this.html.length && !this.bgimage.length){
33936 cls += ' roo-brick-center-title';
33939 if(!this.html.length && this.bgimage.length){
33940 cls += ' roo-brick-bottom-title';
33944 cls += ' ' + this.cls;
33948 tag: (this.href.length) ? 'a' : 'div',
33953 cls: 'roo-brick-paragraph',
33959 if(this.href.length){
33960 cfg.href = this.href;
33963 var cn = cfg.cn[0].cn;
33965 if(this.title.length){
33968 cls: 'roo-brick-title',
33973 if(this.html.length){
33976 cls: 'roo-brick-text',
33983 if(this.bgimage.length){
33986 cls: 'roo-brick-image-view',
33994 initEvents: function()
33996 if(this.title.length || this.html.length){
33997 this.el.on('mouseenter' ,this.enter, this);
33998 this.el.on('mouseleave', this.leave, this);
34001 Roo.EventManager.onWindowResize(this.resize, this);
34003 if(this.bgimage.length){
34004 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
34005 this.imageEl.on('load', this.onImageLoad, this);
34012 onImageLoad : function()
34017 resize : function()
34019 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
34021 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
34023 if(this.bgimage.length){
34024 var image = this.el.select('.roo-brick-image-view', true).first();
34026 image.setWidth(paragraph.getWidth());
34029 image.setHeight(paragraph.getWidth());
34032 this.el.setHeight(image.getHeight());
34033 paragraph.setHeight(image.getHeight());
34039 enter: function(e, el)
34041 e.preventDefault();
34043 if(this.bgimage.length){
34044 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
34045 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
34049 leave: function(e, el)
34051 e.preventDefault();
34053 if(this.bgimage.length){
34054 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
34055 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
34070 * @class Roo.bootstrap.NumberField
34071 * @extends Roo.bootstrap.Input
34072 * Bootstrap NumberField class
34078 * Create a new NumberField
34079 * @param {Object} config The config object
34082 Roo.bootstrap.NumberField = function(config){
34083 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
34086 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
34089 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
34091 allowDecimals : true,
34093 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
34095 decimalSeparator : ".",
34097 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
34099 decimalPrecision : 2,
34101 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
34103 allowNegative : true,
34106 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
34110 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
34112 minValue : Number.NEGATIVE_INFINITY,
34114 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
34116 maxValue : Number.MAX_VALUE,
34118 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
34120 minText : "The minimum value for this field is {0}",
34122 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
34124 maxText : "The maximum value for this field is {0}",
34126 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
34127 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
34129 nanText : "{0} is not a valid number",
34131 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
34133 thousandsDelimiter : false,
34135 * @cfg {String} valueAlign alignment of value
34137 valueAlign : "left",
34139 getAutoCreate : function()
34141 var hiddenInput = {
34145 cls: 'hidden-number-input'
34149 hiddenInput.name = this.name;
34154 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
34156 this.name = hiddenInput.name;
34158 if(cfg.cn.length > 0) {
34159 cfg.cn.push(hiddenInput);
34166 initEvents : function()
34168 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
34170 var allowed = "0123456789";
34172 if(this.allowDecimals){
34173 allowed += this.decimalSeparator;
34176 if(this.allowNegative){
34180 if(this.thousandsDelimiter) {
34184 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
34186 var keyPress = function(e){
34188 var k = e.getKey();
34190 var c = e.getCharCode();
34193 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
34194 allowed.indexOf(String.fromCharCode(c)) === -1
34200 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
34204 if(allowed.indexOf(String.fromCharCode(c)) === -1){
34209 this.el.on("keypress", keyPress, this);
34212 validateValue : function(value)
34215 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
34219 var num = this.parseValue(value);
34222 this.markInvalid(String.format(this.nanText, value));
34226 if(num < this.minValue){
34227 this.markInvalid(String.format(this.minText, this.minValue));
34231 if(num > this.maxValue){
34232 this.markInvalid(String.format(this.maxText, this.maxValue));
34239 getValue : function()
34241 var v = this.hiddenEl().getValue();
34243 return this.fixPrecision(this.parseValue(v));
34246 parseValue : function(value)
34248 if(this.thousandsDelimiter) {
34250 r = new RegExp(",", "g");
34251 value = value.replace(r, "");
34254 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34255 return isNaN(value) ? '' : value;
34258 fixPrecision : function(value)
34260 if(this.thousandsDelimiter) {
34262 r = new RegExp(",", "g");
34263 value = value.replace(r, "");
34266 var nan = isNaN(value);
34268 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34269 return nan ? '' : value;
34271 return parseFloat(value).toFixed(this.decimalPrecision);
34274 setValue : function(v)
34276 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34282 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34284 this.inputEl().dom.value = (v == '') ? '' :
34285 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34287 if(!this.allowZero && v === '0') {
34288 this.hiddenEl().dom.value = '';
34289 this.inputEl().dom.value = '';
34296 decimalPrecisionFcn : function(v)
34298 return Math.floor(v);
34301 beforeBlur : function()
34303 var v = this.parseValue(this.getRawValue());
34305 if(v || v === 0 || v === ''){
34310 hiddenEl : function()
34312 return this.el.select('input.hidden-number-input',true).first();
34324 * @class Roo.bootstrap.DocumentSlider
34325 * @extends Roo.bootstrap.Component
34326 * Bootstrap DocumentSlider class
34329 * Create a new DocumentViewer
34330 * @param {Object} config The config object
34333 Roo.bootstrap.DocumentSlider = function(config){
34334 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34341 * Fire after initEvent
34342 * @param {Roo.bootstrap.DocumentSlider} this
34347 * Fire after update
34348 * @param {Roo.bootstrap.DocumentSlider} this
34354 * @param {Roo.bootstrap.DocumentSlider} this
34360 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
34366 getAutoCreate : function()
34370 cls : 'roo-document-slider',
34374 cls : 'roo-document-slider-header',
34378 cls : 'roo-document-slider-header-title'
34384 cls : 'roo-document-slider-body',
34388 cls : 'roo-document-slider-prev',
34392 cls : 'fa fa-chevron-left'
34398 cls : 'roo-document-slider-thumb',
34402 cls : 'roo-document-slider-image'
34408 cls : 'roo-document-slider-next',
34412 cls : 'fa fa-chevron-right'
34424 initEvents : function()
34426 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34427 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34429 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34430 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34432 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34433 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34435 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34436 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34438 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34439 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34441 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34442 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34444 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34445 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34447 this.thumbEl.on('click', this.onClick, this);
34449 this.prevIndicator.on('click', this.prev, this);
34451 this.nextIndicator.on('click', this.next, this);
34455 initial : function()
34457 if(this.files.length){
34458 this.indicator = 1;
34462 this.fireEvent('initial', this);
34465 update : function()
34467 this.imageEl.attr('src', this.files[this.indicator - 1]);
34469 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34471 this.prevIndicator.show();
34473 if(this.indicator == 1){
34474 this.prevIndicator.hide();
34477 this.nextIndicator.show();
34479 if(this.indicator == this.files.length){
34480 this.nextIndicator.hide();
34483 this.thumbEl.scrollTo('top');
34485 this.fireEvent('update', this);
34488 onClick : function(e)
34490 e.preventDefault();
34492 this.fireEvent('click', this);
34497 e.preventDefault();
34499 this.indicator = Math.max(1, this.indicator - 1);
34506 e.preventDefault();
34508 this.indicator = Math.min(this.files.length, this.indicator + 1);
34522 * @class Roo.bootstrap.RadioSet
34523 * @extends Roo.bootstrap.Input
34524 * Bootstrap RadioSet class
34525 * @cfg {String} indicatorpos (left|right) default left
34526 * @cfg {Boolean} inline (true|false) inline the element (default true)
34527 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34529 * Create a new RadioSet
34530 * @param {Object} config The config object
34533 Roo.bootstrap.RadioSet = function(config){
34535 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34539 Roo.bootstrap.RadioSet.register(this);
34544 * Fires when the element is checked or unchecked.
34545 * @param {Roo.bootstrap.RadioSet} this This radio
34546 * @param {Roo.bootstrap.Radio} item The checked item
34551 * Fires when the element is click.
34552 * @param {Roo.bootstrap.RadioSet} this This radio set
34553 * @param {Roo.bootstrap.Radio} item The checked item
34554 * @param {Roo.EventObject} e The event object
34561 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
34569 indicatorpos : 'left',
34571 getAutoCreate : function()
34575 cls : 'roo-radio-set-label',
34579 html : this.fieldLabel
34583 if (Roo.bootstrap.version == 3) {
34586 if(this.indicatorpos == 'left'){
34589 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34590 tooltip : 'This field is required'
34595 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34596 tooltip : 'This field is required'
34602 cls : 'roo-radio-set-items'
34605 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34607 if (align === 'left' && this.fieldLabel.length) {
34610 cls : "roo-radio-set-right",
34616 if(this.labelWidth > 12){
34617 label.style = "width: " + this.labelWidth + 'px';
34620 if(this.labelWidth < 13 && this.labelmd == 0){
34621 this.labelmd = this.labelWidth;
34624 if(this.labellg > 0){
34625 label.cls += ' col-lg-' + this.labellg;
34626 items.cls += ' col-lg-' + (12 - this.labellg);
34629 if(this.labelmd > 0){
34630 label.cls += ' col-md-' + this.labelmd;
34631 items.cls += ' col-md-' + (12 - this.labelmd);
34634 if(this.labelsm > 0){
34635 label.cls += ' col-sm-' + this.labelsm;
34636 items.cls += ' col-sm-' + (12 - this.labelsm);
34639 if(this.labelxs > 0){
34640 label.cls += ' col-xs-' + this.labelxs;
34641 items.cls += ' col-xs-' + (12 - this.labelxs);
34647 cls : 'roo-radio-set',
34651 cls : 'roo-radio-set-input',
34654 value : this.value ? this.value : ''
34661 if(this.weight.length){
34662 cfg.cls += ' roo-radio-' + this.weight;
34666 cfg.cls += ' roo-radio-set-inline';
34670 ['xs','sm','md','lg'].map(function(size){
34671 if (settings[size]) {
34672 cfg.cls += ' col-' + size + '-' + settings[size];
34680 initEvents : function()
34682 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34683 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34685 if(!this.fieldLabel.length){
34686 this.labelEl.hide();
34689 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34690 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34692 this.indicator = this.indicatorEl();
34694 if(this.indicator){
34695 this.indicator.addClass('invisible');
34698 this.originalValue = this.getValue();
34702 inputEl: function ()
34704 return this.el.select('.roo-radio-set-input', true).first();
34707 getChildContainer : function()
34709 return this.itemsEl;
34712 register : function(item)
34714 this.radioes.push(item);
34718 validate : function()
34720 if(this.getVisibilityEl().hasClass('hidden')){
34726 Roo.each(this.radioes, function(i){
34735 if(this.allowBlank) {
34739 if(this.disabled || valid){
34744 this.markInvalid();
34749 markValid : function()
34751 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34752 this.indicatorEl().removeClass('visible');
34753 this.indicatorEl().addClass('invisible');
34757 if (Roo.bootstrap.version == 3) {
34758 this.el.removeClass([this.invalidClass, this.validClass]);
34759 this.el.addClass(this.validClass);
34761 this.el.removeClass(['is-invalid','is-valid']);
34762 this.el.addClass(['is-valid']);
34764 this.fireEvent('valid', this);
34767 markInvalid : function(msg)
34769 if(this.allowBlank || this.disabled){
34773 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34774 this.indicatorEl().removeClass('invisible');
34775 this.indicatorEl().addClass('visible');
34777 if (Roo.bootstrap.version == 3) {
34778 this.el.removeClass([this.invalidClass, this.validClass]);
34779 this.el.addClass(this.invalidClass);
34781 this.el.removeClass(['is-invalid','is-valid']);
34782 this.el.addClass(['is-invalid']);
34785 this.fireEvent('invalid', this, msg);
34789 setValue : function(v, suppressEvent)
34791 if(this.value === v){
34798 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34801 Roo.each(this.radioes, function(i){
34803 i.el.removeClass('checked');
34806 Roo.each(this.radioes, function(i){
34808 if(i.value === v || i.value.toString() === v.toString()){
34810 i.el.addClass('checked');
34812 if(suppressEvent !== true){
34813 this.fireEvent('check', this, i);
34824 clearInvalid : function(){
34826 if(!this.el || this.preventMark){
34830 this.el.removeClass([this.invalidClass]);
34832 this.fireEvent('valid', this);
34837 Roo.apply(Roo.bootstrap.RadioSet, {
34841 register : function(set)
34843 this.groups[set.name] = set;
34846 get: function(name)
34848 if (typeof(this.groups[name]) == 'undefined') {
34852 return this.groups[name] ;
34858 * Ext JS Library 1.1.1
34859 * Copyright(c) 2006-2007, Ext JS, LLC.
34861 * Originally Released Under LGPL - original licence link has changed is not relivant.
34864 * <script type="text/javascript">
34869 * @class Roo.bootstrap.SplitBar
34870 * @extends Roo.util.Observable
34871 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34875 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34876 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34877 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34878 split.minSize = 100;
34879 split.maxSize = 600;
34880 split.animate = true;
34881 split.on('moved', splitterMoved);
34884 * Create a new SplitBar
34885 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34886 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34887 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34888 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34889 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34890 position of the SplitBar).
34892 Roo.bootstrap.SplitBar = function(cfg){
34897 // dragElement : elm
34898 // resizingElement: el,
34900 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34901 // placement : Roo.bootstrap.SplitBar.LEFT ,
34902 // existingProxy ???
34905 this.el = Roo.get(cfg.dragElement, true);
34906 this.el.dom.unselectable = "on";
34908 this.resizingEl = Roo.get(cfg.resizingElement, true);
34912 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34913 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34916 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34919 * The minimum size of the resizing element. (Defaults to 0)
34925 * The maximum size of the resizing element. (Defaults to 2000)
34928 this.maxSize = 2000;
34931 * Whether to animate the transition to the new size
34934 this.animate = false;
34937 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34940 this.useShim = false;
34945 if(!cfg.existingProxy){
34947 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34949 this.proxy = Roo.get(cfg.existingProxy).dom;
34952 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34955 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34958 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34961 this.dragSpecs = {};
34964 * @private The adapter to use to positon and resize elements
34966 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34967 this.adapter.init(this);
34969 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34971 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34972 this.el.addClass("roo-splitbar-h");
34975 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34976 this.el.addClass("roo-splitbar-v");
34982 * Fires when the splitter is moved (alias for {@link #event-moved})
34983 * @param {Roo.bootstrap.SplitBar} this
34984 * @param {Number} newSize the new width or height
34989 * Fires when the splitter is moved
34990 * @param {Roo.bootstrap.SplitBar} this
34991 * @param {Number} newSize the new width or height
34995 * @event beforeresize
34996 * Fires before the splitter is dragged
34997 * @param {Roo.bootstrap.SplitBar} this
34999 "beforeresize" : true,
35001 "beforeapply" : true
35004 Roo.util.Observable.call(this);
35007 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
35008 onStartProxyDrag : function(x, y){
35009 this.fireEvent("beforeresize", this);
35011 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
35013 o.enableDisplayMode("block");
35014 // all splitbars share the same overlay
35015 Roo.bootstrap.SplitBar.prototype.overlay = o;
35017 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
35018 this.overlay.show();
35019 Roo.get(this.proxy).setDisplayed("block");
35020 var size = this.adapter.getElementSize(this);
35021 this.activeMinSize = this.getMinimumSize();;
35022 this.activeMaxSize = this.getMaximumSize();;
35023 var c1 = size - this.activeMinSize;
35024 var c2 = Math.max(this.activeMaxSize - size, 0);
35025 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35026 this.dd.resetConstraints();
35027 this.dd.setXConstraint(
35028 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
35029 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
35031 this.dd.setYConstraint(0, 0);
35033 this.dd.resetConstraints();
35034 this.dd.setXConstraint(0, 0);
35035 this.dd.setYConstraint(
35036 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
35037 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
35040 this.dragSpecs.startSize = size;
35041 this.dragSpecs.startPoint = [x, y];
35042 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
35046 * @private Called after the drag operation by the DDProxy
35048 onEndProxyDrag : function(e){
35049 Roo.get(this.proxy).setDisplayed(false);
35050 var endPoint = Roo.lib.Event.getXY(e);
35052 this.overlay.hide();
35055 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35056 newSize = this.dragSpecs.startSize +
35057 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
35058 endPoint[0] - this.dragSpecs.startPoint[0] :
35059 this.dragSpecs.startPoint[0] - endPoint[0]
35062 newSize = this.dragSpecs.startSize +
35063 (this.placement == Roo.bootstrap.SplitBar.TOP ?
35064 endPoint[1] - this.dragSpecs.startPoint[1] :
35065 this.dragSpecs.startPoint[1] - endPoint[1]
35068 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
35069 if(newSize != this.dragSpecs.startSize){
35070 if(this.fireEvent('beforeapply', this, newSize) !== false){
35071 this.adapter.setElementSize(this, newSize);
35072 this.fireEvent("moved", this, newSize);
35073 this.fireEvent("resize", this, newSize);
35079 * Get the adapter this SplitBar uses
35080 * @return The adapter object
35082 getAdapter : function(){
35083 return this.adapter;
35087 * Set the adapter this SplitBar uses
35088 * @param {Object} adapter A SplitBar adapter object
35090 setAdapter : function(adapter){
35091 this.adapter = adapter;
35092 this.adapter.init(this);
35096 * Gets the minimum size for the resizing element
35097 * @return {Number} The minimum size
35099 getMinimumSize : function(){
35100 return this.minSize;
35104 * Sets the minimum size for the resizing element
35105 * @param {Number} minSize The minimum size
35107 setMinimumSize : function(minSize){
35108 this.minSize = minSize;
35112 * Gets the maximum size for the resizing element
35113 * @return {Number} The maximum size
35115 getMaximumSize : function(){
35116 return this.maxSize;
35120 * Sets the maximum size for the resizing element
35121 * @param {Number} maxSize The maximum size
35123 setMaximumSize : function(maxSize){
35124 this.maxSize = maxSize;
35128 * Sets the initialize size for the resizing element
35129 * @param {Number} size The initial size
35131 setCurrentSize : function(size){
35132 var oldAnimate = this.animate;
35133 this.animate = false;
35134 this.adapter.setElementSize(this, size);
35135 this.animate = oldAnimate;
35139 * Destroy this splitbar.
35140 * @param {Boolean} removeEl True to remove the element
35142 destroy : function(removeEl){
35144 this.shim.remove();
35147 this.proxy.parentNode.removeChild(this.proxy);
35155 * @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.
35157 Roo.bootstrap.SplitBar.createProxy = function(dir){
35158 var proxy = new Roo.Element(document.createElement("div"));
35159 proxy.unselectable();
35160 var cls = 'roo-splitbar-proxy';
35161 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
35162 document.body.appendChild(proxy.dom);
35167 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
35168 * Default Adapter. It assumes the splitter and resizing element are not positioned
35169 * elements and only gets/sets the width of the element. Generally used for table based layouts.
35171 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
35174 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
35175 // do nothing for now
35176 init : function(s){
35180 * Called before drag operations to get the current size of the resizing element.
35181 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35183 getElementSize : function(s){
35184 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35185 return s.resizingEl.getWidth();
35187 return s.resizingEl.getHeight();
35192 * Called after drag operations to set the size of the resizing element.
35193 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35194 * @param {Number} newSize The new size to set
35195 * @param {Function} onComplete A function to be invoked when resizing is complete
35197 setElementSize : function(s, newSize, onComplete){
35198 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35200 s.resizingEl.setWidth(newSize);
35202 onComplete(s, newSize);
35205 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
35210 s.resizingEl.setHeight(newSize);
35212 onComplete(s, newSize);
35215 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
35222 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
35223 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
35224 * Adapter that moves the splitter element to align with the resized sizing element.
35225 * Used with an absolute positioned SplitBar.
35226 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
35227 * document.body, make sure you assign an id to the body element.
35229 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
35230 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35231 this.container = Roo.get(container);
35234 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
35235 init : function(s){
35236 this.basic.init(s);
35239 getElementSize : function(s){
35240 return this.basic.getElementSize(s);
35243 setElementSize : function(s, newSize, onComplete){
35244 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
35247 moveSplitter : function(s){
35248 var yes = Roo.bootstrap.SplitBar;
35249 switch(s.placement){
35251 s.el.setX(s.resizingEl.getRight());
35254 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35257 s.el.setY(s.resizingEl.getBottom());
35260 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35267 * Orientation constant - Create a vertical SplitBar
35271 Roo.bootstrap.SplitBar.VERTICAL = 1;
35274 * Orientation constant - Create a horizontal SplitBar
35278 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35281 * Placement constant - The resizing element is to the left of the splitter element
35285 Roo.bootstrap.SplitBar.LEFT = 1;
35288 * Placement constant - The resizing element is to the right of the splitter element
35292 Roo.bootstrap.SplitBar.RIGHT = 2;
35295 * Placement constant - The resizing element is positioned above the splitter element
35299 Roo.bootstrap.SplitBar.TOP = 3;
35302 * Placement constant - The resizing element is positioned under splitter element
35306 Roo.bootstrap.SplitBar.BOTTOM = 4;
35307 Roo.namespace("Roo.bootstrap.layout");/*
35309 * Ext JS Library 1.1.1
35310 * Copyright(c) 2006-2007, Ext JS, LLC.
35312 * Originally Released Under LGPL - original licence link has changed is not relivant.
35315 * <script type="text/javascript">
35319 * @class Roo.bootstrap.layout.Manager
35320 * @extends Roo.bootstrap.Component
35321 * Base class for layout managers.
35323 Roo.bootstrap.layout.Manager = function(config)
35325 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35331 /** false to disable window resize monitoring @type Boolean */
35332 this.monitorWindowResize = true;
35337 * Fires when a layout is performed.
35338 * @param {Roo.LayoutManager} this
35342 * @event regionresized
35343 * Fires when the user resizes a region.
35344 * @param {Roo.LayoutRegion} region The resized region
35345 * @param {Number} newSize The new size (width for east/west, height for north/south)
35347 "regionresized" : true,
35349 * @event regioncollapsed
35350 * Fires when a region is collapsed.
35351 * @param {Roo.LayoutRegion} region The collapsed region
35353 "regioncollapsed" : true,
35355 * @event regionexpanded
35356 * Fires when a region is expanded.
35357 * @param {Roo.LayoutRegion} region The expanded region
35359 "regionexpanded" : true
35361 this.updating = false;
35364 this.el = Roo.get(config.el);
35370 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35375 monitorWindowResize : true,
35381 onRender : function(ct, position)
35384 this.el = Roo.get(ct);
35387 //this.fireEvent('render',this);
35391 initEvents: function()
35395 // ie scrollbar fix
35396 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35397 document.body.scroll = "no";
35398 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35399 this.el.position('relative');
35401 this.id = this.el.id;
35402 this.el.addClass("roo-layout-container");
35403 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35404 if(this.el.dom != document.body ) {
35405 this.el.on('resize', this.layout,this);
35406 this.el.on('show', this.layout,this);
35412 * Returns true if this layout is currently being updated
35413 * @return {Boolean}
35415 isUpdating : function(){
35416 return this.updating;
35420 * Suspend the LayoutManager from doing auto-layouts while
35421 * making multiple add or remove calls
35423 beginUpdate : function(){
35424 this.updating = true;
35428 * Restore auto-layouts and optionally disable the manager from performing a layout
35429 * @param {Boolean} noLayout true to disable a layout update
35431 endUpdate : function(noLayout){
35432 this.updating = false;
35438 layout: function(){
35442 onRegionResized : function(region, newSize){
35443 this.fireEvent("regionresized", region, newSize);
35447 onRegionCollapsed : function(region){
35448 this.fireEvent("regioncollapsed", region);
35451 onRegionExpanded : function(region){
35452 this.fireEvent("regionexpanded", region);
35456 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35457 * performs box-model adjustments.
35458 * @return {Object} The size as an object {width: (the width), height: (the height)}
35460 getViewSize : function()
35463 if(this.el.dom != document.body){
35464 size = this.el.getSize();
35466 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35468 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35469 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35474 * Returns the Element this layout is bound to.
35475 * @return {Roo.Element}
35477 getEl : function(){
35482 * Returns the specified region.
35483 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35484 * @return {Roo.LayoutRegion}
35486 getRegion : function(target){
35487 return this.regions[target.toLowerCase()];
35490 onWindowResize : function(){
35491 if(this.monitorWindowResize){
35498 * Ext JS Library 1.1.1
35499 * Copyright(c) 2006-2007, Ext JS, LLC.
35501 * Originally Released Under LGPL - original licence link has changed is not relivant.
35504 * <script type="text/javascript">
35507 * @class Roo.bootstrap.layout.Border
35508 * @extends Roo.bootstrap.layout.Manager
35509 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35510 * please see: examples/bootstrap/nested.html<br><br>
35512 <b>The container the layout is rendered into can be either the body element or any other element.
35513 If it is not the body element, the container needs to either be an absolute positioned element,
35514 or you will need to add "position:relative" to the css of the container. You will also need to specify
35515 the container size if it is not the body element.</b>
35518 * Create a new Border
35519 * @param {Object} config Configuration options
35521 Roo.bootstrap.layout.Border = function(config){
35522 config = config || {};
35523 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35527 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35528 if(config[region]){
35529 config[region].region = region;
35530 this.addRegion(config[region]);
35536 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
35538 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35540 parent : false, // this might point to a 'nest' or a ???
35543 * Creates and adds a new region if it doesn't already exist.
35544 * @param {String} target The target region key (north, south, east, west or center).
35545 * @param {Object} config The regions config object
35546 * @return {BorderLayoutRegion} The new region
35548 addRegion : function(config)
35550 if(!this.regions[config.region]){
35551 var r = this.factory(config);
35552 this.bindRegion(r);
35554 return this.regions[config.region];
35558 bindRegion : function(r){
35559 this.regions[r.config.region] = r;
35561 r.on("visibilitychange", this.layout, this);
35562 r.on("paneladded", this.layout, this);
35563 r.on("panelremoved", this.layout, this);
35564 r.on("invalidated", this.layout, this);
35565 r.on("resized", this.onRegionResized, this);
35566 r.on("collapsed", this.onRegionCollapsed, this);
35567 r.on("expanded", this.onRegionExpanded, this);
35571 * Performs a layout update.
35573 layout : function()
35575 if(this.updating) {
35579 // render all the rebions if they have not been done alreayd?
35580 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35581 if(this.regions[region] && !this.regions[region].bodyEl){
35582 this.regions[region].onRender(this.el)
35586 var size = this.getViewSize();
35587 var w = size.width;
35588 var h = size.height;
35593 //var x = 0, y = 0;
35595 var rs = this.regions;
35596 var north = rs["north"];
35597 var south = rs["south"];
35598 var west = rs["west"];
35599 var east = rs["east"];
35600 var center = rs["center"];
35601 //if(this.hideOnLayout){ // not supported anymore
35602 //c.el.setStyle("display", "none");
35604 if(north && north.isVisible()){
35605 var b = north.getBox();
35606 var m = north.getMargins();
35607 b.width = w - (m.left+m.right);
35610 centerY = b.height + b.y + m.bottom;
35611 centerH -= centerY;
35612 north.updateBox(this.safeBox(b));
35614 if(south && south.isVisible()){
35615 var b = south.getBox();
35616 var m = south.getMargins();
35617 b.width = w - (m.left+m.right);
35619 var totalHeight = (b.height + m.top + m.bottom);
35620 b.y = h - totalHeight + m.top;
35621 centerH -= totalHeight;
35622 south.updateBox(this.safeBox(b));
35624 if(west && west.isVisible()){
35625 var b = west.getBox();
35626 var m = west.getMargins();
35627 b.height = centerH - (m.top+m.bottom);
35629 b.y = centerY + m.top;
35630 var totalWidth = (b.width + m.left + m.right);
35631 centerX += totalWidth;
35632 centerW -= totalWidth;
35633 west.updateBox(this.safeBox(b));
35635 if(east && east.isVisible()){
35636 var b = east.getBox();
35637 var m = east.getMargins();
35638 b.height = centerH - (m.top+m.bottom);
35639 var totalWidth = (b.width + m.left + m.right);
35640 b.x = w - totalWidth + m.left;
35641 b.y = centerY + m.top;
35642 centerW -= totalWidth;
35643 east.updateBox(this.safeBox(b));
35646 var m = center.getMargins();
35648 x: centerX + m.left,
35649 y: centerY + m.top,
35650 width: centerW - (m.left+m.right),
35651 height: centerH - (m.top+m.bottom)
35653 //if(this.hideOnLayout){
35654 //center.el.setStyle("display", "block");
35656 center.updateBox(this.safeBox(centerBox));
35659 this.fireEvent("layout", this);
35663 safeBox : function(box){
35664 box.width = Math.max(0, box.width);
35665 box.height = Math.max(0, box.height);
35670 * Adds a ContentPanel (or subclass) to this layout.
35671 * @param {String} target The target region key (north, south, east, west or center).
35672 * @param {Roo.ContentPanel} panel The panel to add
35673 * @return {Roo.ContentPanel} The added panel
35675 add : function(target, panel){
35677 target = target.toLowerCase();
35678 return this.regions[target].add(panel);
35682 * Remove a ContentPanel (or subclass) to this layout.
35683 * @param {String} target The target region key (north, south, east, west or center).
35684 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35685 * @return {Roo.ContentPanel} The removed panel
35687 remove : function(target, panel){
35688 target = target.toLowerCase();
35689 return this.regions[target].remove(panel);
35693 * Searches all regions for a panel with the specified id
35694 * @param {String} panelId
35695 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35697 findPanel : function(panelId){
35698 var rs = this.regions;
35699 for(var target in rs){
35700 if(typeof rs[target] != "function"){
35701 var p = rs[target].getPanel(panelId);
35711 * Searches all regions for a panel with the specified id and activates (shows) it.
35712 * @param {String/ContentPanel} panelId The panels id or the panel itself
35713 * @return {Roo.ContentPanel} The shown panel or null
35715 showPanel : function(panelId) {
35716 var rs = this.regions;
35717 for(var target in rs){
35718 var r = rs[target];
35719 if(typeof r != "function"){
35720 if(r.hasPanel(panelId)){
35721 return r.showPanel(panelId);
35729 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35730 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35733 restoreState : function(provider){
35735 provider = Roo.state.Manager;
35737 var sm = new Roo.LayoutStateManager();
35738 sm.init(this, provider);
35744 * Adds a xtype elements to the layout.
35748 xtype : 'ContentPanel',
35755 xtype : 'NestedLayoutPanel',
35761 items : [ ... list of content panels or nested layout panels.. ]
35765 * @param {Object} cfg Xtype definition of item to add.
35767 addxtype : function(cfg)
35769 // basically accepts a pannel...
35770 // can accept a layout region..!?!?
35771 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35774 // theory? children can only be panels??
35776 //if (!cfg.xtype.match(/Panel$/)) {
35781 if (typeof(cfg.region) == 'undefined') {
35782 Roo.log("Failed to add Panel, region was not set");
35786 var region = cfg.region;
35792 xitems = cfg.items;
35797 if ( region == 'center') {
35798 Roo.log("Center: " + cfg.title);
35804 case 'Content': // ContentPanel (el, cfg)
35805 case 'Scroll': // ContentPanel (el, cfg)
35807 cfg.autoCreate = cfg.autoCreate || true;
35808 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35810 // var el = this.el.createChild();
35811 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35814 this.add(region, ret);
35818 case 'TreePanel': // our new panel!
35819 cfg.el = this.el.createChild();
35820 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35821 this.add(region, ret);
35826 // create a new Layout (which is a Border Layout...
35828 var clayout = cfg.layout;
35829 clayout.el = this.el.createChild();
35830 clayout.items = clayout.items || [];
35834 // replace this exitems with the clayout ones..
35835 xitems = clayout.items;
35837 // force background off if it's in center...
35838 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35839 cfg.background = false;
35841 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35844 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35845 //console.log('adding nested layout panel ' + cfg.toSource());
35846 this.add(region, ret);
35847 nb = {}; /// find first...
35852 // needs grid and region
35854 //var el = this.getRegion(region).el.createChild();
35856 *var el = this.el.createChild();
35857 // create the grid first...
35858 cfg.grid.container = el;
35859 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35862 if (region == 'center' && this.active ) {
35863 cfg.background = false;
35866 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35868 this.add(region, ret);
35870 if (cfg.background) {
35871 // render grid on panel activation (if panel background)
35872 ret.on('activate', function(gp) {
35873 if (!gp.grid.rendered) {
35874 // gp.grid.render(el);
35878 // cfg.grid.render(el);
35884 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35885 // it was the old xcomponent building that caused this before.
35886 // espeically if border is the top element in the tree.
35896 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35898 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35899 this.add(region, ret);
35903 throw "Can not add '" + cfg.xtype + "' to Border";
35909 this.beginUpdate();
35913 Roo.each(xitems, function(i) {
35914 region = nb && i.region ? i.region : false;
35916 var add = ret.addxtype(i);
35919 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35920 if (!i.background) {
35921 abn[region] = nb[region] ;
35928 // make the last non-background panel active..
35929 //if (nb) { Roo.log(abn); }
35932 for(var r in abn) {
35933 region = this.getRegion(r);
35935 // tried using nb[r], but it does not work..
35937 region.showPanel(abn[r]);
35948 factory : function(cfg)
35951 var validRegions = Roo.bootstrap.layout.Border.regions;
35953 var target = cfg.region;
35956 var r = Roo.bootstrap.layout;
35960 return new r.North(cfg);
35962 return new r.South(cfg);
35964 return new r.East(cfg);
35966 return new r.West(cfg);
35968 return new r.Center(cfg);
35970 throw 'Layout region "'+target+'" not supported.';
35977 * Ext JS Library 1.1.1
35978 * Copyright(c) 2006-2007, Ext JS, LLC.
35980 * Originally Released Under LGPL - original licence link has changed is not relivant.
35983 * <script type="text/javascript">
35987 * @class Roo.bootstrap.layout.Basic
35988 * @extends Roo.util.Observable
35989 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35990 * and does not have a titlebar, tabs or any other features. All it does is size and position
35991 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35992 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35993 * @cfg {string} region the region that it inhabits..
35994 * @cfg {bool} skipConfig skip config?
35998 Roo.bootstrap.layout.Basic = function(config){
36000 this.mgr = config.mgr;
36002 this.position = config.region;
36004 var skipConfig = config.skipConfig;
36008 * @scope Roo.BasicLayoutRegion
36012 * @event beforeremove
36013 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
36014 * @param {Roo.LayoutRegion} this
36015 * @param {Roo.ContentPanel} panel The panel
36016 * @param {Object} e The cancel event object
36018 "beforeremove" : true,
36020 * @event invalidated
36021 * Fires when the layout for this region is changed.
36022 * @param {Roo.LayoutRegion} this
36024 "invalidated" : true,
36026 * @event visibilitychange
36027 * Fires when this region is shown or hidden
36028 * @param {Roo.LayoutRegion} this
36029 * @param {Boolean} visibility true or false
36031 "visibilitychange" : true,
36033 * @event paneladded
36034 * Fires when a panel is added.
36035 * @param {Roo.LayoutRegion} this
36036 * @param {Roo.ContentPanel} panel The panel
36038 "paneladded" : true,
36040 * @event panelremoved
36041 * Fires when a panel is removed.
36042 * @param {Roo.LayoutRegion} this
36043 * @param {Roo.ContentPanel} panel The panel
36045 "panelremoved" : true,
36047 * @event beforecollapse
36048 * Fires when this region before collapse.
36049 * @param {Roo.LayoutRegion} this
36051 "beforecollapse" : true,
36054 * Fires when this region is collapsed.
36055 * @param {Roo.LayoutRegion} this
36057 "collapsed" : true,
36060 * Fires when this region is expanded.
36061 * @param {Roo.LayoutRegion} this
36066 * Fires when this region is slid into view.
36067 * @param {Roo.LayoutRegion} this
36069 "slideshow" : true,
36072 * Fires when this region slides out of view.
36073 * @param {Roo.LayoutRegion} this
36075 "slidehide" : true,
36077 * @event panelactivated
36078 * Fires when a panel is activated.
36079 * @param {Roo.LayoutRegion} this
36080 * @param {Roo.ContentPanel} panel The activated panel
36082 "panelactivated" : true,
36085 * Fires when the user resizes this region.
36086 * @param {Roo.LayoutRegion} this
36087 * @param {Number} newSize The new size (width for east/west, height for north/south)
36091 /** A collection of panels in this region. @type Roo.util.MixedCollection */
36092 this.panels = new Roo.util.MixedCollection();
36093 this.panels.getKey = this.getPanelId.createDelegate(this);
36095 this.activePanel = null;
36096 // ensure listeners are added...
36098 if (config.listeners || config.events) {
36099 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
36100 listeners : config.listeners || {},
36101 events : config.events || {}
36105 if(skipConfig !== true){
36106 this.applyConfig(config);
36110 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
36112 getPanelId : function(p){
36116 applyConfig : function(config){
36117 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36118 this.config = config;
36123 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
36124 * the width, for horizontal (north, south) the height.
36125 * @param {Number} newSize The new width or height
36127 resizeTo : function(newSize){
36128 var el = this.el ? this.el :
36129 (this.activePanel ? this.activePanel.getEl() : null);
36131 switch(this.position){
36134 el.setWidth(newSize);
36135 this.fireEvent("resized", this, newSize);
36139 el.setHeight(newSize);
36140 this.fireEvent("resized", this, newSize);
36146 getBox : function(){
36147 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
36150 getMargins : function(){
36151 return this.margins;
36154 updateBox : function(box){
36156 var el = this.activePanel.getEl();
36157 el.dom.style.left = box.x + "px";
36158 el.dom.style.top = box.y + "px";
36159 this.activePanel.setSize(box.width, box.height);
36163 * Returns the container element for this region.
36164 * @return {Roo.Element}
36166 getEl : function(){
36167 return this.activePanel;
36171 * Returns true if this region is currently visible.
36172 * @return {Boolean}
36174 isVisible : function(){
36175 return this.activePanel ? true : false;
36178 setActivePanel : function(panel){
36179 panel = this.getPanel(panel);
36180 if(this.activePanel && this.activePanel != panel){
36181 this.activePanel.setActiveState(false);
36182 this.activePanel.getEl().setLeftTop(-10000,-10000);
36184 this.activePanel = panel;
36185 panel.setActiveState(true);
36187 panel.setSize(this.box.width, this.box.height);
36189 this.fireEvent("panelactivated", this, panel);
36190 this.fireEvent("invalidated");
36194 * Show the specified panel.
36195 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
36196 * @return {Roo.ContentPanel} The shown panel or null
36198 showPanel : function(panel){
36199 panel = this.getPanel(panel);
36201 this.setActivePanel(panel);
36207 * Get the active panel for this region.
36208 * @return {Roo.ContentPanel} The active panel or null
36210 getActivePanel : function(){
36211 return this.activePanel;
36215 * Add the passed ContentPanel(s)
36216 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36217 * @return {Roo.ContentPanel} The panel added (if only one was added)
36219 add : function(panel){
36220 if(arguments.length > 1){
36221 for(var i = 0, len = arguments.length; i < len; i++) {
36222 this.add(arguments[i]);
36226 if(this.hasPanel(panel)){
36227 this.showPanel(panel);
36230 var el = panel.getEl();
36231 if(el.dom.parentNode != this.mgr.el.dom){
36232 this.mgr.el.dom.appendChild(el.dom);
36234 if(panel.setRegion){
36235 panel.setRegion(this);
36237 this.panels.add(panel);
36238 el.setStyle("position", "absolute");
36239 if(!panel.background){
36240 this.setActivePanel(panel);
36241 if(this.config.initialSize && this.panels.getCount()==1){
36242 this.resizeTo(this.config.initialSize);
36245 this.fireEvent("paneladded", this, panel);
36250 * Returns true if the panel is in this region.
36251 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36252 * @return {Boolean}
36254 hasPanel : function(panel){
36255 if(typeof panel == "object"){ // must be panel obj
36256 panel = panel.getId();
36258 return this.getPanel(panel) ? true : false;
36262 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36263 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36264 * @param {Boolean} preservePanel Overrides the config preservePanel option
36265 * @return {Roo.ContentPanel} The panel that was removed
36267 remove : function(panel, preservePanel){
36268 panel = this.getPanel(panel);
36273 this.fireEvent("beforeremove", this, panel, e);
36274 if(e.cancel === true){
36277 var panelId = panel.getId();
36278 this.panels.removeKey(panelId);
36283 * Returns the panel specified or null if it's not in this region.
36284 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36285 * @return {Roo.ContentPanel}
36287 getPanel : function(id){
36288 if(typeof id == "object"){ // must be panel obj
36291 return this.panels.get(id);
36295 * Returns this regions position (north/south/east/west/center).
36298 getPosition: function(){
36299 return this.position;
36303 * Ext JS Library 1.1.1
36304 * Copyright(c) 2006-2007, Ext JS, LLC.
36306 * Originally Released Under LGPL - original licence link has changed is not relivant.
36309 * <script type="text/javascript">
36313 * @class Roo.bootstrap.layout.Region
36314 * @extends Roo.bootstrap.layout.Basic
36315 * This class represents a region in a layout manager.
36317 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36318 * @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})
36319 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
36320 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
36321 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
36322 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
36323 * @cfg {String} title The title for the region (overrides panel titles)
36324 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
36325 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36326 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
36327 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36328 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
36329 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36330 * the space available, similar to FireFox 1.5 tabs (defaults to false)
36331 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
36332 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
36333 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
36335 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
36336 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
36337 * @cfg {Boolean} disableTabTips True to disable tab tooltips
36338 * @cfg {Number} width For East/West panels
36339 * @cfg {Number} height For North/South panels
36340 * @cfg {Boolean} split To show the splitter
36341 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
36343 * @cfg {string} cls Extra CSS classes to add to region
36345 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
36346 * @cfg {string} region the region that it inhabits..
36349 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
36350 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
36352 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
36353 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
36354 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
36356 Roo.bootstrap.layout.Region = function(config)
36358 this.applyConfig(config);
36360 var mgr = config.mgr;
36361 var pos = config.region;
36362 config.skipConfig = true;
36363 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36366 this.onRender(mgr.el);
36369 this.visible = true;
36370 this.collapsed = false;
36371 this.unrendered_panels = [];
36374 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36376 position: '', // set by wrapper (eg. north/south etc..)
36377 unrendered_panels : null, // unrendered panels.
36379 tabPosition : false,
36381 mgr: false, // points to 'Border'
36384 createBody : function(){
36385 /** This region's body element
36386 * @type Roo.Element */
36387 this.bodyEl = this.el.createChild({
36389 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36393 onRender: function(ctr, pos)
36395 var dh = Roo.DomHelper;
36396 /** This region's container element
36397 * @type Roo.Element */
36398 this.el = dh.append(ctr.dom, {
36400 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36402 /** This region's title element
36403 * @type Roo.Element */
36405 this.titleEl = dh.append(this.el.dom, {
36407 unselectable: "on",
36408 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36410 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
36411 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36415 this.titleEl.enableDisplayMode();
36416 /** This region's title text element
36417 * @type HTMLElement */
36418 this.titleTextEl = this.titleEl.dom.firstChild;
36419 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36421 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36422 this.closeBtn.enableDisplayMode();
36423 this.closeBtn.on("click", this.closeClicked, this);
36424 this.closeBtn.hide();
36426 this.createBody(this.config);
36427 if(this.config.hideWhenEmpty){
36429 this.on("paneladded", this.validateVisibility, this);
36430 this.on("panelremoved", this.validateVisibility, this);
36432 if(this.autoScroll){
36433 this.bodyEl.setStyle("overflow", "auto");
36435 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36437 //if(c.titlebar !== false){
36438 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36439 this.titleEl.hide();
36441 this.titleEl.show();
36442 if(this.config.title){
36443 this.titleTextEl.innerHTML = this.config.title;
36447 if(this.config.collapsed){
36448 this.collapse(true);
36450 if(this.config.hidden){
36454 if (this.unrendered_panels && this.unrendered_panels.length) {
36455 for (var i =0;i< this.unrendered_panels.length; i++) {
36456 this.add(this.unrendered_panels[i]);
36458 this.unrendered_panels = null;
36464 applyConfig : function(c)
36467 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36468 var dh = Roo.DomHelper;
36469 if(c.titlebar !== false){
36470 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36471 this.collapseBtn.on("click", this.collapse, this);
36472 this.collapseBtn.enableDisplayMode();
36474 if(c.showPin === true || this.showPin){
36475 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36476 this.stickBtn.enableDisplayMode();
36477 this.stickBtn.on("click", this.expand, this);
36478 this.stickBtn.hide();
36483 /** This region's collapsed element
36484 * @type Roo.Element */
36487 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36488 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36491 if(c.floatable !== false){
36492 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36493 this.collapsedEl.on("click", this.collapseClick, this);
36496 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36497 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36498 id: "message", unselectable: "on", style:{"float":"left"}});
36499 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36501 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36502 this.expandBtn.on("click", this.expand, this);
36506 if(this.collapseBtn){
36507 this.collapseBtn.setVisible(c.collapsible == true);
36510 this.cmargins = c.cmargins || this.cmargins ||
36511 (this.position == "west" || this.position == "east" ?
36512 {top: 0, left: 2, right:2, bottom: 0} :
36513 {top: 2, left: 0, right:0, bottom: 2});
36515 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36518 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36520 this.autoScroll = c.autoScroll || false;
36525 this.duration = c.duration || .30;
36526 this.slideDuration = c.slideDuration || .45;
36531 * Returns true if this region is currently visible.
36532 * @return {Boolean}
36534 isVisible : function(){
36535 return this.visible;
36539 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36540 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
36542 //setCollapsedTitle : function(title){
36543 // title = title || " ";
36544 // if(this.collapsedTitleTextEl){
36545 // this.collapsedTitleTextEl.innerHTML = title;
36549 getBox : function(){
36551 // if(!this.collapsed){
36552 b = this.el.getBox(false, true);
36554 // b = this.collapsedEl.getBox(false, true);
36559 getMargins : function(){
36560 return this.margins;
36561 //return this.collapsed ? this.cmargins : this.margins;
36564 highlight : function(){
36565 this.el.addClass("x-layout-panel-dragover");
36568 unhighlight : function(){
36569 this.el.removeClass("x-layout-panel-dragover");
36572 updateBox : function(box)
36574 if (!this.bodyEl) {
36575 return; // not rendered yet..
36579 if(!this.collapsed){
36580 this.el.dom.style.left = box.x + "px";
36581 this.el.dom.style.top = box.y + "px";
36582 this.updateBody(box.width, box.height);
36584 this.collapsedEl.dom.style.left = box.x + "px";
36585 this.collapsedEl.dom.style.top = box.y + "px";
36586 this.collapsedEl.setSize(box.width, box.height);
36589 this.tabs.autoSizeTabs();
36593 updateBody : function(w, h)
36596 this.el.setWidth(w);
36597 w -= this.el.getBorderWidth("rl");
36598 if(this.config.adjustments){
36599 w += this.config.adjustments[0];
36602 if(h !== null && h > 0){
36603 this.el.setHeight(h);
36604 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36605 h -= this.el.getBorderWidth("tb");
36606 if(this.config.adjustments){
36607 h += this.config.adjustments[1];
36609 this.bodyEl.setHeight(h);
36611 h = this.tabs.syncHeight(h);
36614 if(this.panelSize){
36615 w = w !== null ? w : this.panelSize.width;
36616 h = h !== null ? h : this.panelSize.height;
36618 if(this.activePanel){
36619 var el = this.activePanel.getEl();
36620 w = w !== null ? w : el.getWidth();
36621 h = h !== null ? h : el.getHeight();
36622 this.panelSize = {width: w, height: h};
36623 this.activePanel.setSize(w, h);
36625 if(Roo.isIE && this.tabs){
36626 this.tabs.el.repaint();
36631 * Returns the container element for this region.
36632 * @return {Roo.Element}
36634 getEl : function(){
36639 * Hides this region.
36642 //if(!this.collapsed){
36643 this.el.dom.style.left = "-2000px";
36646 // this.collapsedEl.dom.style.left = "-2000px";
36647 // this.collapsedEl.hide();
36649 this.visible = false;
36650 this.fireEvent("visibilitychange", this, false);
36654 * Shows this region if it was previously hidden.
36657 //if(!this.collapsed){
36660 // this.collapsedEl.show();
36662 this.visible = true;
36663 this.fireEvent("visibilitychange", this, true);
36666 closeClicked : function(){
36667 if(this.activePanel){
36668 this.remove(this.activePanel);
36672 collapseClick : function(e){
36674 e.stopPropagation();
36677 e.stopPropagation();
36683 * Collapses this region.
36684 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36687 collapse : function(skipAnim, skipCheck = false){
36688 if(this.collapsed) {
36692 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36694 this.collapsed = true;
36696 this.split.el.hide();
36698 if(this.config.animate && skipAnim !== true){
36699 this.fireEvent("invalidated", this);
36700 this.animateCollapse();
36702 this.el.setLocation(-20000,-20000);
36704 this.collapsedEl.show();
36705 this.fireEvent("collapsed", this);
36706 this.fireEvent("invalidated", this);
36712 animateCollapse : function(){
36717 * Expands this region if it was previously collapsed.
36718 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36719 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36722 expand : function(e, skipAnim){
36724 e.stopPropagation();
36726 if(!this.collapsed || this.el.hasActiveFx()) {
36730 this.afterSlideIn();
36733 this.collapsed = false;
36734 if(this.config.animate && skipAnim !== true){
36735 this.animateExpand();
36739 this.split.el.show();
36741 this.collapsedEl.setLocation(-2000,-2000);
36742 this.collapsedEl.hide();
36743 this.fireEvent("invalidated", this);
36744 this.fireEvent("expanded", this);
36748 animateExpand : function(){
36752 initTabs : function()
36754 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36756 var ts = new Roo.bootstrap.panel.Tabs({
36757 el: this.bodyEl.dom,
36759 tabPosition: this.tabPosition ? this.tabPosition : 'top',
36760 disableTooltips: this.config.disableTabTips,
36761 toolbar : this.config.toolbar
36764 if(this.config.hideTabs){
36765 ts.stripWrap.setDisplayed(false);
36768 ts.resizeTabs = this.config.resizeTabs === true;
36769 ts.minTabWidth = this.config.minTabWidth || 40;
36770 ts.maxTabWidth = this.config.maxTabWidth || 250;
36771 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36772 ts.monitorResize = false;
36773 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36774 ts.bodyEl.addClass('roo-layout-tabs-body');
36775 this.panels.each(this.initPanelAsTab, this);
36778 initPanelAsTab : function(panel){
36779 var ti = this.tabs.addTab(
36783 this.config.closeOnTab && panel.isClosable(),
36786 if(panel.tabTip !== undefined){
36787 ti.setTooltip(panel.tabTip);
36789 ti.on("activate", function(){
36790 this.setActivePanel(panel);
36793 if(this.config.closeOnTab){
36794 ti.on("beforeclose", function(t, e){
36796 this.remove(panel);
36800 panel.tabItem = ti;
36805 updatePanelTitle : function(panel, title)
36807 if(this.activePanel == panel){
36808 this.updateTitle(title);
36811 var ti = this.tabs.getTab(panel.getEl().id);
36813 if(panel.tabTip !== undefined){
36814 ti.setTooltip(panel.tabTip);
36819 updateTitle : function(title){
36820 if(this.titleTextEl && !this.config.title){
36821 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36825 setActivePanel : function(panel)
36827 panel = this.getPanel(panel);
36828 if(this.activePanel && this.activePanel != panel){
36829 if(this.activePanel.setActiveState(false) === false){
36833 this.activePanel = panel;
36834 panel.setActiveState(true);
36835 if(this.panelSize){
36836 panel.setSize(this.panelSize.width, this.panelSize.height);
36839 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36841 this.updateTitle(panel.getTitle());
36843 this.fireEvent("invalidated", this);
36845 this.fireEvent("panelactivated", this, panel);
36849 * Shows the specified panel.
36850 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36851 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36853 showPanel : function(panel)
36855 panel = this.getPanel(panel);
36858 var tab = this.tabs.getTab(panel.getEl().id);
36859 if(tab.isHidden()){
36860 this.tabs.unhideTab(tab.id);
36864 this.setActivePanel(panel);
36871 * Get the active panel for this region.
36872 * @return {Roo.ContentPanel} The active panel or null
36874 getActivePanel : function(){
36875 return this.activePanel;
36878 validateVisibility : function(){
36879 if(this.panels.getCount() < 1){
36880 this.updateTitle(" ");
36881 this.closeBtn.hide();
36884 if(!this.isVisible()){
36891 * Adds the passed ContentPanel(s) to this region.
36892 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36893 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36895 add : function(panel)
36897 if(arguments.length > 1){
36898 for(var i = 0, len = arguments.length; i < len; i++) {
36899 this.add(arguments[i]);
36904 // if we have not been rendered yet, then we can not really do much of this..
36905 if (!this.bodyEl) {
36906 this.unrendered_panels.push(panel);
36913 if(this.hasPanel(panel)){
36914 this.showPanel(panel);
36917 panel.setRegion(this);
36918 this.panels.add(panel);
36919 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36920 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36921 // and hide them... ???
36922 this.bodyEl.dom.appendChild(panel.getEl().dom);
36923 if(panel.background !== true){
36924 this.setActivePanel(panel);
36926 this.fireEvent("paneladded", this, panel);
36933 this.initPanelAsTab(panel);
36937 if(panel.background !== true){
36938 this.tabs.activate(panel.getEl().id);
36940 this.fireEvent("paneladded", this, panel);
36945 * Hides the tab for the specified panel.
36946 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36948 hidePanel : function(panel){
36949 if(this.tabs && (panel = this.getPanel(panel))){
36950 this.tabs.hideTab(panel.getEl().id);
36955 * Unhides the tab for a previously hidden panel.
36956 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36958 unhidePanel : function(panel){
36959 if(this.tabs && (panel = this.getPanel(panel))){
36960 this.tabs.unhideTab(panel.getEl().id);
36964 clearPanels : function(){
36965 while(this.panels.getCount() > 0){
36966 this.remove(this.panels.first());
36971 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36972 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36973 * @param {Boolean} preservePanel Overrides the config preservePanel option
36974 * @return {Roo.ContentPanel} The panel that was removed
36976 remove : function(panel, preservePanel)
36978 panel = this.getPanel(panel);
36983 this.fireEvent("beforeremove", this, panel, e);
36984 if(e.cancel === true){
36987 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36988 var panelId = panel.getId();
36989 this.panels.removeKey(panelId);
36991 document.body.appendChild(panel.getEl().dom);
36994 this.tabs.removeTab(panel.getEl().id);
36995 }else if (!preservePanel){
36996 this.bodyEl.dom.removeChild(panel.getEl().dom);
36998 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36999 var p = this.panels.first();
37000 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
37001 tempEl.appendChild(p.getEl().dom);
37002 this.bodyEl.update("");
37003 this.bodyEl.dom.appendChild(p.getEl().dom);
37005 this.updateTitle(p.getTitle());
37007 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
37008 this.setActivePanel(p);
37010 panel.setRegion(null);
37011 if(this.activePanel == panel){
37012 this.activePanel = null;
37014 if(this.config.autoDestroy !== false && preservePanel !== true){
37015 try{panel.destroy();}catch(e){}
37017 this.fireEvent("panelremoved", this, panel);
37022 * Returns the TabPanel component used by this region
37023 * @return {Roo.TabPanel}
37025 getTabs : function(){
37029 createTool : function(parentEl, className){
37030 var btn = Roo.DomHelper.append(parentEl, {
37032 cls: "x-layout-tools-button",
37035 cls: "roo-layout-tools-button-inner " + className,
37039 btn.addClassOnOver("roo-layout-tools-button-over");
37044 * Ext JS Library 1.1.1
37045 * Copyright(c) 2006-2007, Ext JS, LLC.
37047 * Originally Released Under LGPL - original licence link has changed is not relivant.
37050 * <script type="text/javascript">
37056 * @class Roo.SplitLayoutRegion
37057 * @extends Roo.LayoutRegion
37058 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
37060 Roo.bootstrap.layout.Split = function(config){
37061 this.cursor = config.cursor;
37062 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
37065 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
37067 splitTip : "Drag to resize.",
37068 collapsibleSplitTip : "Drag to resize. Double click to hide.",
37069 useSplitTips : false,
37071 applyConfig : function(config){
37072 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
37075 onRender : function(ctr,pos) {
37077 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
37078 if(!this.config.split){
37083 var splitEl = Roo.DomHelper.append(ctr.dom, {
37085 id: this.el.id + "-split",
37086 cls: "roo-layout-split roo-layout-split-"+this.position,
37089 /** The SplitBar for this region
37090 * @type Roo.SplitBar */
37091 // does not exist yet...
37092 Roo.log([this.position, this.orientation]);
37094 this.split = new Roo.bootstrap.SplitBar({
37095 dragElement : splitEl,
37096 resizingElement: this.el,
37097 orientation : this.orientation
37100 this.split.on("moved", this.onSplitMove, this);
37101 this.split.useShim = this.config.useShim === true;
37102 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
37103 if(this.useSplitTips){
37104 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
37106 //if(config.collapsible){
37107 // this.split.el.on("dblclick", this.collapse, this);
37110 if(typeof this.config.minSize != "undefined"){
37111 this.split.minSize = this.config.minSize;
37113 if(typeof this.config.maxSize != "undefined"){
37114 this.split.maxSize = this.config.maxSize;
37116 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
37117 this.hideSplitter();
37122 getHMaxSize : function(){
37123 var cmax = this.config.maxSize || 10000;
37124 var center = this.mgr.getRegion("center");
37125 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
37128 getVMaxSize : function(){
37129 var cmax = this.config.maxSize || 10000;
37130 var center = this.mgr.getRegion("center");
37131 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
37134 onSplitMove : function(split, newSize){
37135 this.fireEvent("resized", this, newSize);
37139 * Returns the {@link Roo.SplitBar} for this region.
37140 * @return {Roo.SplitBar}
37142 getSplitBar : function(){
37147 this.hideSplitter();
37148 Roo.bootstrap.layout.Split.superclass.hide.call(this);
37151 hideSplitter : function(){
37153 this.split.el.setLocation(-2000,-2000);
37154 this.split.el.hide();
37160 this.split.el.show();
37162 Roo.bootstrap.layout.Split.superclass.show.call(this);
37165 beforeSlide: function(){
37166 if(Roo.isGecko){// firefox overflow auto bug workaround
37167 this.bodyEl.clip();
37169 this.tabs.bodyEl.clip();
37171 if(this.activePanel){
37172 this.activePanel.getEl().clip();
37174 if(this.activePanel.beforeSlide){
37175 this.activePanel.beforeSlide();
37181 afterSlide : function(){
37182 if(Roo.isGecko){// firefox overflow auto bug workaround
37183 this.bodyEl.unclip();
37185 this.tabs.bodyEl.unclip();
37187 if(this.activePanel){
37188 this.activePanel.getEl().unclip();
37189 if(this.activePanel.afterSlide){
37190 this.activePanel.afterSlide();
37196 initAutoHide : function(){
37197 if(this.autoHide !== false){
37198 if(!this.autoHideHd){
37199 var st = new Roo.util.DelayedTask(this.slideIn, this);
37200 this.autoHideHd = {
37201 "mouseout": function(e){
37202 if(!e.within(this.el, true)){
37206 "mouseover" : function(e){
37212 this.el.on(this.autoHideHd);
37216 clearAutoHide : function(){
37217 if(this.autoHide !== false){
37218 this.el.un("mouseout", this.autoHideHd.mouseout);
37219 this.el.un("mouseover", this.autoHideHd.mouseover);
37223 clearMonitor : function(){
37224 Roo.get(document).un("click", this.slideInIf, this);
37227 // these names are backwards but not changed for compat
37228 slideOut : function(){
37229 if(this.isSlid || this.el.hasActiveFx()){
37232 this.isSlid = true;
37233 if(this.collapseBtn){
37234 this.collapseBtn.hide();
37236 this.closeBtnState = this.closeBtn.getStyle('display');
37237 this.closeBtn.hide();
37239 this.stickBtn.show();
37242 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37243 this.beforeSlide();
37244 this.el.setStyle("z-index", 10001);
37245 this.el.slideIn(this.getSlideAnchor(), {
37246 callback: function(){
37248 this.initAutoHide();
37249 Roo.get(document).on("click", this.slideInIf, this);
37250 this.fireEvent("slideshow", this);
37257 afterSlideIn : function(){
37258 this.clearAutoHide();
37259 this.isSlid = false;
37260 this.clearMonitor();
37261 this.el.setStyle("z-index", "");
37262 if(this.collapseBtn){
37263 this.collapseBtn.show();
37265 this.closeBtn.setStyle('display', this.closeBtnState);
37267 this.stickBtn.hide();
37269 this.fireEvent("slidehide", this);
37272 slideIn : function(cb){
37273 if(!this.isSlid || this.el.hasActiveFx()){
37277 this.isSlid = false;
37278 this.beforeSlide();
37279 this.el.slideOut(this.getSlideAnchor(), {
37280 callback: function(){
37281 this.el.setLeftTop(-10000, -10000);
37283 this.afterSlideIn();
37291 slideInIf : function(e){
37292 if(!e.within(this.el)){
37297 animateCollapse : function(){
37298 this.beforeSlide();
37299 this.el.setStyle("z-index", 20000);
37300 var anchor = this.getSlideAnchor();
37301 this.el.slideOut(anchor, {
37302 callback : function(){
37303 this.el.setStyle("z-index", "");
37304 this.collapsedEl.slideIn(anchor, {duration:.3});
37306 this.el.setLocation(-10000,-10000);
37308 this.fireEvent("collapsed", this);
37315 animateExpand : function(){
37316 this.beforeSlide();
37317 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37318 this.el.setStyle("z-index", 20000);
37319 this.collapsedEl.hide({
37322 this.el.slideIn(this.getSlideAnchor(), {
37323 callback : function(){
37324 this.el.setStyle("z-index", "");
37327 this.split.el.show();
37329 this.fireEvent("invalidated", this);
37330 this.fireEvent("expanded", this);
37358 getAnchor : function(){
37359 return this.anchors[this.position];
37362 getCollapseAnchor : function(){
37363 return this.canchors[this.position];
37366 getSlideAnchor : function(){
37367 return this.sanchors[this.position];
37370 getAlignAdj : function(){
37371 var cm = this.cmargins;
37372 switch(this.position){
37388 getExpandAdj : function(){
37389 var c = this.collapsedEl, cm = this.cmargins;
37390 switch(this.position){
37392 return [-(cm.right+c.getWidth()+cm.left), 0];
37395 return [cm.right+c.getWidth()+cm.left, 0];
37398 return [0, -(cm.top+cm.bottom+c.getHeight())];
37401 return [0, cm.top+cm.bottom+c.getHeight()];
37407 * Ext JS Library 1.1.1
37408 * Copyright(c) 2006-2007, Ext JS, LLC.
37410 * Originally Released Under LGPL - original licence link has changed is not relivant.
37413 * <script type="text/javascript">
37416 * These classes are private internal classes
37418 Roo.bootstrap.layout.Center = function(config){
37419 config.region = "center";
37420 Roo.bootstrap.layout.Region.call(this, config);
37421 this.visible = true;
37422 this.minWidth = config.minWidth || 20;
37423 this.minHeight = config.minHeight || 20;
37426 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37428 // center panel can't be hidden
37432 // center panel can't be hidden
37435 getMinWidth: function(){
37436 return this.minWidth;
37439 getMinHeight: function(){
37440 return this.minHeight;
37454 Roo.bootstrap.layout.North = function(config)
37456 config.region = 'north';
37457 config.cursor = 'n-resize';
37459 Roo.bootstrap.layout.Split.call(this, config);
37463 this.split.placement = Roo.bootstrap.SplitBar.TOP;
37464 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37465 this.split.el.addClass("roo-layout-split-v");
37467 var size = config.initialSize || config.height;
37468 if(typeof size != "undefined"){
37469 this.el.setHeight(size);
37472 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37474 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37478 getBox : function(){
37479 if(this.collapsed){
37480 return this.collapsedEl.getBox();
37482 var box = this.el.getBox();
37484 box.height += this.split.el.getHeight();
37489 updateBox : function(box){
37490 if(this.split && !this.collapsed){
37491 box.height -= this.split.el.getHeight();
37492 this.split.el.setLeft(box.x);
37493 this.split.el.setTop(box.y+box.height);
37494 this.split.el.setWidth(box.width);
37496 if(this.collapsed){
37497 this.updateBody(box.width, null);
37499 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37507 Roo.bootstrap.layout.South = function(config){
37508 config.region = 'south';
37509 config.cursor = 's-resize';
37510 Roo.bootstrap.layout.Split.call(this, config);
37512 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37513 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37514 this.split.el.addClass("roo-layout-split-v");
37516 var size = config.initialSize || config.height;
37517 if(typeof size != "undefined"){
37518 this.el.setHeight(size);
37522 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37523 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37524 getBox : function(){
37525 if(this.collapsed){
37526 return this.collapsedEl.getBox();
37528 var box = this.el.getBox();
37530 var sh = this.split.el.getHeight();
37537 updateBox : function(box){
37538 if(this.split && !this.collapsed){
37539 var sh = this.split.el.getHeight();
37542 this.split.el.setLeft(box.x);
37543 this.split.el.setTop(box.y-sh);
37544 this.split.el.setWidth(box.width);
37546 if(this.collapsed){
37547 this.updateBody(box.width, null);
37549 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37553 Roo.bootstrap.layout.East = function(config){
37554 config.region = "east";
37555 config.cursor = "e-resize";
37556 Roo.bootstrap.layout.Split.call(this, config);
37558 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37559 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37560 this.split.el.addClass("roo-layout-split-h");
37562 var size = config.initialSize || config.width;
37563 if(typeof size != "undefined"){
37564 this.el.setWidth(size);
37567 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37568 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37569 getBox : function(){
37570 if(this.collapsed){
37571 return this.collapsedEl.getBox();
37573 var box = this.el.getBox();
37575 var sw = this.split.el.getWidth();
37582 updateBox : function(box){
37583 if(this.split && !this.collapsed){
37584 var sw = this.split.el.getWidth();
37586 this.split.el.setLeft(box.x);
37587 this.split.el.setTop(box.y);
37588 this.split.el.setHeight(box.height);
37591 if(this.collapsed){
37592 this.updateBody(null, box.height);
37594 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37598 Roo.bootstrap.layout.West = function(config){
37599 config.region = "west";
37600 config.cursor = "w-resize";
37602 Roo.bootstrap.layout.Split.call(this, config);
37604 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37605 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37606 this.split.el.addClass("roo-layout-split-h");
37610 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37611 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37613 onRender: function(ctr, pos)
37615 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37616 var size = this.config.initialSize || this.config.width;
37617 if(typeof size != "undefined"){
37618 this.el.setWidth(size);
37622 getBox : function(){
37623 if(this.collapsed){
37624 return this.collapsedEl.getBox();
37626 var box = this.el.getBox();
37628 box.width += this.split.el.getWidth();
37633 updateBox : function(box){
37634 if(this.split && !this.collapsed){
37635 var sw = this.split.el.getWidth();
37637 this.split.el.setLeft(box.x+box.width);
37638 this.split.el.setTop(box.y);
37639 this.split.el.setHeight(box.height);
37641 if(this.collapsed){
37642 this.updateBody(null, box.height);
37644 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37646 });Roo.namespace("Roo.bootstrap.panel");/*
37648 * Ext JS Library 1.1.1
37649 * Copyright(c) 2006-2007, Ext JS, LLC.
37651 * Originally Released Under LGPL - original licence link has changed is not relivant.
37654 * <script type="text/javascript">
37657 * @class Roo.ContentPanel
37658 * @extends Roo.util.Observable
37659 * A basic ContentPanel element.
37660 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37661 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37662 * @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
37663 * @cfg {Boolean} closable True if the panel can be closed/removed
37664 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37665 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37666 * @cfg {Toolbar} toolbar A toolbar for this panel
37667 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37668 * @cfg {String} title The title for this panel
37669 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37670 * @cfg {String} url Calls {@link #setUrl} with this value
37671 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37672 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37673 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37674 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37675 * @cfg {Boolean} badges render the badges
37678 * Create a new ContentPanel.
37679 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37680 * @param {String/Object} config A string to set only the title or a config object
37681 * @param {String} content (optional) Set the HTML content for this panel
37682 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37684 Roo.bootstrap.panel.Content = function( config){
37686 this.tpl = config.tpl || false;
37688 var el = config.el;
37689 var content = config.content;
37691 if(config.autoCreate){ // xtype is available if this is called from factory
37694 this.el = Roo.get(el);
37695 if(!this.el && config && config.autoCreate){
37696 if(typeof config.autoCreate == "object"){
37697 if(!config.autoCreate.id){
37698 config.autoCreate.id = config.id||el;
37700 this.el = Roo.DomHelper.append(document.body,
37701 config.autoCreate, true);
37703 var elcfg = { tag: "div",
37704 cls: "roo-layout-inactive-content",
37708 elcfg.html = config.html;
37712 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37715 this.closable = false;
37716 this.loaded = false;
37717 this.active = false;
37720 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37722 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37724 this.wrapEl = this.el; //this.el.wrap();
37726 if (config.toolbar.items) {
37727 ti = config.toolbar.items ;
37728 delete config.toolbar.items ;
37732 this.toolbar.render(this.wrapEl, 'before');
37733 for(var i =0;i < ti.length;i++) {
37734 // Roo.log(['add child', items[i]]);
37735 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37737 this.toolbar.items = nitems;
37738 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37739 delete config.toolbar;
37743 // xtype created footer. - not sure if will work as we normally have to render first..
37744 if (this.footer && !this.footer.el && this.footer.xtype) {
37745 if (!this.wrapEl) {
37746 this.wrapEl = this.el.wrap();
37749 this.footer.container = this.wrapEl.createChild();
37751 this.footer = Roo.factory(this.footer, Roo);
37756 if(typeof config == "string"){
37757 this.title = config;
37759 Roo.apply(this, config);
37763 this.resizeEl = Roo.get(this.resizeEl, true);
37765 this.resizeEl = this.el;
37767 // handle view.xtype
37775 * Fires when this panel is activated.
37776 * @param {Roo.ContentPanel} this
37780 * @event deactivate
37781 * Fires when this panel is activated.
37782 * @param {Roo.ContentPanel} this
37784 "deactivate" : true,
37788 * Fires when this panel is resized if fitToFrame is true.
37789 * @param {Roo.ContentPanel} this
37790 * @param {Number} width The width after any component adjustments
37791 * @param {Number} height The height after any component adjustments
37797 * Fires when this tab is created
37798 * @param {Roo.ContentPanel} this
37809 if(this.autoScroll){
37810 this.resizeEl.setStyle("overflow", "auto");
37812 // fix randome scrolling
37813 //this.el.on('scroll', function() {
37814 // Roo.log('fix random scolling');
37815 // this.scrollTo('top',0);
37818 content = content || this.content;
37820 this.setContent(content);
37822 if(config && config.url){
37823 this.setUrl(this.url, this.params, this.loadOnce);
37828 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37830 if (this.view && typeof(this.view.xtype) != 'undefined') {
37831 this.view.el = this.el.appendChild(document.createElement("div"));
37832 this.view = Roo.factory(this.view);
37833 this.view.render && this.view.render(false, '');
37837 this.fireEvent('render', this);
37840 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37844 setRegion : function(region){
37845 this.region = region;
37846 this.setActiveClass(region && !this.background);
37850 setActiveClass: function(state)
37853 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37854 this.el.setStyle('position','relative');
37856 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37857 this.el.setStyle('position', 'absolute');
37862 * Returns the toolbar for this Panel if one was configured.
37863 * @return {Roo.Toolbar}
37865 getToolbar : function(){
37866 return this.toolbar;
37869 setActiveState : function(active)
37871 this.active = active;
37872 this.setActiveClass(active);
37874 if(this.fireEvent("deactivate", this) === false){
37879 this.fireEvent("activate", this);
37883 * Updates this panel's element
37884 * @param {String} content The new content
37885 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37887 setContent : function(content, loadScripts){
37888 this.el.update(content, loadScripts);
37891 ignoreResize : function(w, h){
37892 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37895 this.lastSize = {width: w, height: h};
37900 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37901 * @return {Roo.UpdateManager} The UpdateManager
37903 getUpdateManager : function(){
37904 return this.el.getUpdateManager();
37907 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37908 * @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:
37911 url: "your-url.php",
37912 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37913 callback: yourFunction,
37914 scope: yourObject, //(optional scope)
37917 text: "Loading...",
37922 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37923 * 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.
37924 * @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}
37925 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37926 * @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.
37927 * @return {Roo.ContentPanel} this
37930 var um = this.el.getUpdateManager();
37931 um.update.apply(um, arguments);
37937 * 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.
37938 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37939 * @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)
37940 * @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)
37941 * @return {Roo.UpdateManager} The UpdateManager
37943 setUrl : function(url, params, loadOnce){
37944 if(this.refreshDelegate){
37945 this.removeListener("activate", this.refreshDelegate);
37947 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37948 this.on("activate", this.refreshDelegate);
37949 return this.el.getUpdateManager();
37952 _handleRefresh : function(url, params, loadOnce){
37953 if(!loadOnce || !this.loaded){
37954 var updater = this.el.getUpdateManager();
37955 updater.update(url, params, this._setLoaded.createDelegate(this));
37959 _setLoaded : function(){
37960 this.loaded = true;
37964 * Returns this panel's id
37967 getId : function(){
37972 * Returns this panel's element - used by regiosn to add.
37973 * @return {Roo.Element}
37975 getEl : function(){
37976 return this.wrapEl || this.el;
37981 adjustForComponents : function(width, height)
37983 //Roo.log('adjustForComponents ');
37984 if(this.resizeEl != this.el){
37985 width -= this.el.getFrameWidth('lr');
37986 height -= this.el.getFrameWidth('tb');
37989 var te = this.toolbar.getEl();
37990 te.setWidth(width);
37991 height -= te.getHeight();
37994 var te = this.footer.getEl();
37995 te.setWidth(width);
37996 height -= te.getHeight();
38000 if(this.adjustments){
38001 width += this.adjustments[0];
38002 height += this.adjustments[1];
38004 return {"width": width, "height": height};
38007 setSize : function(width, height){
38008 if(this.fitToFrame && !this.ignoreResize(width, height)){
38009 if(this.fitContainer && this.resizeEl != this.el){
38010 this.el.setSize(width, height);
38012 var size = this.adjustForComponents(width, height);
38013 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
38014 this.fireEvent('resize', this, size.width, size.height);
38019 * Returns this panel's title
38022 getTitle : function(){
38024 if (typeof(this.title) != 'object') {
38029 for (var k in this.title) {
38030 if (!this.title.hasOwnProperty(k)) {
38034 if (k.indexOf('-') >= 0) {
38035 var s = k.split('-');
38036 for (var i = 0; i<s.length; i++) {
38037 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
38040 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
38047 * Set this panel's title
38048 * @param {String} title
38050 setTitle : function(title){
38051 this.title = title;
38053 this.region.updatePanelTitle(this, title);
38058 * Returns true is this panel was configured to be closable
38059 * @return {Boolean}
38061 isClosable : function(){
38062 return this.closable;
38065 beforeSlide : function(){
38067 this.resizeEl.clip();
38070 afterSlide : function(){
38072 this.resizeEl.unclip();
38076 * Force a content refresh from the URL specified in the {@link #setUrl} method.
38077 * Will fail silently if the {@link #setUrl} method has not been called.
38078 * This does not activate the panel, just updates its content.
38080 refresh : function(){
38081 if(this.refreshDelegate){
38082 this.loaded = false;
38083 this.refreshDelegate();
38088 * Destroys this panel
38090 destroy : function(){
38091 this.el.removeAllListeners();
38092 var tempEl = document.createElement("span");
38093 tempEl.appendChild(this.el.dom);
38094 tempEl.innerHTML = "";
38100 * form - if the content panel contains a form - this is a reference to it.
38101 * @type {Roo.form.Form}
38105 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
38106 * This contains a reference to it.
38112 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
38122 * @param {Object} cfg Xtype definition of item to add.
38126 getChildContainer: function () {
38127 return this.getEl();
38132 var ret = new Roo.factory(cfg);
38137 if (cfg.xtype.match(/^Form$/)) {
38140 //if (this.footer) {
38141 // el = this.footer.container.insertSibling(false, 'before');
38143 el = this.el.createChild();
38146 this.form = new Roo.form.Form(cfg);
38149 if ( this.form.allItems.length) {
38150 this.form.render(el.dom);
38154 // should only have one of theses..
38155 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
38156 // views.. should not be just added - used named prop 'view''
38158 cfg.el = this.el.appendChild(document.createElement("div"));
38161 var ret = new Roo.factory(cfg);
38163 ret.render && ret.render(false, ''); // render blank..
38173 * @class Roo.bootstrap.panel.Grid
38174 * @extends Roo.bootstrap.panel.Content
38176 * Create a new GridPanel.
38177 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
38178 * @param {Object} config A the config object
38184 Roo.bootstrap.panel.Grid = function(config)
38188 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
38189 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
38191 config.el = this.wrapper;
38192 //this.el = this.wrapper;
38194 if (config.container) {
38195 // ctor'ed from a Border/panel.grid
38198 this.wrapper.setStyle("overflow", "hidden");
38199 this.wrapper.addClass('roo-grid-container');
38204 if(config.toolbar){
38205 var tool_el = this.wrapper.createChild();
38206 this.toolbar = Roo.factory(config.toolbar);
38208 if (config.toolbar.items) {
38209 ti = config.toolbar.items ;
38210 delete config.toolbar.items ;
38214 this.toolbar.render(tool_el);
38215 for(var i =0;i < ti.length;i++) {
38216 // Roo.log(['add child', items[i]]);
38217 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38219 this.toolbar.items = nitems;
38221 delete config.toolbar;
38224 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
38225 config.grid.scrollBody = true;;
38226 config.grid.monitorWindowResize = false; // turn off autosizing
38227 config.grid.autoHeight = false;
38228 config.grid.autoWidth = false;
38230 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
38232 if (config.background) {
38233 // render grid on panel activation (if panel background)
38234 this.on('activate', function(gp) {
38235 if (!gp.grid.rendered) {
38236 gp.grid.render(this.wrapper);
38237 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
38242 this.grid.render(this.wrapper);
38243 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
38246 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
38247 // ??? needed ??? config.el = this.wrapper;
38252 // xtype created footer. - not sure if will work as we normally have to render first..
38253 if (this.footer && !this.footer.el && this.footer.xtype) {
38255 var ctr = this.grid.getView().getFooterPanel(true);
38256 this.footer.dataSource = this.grid.dataSource;
38257 this.footer = Roo.factory(this.footer, Roo);
38258 this.footer.render(ctr);
38268 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38269 getId : function(){
38270 return this.grid.id;
38274 * Returns the grid for this panel
38275 * @return {Roo.bootstrap.Table}
38277 getGrid : function(){
38281 setSize : function(width, height){
38282 if(!this.ignoreResize(width, height)){
38283 var grid = this.grid;
38284 var size = this.adjustForComponents(width, height);
38285 var gridel = grid.getGridEl();
38286 gridel.setSize(size.width, size.height);
38288 var thd = grid.getGridEl().select('thead',true).first();
38289 var tbd = grid.getGridEl().select('tbody', true).first();
38291 tbd.setSize(width, height - thd.getHeight());
38300 beforeSlide : function(){
38301 this.grid.getView().scroller.clip();
38304 afterSlide : function(){
38305 this.grid.getView().scroller.unclip();
38308 destroy : function(){
38309 this.grid.destroy();
38311 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
38316 * @class Roo.bootstrap.panel.Nest
38317 * @extends Roo.bootstrap.panel.Content
38319 * Create a new Panel, that can contain a layout.Border.
38322 * @param {Roo.BorderLayout} layout The layout for this panel
38323 * @param {String/Object} config A string to set only the title or a config object
38325 Roo.bootstrap.panel.Nest = function(config)
38327 // construct with only one argument..
38328 /* FIXME - implement nicer consturctors
38329 if (layout.layout) {
38331 layout = config.layout;
38332 delete config.layout;
38334 if (layout.xtype && !layout.getEl) {
38335 // then layout needs constructing..
38336 layout = Roo.factory(layout, Roo);
38340 config.el = config.layout.getEl();
38342 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38344 config.layout.monitorWindowResize = false; // turn off autosizing
38345 this.layout = config.layout;
38346 this.layout.getEl().addClass("roo-layout-nested-layout");
38347 this.layout.parent = this;
38354 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38356 setSize : function(width, height){
38357 if(!this.ignoreResize(width, height)){
38358 var size = this.adjustForComponents(width, height);
38359 var el = this.layout.getEl();
38360 if (size.height < 1) {
38361 el.setWidth(size.width);
38363 el.setSize(size.width, size.height);
38365 var touch = el.dom.offsetWidth;
38366 this.layout.layout();
38367 // ie requires a double layout on the first pass
38368 if(Roo.isIE && !this.initialized){
38369 this.initialized = true;
38370 this.layout.layout();
38375 // activate all subpanels if not currently active..
38377 setActiveState : function(active){
38378 this.active = active;
38379 this.setActiveClass(active);
38382 this.fireEvent("deactivate", this);
38386 this.fireEvent("activate", this);
38387 // not sure if this should happen before or after..
38388 if (!this.layout) {
38389 return; // should not happen..
38392 for (var r in this.layout.regions) {
38393 reg = this.layout.getRegion(r);
38394 if (reg.getActivePanel()) {
38395 //reg.showPanel(reg.getActivePanel()); // force it to activate..
38396 reg.setActivePanel(reg.getActivePanel());
38399 if (!reg.panels.length) {
38402 reg.showPanel(reg.getPanel(0));
38411 * Returns the nested BorderLayout for this panel
38412 * @return {Roo.BorderLayout}
38414 getLayout : function(){
38415 return this.layout;
38419 * Adds a xtype elements to the layout of the nested panel
38423 xtype : 'ContentPanel',
38430 xtype : 'NestedLayoutPanel',
38436 items : [ ... list of content panels or nested layout panels.. ]
38440 * @param {Object} cfg Xtype definition of item to add.
38442 addxtype : function(cfg) {
38443 return this.layout.addxtype(cfg);
38448 * Ext JS Library 1.1.1
38449 * Copyright(c) 2006-2007, Ext JS, LLC.
38451 * Originally Released Under LGPL - original licence link has changed is not relivant.
38454 * <script type="text/javascript">
38457 * @class Roo.TabPanel
38458 * @extends Roo.util.Observable
38459 * A lightweight tab container.
38463 // basic tabs 1, built from existing content
38464 var tabs = new Roo.TabPanel("tabs1");
38465 tabs.addTab("script", "View Script");
38466 tabs.addTab("markup", "View Markup");
38467 tabs.activate("script");
38469 // more advanced tabs, built from javascript
38470 var jtabs = new Roo.TabPanel("jtabs");
38471 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38473 // set up the UpdateManager
38474 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38475 var updater = tab2.getUpdateManager();
38476 updater.setDefaultUrl("ajax1.htm");
38477 tab2.on('activate', updater.refresh, updater, true);
38479 // Use setUrl for Ajax loading
38480 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38481 tab3.setUrl("ajax2.htm", null, true);
38484 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38487 jtabs.activate("jtabs-1");
38490 * Create a new TabPanel.
38491 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38492 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38494 Roo.bootstrap.panel.Tabs = function(config){
38496 * The container element for this TabPanel.
38497 * @type Roo.Element
38499 this.el = Roo.get(config.el);
38502 if(typeof config == "boolean"){
38503 this.tabPosition = config ? "bottom" : "top";
38505 Roo.apply(this, config);
38509 if(this.tabPosition == "bottom"){
38510 // if tabs are at the bottom = create the body first.
38511 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38512 this.el.addClass("roo-tabs-bottom");
38514 // next create the tabs holders
38516 if (this.tabPosition == "west"){
38518 var reg = this.region; // fake it..
38520 if (!reg.mgr.parent) {
38523 reg = reg.mgr.parent.region;
38525 Roo.log("got nest?");
38527 if (reg.mgr.getRegion('west')) {
38528 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38529 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38530 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38531 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38532 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38540 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38541 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38542 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38543 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38548 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38551 // finally - if tabs are at the top, then create the body last..
38552 if(this.tabPosition != "bottom"){
38553 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38554 * @type Roo.Element
38556 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38557 this.el.addClass("roo-tabs-top");
38561 this.bodyEl.setStyle("position", "relative");
38563 this.active = null;
38564 this.activateDelegate = this.activate.createDelegate(this);
38569 * Fires when the active tab changes
38570 * @param {Roo.TabPanel} this
38571 * @param {Roo.TabPanelItem} activePanel The new active tab
38575 * @event beforetabchange
38576 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38577 * @param {Roo.TabPanel} this
38578 * @param {Object} e Set cancel to true on this object to cancel the tab change
38579 * @param {Roo.TabPanelItem} tab The tab being changed to
38581 "beforetabchange" : true
38584 Roo.EventManager.onWindowResize(this.onResize, this);
38585 this.cpad = this.el.getPadding("lr");
38586 this.hiddenCount = 0;
38589 // toolbar on the tabbar support...
38590 if (this.toolbar) {
38591 alert("no toolbar support yet");
38592 this.toolbar = false;
38594 var tcfg = this.toolbar;
38595 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
38596 this.toolbar = new Roo.Toolbar(tcfg);
38597 if (Roo.isSafari) {
38598 var tbl = tcfg.container.child('table', true);
38599 tbl.setAttribute('width', '100%');
38607 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38610 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38612 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38614 tabPosition : "top",
38616 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38618 currentTabWidth : 0,
38620 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38624 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38628 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38630 preferredTabWidth : 175,
38632 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38634 resizeTabs : false,
38636 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38638 monitorResize : true,
38640 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
38642 toolbar : false, // set by caller..
38644 region : false, /// set by caller
38646 disableTooltips : true, // not used yet...
38649 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38650 * @param {String} id The id of the div to use <b>or create</b>
38651 * @param {String} text The text for the tab
38652 * @param {String} content (optional) Content to put in the TabPanelItem body
38653 * @param {Boolean} closable (optional) True to create a close icon on the tab
38654 * @return {Roo.TabPanelItem} The created TabPanelItem
38656 addTab : function(id, text, content, closable, tpl)
38658 var item = new Roo.bootstrap.panel.TabItem({
38662 closable : closable,
38665 this.addTabItem(item);
38667 item.setContent(content);
38673 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38674 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38675 * @return {Roo.TabPanelItem}
38677 getTab : function(id){
38678 return this.items[id];
38682 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38683 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38685 hideTab : function(id){
38686 var t = this.items[id];
38689 this.hiddenCount++;
38690 this.autoSizeTabs();
38695 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38696 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38698 unhideTab : function(id){
38699 var t = this.items[id];
38701 t.setHidden(false);
38702 this.hiddenCount--;
38703 this.autoSizeTabs();
38708 * Adds an existing {@link Roo.TabPanelItem}.
38709 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38711 addTabItem : function(item)
38713 this.items[item.id] = item;
38714 this.items.push(item);
38715 this.autoSizeTabs();
38716 // if(this.resizeTabs){
38717 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38718 // this.autoSizeTabs();
38720 // item.autoSize();
38725 * Removes a {@link Roo.TabPanelItem}.
38726 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38728 removeTab : function(id){
38729 var items = this.items;
38730 var tab = items[id];
38731 if(!tab) { return; }
38732 var index = items.indexOf(tab);
38733 if(this.active == tab && items.length > 1){
38734 var newTab = this.getNextAvailable(index);
38739 this.stripEl.dom.removeChild(tab.pnode.dom);
38740 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38741 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38743 items.splice(index, 1);
38744 delete this.items[tab.id];
38745 tab.fireEvent("close", tab);
38746 tab.purgeListeners();
38747 this.autoSizeTabs();
38750 getNextAvailable : function(start){
38751 var items = this.items;
38753 // look for a next tab that will slide over to
38754 // replace the one being removed
38755 while(index < items.length){
38756 var item = items[++index];
38757 if(item && !item.isHidden()){
38761 // if one isn't found select the previous tab (on the left)
38764 var item = items[--index];
38765 if(item && !item.isHidden()){
38773 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38774 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38776 disableTab : function(id){
38777 var tab = this.items[id];
38778 if(tab && this.active != tab){
38784 * Enables a {@link Roo.TabPanelItem} that is disabled.
38785 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38787 enableTab : function(id){
38788 var tab = this.items[id];
38793 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38794 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38795 * @return {Roo.TabPanelItem} The TabPanelItem.
38797 activate : function(id)
38799 //Roo.log('activite:' + id);
38801 var tab = this.items[id];
38805 if(tab == this.active || tab.disabled){
38809 this.fireEvent("beforetabchange", this, e, tab);
38810 if(e.cancel !== true && !tab.disabled){
38812 this.active.hide();
38814 this.active = this.items[id];
38815 this.active.show();
38816 this.fireEvent("tabchange", this, this.active);
38822 * Gets the active {@link Roo.TabPanelItem}.
38823 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38825 getActiveTab : function(){
38826 return this.active;
38830 * Updates the tab body element to fit the height of the container element
38831 * for overflow scrolling
38832 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38834 syncHeight : function(targetHeight){
38835 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38836 var bm = this.bodyEl.getMargins();
38837 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38838 this.bodyEl.setHeight(newHeight);
38842 onResize : function(){
38843 if(this.monitorResize){
38844 this.autoSizeTabs();
38849 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38851 beginUpdate : function(){
38852 this.updating = true;
38856 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38858 endUpdate : function(){
38859 this.updating = false;
38860 this.autoSizeTabs();
38864 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38866 autoSizeTabs : function()
38868 var count = this.items.length;
38869 var vcount = count - this.hiddenCount;
38872 this.stripEl.hide();
38874 this.stripEl.show();
38877 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38882 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38883 var availWidth = Math.floor(w / vcount);
38884 var b = this.stripBody;
38885 if(b.getWidth() > w){
38886 var tabs = this.items;
38887 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38888 if(availWidth < this.minTabWidth){
38889 /*if(!this.sleft){ // incomplete scrolling code
38890 this.createScrollButtons();
38893 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38896 if(this.currentTabWidth < this.preferredTabWidth){
38897 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38903 * Returns the number of tabs in this TabPanel.
38906 getCount : function(){
38907 return this.items.length;
38911 * Resizes all the tabs to the passed width
38912 * @param {Number} The new width
38914 setTabWidth : function(width){
38915 this.currentTabWidth = width;
38916 for(var i = 0, len = this.items.length; i < len; i++) {
38917 if(!this.items[i].isHidden()) {
38918 this.items[i].setWidth(width);
38924 * Destroys this TabPanel
38925 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38927 destroy : function(removeEl){
38928 Roo.EventManager.removeResizeListener(this.onResize, this);
38929 for(var i = 0, len = this.items.length; i < len; i++){
38930 this.items[i].purgeListeners();
38932 if(removeEl === true){
38933 this.el.update("");
38938 createStrip : function(container)
38940 var strip = document.createElement("nav");
38941 strip.className = Roo.bootstrap.version == 4 ?
38942 "navbar-light bg-light" :
38943 "navbar navbar-default"; //"x-tabs-wrap";
38944 container.appendChild(strip);
38948 createStripList : function(strip)
38950 // div wrapper for retard IE
38951 // returns the "tr" element.
38952 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38953 //'<div class="x-tabs-strip-wrap">'+
38954 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38955 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38956 return strip.firstChild; //.firstChild.firstChild.firstChild;
38958 createBody : function(container)
38960 var body = document.createElement("div");
38961 Roo.id(body, "tab-body");
38962 //Roo.fly(body).addClass("x-tabs-body");
38963 Roo.fly(body).addClass("tab-content");
38964 container.appendChild(body);
38967 createItemBody :function(bodyEl, id){
38968 var body = Roo.getDom(id);
38970 body = document.createElement("div");
38973 //Roo.fly(body).addClass("x-tabs-item-body");
38974 Roo.fly(body).addClass("tab-pane");
38975 bodyEl.insertBefore(body, bodyEl.firstChild);
38979 createStripElements : function(stripEl, text, closable, tpl)
38981 var td = document.createElement("li"); // was td..
38982 td.className = 'nav-item';
38984 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38987 stripEl.appendChild(td);
38989 td.className = "x-tabs-closable";
38990 if(!this.closeTpl){
38991 this.closeTpl = new Roo.Template(
38992 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38993 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38994 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38997 var el = this.closeTpl.overwrite(td, {"text": text});
38998 var close = el.getElementsByTagName("div")[0];
38999 var inner = el.getElementsByTagName("em")[0];
39000 return {"el": el, "close": close, "inner": inner};
39003 // not sure what this is..
39004 // if(!this.tabTpl){
39005 //this.tabTpl = new Roo.Template(
39006 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39007 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
39009 // this.tabTpl = new Roo.Template(
39010 // '<a href="#">' +
39011 // '<span unselectable="on"' +
39012 // (this.disableTooltips ? '' : ' title="{text}"') +
39013 // ' >{text}</span></a>'
39019 var template = tpl || this.tabTpl || false;
39022 template = new Roo.Template(
39023 Roo.bootstrap.version == 4 ?
39025 '<a class="nav-link" href="#" unselectable="on"' +
39026 (this.disableTooltips ? '' : ' title="{text}"') +
39029 '<a class="nav-link" href="#">' +
39030 '<span unselectable="on"' +
39031 (this.disableTooltips ? '' : ' title="{text}"') +
39032 ' >{text}</span></a>'
39037 switch (typeof(template)) {
39041 template = new Roo.Template(template);
39047 var el = template.overwrite(td, {"text": text});
39049 var inner = el.getElementsByTagName("span")[0];
39051 return {"el": el, "inner": inner};
39059 * @class Roo.TabPanelItem
39060 * @extends Roo.util.Observable
39061 * Represents an individual item (tab plus body) in a TabPanel.
39062 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
39063 * @param {String} id The id of this TabPanelItem
39064 * @param {String} text The text for the tab of this TabPanelItem
39065 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
39067 Roo.bootstrap.panel.TabItem = function(config){
39069 * The {@link Roo.TabPanel} this TabPanelItem belongs to
39070 * @type Roo.TabPanel
39072 this.tabPanel = config.panel;
39074 * The id for this TabPanelItem
39077 this.id = config.id;
39079 this.disabled = false;
39081 this.text = config.text;
39083 this.loaded = false;
39084 this.closable = config.closable;
39087 * The body element for this TabPanelItem.
39088 * @type Roo.Element
39090 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
39091 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
39092 this.bodyEl.setStyle("display", "block");
39093 this.bodyEl.setStyle("zoom", "1");
39094 //this.hideAction();
39096 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
39098 this.el = Roo.get(els.el);
39099 this.inner = Roo.get(els.inner, true);
39100 this.textEl = Roo.bootstrap.version == 4 ?
39101 this.el : Roo.get(this.el.dom.firstChild, true);
39103 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
39104 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
39107 // this.el.on("mousedown", this.onTabMouseDown, this);
39108 this.el.on("click", this.onTabClick, this);
39110 if(config.closable){
39111 var c = Roo.get(els.close, true);
39112 c.dom.title = this.closeText;
39113 c.addClassOnOver("close-over");
39114 c.on("click", this.closeClick, this);
39120 * Fires when this tab becomes the active tab.
39121 * @param {Roo.TabPanel} tabPanel The parent TabPanel
39122 * @param {Roo.TabPanelItem} this
39126 * @event beforeclose
39127 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
39128 * @param {Roo.TabPanelItem} this
39129 * @param {Object} e Set cancel to true on this object to cancel the close.
39131 "beforeclose": true,
39134 * Fires when this tab is closed.
39135 * @param {Roo.TabPanelItem} this
39139 * @event deactivate
39140 * Fires when this tab is no longer the active tab.
39141 * @param {Roo.TabPanel} tabPanel The parent TabPanel
39142 * @param {Roo.TabPanelItem} this
39144 "deactivate" : true
39146 this.hidden = false;
39148 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
39151 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
39153 purgeListeners : function(){
39154 Roo.util.Observable.prototype.purgeListeners.call(this);
39155 this.el.removeAllListeners();
39158 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
39161 this.status_node.addClass("active");
39164 this.tabPanel.stripWrap.repaint();
39166 this.fireEvent("activate", this.tabPanel, this);
39170 * Returns true if this tab is the active tab.
39171 * @return {Boolean}
39173 isActive : function(){
39174 return this.tabPanel.getActiveTab() == this;
39178 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
39181 this.status_node.removeClass("active");
39183 this.fireEvent("deactivate", this.tabPanel, this);
39186 hideAction : function(){
39187 this.bodyEl.hide();
39188 this.bodyEl.setStyle("position", "absolute");
39189 this.bodyEl.setLeft("-20000px");
39190 this.bodyEl.setTop("-20000px");
39193 showAction : function(){
39194 this.bodyEl.setStyle("position", "relative");
39195 this.bodyEl.setTop("");
39196 this.bodyEl.setLeft("");
39197 this.bodyEl.show();
39201 * Set the tooltip for the tab.
39202 * @param {String} tooltip The tab's tooltip
39204 setTooltip : function(text){
39205 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
39206 this.textEl.dom.qtip = text;
39207 this.textEl.dom.removeAttribute('title');
39209 this.textEl.dom.title = text;
39213 onTabClick : function(e){
39214 e.preventDefault();
39215 this.tabPanel.activate(this.id);
39218 onTabMouseDown : function(e){
39219 e.preventDefault();
39220 this.tabPanel.activate(this.id);
39223 getWidth : function(){
39224 return this.inner.getWidth();
39227 setWidth : function(width){
39228 var iwidth = width - this.linode.getPadding("lr");
39229 this.inner.setWidth(iwidth);
39230 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
39231 this.linode.setWidth(width);
39235 * Show or hide the tab
39236 * @param {Boolean} hidden True to hide or false to show.
39238 setHidden : function(hidden){
39239 this.hidden = hidden;
39240 this.linode.setStyle("display", hidden ? "none" : "");
39244 * Returns true if this tab is "hidden"
39245 * @return {Boolean}
39247 isHidden : function(){
39248 return this.hidden;
39252 * Returns the text for this tab
39255 getText : function(){
39259 autoSize : function(){
39260 //this.el.beginMeasure();
39261 this.textEl.setWidth(1);
39263 * #2804 [new] Tabs in Roojs
39264 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39266 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39267 //this.el.endMeasure();
39271 * Sets the text for the tab (Note: this also sets the tooltip text)
39272 * @param {String} text The tab's text and tooltip
39274 setText : function(text){
39276 this.textEl.update(text);
39277 this.setTooltip(text);
39278 //if(!this.tabPanel.resizeTabs){
39279 // this.autoSize();
39283 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39285 activate : function(){
39286 this.tabPanel.activate(this.id);
39290 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39292 disable : function(){
39293 if(this.tabPanel.active != this){
39294 this.disabled = true;
39295 this.status_node.addClass("disabled");
39300 * Enables this TabPanelItem if it was previously disabled.
39302 enable : function(){
39303 this.disabled = false;
39304 this.status_node.removeClass("disabled");
39308 * Sets the content for this TabPanelItem.
39309 * @param {String} content The content
39310 * @param {Boolean} loadScripts true to look for and load scripts
39312 setContent : function(content, loadScripts){
39313 this.bodyEl.update(content, loadScripts);
39317 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39318 * @return {Roo.UpdateManager} The UpdateManager
39320 getUpdateManager : function(){
39321 return this.bodyEl.getUpdateManager();
39325 * Set a URL to be used to load the content for this TabPanelItem.
39326 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39327 * @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)
39328 * @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)
39329 * @return {Roo.UpdateManager} The UpdateManager
39331 setUrl : function(url, params, loadOnce){
39332 if(this.refreshDelegate){
39333 this.un('activate', this.refreshDelegate);
39335 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39336 this.on("activate", this.refreshDelegate);
39337 return this.bodyEl.getUpdateManager();
39341 _handleRefresh : function(url, params, loadOnce){
39342 if(!loadOnce || !this.loaded){
39343 var updater = this.bodyEl.getUpdateManager();
39344 updater.update(url, params, this._setLoaded.createDelegate(this));
39349 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
39350 * Will fail silently if the setUrl method has not been called.
39351 * This does not activate the panel, just updates its content.
39353 refresh : function(){
39354 if(this.refreshDelegate){
39355 this.loaded = false;
39356 this.refreshDelegate();
39361 _setLoaded : function(){
39362 this.loaded = true;
39366 closeClick : function(e){
39369 this.fireEvent("beforeclose", this, o);
39370 if(o.cancel !== true){
39371 this.tabPanel.removeTab(this.id);
39375 * The text displayed in the tooltip for the close icon.
39378 closeText : "Close this tab"
39381 * This script refer to:
39382 * Title: International Telephone Input
39383 * Author: Jack O'Connor
39384 * Code version: v12.1.12
39385 * Availability: https://github.com/jackocnr/intl-tel-input.git
39388 Roo.bootstrap.PhoneInputData = function() {
39391 "Afghanistan (افغانستان)",
39396 "Albania (Shqipëri)",
39401 "Algeria (الجزائر)",
39426 "Antigua and Barbuda",
39436 "Armenia (Հայաստան)",
39452 "Austria (Österreich)",
39457 "Azerbaijan (Azərbaycan)",
39467 "Bahrain (البحرين)",
39472 "Bangladesh (বাংলাদেশ)",
39482 "Belarus (Беларусь)",
39487 "Belgium (België)",
39517 "Bosnia and Herzegovina (Босна и Херцеговина)",
39532 "British Indian Ocean Territory",
39537 "British Virgin Islands",
39547 "Bulgaria (България)",
39557 "Burundi (Uburundi)",
39562 "Cambodia (កម្ពុជា)",
39567 "Cameroon (Cameroun)",
39576 ["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"]
39579 "Cape Verde (Kabu Verdi)",
39584 "Caribbean Netherlands",
39595 "Central African Republic (République centrafricaine)",
39615 "Christmas Island",
39621 "Cocos (Keeling) Islands",
39632 "Comoros (جزر القمر)",
39637 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39642 "Congo (Republic) (Congo-Brazzaville)",
39662 "Croatia (Hrvatska)",
39683 "Czech Republic (Česká republika)",
39688 "Denmark (Danmark)",
39703 "Dominican Republic (República Dominicana)",
39707 ["809", "829", "849"]
39725 "Equatorial Guinea (Guinea Ecuatorial)",
39745 "Falkland Islands (Islas Malvinas)",
39750 "Faroe Islands (Føroyar)",
39771 "French Guiana (Guyane française)",
39776 "French Polynesia (Polynésie française)",
39791 "Georgia (საქართველო)",
39796 "Germany (Deutschland)",
39816 "Greenland (Kalaallit Nunaat)",
39853 "Guinea-Bissau (Guiné Bissau)",
39878 "Hungary (Magyarország)",
39883 "Iceland (Ísland)",
39903 "Iraq (العراق)",
39919 "Israel (ישראל)",
39946 "Jordan (الأردن)",
39951 "Kazakhstan (Казахстан)",
39972 "Kuwait (الكويت)",
39977 "Kyrgyzstan (Кыргызстан)",
39987 "Latvia (Latvija)",
39992 "Lebanon (لبنان)",
40007 "Libya (ليبيا)",
40017 "Lithuania (Lietuva)",
40032 "Macedonia (FYROM) (Македонија)",
40037 "Madagascar (Madagasikara)",
40067 "Marshall Islands",
40077 "Mauritania (موريتانيا)",
40082 "Mauritius (Moris)",
40103 "Moldova (Republica Moldova)",
40113 "Mongolia (Монгол)",
40118 "Montenegro (Crna Gora)",
40128 "Morocco (المغرب)",
40134 "Mozambique (Moçambique)",
40139 "Myanmar (Burma) (မြန်မာ)",
40144 "Namibia (Namibië)",
40159 "Netherlands (Nederland)",
40164 "New Caledonia (Nouvelle-Calédonie)",
40199 "North Korea (조선 민주주의 인민 공화국)",
40204 "Northern Mariana Islands",
40220 "Pakistan (پاکستان)",
40230 "Palestine (فلسطين)",
40240 "Papua New Guinea",
40282 "Réunion (La Réunion)",
40288 "Romania (România)",
40304 "Saint Barthélemy",
40315 "Saint Kitts and Nevis",
40325 "Saint Martin (Saint-Martin (partie française))",
40331 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40336 "Saint Vincent and the Grenadines",
40351 "São Tomé and Príncipe (São Tomé e Príncipe)",
40356 "Saudi Arabia (المملكة العربية السعودية)",
40361 "Senegal (Sénégal)",
40391 "Slovakia (Slovensko)",
40396 "Slovenia (Slovenija)",
40406 "Somalia (Soomaaliya)",
40416 "South Korea (대한민국)",
40421 "South Sudan (جنوب السودان)",
40431 "Sri Lanka (ශ්රී ලංකාව)",
40436 "Sudan (السودان)",
40446 "Svalbard and Jan Mayen",
40457 "Sweden (Sverige)",
40462 "Switzerland (Schweiz)",
40467 "Syria (سوريا)",
40512 "Trinidad and Tobago",
40517 "Tunisia (تونس)",
40522 "Turkey (Türkiye)",
40532 "Turks and Caicos Islands",
40542 "U.S. Virgin Islands",
40552 "Ukraine (Україна)",
40557 "United Arab Emirates (الإمارات العربية المتحدة)",
40579 "Uzbekistan (Oʻzbekiston)",
40589 "Vatican City (Città del Vaticano)",
40600 "Vietnam (Việt Nam)",
40605 "Wallis and Futuna (Wallis-et-Futuna)",
40610 "Western Sahara (الصحراء الغربية)",
40616 "Yemen (اليمن)",
40640 * This script refer to:
40641 * Title: International Telephone Input
40642 * Author: Jack O'Connor
40643 * Code version: v12.1.12
40644 * Availability: https://github.com/jackocnr/intl-tel-input.git
40648 * @class Roo.bootstrap.PhoneInput
40649 * @extends Roo.bootstrap.TriggerField
40650 * An input with International dial-code selection
40652 * @cfg {String} defaultDialCode default '+852'
40653 * @cfg {Array} preferedCountries default []
40656 * Create a new PhoneInput.
40657 * @param {Object} config Configuration options
40660 Roo.bootstrap.PhoneInput = function(config) {
40661 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40664 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40666 listWidth: undefined,
40668 selectedClass: 'active',
40670 invalidClass : "has-warning",
40672 validClass: 'has-success',
40674 allowed: '0123456789',
40679 * @cfg {String} defaultDialCode The default dial code when initializing the input
40681 defaultDialCode: '+852',
40684 * @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
40686 preferedCountries: false,
40688 getAutoCreate : function()
40690 var data = Roo.bootstrap.PhoneInputData();
40691 var align = this.labelAlign || this.parentLabelAlign();
40694 this.allCountries = [];
40695 this.dialCodeMapping = [];
40697 for (var i = 0; i < data.length; i++) {
40699 this.allCountries[i] = {
40703 priority: c[3] || 0,
40704 areaCodes: c[4] || null
40706 this.dialCodeMapping[c[2]] = {
40709 priority: c[3] || 0,
40710 areaCodes: c[4] || null
40722 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40723 maxlength: this.max_length,
40724 cls : 'form-control tel-input',
40725 autocomplete: 'new-password'
40728 var hiddenInput = {
40731 cls: 'hidden-tel-input'
40735 hiddenInput.name = this.name;
40738 if (this.disabled) {
40739 input.disabled = true;
40742 var flag_container = {
40759 cls: this.hasFeedback ? 'has-feedback' : '',
40765 cls: 'dial-code-holder',
40772 cls: 'roo-select2-container input-group',
40779 if (this.fieldLabel.length) {
40782 tooltip: 'This field is required'
40788 cls: 'control-label',
40794 html: this.fieldLabel
40797 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40803 if(this.indicatorpos == 'right') {
40804 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40811 if(align == 'left') {
40819 if(this.labelWidth > 12){
40820 label.style = "width: " + this.labelWidth + 'px';
40822 if(this.labelWidth < 13 && this.labelmd == 0){
40823 this.labelmd = this.labelWidth;
40825 if(this.labellg > 0){
40826 label.cls += ' col-lg-' + this.labellg;
40827 input.cls += ' col-lg-' + (12 - this.labellg);
40829 if(this.labelmd > 0){
40830 label.cls += ' col-md-' + this.labelmd;
40831 container.cls += ' col-md-' + (12 - this.labelmd);
40833 if(this.labelsm > 0){
40834 label.cls += ' col-sm-' + this.labelsm;
40835 container.cls += ' col-sm-' + (12 - this.labelsm);
40837 if(this.labelxs > 0){
40838 label.cls += ' col-xs-' + this.labelxs;
40839 container.cls += ' col-xs-' + (12 - this.labelxs);
40849 var settings = this;
40851 ['xs','sm','md','lg'].map(function(size){
40852 if (settings[size]) {
40853 cfg.cls += ' col-' + size + '-' + settings[size];
40857 this.store = new Roo.data.Store({
40858 proxy : new Roo.data.MemoryProxy({}),
40859 reader : new Roo.data.JsonReader({
40870 'name' : 'dialCode',
40874 'name' : 'priority',
40878 'name' : 'areaCodes',
40885 if(!this.preferedCountries) {
40886 this.preferedCountries = [
40893 var p = this.preferedCountries.reverse();
40896 for (var i = 0; i < p.length; i++) {
40897 for (var j = 0; j < this.allCountries.length; j++) {
40898 if(this.allCountries[j].iso2 == p[i]) {
40899 var t = this.allCountries[j];
40900 this.allCountries.splice(j,1);
40901 this.allCountries.unshift(t);
40907 this.store.proxy.data = {
40909 data: this.allCountries
40915 initEvents : function()
40918 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40920 this.indicator = this.indicatorEl();
40921 this.flag = this.flagEl();
40922 this.dialCodeHolder = this.dialCodeHolderEl();
40924 this.trigger = this.el.select('div.flag-box',true).first();
40925 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40930 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40931 _this.list.setWidth(lw);
40934 this.list.on('mouseover', this.onViewOver, this);
40935 this.list.on('mousemove', this.onViewMove, this);
40936 this.inputEl().on("keyup", this.onKeyUp, this);
40937 this.inputEl().on("keypress", this.onKeyPress, this);
40939 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40941 this.view = new Roo.View(this.list, this.tpl, {
40942 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40945 this.view.on('click', this.onViewClick, this);
40946 this.setValue(this.defaultDialCode);
40949 onTriggerClick : function(e)
40951 Roo.log('trigger click');
40956 if(this.isExpanded()){
40958 this.hasFocus = false;
40960 this.store.load({});
40961 this.hasFocus = true;
40966 isExpanded : function()
40968 return this.list.isVisible();
40971 collapse : function()
40973 if(!this.isExpanded()){
40977 Roo.get(document).un('mousedown', this.collapseIf, this);
40978 Roo.get(document).un('mousewheel', this.collapseIf, this);
40979 this.fireEvent('collapse', this);
40983 expand : function()
40987 if(this.isExpanded() || !this.hasFocus){
40991 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40992 this.list.setWidth(lw);
40995 this.restrictHeight();
40997 Roo.get(document).on('mousedown', this.collapseIf, this);
40998 Roo.get(document).on('mousewheel', this.collapseIf, this);
41000 this.fireEvent('expand', this);
41003 restrictHeight : function()
41005 this.list.alignTo(this.inputEl(), this.listAlign);
41006 this.list.alignTo(this.inputEl(), this.listAlign);
41009 onViewOver : function(e, t)
41011 if(this.inKeyMode){
41014 var item = this.view.findItemFromChild(t);
41017 var index = this.view.indexOf(item);
41018 this.select(index, false);
41023 onViewClick : function(view, doFocus, el, e)
41025 var index = this.view.getSelectedIndexes()[0];
41027 var r = this.store.getAt(index);
41030 this.onSelect(r, index);
41032 if(doFocus !== false && !this.blockFocus){
41033 this.inputEl().focus();
41037 onViewMove : function(e, t)
41039 this.inKeyMode = false;
41042 select : function(index, scrollIntoView)
41044 this.selectedIndex = index;
41045 this.view.select(index);
41046 if(scrollIntoView !== false){
41047 var el = this.view.getNode(index);
41049 this.list.scrollChildIntoView(el, false);
41054 createList : function()
41056 this.list = Roo.get(document.body).createChild({
41058 cls: 'typeahead typeahead-long dropdown-menu tel-list',
41059 style: 'display:none'
41062 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
41065 collapseIf : function(e)
41067 var in_combo = e.within(this.el);
41068 var in_list = e.within(this.list);
41069 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
41071 if (in_combo || in_list || is_list) {
41077 onSelect : function(record, index)
41079 if(this.fireEvent('beforeselect', this, record, index) !== false){
41081 this.setFlagClass(record.data.iso2);
41082 this.setDialCode(record.data.dialCode);
41083 this.hasFocus = false;
41085 this.fireEvent('select', this, record, index);
41089 flagEl : function()
41091 var flag = this.el.select('div.flag',true).first();
41098 dialCodeHolderEl : function()
41100 var d = this.el.select('input.dial-code-holder',true).first();
41107 setDialCode : function(v)
41109 this.dialCodeHolder.dom.value = '+'+v;
41112 setFlagClass : function(n)
41114 this.flag.dom.className = 'flag '+n;
41117 getValue : function()
41119 var v = this.inputEl().getValue();
41120 if(this.dialCodeHolder) {
41121 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
41126 setValue : function(v)
41128 var d = this.getDialCode(v);
41130 //invalid dial code
41131 if(v.length == 0 || !d || d.length == 0) {
41133 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41134 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41140 this.setFlagClass(this.dialCodeMapping[d].iso2);
41141 this.setDialCode(d);
41142 this.inputEl().dom.value = v.replace('+'+d,'');
41143 this.hiddenEl().dom.value = this.getValue();
41148 getDialCode : function(v)
41152 if (v.length == 0) {
41153 return this.dialCodeHolder.dom.value;
41157 if (v.charAt(0) != "+") {
41160 var numericChars = "";
41161 for (var i = 1; i < v.length; i++) {
41162 var c = v.charAt(i);
41165 if (this.dialCodeMapping[numericChars]) {
41166 dialCode = v.substr(1, i);
41168 if (numericChars.length == 4) {
41178 this.setValue(this.defaultDialCode);
41182 hiddenEl : function()
41184 return this.el.select('input.hidden-tel-input',true).first();
41187 // after setting val
41188 onKeyUp : function(e){
41189 this.setValue(this.getValue());
41192 onKeyPress : function(e){
41193 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
41200 * @class Roo.bootstrap.MoneyField
41201 * @extends Roo.bootstrap.ComboBox
41202 * Bootstrap MoneyField class
41205 * Create a new MoneyField.
41206 * @param {Object} config Configuration options
41209 Roo.bootstrap.MoneyField = function(config) {
41211 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
41215 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
41218 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41220 allowDecimals : true,
41222 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41224 decimalSeparator : ".",
41226 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41228 decimalPrecision : 0,
41230 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41232 allowNegative : true,
41234 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41238 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41240 minValue : Number.NEGATIVE_INFINITY,
41242 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41244 maxValue : Number.MAX_VALUE,
41246 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41248 minText : "The minimum value for this field is {0}",
41250 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41252 maxText : "The maximum value for this field is {0}",
41254 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
41255 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41257 nanText : "{0} is not a valid number",
41259 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41263 * @cfg {String} defaults currency of the MoneyField
41264 * value should be in lkey
41266 defaultCurrency : false,
41268 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41270 thousandsDelimiter : false,
41272 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41283 getAutoCreate : function()
41285 var align = this.labelAlign || this.parentLabelAlign();
41297 cls : 'form-control roo-money-amount-input',
41298 autocomplete: 'new-password'
41301 var hiddenInput = {
41305 cls: 'hidden-number-input'
41308 if(this.max_length) {
41309 input.maxlength = this.max_length;
41313 hiddenInput.name = this.name;
41316 if (this.disabled) {
41317 input.disabled = true;
41320 var clg = 12 - this.inputlg;
41321 var cmd = 12 - this.inputmd;
41322 var csm = 12 - this.inputsm;
41323 var cxs = 12 - this.inputxs;
41327 cls : 'row roo-money-field',
41331 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41335 cls: 'roo-select2-container input-group',
41339 cls : 'form-control roo-money-currency-input',
41340 autocomplete: 'new-password',
41342 name : this.currencyName
41346 cls : 'input-group-addon',
41360 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41364 cls: this.hasFeedback ? 'has-feedback' : '',
41375 if (this.fieldLabel.length) {
41378 tooltip: 'This field is required'
41384 cls: 'control-label',
41390 html: this.fieldLabel
41393 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41399 if(this.indicatorpos == 'right') {
41400 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41407 if(align == 'left') {
41415 if(this.labelWidth > 12){
41416 label.style = "width: " + this.labelWidth + 'px';
41418 if(this.labelWidth < 13 && this.labelmd == 0){
41419 this.labelmd = this.labelWidth;
41421 if(this.labellg > 0){
41422 label.cls += ' col-lg-' + this.labellg;
41423 input.cls += ' col-lg-' + (12 - this.labellg);
41425 if(this.labelmd > 0){
41426 label.cls += ' col-md-' + this.labelmd;
41427 container.cls += ' col-md-' + (12 - this.labelmd);
41429 if(this.labelsm > 0){
41430 label.cls += ' col-sm-' + this.labelsm;
41431 container.cls += ' col-sm-' + (12 - this.labelsm);
41433 if(this.labelxs > 0){
41434 label.cls += ' col-xs-' + this.labelxs;
41435 container.cls += ' col-xs-' + (12 - this.labelxs);
41446 var settings = this;
41448 ['xs','sm','md','lg'].map(function(size){
41449 if (settings[size]) {
41450 cfg.cls += ' col-' + size + '-' + settings[size];
41457 initEvents : function()
41459 this.indicator = this.indicatorEl();
41461 this.initCurrencyEvent();
41463 this.initNumberEvent();
41466 initCurrencyEvent : function()
41469 throw "can not find store for combo";
41472 this.store = Roo.factory(this.store, Roo.data);
41473 this.store.parent = this;
41477 this.triggerEl = this.el.select('.input-group-addon', true).first();
41479 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41484 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41485 _this.list.setWidth(lw);
41488 this.list.on('mouseover', this.onViewOver, this);
41489 this.list.on('mousemove', this.onViewMove, this);
41490 this.list.on('scroll', this.onViewScroll, this);
41493 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41496 this.view = new Roo.View(this.list, this.tpl, {
41497 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41500 this.view.on('click', this.onViewClick, this);
41502 this.store.on('beforeload', this.onBeforeLoad, this);
41503 this.store.on('load', this.onLoad, this);
41504 this.store.on('loadexception', this.onLoadException, this);
41506 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41507 "up" : function(e){
41508 this.inKeyMode = true;
41512 "down" : function(e){
41513 if(!this.isExpanded()){
41514 this.onTriggerClick();
41516 this.inKeyMode = true;
41521 "enter" : function(e){
41524 if(this.fireEvent("specialkey", this, e)){
41525 this.onViewClick(false);
41531 "esc" : function(e){
41535 "tab" : function(e){
41538 if(this.fireEvent("specialkey", this, e)){
41539 this.onViewClick(false);
41547 doRelay : function(foo, bar, hname){
41548 if(hname == 'down' || this.scope.isExpanded()){
41549 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41557 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41561 initNumberEvent : function(e)
41563 this.inputEl().on("keydown" , this.fireKey, this);
41564 this.inputEl().on("focus", this.onFocus, this);
41565 this.inputEl().on("blur", this.onBlur, this);
41567 this.inputEl().relayEvent('keyup', this);
41569 if(this.indicator){
41570 this.indicator.addClass('invisible');
41573 this.originalValue = this.getValue();
41575 if(this.validationEvent == 'keyup'){
41576 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41577 this.inputEl().on('keyup', this.filterValidation, this);
41579 else if(this.validationEvent !== false){
41580 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41583 if(this.selectOnFocus){
41584 this.on("focus", this.preFocus, this);
41587 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41588 this.inputEl().on("keypress", this.filterKeys, this);
41590 this.inputEl().relayEvent('keypress', this);
41593 var allowed = "0123456789";
41595 if(this.allowDecimals){
41596 allowed += this.decimalSeparator;
41599 if(this.allowNegative){
41603 if(this.thousandsDelimiter) {
41607 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41609 var keyPress = function(e){
41611 var k = e.getKey();
41613 var c = e.getCharCode();
41616 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41617 allowed.indexOf(String.fromCharCode(c)) === -1
41623 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41627 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41632 this.inputEl().on("keypress", keyPress, this);
41636 onTriggerClick : function(e)
41643 this.loadNext = false;
41645 if(this.isExpanded()){
41650 this.hasFocus = true;
41652 if(this.triggerAction == 'all') {
41653 this.doQuery(this.allQuery, true);
41657 this.doQuery(this.getRawValue());
41660 getCurrency : function()
41662 var v = this.currencyEl().getValue();
41667 restrictHeight : function()
41669 this.list.alignTo(this.currencyEl(), this.listAlign);
41670 this.list.alignTo(this.currencyEl(), this.listAlign);
41673 onViewClick : function(view, doFocus, el, e)
41675 var index = this.view.getSelectedIndexes()[0];
41677 var r = this.store.getAt(index);
41680 this.onSelect(r, index);
41684 onSelect : function(record, index){
41686 if(this.fireEvent('beforeselect', this, record, index) !== false){
41688 this.setFromCurrencyData(index > -1 ? record.data : false);
41692 this.fireEvent('select', this, record, index);
41696 setFromCurrencyData : function(o)
41700 this.lastCurrency = o;
41702 if (this.currencyField) {
41703 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41705 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41708 this.lastSelectionText = currency;
41710 //setting default currency
41711 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41712 this.setCurrency(this.defaultCurrency);
41716 this.setCurrency(currency);
41719 setFromData : function(o)
41723 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41725 this.setFromCurrencyData(c);
41730 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41732 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41735 this.setValue(value);
41739 setCurrency : function(v)
41741 this.currencyValue = v;
41744 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41749 setValue : function(v)
41751 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41757 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41759 this.inputEl().dom.value = (v == '') ? '' :
41760 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41762 if(!this.allowZero && v === '0') {
41763 this.hiddenEl().dom.value = '';
41764 this.inputEl().dom.value = '';
41771 getRawValue : function()
41773 var v = this.inputEl().getValue();
41778 getValue : function()
41780 return this.fixPrecision(this.parseValue(this.getRawValue()));
41783 parseValue : function(value)
41785 if(this.thousandsDelimiter) {
41787 r = new RegExp(",", "g");
41788 value = value.replace(r, "");
41791 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41792 return isNaN(value) ? '' : value;
41796 fixPrecision : function(value)
41798 if(this.thousandsDelimiter) {
41800 r = new RegExp(",", "g");
41801 value = value.replace(r, "");
41804 var nan = isNaN(value);
41806 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41807 return nan ? '' : value;
41809 return parseFloat(value).toFixed(this.decimalPrecision);
41812 decimalPrecisionFcn : function(v)
41814 return Math.floor(v);
41817 validateValue : function(value)
41819 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41823 var num = this.parseValue(value);
41826 this.markInvalid(String.format(this.nanText, value));
41830 if(num < this.minValue){
41831 this.markInvalid(String.format(this.minText, this.minValue));
41835 if(num > this.maxValue){
41836 this.markInvalid(String.format(this.maxText, this.maxValue));
41843 validate : function()
41845 if(this.disabled || this.allowBlank){
41850 var currency = this.getCurrency();
41852 if(this.validateValue(this.getRawValue()) && currency.length){
41857 this.markInvalid();
41861 getName: function()
41866 beforeBlur : function()
41872 var v = this.parseValue(this.getRawValue());
41879 onBlur : function()
41883 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41884 //this.el.removeClass(this.focusClass);
41887 this.hasFocus = false;
41889 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41893 var v = this.getValue();
41895 if(String(v) !== String(this.startValue)){
41896 this.fireEvent('change', this, v, this.startValue);
41899 this.fireEvent("blur", this);
41902 inputEl : function()
41904 return this.el.select('.roo-money-amount-input', true).first();
41907 currencyEl : function()
41909 return this.el.select('.roo-money-currency-input', true).first();
41912 hiddenEl : function()
41914 return this.el.select('input.hidden-number-input',true).first();
41918 * @class Roo.bootstrap.BezierSignature
41919 * @extends Roo.bootstrap.Component
41920 * Bootstrap BezierSignature class
41921 * This script refer to:
41922 * Title: Signature Pad
41924 * Availability: https://github.com/szimek/signature_pad
41927 * Create a new BezierSignature
41928 * @param {Object} config The config object
41931 Roo.bootstrap.BezierSignature = function(config){
41932 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41938 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41945 mouse_btn_down: true,
41948 * @cfg {int} canvas height
41950 canvas_height: '200px',
41953 * @cfg {float|function} Radius of a single dot.
41958 * @cfg {float} Minimum width of a line. Defaults to 0.5.
41963 * @cfg {float} Maximum width of a line. Defaults to 2.5.
41968 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41973 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41978 * @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.
41980 bg_color: 'rgba(0, 0, 0, 0)',
41983 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41985 dot_color: 'black',
41988 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41990 velocity_filter_weight: 0.7,
41993 * @cfg {function} Callback when stroke begin.
41998 * @cfg {function} Callback when stroke end.
42002 getAutoCreate : function()
42004 var cls = 'roo-signature column';
42007 cls += ' ' + this.cls;
42017 for(var i = 0; i < col_sizes.length; i++) {
42018 if(this[col_sizes[i]]) {
42019 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
42029 cls: 'roo-signature-body',
42033 cls: 'roo-signature-body-canvas',
42034 height: this.canvas_height,
42035 width: this.canvas_width
42042 style: 'display: none'
42050 initEvents: function()
42052 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
42054 var canvas = this.canvasEl();
42056 // mouse && touch event swapping...
42057 canvas.dom.style.touchAction = 'none';
42058 canvas.dom.style.msTouchAction = 'none';
42060 this.mouse_btn_down = false;
42061 canvas.on('mousedown', this._handleMouseDown, this);
42062 canvas.on('mousemove', this._handleMouseMove, this);
42063 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
42065 if (window.PointerEvent) {
42066 canvas.on('pointerdown', this._handleMouseDown, this);
42067 canvas.on('pointermove', this._handleMouseMove, this);
42068 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
42071 if ('ontouchstart' in window) {
42072 canvas.on('touchstart', this._handleTouchStart, this);
42073 canvas.on('touchmove', this._handleTouchMove, this);
42074 canvas.on('touchend', this._handleTouchEnd, this);
42077 Roo.EventManager.onWindowResize(this.resize, this, true);
42079 // file input event
42080 this.fileEl().on('change', this.uploadImage, this);
42087 resize: function(){
42089 var canvas = this.canvasEl().dom;
42090 var ctx = this.canvasElCtx();
42091 var img_data = false;
42093 if(canvas.width > 0) {
42094 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
42096 // setting canvas width will clean img data
42099 var style = window.getComputedStyle ?
42100 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
42102 var padding_left = parseInt(style.paddingLeft) || 0;
42103 var padding_right = parseInt(style.paddingRight) || 0;
42105 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
42108 ctx.putImageData(img_data, 0, 0);
42112 _handleMouseDown: function(e)
42114 if (e.browserEvent.which === 1) {
42115 this.mouse_btn_down = true;
42116 this.strokeBegin(e);
42120 _handleMouseMove: function (e)
42122 if (this.mouse_btn_down) {
42123 this.strokeMoveUpdate(e);
42127 _handleMouseUp: function (e)
42129 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
42130 this.mouse_btn_down = false;
42135 _handleTouchStart: function (e) {
42137 e.preventDefault();
42138 if (e.browserEvent.targetTouches.length === 1) {
42139 // var touch = e.browserEvent.changedTouches[0];
42140 // this.strokeBegin(touch);
42142 this.strokeBegin(e); // assume e catching the correct xy...
42146 _handleTouchMove: function (e) {
42147 e.preventDefault();
42148 // var touch = event.targetTouches[0];
42149 // _this._strokeMoveUpdate(touch);
42150 this.strokeMoveUpdate(e);
42153 _handleTouchEnd: function (e) {
42154 var wasCanvasTouched = e.target === this.canvasEl().dom;
42155 if (wasCanvasTouched) {
42156 e.preventDefault();
42157 // var touch = event.changedTouches[0];
42158 // _this._strokeEnd(touch);
42163 reset: function () {
42164 this._lastPoints = [];
42165 this._lastVelocity = 0;
42166 this._lastWidth = (this.min_width + this.max_width) / 2;
42167 this.canvasElCtx().fillStyle = this.dot_color;
42170 strokeMoveUpdate: function(e)
42172 this.strokeUpdate(e);
42174 if (this.throttle) {
42175 this.throttleStroke(this.strokeUpdate, this.throttle);
42178 this.strokeUpdate(e);
42182 strokeBegin: function(e)
42184 var newPointGroup = {
42185 color: this.dot_color,
42189 if (typeof this.onBegin === 'function') {
42193 this.curve_data.push(newPointGroup);
42195 this.strokeUpdate(e);
42198 strokeUpdate: function(e)
42200 var rect = this.canvasEl().dom.getBoundingClientRect();
42201 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
42202 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
42203 var lastPoints = lastPointGroup.points;
42204 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
42205 var isLastPointTooClose = lastPoint
42206 ? point.distanceTo(lastPoint) <= this.min_distance
42208 var color = lastPointGroup.color;
42209 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
42210 var curve = this.addPoint(point);
42212 this.drawDot({color: color, point: point});
42215 this.drawCurve({color: color, curve: curve});
42225 strokeEnd: function(e)
42227 this.strokeUpdate(e);
42228 if (typeof this.onEnd === 'function') {
42233 addPoint: function (point) {
42234 var _lastPoints = this._lastPoints;
42235 _lastPoints.push(point);
42236 if (_lastPoints.length > 2) {
42237 if (_lastPoints.length === 3) {
42238 _lastPoints.unshift(_lastPoints[0]);
42240 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
42241 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
42242 _lastPoints.shift();
42248 calculateCurveWidths: function (startPoint, endPoint) {
42249 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42250 (1 - this.velocity_filter_weight) * this._lastVelocity;
42252 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42255 start: this._lastWidth
42258 this._lastVelocity = velocity;
42259 this._lastWidth = newWidth;
42263 drawDot: function (_a) {
42264 var color = _a.color, point = _a.point;
42265 var ctx = this.canvasElCtx();
42266 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42268 this.drawCurveSegment(point.x, point.y, width);
42270 ctx.fillStyle = color;
42274 drawCurve: function (_a) {
42275 var color = _a.color, curve = _a.curve;
42276 var ctx = this.canvasElCtx();
42277 var widthDelta = curve.endWidth - curve.startWidth;
42278 var drawSteps = Math.floor(curve.length()) * 2;
42280 ctx.fillStyle = color;
42281 for (var i = 0; i < drawSteps; i += 1) {
42282 var t = i / drawSteps;
42288 var x = uuu * curve.startPoint.x;
42289 x += 3 * uu * t * curve.control1.x;
42290 x += 3 * u * tt * curve.control2.x;
42291 x += ttt * curve.endPoint.x;
42292 var y = uuu * curve.startPoint.y;
42293 y += 3 * uu * t * curve.control1.y;
42294 y += 3 * u * tt * curve.control2.y;
42295 y += ttt * curve.endPoint.y;
42296 var width = curve.startWidth + ttt * widthDelta;
42297 this.drawCurveSegment(x, y, width);
42303 drawCurveSegment: function (x, y, width) {
42304 var ctx = this.canvasElCtx();
42306 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42307 this.is_empty = false;
42312 var ctx = this.canvasElCtx();
42313 var canvas = this.canvasEl().dom;
42314 ctx.fillStyle = this.bg_color;
42315 ctx.clearRect(0, 0, canvas.width, canvas.height);
42316 ctx.fillRect(0, 0, canvas.width, canvas.height);
42317 this.curve_data = [];
42319 this.is_empty = true;
42324 return this.el.select('input',true).first();
42327 canvasEl: function()
42329 return this.el.select('canvas',true).first();
42332 canvasElCtx: function()
42334 return this.el.select('canvas',true).first().dom.getContext('2d');
42337 getImage: function(type)
42339 if(this.is_empty) {
42344 return this.canvasEl().dom.toDataURL('image/'+type, 1);
42347 drawFromImage: function(img_src)
42349 var img = new Image();
42351 img.onload = function(){
42352 this.canvasElCtx().drawImage(img, 0, 0);
42357 this.is_empty = false;
42360 selectImage: function()
42362 this.fileEl().dom.click();
42365 uploadImage: function(e)
42367 var reader = new FileReader();
42369 reader.onload = function(e){
42370 var img = new Image();
42371 img.onload = function(){
42373 this.canvasElCtx().drawImage(img, 0, 0);
42375 img.src = e.target.result;
42378 reader.readAsDataURL(e.target.files[0]);
42381 // Bezier Point Constructor
42382 Point: (function () {
42383 function Point(x, y, time) {
42386 this.time = time || Date.now();
42388 Point.prototype.distanceTo = function (start) {
42389 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42391 Point.prototype.equals = function (other) {
42392 return this.x === other.x && this.y === other.y && this.time === other.time;
42394 Point.prototype.velocityFrom = function (start) {
42395 return this.time !== start.time
42396 ? this.distanceTo(start) / (this.time - start.time)
42403 // Bezier Constructor
42404 Bezier: (function () {
42405 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42406 this.startPoint = startPoint;
42407 this.control2 = control2;
42408 this.control1 = control1;
42409 this.endPoint = endPoint;
42410 this.startWidth = startWidth;
42411 this.endWidth = endWidth;
42413 Bezier.fromPoints = function (points, widths, scope) {
42414 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42415 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42416 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42418 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42419 var dx1 = s1.x - s2.x;
42420 var dy1 = s1.y - s2.y;
42421 var dx2 = s2.x - s3.x;
42422 var dy2 = s2.y - s3.y;
42423 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42424 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42425 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42426 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42427 var dxm = m1.x - m2.x;
42428 var dym = m1.y - m2.y;
42429 var k = l2 / (l1 + l2);
42430 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42431 var tx = s2.x - cm.x;
42432 var ty = s2.y - cm.y;
42434 c1: new scope.Point(m1.x + tx, m1.y + ty),
42435 c2: new scope.Point(m2.x + tx, m2.y + ty)
42438 Bezier.prototype.length = function () {
42443 for (var i = 0; i <= steps; i += 1) {
42445 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42446 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42448 var xdiff = cx - px;
42449 var ydiff = cy - py;
42450 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42457 Bezier.prototype.point = function (t, start, c1, c2, end) {
42458 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42459 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42460 + (3.0 * c2 * (1.0 - t) * t * t)
42461 + (end * t * t * t);
42466 throttleStroke: function(fn, wait) {
42467 if (wait === void 0) { wait = 250; }
42469 var timeout = null;
42473 var later = function () {
42474 previous = Date.now();
42476 result = fn.apply(storedContext, storedArgs);
42478 storedContext = null;
42482 return function wrapper() {
42484 for (var _i = 0; _i < arguments.length; _i++) {
42485 args[_i] = arguments[_i];
42487 var now = Date.now();
42488 var remaining = wait - (now - previous);
42489 storedContext = this;
42491 if (remaining <= 0 || remaining > wait) {
42493 clearTimeout(timeout);
42497 result = fn.apply(storedContext, storedArgs);
42499 storedContext = null;
42503 else if (!timeout) {
42504 timeout = window.setTimeout(later, remaining);