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.Element
447 * @extends Roo.bootstrap.Component
448 * Bootstrap Element class
449 * @cfg {String} html contents of the element
450 * @cfg {String} tag tag of the element
451 * @cfg {String} cls class of the element
452 * @cfg {Boolean} preventDefault (true|false) default false
453 * @cfg {Boolean} clickable (true|false) default false
456 * Create a new Element
457 * @param {Object} config The config object
460 Roo.bootstrap.Element = function(config){
461 Roo.bootstrap.Element.superclass.constructor.call(this, config);
467 * When a element is chick
468 * @param {Roo.bootstrap.Element} this
469 * @param {Roo.EventObject} e
475 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
480 preventDefault: false,
483 getAutoCreate : function(){
487 // cls: this.cls, double assign in parent class Component.js :: onRender
494 initEvents: function()
496 Roo.bootstrap.Element.superclass.initEvents.call(this);
499 this.el.on('click', this.onClick, this);
504 onClick : function(e)
506 if(this.preventDefault){
510 this.fireEvent('click', this, e);
513 getValue : function()
515 return this.el.dom.innerHTML;
518 setValue : function(value)
520 this.el.dom.innerHTML = value;
535 * @class Roo.bootstrap.Body
536 * @extends Roo.bootstrap.Component
537 * Bootstrap Body class
541 * @param {Object} config The config object
544 Roo.bootstrap.Body = function(config){
546 config = config || {};
548 Roo.bootstrap.Body.superclass.constructor.call(this, config);
549 this.el = Roo.get(config.el ? config.el : document.body );
550 if (this.cls && this.cls.length) {
551 Roo.get(document.body).addClass(this.cls);
555 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
557 is_body : true,// just to make sure it's constructed?
562 onRender : function(ct, position)
564 /* Roo.log("Roo.bootstrap.Body - onRender");
565 if (this.cls && this.cls.length) {
566 Roo.get(document.body).addClass(this.cls);
585 * @class Roo.bootstrap.ButtonGroup
586 * @extends Roo.bootstrap.Component
587 * Bootstrap ButtonGroup class
588 * @cfg {String} size lg | sm | xs (default empty normal)
589 * @cfg {String} align vertical | justified (default none)
590 * @cfg {String} direction up | down (default down)
591 * @cfg {Boolean} toolbar false | true
592 * @cfg {Boolean} btn true | false
597 * @param {Object} config The config object
600 Roo.bootstrap.ButtonGroup = function(config){
601 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
604 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
612 getAutoCreate : function(){
618 cfg.html = this.html || cfg.html;
629 if (['vertical','justified'].indexOf(this.align)!==-1) {
630 cfg.cls = 'btn-group-' + this.align;
632 if (this.align == 'justified') {
633 console.log(this.items);
637 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
638 cfg.cls += ' btn-group-' + this.size;
641 if (this.direction == 'up') {
642 cfg.cls += ' dropup' ;
648 * Add a button to the group (similar to NavItem API.)
650 addItem : function(cfg)
652 var cn = new Roo.bootstrap.Button(cfg);
654 cn.parentId = this.id;
655 cn.onRender(this.el, null);
669 * @class Roo.bootstrap.Button
670 * @extends Roo.bootstrap.Component
671 * Bootstrap Button class
672 * @cfg {String} html The button content
673 * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
674 * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
675 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
676 * @cfg {String} size ( lg | sm | xs)
677 * @cfg {String} tag ( a | input | submit)
678 * @cfg {String} href empty or href
679 * @cfg {Boolean} disabled default false;
680 * @cfg {Boolean} isClose default false;
681 * @cfg {String} glyphicon depricated - use fa
682 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
683 * @cfg {String} badge text for badge
684 * @cfg {String} theme (default|glow)
685 * @cfg {Boolean} inverse dark themed version
686 * @cfg {Boolean} toggle is it a slidy toggle button
687 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
688 * @cfg {String} ontext text for on slidy toggle state
689 * @cfg {String} offtext text for off slidy toggle state
690 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
691 * @cfg {Boolean} removeClass remove the standard class..
692 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
695 * Create a new button
696 * @param {Object} config The config object
700 Roo.bootstrap.Button = function(config){
701 Roo.bootstrap.Button.superclass.constructor.call(this, config);
702 this.weightClass = ["btn-default btn-outline-secondary",
714 * When a butotn is pressed
715 * @param {Roo.bootstrap.Button} btn
716 * @param {Roo.EventObject} e
721 * After the button has been toggles
722 * @param {Roo.bootstrap.Button} btn
723 * @param {Roo.EventObject} e
724 * @param {boolean} pressed (also available as button.pressed)
730 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
751 preventDefault: true,
759 getAutoCreate : function(){
767 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
768 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
773 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
775 if (this.toggle == true) {
778 cls: 'slider-frame roo-button',
783 'data-off-text':'OFF',
784 cls: 'slider-button',
790 if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
791 cfg.cls += ' '+this.weight;
800 cfg["aria-hidden"] = true;
802 cfg.html = "×";
808 if (this.theme==='default') {
809 cfg.cls = 'btn roo-button';
811 //if (this.parentType != 'Navbar') {
812 this.weight = this.weight.length ? this.weight : 'default';
814 if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
816 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
817 var weight = this.weight == 'default' ? 'secondary' : this.weight;
818 cfg.cls += ' btn-' + outline + weight;
819 if (this.weight == 'default') {
821 cfg.cls += ' btn-' + this.weight;
824 } else if (this.theme==='glow') {
827 cfg.cls = 'btn-glow roo-button';
829 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
831 cfg.cls += ' ' + this.weight;
837 this.cls += ' inverse';
841 if (this.active || this.pressed === true) {
842 cfg.cls += ' active';
846 cfg.disabled = 'disabled';
850 Roo.log('changing to ul' );
852 this.glyphicon = 'caret';
853 if (Roo.bootstrap.version == 4) {
854 this.fa = 'caret-down';
859 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
861 //gsRoo.log(this.parentType);
862 if (this.parentType === 'Navbar' && !this.parent().bar) {
863 Roo.log('changing to li?');
872 href : this.href || '#'
875 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
876 cfg.cls += ' dropdown';
883 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
885 if (this.glyphicon) {
886 cfg.html = ' ' + cfg.html;
891 cls: 'glyphicon glyphicon-' + this.glyphicon
896 cfg.html = ' ' + cfg.html;
901 cls: 'fa fas fa-' + this.fa
911 // cfg.cls='btn roo-button';
915 var value = cfg.html;
920 cls: 'glyphicon glyphicon-' + this.glyphicon,
927 cls: 'fa fas fa-' + this.fa,
932 var bw = this.badge_weight.length ? this.badge_weight :
933 (this.weight.length ? this.weight : 'secondary');
934 bw = bw == 'default' ? 'secondary' : bw;
940 cls: 'badge badge-' + bw,
949 cfg.cls += ' dropdown';
950 cfg.html = typeof(cfg.html) != 'undefined' ?
951 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
954 if (cfg.tag !== 'a' && this.href !== '') {
955 throw "Tag must be a to set href.";
956 } else if (this.href.length > 0) {
957 cfg.href = this.href;
960 if(this.removeClass){
965 cfg.target = this.target;
970 initEvents: function() {
971 // Roo.log('init events?');
972 // Roo.log(this.el.dom);
975 if (typeof (this.menu) != 'undefined') {
976 this.menu.parentType = this.xtype;
977 this.menu.triggerEl = this.el;
978 this.addxtype(Roo.apply({}, this.menu));
982 if (this.el.hasClass('roo-button')) {
983 this.el.on('click', this.onClick, this);
985 this.el.select('.roo-button').on('click', this.onClick, this);
988 if(this.removeClass){
989 this.el.on('click', this.onClick, this);
992 this.el.enableDisplayMode();
995 onClick : function(e)
1001 Roo.log('button on click ');
1002 if(this.preventDefault){
1006 if (this.pressed === true || this.pressed === false) {
1007 this.toggleActive(e);
1011 this.fireEvent('click', this, e);
1015 * Enables this button
1019 this.disabled = false;
1020 this.el.removeClass('disabled');
1024 * Disable this button
1026 disable : function()
1028 this.disabled = true;
1029 this.el.addClass('disabled');
1032 * sets the active state on/off,
1033 * @param {Boolean} state (optional) Force a particular state
1035 setActive : function(v) {
1037 this.el[v ? 'addClass' : 'removeClass']('active');
1041 * toggles the current active state
1043 toggleActive : function(e)
1045 this.setActive(!this.pressed);
1046 this.fireEvent('toggle', this, e, !this.pressed);
1049 * get the current active state
1050 * @return {boolean} true if it's active
1052 isActive : function()
1054 return this.el.hasClass('active');
1057 * set the text of the first selected button
1059 setText : function(str)
1061 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1064 * get the text of the first selected button
1066 getText : function()
1068 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1071 setWeight : function(str)
1073 this.el.removeClass(this.weightClass);
1075 var outline = this.outline ? 'outline-' : '';
1076 if (str == 'default') {
1077 this.el.addClass('btn-default btn-outline-secondary');
1080 this.el.addClass('btn-' + outline + str);
1094 * @class Roo.bootstrap.Column
1095 * @extends Roo.bootstrap.Component
1096 * Bootstrap Column class
1097 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1098 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1099 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1100 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1101 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1102 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1103 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1104 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1107 * @cfg {Boolean} hidden (true|false) hide the element
1108 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1109 * @cfg {String} fa (ban|check|...) font awesome icon
1110 * @cfg {Number} fasize (1|2|....) font awsome size
1112 * @cfg {String} icon (info-sign|check|...) glyphicon name
1114 * @cfg {String} html content of column.
1117 * Create a new Column
1118 * @param {Object} config The config object
1121 Roo.bootstrap.Column = function(config){
1122 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1125 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1143 getAutoCreate : function(){
1144 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1152 var sizes = ['xs','sm','md','lg'];
1153 sizes.map(function(size ,ix){
1154 //Roo.log( size + ':' + settings[size]);
1156 if (settings[size+'off'] !== false) {
1157 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1160 if (settings[size] === false) {
1164 if (!settings[size]) { // 0 = hidden
1165 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1167 for (var i = ix; i > -1; i--) {
1168 cfg.cls += ' d-' + sizes[i] + '-none';
1174 cfg.cls += ' col-' + size + '-' + settings[size] + (
1175 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1181 cfg.cls += ' hidden';
1184 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1185 cfg.cls +=' alert alert-' + this.alert;
1189 if (this.html.length) {
1190 cfg.html = this.html;
1194 if (this.fasize > 1) {
1195 fasize = ' fa-' + this.fasize + 'x';
1197 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1202 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1221 * @class Roo.bootstrap.Container
1222 * @extends Roo.bootstrap.Component
1223 * Bootstrap Container class
1224 * @cfg {Boolean} jumbotron is it a jumbotron element
1225 * @cfg {String} html content of element
1226 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1227 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1228 * @cfg {String} header content of header (for panel)
1229 * @cfg {String} footer content of footer (for panel)
1230 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1231 * @cfg {String} tag (header|aside|section) type of HTML tag.
1232 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1233 * @cfg {String} fa font awesome icon
1234 * @cfg {String} icon (info-sign|check|...) glyphicon name
1235 * @cfg {Boolean} hidden (true|false) hide the element
1236 * @cfg {Boolean} expandable (true|false) default false
1237 * @cfg {Boolean} expanded (true|false) default true
1238 * @cfg {String} rheader contet on the right of header
1239 * @cfg {Boolean} clickable (true|false) default false
1243 * Create a new Container
1244 * @param {Object} config The config object
1247 Roo.bootstrap.Container = function(config){
1248 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1254 * After the panel has been expand
1256 * @param {Roo.bootstrap.Container} this
1261 * After the panel has been collapsed
1263 * @param {Roo.bootstrap.Container} this
1268 * When a element is chick
1269 * @param {Roo.bootstrap.Container} this
1270 * @param {Roo.EventObject} e
1276 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1294 getChildContainer : function() {
1300 if (this.panel.length) {
1301 return this.el.select('.panel-body',true).first();
1308 getAutoCreate : function(){
1311 tag : this.tag || 'div',
1315 if (this.jumbotron) {
1316 cfg.cls = 'jumbotron';
1321 // - this is applied by the parent..
1323 // cfg.cls = this.cls + '';
1326 if (this.sticky.length) {
1328 var bd = Roo.get(document.body);
1329 if (!bd.hasClass('bootstrap-sticky')) {
1330 bd.addClass('bootstrap-sticky');
1331 Roo.select('html',true).setStyle('height', '100%');
1334 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1338 if (this.well.length) {
1339 switch (this.well) {
1342 cfg.cls +=' well well-' +this.well;
1351 cfg.cls += ' hidden';
1355 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1356 cfg.cls +=' alert alert-' + this.alert;
1361 if (this.panel.length) {
1362 cfg.cls += ' panel panel-' + this.panel;
1364 if (this.header.length) {
1368 if(this.expandable){
1370 cfg.cls = cfg.cls + ' expandable';
1374 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1382 cls : 'panel-title',
1383 html : (this.expandable ? ' ' : '') + this.header
1387 cls: 'panel-header-right',
1393 cls : 'panel-heading',
1394 style : this.expandable ? 'cursor: pointer' : '',
1402 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1407 if (this.footer.length) {
1409 cls : 'panel-footer',
1418 body.html = this.html || cfg.html;
1419 // prefix with the icons..
1421 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1424 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1429 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1430 cfg.cls = 'container';
1436 initEvents: function()
1438 if(this.expandable){
1439 var headerEl = this.headerEl();
1442 headerEl.on('click', this.onToggleClick, this);
1447 this.el.on('click', this.onClick, this);
1452 onToggleClick : function()
1454 var headerEl = this.headerEl();
1470 if(this.fireEvent('expand', this)) {
1472 this.expanded = true;
1474 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1476 this.el.select('.panel-body',true).first().removeClass('hide');
1478 var toggleEl = this.toggleEl();
1484 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1489 collapse : function()
1491 if(this.fireEvent('collapse', this)) {
1493 this.expanded = false;
1495 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1496 this.el.select('.panel-body',true).first().addClass('hide');
1498 var toggleEl = this.toggleEl();
1504 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1508 toggleEl : function()
1510 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1514 return this.el.select('.panel-heading .fa',true).first();
1517 headerEl : function()
1519 if(!this.el || !this.panel.length || !this.header.length){
1523 return this.el.select('.panel-heading',true).first()
1528 if(!this.el || !this.panel.length){
1532 return this.el.select('.panel-body',true).first()
1535 titleEl : function()
1537 if(!this.el || !this.panel.length || !this.header.length){
1541 return this.el.select('.panel-title',true).first();
1544 setTitle : function(v)
1546 var titleEl = this.titleEl();
1552 titleEl.dom.innerHTML = v;
1555 getTitle : function()
1558 var titleEl = this.titleEl();
1564 return titleEl.dom.innerHTML;
1567 setRightTitle : function(v)
1569 var t = this.el.select('.panel-header-right',true).first();
1575 t.dom.innerHTML = v;
1578 onClick : function(e)
1582 this.fireEvent('click', this, e);
1589 * This is BS4's Card element.. - similar to our containers probably..
1593 * @class Roo.bootstrap.Card
1594 * @extends Roo.bootstrap.Component
1595 * Bootstrap Card class
1598 * possible... may not be implemented..
1599 * @cfg {String} header_image src url of image.
1600 * @cfg {String|Object} header
1601 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1603 * @cfg {String} title
1604 * @cfg {String} subtitle
1605 * @cfg {String} html -- html contents - or just use children..
1606 * @cfg {String} footer
1608 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1610 * @cfg {String} margin (0|1|2|3|4|5|auto)
1611 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1612 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1613 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1614 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1615 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1616 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1618 * @cfg {String} padding (0|1|2|3|4|5)
1619 * @cfg {String} padding_top (0|1|2|3|4|5)
1620 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1621 * @cfg {String} padding_left (0|1|2|3|4|5)
1622 * @cfg {String} padding_right (0|1|2|3|4|5)
1623 * @cfg {String} padding_x (0|1|2|3|4|5)
1624 * @cfg {String} padding_y (0|1|2|3|4|5)
1626 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1627 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1628 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1629 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1630 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1632 * @config {Boolean} dragable if this card can be dragged.
1633 * @config {Boolean} drag_group group for drag
1637 * Create a new Container
1638 * @param {Object} config The config object
1641 Roo.bootstrap.Card = function(config){
1642 Roo.bootstrap.Card.superclass.constructor.call(this, config);
1650 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
1655 margin: '', /// may be better in component?
1688 childContainer : false,
1690 layoutCls : function()
1694 Roo.log(this.margin_bottom.length);
1695 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
1696 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
1698 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
1699 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
1701 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
1702 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
1706 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
1707 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
1708 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
1712 // more generic support?
1720 // Roo.log("Call onRender: " + this.xtype);
1721 /* We are looking at something like this.
1723 <img src="..." class="card-img-top" alt="...">
1724 <div class="card-body">
1725 <h5 class="card-title">Card title</h5>
1726 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
1728 >> this bit is really the body...
1729 <div> << we will ad dthis in hopefully it will not break shit.
1731 ** card text does not actually have any styling...
1733 <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>
1736 <a href="#" class="card-link">Card link</a>
1739 <div class="card-footer">
1740 <small class="text-muted">Last updated 3 mins ago</small>
1744 getAutoCreate : function(){
1752 if (this.weight.length && this.weight != 'light') {
1753 cfg.cls += ' text-white';
1755 cfg.cls += ' text-dark'; // need as it's nested..
1757 if (this.weight.length) {
1758 cfg.cls += ' bg-' + this.weight;
1761 cfg.cls += this.layoutCls();
1763 if (this.header.length) {
1765 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
1766 cls : 'card-header',
1767 html : this.header // escape?
1772 cls : 'card-header d-none'
1775 if (this.header_image.length) {
1778 cls : 'card-img-top',
1779 src: this.header_image // escape?
1784 cls : 'card-img-top d-none'
1795 if (this.title.length) {
1799 src: this.title // escape?
1803 if (this.subtitle.length) {
1807 src: this.subtitle // escape?
1813 cls : 'roo-card-body-ctr'
1816 if (this.html.length) {
1822 // fixme ? handle objects?
1823 if (this.footer.length) {
1826 cls : 'card-footer',
1827 html: this.footer // escape?
1836 getCardHeader : function()
1838 var ret = this.el.select('.card-header',true).first();
1839 if (ret.hasClass('d-none')) {
1840 ret.removeClass('d-none');
1846 getCardImageTop : function()
1848 var ret = this.el.select('.card-img-top',true).first();
1849 if (ret.hasClass('d-none')) {
1850 ret.removeClass('d-none');
1856 getChildContainer : function()
1862 return this.el.select('.roo-card-body-ctr',true).first();
1865 initEvents: function()
1868 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
1869 containerScroll: true,
1870 ddGroup: this.drag_group || 'default_card_drag_group'
1872 this.dragZone.getDragData = this.getDragData.createDelegate(this);
1878 getDragData : function(e) {
1879 var target = this.getEl();
1881 //this.handleSelection(e);
1886 nodes: this.getEl(),
1891 dragData.ddel = target.dom ; // the div element
1892 Roo.log(target.getWidth( ));
1893 dragData.ddel.style.width = target.getWidth() + 'px';
1905 * Card header - holder for the card header elements.
1910 * @class Roo.bootstrap.CardHeader
1911 * @extends Roo.bootstrap.Element
1912 * Bootstrap CardHeader class
1914 * Create a new Card Header - that you can embed children into
1915 * @param {Object} config The config object
1918 Roo.bootstrap.CardHeader = function(config){
1919 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
1922 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
1925 container_method : 'getCardHeader'
1938 * Card header - holder for the card header elements.
1943 * @class Roo.bootstrap.CardImageTop
1944 * @extends Roo.bootstrap.Element
1945 * Bootstrap CardImageTop class
1947 * Create a new Card Image Top container
1948 * @param {Object} config The config object
1951 Roo.bootstrap.CardImageTop = function(config){
1952 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
1955 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
1958 container_method : 'getCardImageTop'
1976 * @class Roo.bootstrap.Img
1977 * @extends Roo.bootstrap.Component
1978 * Bootstrap Img class
1979 * @cfg {Boolean} imgResponsive false | true
1980 * @cfg {String} border rounded | circle | thumbnail
1981 * @cfg {String} src image source
1982 * @cfg {String} alt image alternative text
1983 * @cfg {String} href a tag href
1984 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1985 * @cfg {String} xsUrl xs image source
1986 * @cfg {String} smUrl sm image source
1987 * @cfg {String} mdUrl md image source
1988 * @cfg {String} lgUrl lg image source
1991 * Create a new Input
1992 * @param {Object} config The config object
1995 Roo.bootstrap.Img = function(config){
1996 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2002 * The img click event for the img.
2003 * @param {Roo.EventObject} e
2009 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2011 imgResponsive: true,
2021 getAutoCreate : function()
2023 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2024 return this.createSingleImg();
2029 cls: 'roo-image-responsive-group',
2034 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2036 if(!_this[size + 'Url']){
2042 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2043 html: _this.html || cfg.html,
2044 src: _this[size + 'Url']
2047 img.cls += ' roo-image-responsive-' + size;
2049 var s = ['xs', 'sm', 'md', 'lg'];
2051 s.splice(s.indexOf(size), 1);
2053 Roo.each(s, function(ss){
2054 img.cls += ' hidden-' + ss;
2057 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2058 cfg.cls += ' img-' + _this.border;
2062 cfg.alt = _this.alt;
2075 a.target = _this.target;
2079 cfg.cn.push((_this.href) ? a : img);
2086 createSingleImg : function()
2090 cls: (this.imgResponsive) ? 'img-responsive' : '',
2092 src : 'about:blank' // just incase src get's set to undefined?!?
2095 cfg.html = this.html || cfg.html;
2097 cfg.src = this.src || cfg.src;
2099 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2100 cfg.cls += ' img-' + this.border;
2117 a.target = this.target;
2122 return (this.href) ? a : cfg;
2125 initEvents: function()
2128 this.el.on('click', this.onClick, this);
2133 onClick : function(e)
2135 Roo.log('img onclick');
2136 this.fireEvent('click', this, e);
2139 * Sets the url of the image - used to update it
2140 * @param {String} url the url of the image
2143 setSrc : function(url)
2147 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2148 this.el.dom.src = url;
2152 this.el.select('img', true).first().dom.src = url;
2168 * @class Roo.bootstrap.Link
2169 * @extends Roo.bootstrap.Component
2170 * Bootstrap Link Class
2171 * @cfg {String} alt image alternative text
2172 * @cfg {String} href a tag href
2173 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2174 * @cfg {String} html the content of the link.
2175 * @cfg {String} anchor name for the anchor link
2176 * @cfg {String} fa - favicon
2178 * @cfg {Boolean} preventDefault (true | false) default false
2182 * Create a new Input
2183 * @param {Object} config The config object
2186 Roo.bootstrap.Link = function(config){
2187 Roo.bootstrap.Link.superclass.constructor.call(this, config);
2193 * The img click event for the img.
2194 * @param {Roo.EventObject} e
2200 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
2204 preventDefault: false,
2210 getAutoCreate : function()
2212 var html = this.html || '';
2214 if (this.fa !== false) {
2215 html = '<i class="fa fa-' + this.fa + '"></i>';
2220 // anchor's do not require html/href...
2221 if (this.anchor === false) {
2223 cfg.href = this.href || '#';
2225 cfg.name = this.anchor;
2226 if (this.html !== false || this.fa !== false) {
2229 if (this.href !== false) {
2230 cfg.href = this.href;
2234 if(this.alt !== false){
2239 if(this.target !== false) {
2240 cfg.target = this.target;
2246 initEvents: function() {
2248 if(!this.href || this.preventDefault){
2249 this.el.on('click', this.onClick, this);
2253 onClick : function(e)
2255 if(this.preventDefault){
2258 //Roo.log('img onclick');
2259 this.fireEvent('click', this, e);
2272 * @class Roo.bootstrap.Header
2273 * @extends Roo.bootstrap.Component
2274 * Bootstrap Header class
2275 * @cfg {String} html content of header
2276 * @cfg {Number} level (1|2|3|4|5|6) default 1
2279 * Create a new Header
2280 * @param {Object} config The config object
2284 Roo.bootstrap.Header = function(config){
2285 Roo.bootstrap.Header.superclass.constructor.call(this, config);
2288 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
2296 getAutoCreate : function(){
2301 tag: 'h' + (1 *this.level),
2302 html: this.html || ''
2314 * Ext JS Library 1.1.1
2315 * Copyright(c) 2006-2007, Ext JS, LLC.
2317 * Originally Released Under LGPL - original licence link has changed is not relivant.
2320 * <script type="text/javascript">
2324 * @class Roo.bootstrap.MenuMgr
2325 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
2328 Roo.bootstrap.MenuMgr = function(){
2329 var menus, active, groups = {}, attached = false, lastShow = new Date();
2331 // private - called when first menu is created
2334 active = new Roo.util.MixedCollection();
2335 Roo.get(document).addKeyListener(27, function(){
2336 if(active.length > 0){
2344 if(active && active.length > 0){
2345 var c = active.clone();
2355 if(active.length < 1){
2356 Roo.get(document).un("mouseup", onMouseDown);
2364 var last = active.last();
2365 lastShow = new Date();
2368 Roo.get(document).on("mouseup", onMouseDown);
2373 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
2374 m.parentMenu.activeChild = m;
2375 }else if(last && last.isVisible()){
2376 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
2381 function onBeforeHide(m){
2383 m.activeChild.hide();
2385 if(m.autoHideTimer){
2386 clearTimeout(m.autoHideTimer);
2387 delete m.autoHideTimer;
2392 function onBeforeShow(m){
2393 var pm = m.parentMenu;
2394 if(!pm && !m.allowOtherMenus){
2396 }else if(pm && pm.activeChild && active != m){
2397 pm.activeChild.hide();
2401 // private this should really trigger on mouseup..
2402 function onMouseDown(e){
2403 Roo.log("on Mouse Up");
2405 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
2406 Roo.log("MenuManager hideAll");
2415 function onBeforeCheck(mi, state){
2417 var g = groups[mi.group];
2418 for(var i = 0, l = g.length; i < l; i++){
2420 g[i].setChecked(false);
2429 * Hides all menus that are currently visible
2431 hideAll : function(){
2436 register : function(menu){
2440 menus[menu.id] = menu;
2441 menu.on("beforehide", onBeforeHide);
2442 menu.on("hide", onHide);
2443 menu.on("beforeshow", onBeforeShow);
2444 menu.on("show", onShow);
2446 if(g && menu.events["checkchange"]){
2450 groups[g].push(menu);
2451 menu.on("checkchange", onCheck);
2456 * Returns a {@link Roo.menu.Menu} object
2457 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
2458 * be used to generate and return a new Menu instance.
2460 get : function(menu){
2461 if(typeof menu == "string"){ // menu id
2463 }else if(menu.events){ // menu instance
2466 /*else if(typeof menu.length == 'number'){ // array of menu items?
2467 return new Roo.bootstrap.Menu({items:menu});
2468 }else{ // otherwise, must be a config
2469 return new Roo.bootstrap.Menu(menu);
2476 unregister : function(menu){
2477 delete menus[menu.id];
2478 menu.un("beforehide", onBeforeHide);
2479 menu.un("hide", onHide);
2480 menu.un("beforeshow", onBeforeShow);
2481 menu.un("show", onShow);
2483 if(g && menu.events["checkchange"]){
2484 groups[g].remove(menu);
2485 menu.un("checkchange", onCheck);
2490 registerCheckable : function(menuItem){
2491 var g = menuItem.group;
2496 groups[g].push(menuItem);
2497 menuItem.on("beforecheckchange", onBeforeCheck);
2502 unregisterCheckable : function(menuItem){
2503 var g = menuItem.group;
2505 groups[g].remove(menuItem);
2506 menuItem.un("beforecheckchange", onBeforeCheck);
2518 * @class Roo.bootstrap.Menu
2519 * @extends Roo.bootstrap.Component
2520 * Bootstrap Menu class - container for MenuItems
2521 * @cfg {String} type (dropdown|treeview|submenu) type of menu
2522 * @cfg {bool} hidden if the menu should be hidden when rendered.
2523 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
2524 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
2528 * @param {Object} config The config object
2532 Roo.bootstrap.Menu = function(config){
2533 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2534 if (this.registerMenu && this.type != 'treeview') {
2535 Roo.bootstrap.MenuMgr.register(this);
2542 * Fires before this menu is displayed (return false to block)
2543 * @param {Roo.menu.Menu} this
2548 * Fires before this menu is hidden (return false to block)
2549 * @param {Roo.menu.Menu} this
2554 * Fires after this menu is displayed
2555 * @param {Roo.menu.Menu} this
2560 * Fires after this menu is hidden
2561 * @param {Roo.menu.Menu} this
2566 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2567 * @param {Roo.menu.Menu} this
2568 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2569 * @param {Roo.EventObject} e
2574 * Fires when the mouse is hovering over this menu
2575 * @param {Roo.menu.Menu} this
2576 * @param {Roo.EventObject} e
2577 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2582 * Fires when the mouse exits this menu
2583 * @param {Roo.menu.Menu} this
2584 * @param {Roo.EventObject} e
2585 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2590 * Fires when a menu item contained in this menu is clicked
2591 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2592 * @param {Roo.EventObject} e
2596 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2599 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2603 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2606 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2608 registerMenu : true,
2610 menuItems :false, // stores the menu items..
2620 getChildContainer : function() {
2624 getAutoCreate : function(){
2626 //if (['right'].indexOf(this.align)!==-1) {
2627 // cfg.cn[1].cls += ' pull-right'
2633 cls : 'dropdown-menu' ,
2634 style : 'z-index:1000'
2638 if (this.type === 'submenu') {
2639 cfg.cls = 'submenu active';
2641 if (this.type === 'treeview') {
2642 cfg.cls = 'treeview-menu';
2647 initEvents : function() {
2649 // Roo.log("ADD event");
2650 // Roo.log(this.triggerEl.dom);
2652 this.triggerEl.on('click', this.onTriggerClick, this);
2654 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2657 if (this.triggerEl.hasClass('nav-item')) {
2658 // dropdown toggle on the 'a' in BS4?
2659 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2661 this.triggerEl.addClass('dropdown-toggle');
2664 this.el.on('touchstart' , this.onTouch, this);
2666 this.el.on('click' , this.onClick, this);
2668 this.el.on("mouseover", this.onMouseOver, this);
2669 this.el.on("mouseout", this.onMouseOut, this);
2673 findTargetItem : function(e)
2675 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2679 //Roo.log(t); Roo.log(t.id);
2681 //Roo.log(this.menuitems);
2682 return this.menuitems.get(t.id);
2684 //return this.items.get(t.menuItemId);
2690 onTouch : function(e)
2692 Roo.log("menu.onTouch");
2693 //e.stopEvent(); this make the user popdown broken
2697 onClick : function(e)
2699 Roo.log("menu.onClick");
2701 var t = this.findTargetItem(e);
2702 if(!t || t.isContainer){
2707 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2708 if(t == this.activeItem && t.shouldDeactivate(e)){
2709 this.activeItem.deactivate();
2710 delete this.activeItem;
2714 this.setActiveItem(t, true);
2722 Roo.log('pass click event');
2726 this.fireEvent("click", this, t, e);
2730 if(!t.href.length || t.href == '#'){
2731 (function() { _this.hide(); }).defer(100);
2736 onMouseOver : function(e){
2737 var t = this.findTargetItem(e);
2740 // if(t.canActivate && !t.disabled){
2741 // this.setActiveItem(t, true);
2745 this.fireEvent("mouseover", this, e, t);
2747 isVisible : function(){
2748 return !this.hidden;
2750 onMouseOut : function(e){
2751 var t = this.findTargetItem(e);
2754 // if(t == this.activeItem && t.shouldDeactivate(e)){
2755 // this.activeItem.deactivate();
2756 // delete this.activeItem;
2759 this.fireEvent("mouseout", this, e, t);
2764 * Displays this menu relative to another element
2765 * @param {String/HTMLElement/Roo.Element} element The element to align to
2766 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2767 * the element (defaults to this.defaultAlign)
2768 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2770 show : function(el, pos, parentMenu)
2772 if (false === this.fireEvent("beforeshow", this)) {
2773 Roo.log("show canceled");
2776 this.parentMenu = parentMenu;
2781 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2784 * Displays this menu at a specific xy position
2785 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2786 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2788 showAt : function(xy, parentMenu, /* private: */_e){
2789 this.parentMenu = parentMenu;
2794 this.fireEvent("beforeshow", this);
2795 //xy = this.el.adjustForConstraints(xy);
2799 this.hideMenuItems();
2800 this.hidden = false;
2801 this.triggerEl.addClass('open');
2802 this.el.addClass('show');
2804 // reassign x when hitting right
2805 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2806 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2809 // reassign y when hitting bottom
2810 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2811 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2814 // but the list may align on trigger left or trigger top... should it be a properity?
2816 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2821 this.fireEvent("show", this);
2827 this.doFocus.defer(50, this);
2831 doFocus : function(){
2833 this.focusEl.focus();
2838 * Hides this menu and optionally all parent menus
2839 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2841 hide : function(deep)
2843 if (false === this.fireEvent("beforehide", this)) {
2844 Roo.log("hide canceled");
2847 this.hideMenuItems();
2848 if(this.el && this.isVisible()){
2850 if(this.activeItem){
2851 this.activeItem.deactivate();
2852 this.activeItem = null;
2854 this.triggerEl.removeClass('open');;
2855 this.el.removeClass('show');
2857 this.fireEvent("hide", this);
2859 if(deep === true && this.parentMenu){
2860 this.parentMenu.hide(true);
2864 onTriggerClick : function(e)
2866 Roo.log('trigger click');
2868 var target = e.getTarget();
2870 Roo.log(target.nodeName.toLowerCase());
2872 if(target.nodeName.toLowerCase() === 'i'){
2878 onTriggerPress : function(e)
2880 Roo.log('trigger press');
2881 //Roo.log(e.getTarget());
2882 // Roo.log(this.triggerEl.dom);
2884 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2885 var pel = Roo.get(e.getTarget());
2886 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2887 Roo.log('is treeview or dropdown?');
2891 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2895 if (this.isVisible()) {
2900 this.show(this.triggerEl, '?', false);
2903 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2910 hideMenuItems : function()
2912 Roo.log("hide Menu Items");
2917 this.el.select('.open',true).each(function(aa) {
2919 aa.removeClass('open');
2923 addxtypeChild : function (tree, cntr) {
2924 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2926 this.menuitems.add(comp);
2938 this.getEl().dom.innerHTML = '';
2939 this.menuitems.clear();
2953 * @class Roo.bootstrap.MenuItem
2954 * @extends Roo.bootstrap.Component
2955 * Bootstrap MenuItem class
2956 * @cfg {String} html the menu label
2957 * @cfg {String} href the link
2958 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2959 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2960 * @cfg {Boolean} active used on sidebars to highlight active itesm
2961 * @cfg {String} fa favicon to show on left of menu item.
2962 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2966 * Create a new MenuItem
2967 * @param {Object} config The config object
2971 Roo.bootstrap.MenuItem = function(config){
2972 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2977 * The raw click event for the entire grid.
2978 * @param {Roo.bootstrap.MenuItem} this
2979 * @param {Roo.EventObject} e
2985 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2989 preventDefault: false,
2990 isContainer : false,
2994 getAutoCreate : function(){
2996 if(this.isContainer){
2999 cls: 'dropdown-menu-item '
3009 cls : 'dropdown-item',
3014 if (this.fa !== false) {
3017 cls : 'fa fa-' + this.fa
3026 cls: 'dropdown-menu-item',
3029 if (this.parent().type == 'treeview') {
3030 cfg.cls = 'treeview-menu';
3033 cfg.cls += ' active';
3038 anc.href = this.href || cfg.cn[0].href ;
3039 ctag.html = this.html || cfg.cn[0].html ;
3043 initEvents: function()
3045 if (this.parent().type == 'treeview') {
3046 this.el.select('a').on('click', this.onClick, this);
3050 this.menu.parentType = this.xtype;
3051 this.menu.triggerEl = this.el;
3052 this.menu = this.addxtype(Roo.apply({}, this.menu));
3056 onClick : function(e)
3058 Roo.log('item on click ');
3060 if(this.preventDefault){
3063 //this.parent().hideMenuItems();
3065 this.fireEvent('click', this, e);
3084 * @class Roo.bootstrap.MenuSeparator
3085 * @extends Roo.bootstrap.Component
3086 * Bootstrap MenuSeparator class
3089 * Create a new MenuItem
3090 * @param {Object} config The config object
3094 Roo.bootstrap.MenuSeparator = function(config){
3095 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3098 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3100 getAutoCreate : function(){
3119 * @class Roo.bootstrap.Modal
3120 * @extends Roo.bootstrap.Component
3121 * Bootstrap Modal class
3122 * @cfg {String} title Title of dialog
3123 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3124 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3125 * @cfg {Boolean} specificTitle default false
3126 * @cfg {Array} buttons Array of buttons or standard button set..
3127 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3128 * @cfg {Boolean} animate default true
3129 * @cfg {Boolean} allow_close default true
3130 * @cfg {Boolean} fitwindow default false
3131 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3132 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3133 * @cfg {String} size (sm|lg) default empty
3134 * @cfg {Number} max_width set the max width of modal
3138 * Create a new Modal Dialog
3139 * @param {Object} config The config object
3142 Roo.bootstrap.Modal = function(config){
3143 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3148 * The raw btnclick event for the button
3149 * @param {Roo.EventObject} e
3154 * Fire when dialog resize
3155 * @param {Roo.bootstrap.Modal} this
3156 * @param {Roo.EventObject} e
3160 this.buttons = this.buttons || [];
3163 this.tmpl = Roo.factory(this.tmpl);
3168 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
3170 title : 'test dialog',
3180 specificTitle: false,
3182 buttonPosition: 'right',
3205 onRender : function(ct, position)
3207 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3210 var cfg = Roo.apply({}, this.getAutoCreate());
3213 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3215 //if (!cfg.name.length) {
3219 cfg.cls += ' ' + this.cls;
3222 cfg.style = this.style;
3224 this.el = Roo.get(document.body).createChild(cfg, position);
3226 //var type = this.el.dom.type;
3229 if(this.tabIndex !== undefined){
3230 this.el.dom.setAttribute('tabIndex', this.tabIndex);
3233 this.dialogEl = this.el.select('.modal-dialog',true).first();
3234 this.bodyEl = this.el.select('.modal-body',true).first();
3235 this.closeEl = this.el.select('.modal-header .close', true).first();
3236 this.headerEl = this.el.select('.modal-header',true).first();
3237 this.titleEl = this.el.select('.modal-title',true).first();
3238 this.footerEl = this.el.select('.modal-footer',true).first();
3240 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3242 //this.el.addClass("x-dlg-modal");
3244 if (this.buttons.length) {
3245 Roo.each(this.buttons, function(bb) {
3246 var b = Roo.apply({}, bb);
3247 b.xns = b.xns || Roo.bootstrap;
3248 b.xtype = b.xtype || 'Button';
3249 if (typeof(b.listeners) == 'undefined') {
3250 b.listeners = { click : this.onButtonClick.createDelegate(this) };
3253 var btn = Roo.factory(b);
3255 btn.render(this.getButtonContainer());
3259 // render the children.
3262 if(typeof(this.items) != 'undefined'){
3263 var items = this.items;
3266 for(var i =0;i < items.length;i++) {
3267 nitems.push(this.addxtype(Roo.apply({}, items[i])));
3271 this.items = nitems;
3273 // where are these used - they used to be body/close/footer
3277 //this.el.addClass([this.fieldClass, this.cls]);
3281 getAutoCreate : function()
3283 // we will default to modal-body-overflow - might need to remove or make optional later.
3285 cls : 'modal-body enable-modal-body-overflow ',
3286 html : this.html || ''
3291 cls : 'modal-title',
3295 if(this.specificTitle){
3301 if (this.allow_close && Roo.bootstrap.version == 3) {
3311 if (this.allow_close && Roo.bootstrap.version == 4) {
3321 if(this.size.length){
3322 size = 'modal-' + this.size;
3325 var footer = Roo.bootstrap.version == 3 ?
3327 cls : 'modal-footer',
3331 cls: 'btn-' + this.buttonPosition
3336 { // BS4 uses mr-auto on left buttons....
3337 cls : 'modal-footer'
3348 cls: "modal-dialog " + size,
3351 cls : "modal-content",
3354 cls : 'modal-header',
3369 modal.cls += ' fade';
3375 getChildContainer : function() {
3380 getButtonContainer : function() {
3382 return Roo.bootstrap.version == 4 ?
3383 this.el.select('.modal-footer',true).first()
3384 : this.el.select('.modal-footer div',true).first();
3387 initEvents : function()
3389 if (this.allow_close) {
3390 this.closeEl.on('click', this.hide, this);
3392 Roo.EventManager.onWindowResize(this.resize, this, true);
3400 this.maskEl.setSize(
3401 Roo.lib.Dom.getViewWidth(true),
3402 Roo.lib.Dom.getViewHeight(true)
3405 if (this.fitwindow) {
3409 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
3410 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
3415 if(this.max_width !== 0) {
3417 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
3420 this.setSize(w, this.height);
3424 if(this.max_height) {
3425 this.setSize(w,Math.min(
3427 Roo.lib.Dom.getViewportHeight(true) - 60
3433 if(!this.fit_content) {
3434 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
3438 this.setSize(w, Math.min(
3440 this.headerEl.getHeight() +
3441 this.footerEl.getHeight() +
3442 this.getChildHeight(this.bodyEl.dom.childNodes),
3443 Roo.lib.Dom.getViewportHeight(true) - 60)
3449 setSize : function(w,h)
3460 if (!this.rendered) {
3464 //this.el.setStyle('display', 'block');
3465 this.el.removeClass('hideing');
3466 this.el.dom.style.display='block';
3468 Roo.get(document.body).addClass('modal-open');
3470 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
3473 this.el.addClass('show');
3474 this.el.addClass('in');
3477 this.el.addClass('show');
3478 this.el.addClass('in');
3481 // not sure how we can show data in here..
3483 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3486 Roo.get(document.body).addClass("x-body-masked");
3488 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3489 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3490 this.maskEl.dom.style.display = 'block';
3491 this.maskEl.addClass('show');
3496 this.fireEvent('show', this);
3498 // set zindex here - otherwise it appears to be ignored...
3499 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3502 this.items.forEach( function(e) {
3503 e.layout ? e.layout() : false;
3511 if(this.fireEvent("beforehide", this) !== false){
3513 this.maskEl.removeClass('show');
3515 this.maskEl.dom.style.display = '';
3516 Roo.get(document.body).removeClass("x-body-masked");
3517 this.el.removeClass('in');
3518 this.el.select('.modal-dialog', true).first().setStyle('transform','');
3520 if(this.animate){ // why
3521 this.el.addClass('hideing');
3522 this.el.removeClass('show');
3524 if (!this.el.hasClass('hideing')) {
3525 return; // it's been shown again...
3528 this.el.dom.style.display='';
3530 Roo.get(document.body).removeClass('modal-open');
3531 this.el.removeClass('hideing');
3535 this.el.removeClass('show');
3536 this.el.dom.style.display='';
3537 Roo.get(document.body).removeClass('modal-open');
3540 this.fireEvent('hide', this);
3543 isVisible : function()
3546 return this.el.hasClass('show') && !this.el.hasClass('hideing');
3550 addButton : function(str, cb)
3554 var b = Roo.apply({}, { html : str } );
3555 b.xns = b.xns || Roo.bootstrap;
3556 b.xtype = b.xtype || 'Button';
3557 if (typeof(b.listeners) == 'undefined') {
3558 b.listeners = { click : cb.createDelegate(this) };
3561 var btn = Roo.factory(b);
3563 btn.render(this.getButtonContainer());
3569 setDefaultButton : function(btn)
3571 //this.el.select('.modal-footer').()
3574 resizeTo: function(w,h)
3576 this.dialogEl.setWidth(w);
3578 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
3580 this.bodyEl.setHeight(h - diff);
3582 this.fireEvent('resize', this);
3585 setContentSize : function(w, h)
3589 onButtonClick: function(btn,e)
3592 this.fireEvent('btnclick', btn.name, e);
3595 * Set the title of the Dialog
3596 * @param {String} str new Title
3598 setTitle: function(str) {
3599 this.titleEl.dom.innerHTML = str;
3602 * Set the body of the Dialog
3603 * @param {String} str new Title
3605 setBody: function(str) {
3606 this.bodyEl.dom.innerHTML = str;
3609 * Set the body of the Dialog using the template
3610 * @param {Obj} data - apply this data to the template and replace the body contents.
3612 applyBody: function(obj)
3615 Roo.log("Error - using apply Body without a template");
3618 this.tmpl.overwrite(this.bodyEl, obj);
3621 getChildHeight : function(child_nodes)
3625 child_nodes.length == 0
3630 var child_height = 0;
3632 for(var i = 0; i < child_nodes.length; i++) {
3635 * for modal with tabs...
3636 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3638 var layout_childs = child_nodes[i].childNodes;
3640 for(var j = 0; j < layout_childs.length; j++) {
3642 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3644 var layout_body_childs = layout_childs[j].childNodes;
3646 for(var k = 0; k < layout_body_childs.length; k++) {
3648 if(layout_body_childs[k].classList.contains('navbar')) {
3649 child_height += layout_body_childs[k].offsetHeight;
3653 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3655 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3657 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3659 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3660 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3675 child_height += child_nodes[i].offsetHeight;
3676 // Roo.log(child_nodes[i].offsetHeight);
3679 return child_height;
3685 Roo.apply(Roo.bootstrap.Modal, {
3687 * Button config that displays a single OK button
3696 * Button config that displays Yes and No buttons
3712 * Button config that displays OK and Cancel buttons
3727 * Button config that displays Yes, No and Cancel buttons
3751 * messagebox - can be used as a replace
3755 * @class Roo.MessageBox
3756 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3760 Roo.Msg.alert('Status', 'Changes saved successfully.');
3762 // Prompt for user data:
3763 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3765 // process text value...
3769 // Show a dialog using config options:
3771 title:'Save Changes?',
3772 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3773 buttons: Roo.Msg.YESNOCANCEL,
3780 Roo.bootstrap.MessageBox = function(){
3781 var dlg, opt, mask, waitTimer;
3782 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3783 var buttons, activeTextEl, bwidth;
3787 var handleButton = function(button){
3789 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3793 var handleHide = function(){
3795 dlg.el.removeClass(opt.cls);
3798 // Roo.TaskMgr.stop(waitTimer);
3799 // waitTimer = null;
3804 var updateButtons = function(b){
3807 buttons["ok"].hide();
3808 buttons["cancel"].hide();
3809 buttons["yes"].hide();
3810 buttons["no"].hide();
3811 dlg.footerEl.hide();
3815 dlg.footerEl.show();
3816 for(var k in buttons){
3817 if(typeof buttons[k] != "function"){
3820 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3821 width += buttons[k].el.getWidth()+15;
3831 var handleEsc = function(d, k, e){
3832 if(opt && opt.closable !== false){
3842 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3843 * @return {Roo.BasicDialog} The BasicDialog element
3845 getDialog : function(){
3847 dlg = new Roo.bootstrap.Modal( {
3850 //constraintoviewport:false,
3852 //collapsible : false,
3857 //buttonAlign:"center",
3858 closeClick : function(){
3859 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3862 handleButton("cancel");
3867 dlg.on("hide", handleHide);
3869 //dlg.addKeyListener(27, handleEsc);
3871 this.buttons = buttons;
3872 var bt = this.buttonText;
3873 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3874 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3875 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3876 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3878 bodyEl = dlg.bodyEl.createChild({
3880 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3881 '<textarea class="roo-mb-textarea"></textarea>' +
3882 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3884 msgEl = bodyEl.dom.firstChild;
3885 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3886 textboxEl.enableDisplayMode();
3887 textboxEl.addKeyListener([10,13], function(){
3888 if(dlg.isVisible() && opt && opt.buttons){
3891 }else if(opt.buttons.yes){
3892 handleButton("yes");
3896 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3897 textareaEl.enableDisplayMode();
3898 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3899 progressEl.enableDisplayMode();
3901 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3902 var pf = progressEl.dom.firstChild;
3904 pp = Roo.get(pf.firstChild);
3905 pp.setHeight(pf.offsetHeight);
3913 * Updates the message box body text
3914 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3915 * the XHTML-compliant non-breaking space character '&#160;')
3916 * @return {Roo.MessageBox} This message box
3918 updateText : function(text)
3920 if(!dlg.isVisible() && !opt.width){
3921 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3922 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3924 msgEl.innerHTML = text || ' ';
3926 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3927 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3929 Math.min(opt.width || cw , this.maxWidth),
3930 Math.max(opt.minWidth || this.minWidth, bwidth)
3933 activeTextEl.setWidth(w);
3935 if(dlg.isVisible()){
3936 dlg.fixedcenter = false;
3938 // to big, make it scroll. = But as usual stupid IE does not support
3941 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3942 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3943 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3945 bodyEl.dom.style.height = '';
3946 bodyEl.dom.style.overflowY = '';
3949 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3951 bodyEl.dom.style.overflowX = '';
3954 dlg.setContentSize(w, bodyEl.getHeight());
3955 if(dlg.isVisible()){
3956 dlg.fixedcenter = true;
3962 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3963 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3964 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3965 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3966 * @return {Roo.MessageBox} This message box
3968 updateProgress : function(value, text){
3970 this.updateText(text);
3973 if (pp) { // weird bug on my firefox - for some reason this is not defined
3974 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3975 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3981 * Returns true if the message box is currently displayed
3982 * @return {Boolean} True if the message box is visible, else false
3984 isVisible : function(){
3985 return dlg && dlg.isVisible();
3989 * Hides the message box if it is displayed
3992 if(this.isVisible()){
3998 * Displays a new message box, or reinitializes an existing message box, based on the config options
3999 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4000 * The following config object properties are supported:
4002 Property Type Description
4003 ---------- --------------- ------------------------------------------------------------------------------------
4004 animEl String/Element An id or Element from which the message box should animate as it opens and
4005 closes (defaults to undefined)
4006 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4007 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4008 closable Boolean False to hide the top-right close button (defaults to true). Note that
4009 progress and wait dialogs will ignore this property and always hide the
4010 close button as they can only be closed programmatically.
4011 cls String A custom CSS class to apply to the message box element
4012 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4013 displayed (defaults to 75)
4014 fn Function A callback function to execute after closing the dialog. The arguments to the
4015 function will be btn (the name of the button that was clicked, if applicable,
4016 e.g. "ok"), and text (the value of the active text field, if applicable).
4017 Progress and wait dialogs will ignore this option since they do not respond to
4018 user actions and can only be closed programmatically, so any required function
4019 should be called by the same code after it closes the dialog.
4020 icon String A CSS class that provides a background image to be used as an icon for
4021 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4022 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4023 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4024 modal Boolean False to allow user interaction with the page while the message box is
4025 displayed (defaults to true)
4026 msg String A string that will replace the existing message box body text (defaults
4027 to the XHTML-compliant non-breaking space character ' ')
4028 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4029 progress Boolean True to display a progress bar (defaults to false)
4030 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4031 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4032 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4033 title String The title text
4034 value String The string value to set into the active textbox element if displayed
4035 wait Boolean True to display a progress bar (defaults to false)
4036 width Number The width of the dialog in pixels
4043 msg: 'Please enter your address:',
4045 buttons: Roo.MessageBox.OKCANCEL,
4048 animEl: 'addAddressBtn'
4051 * @param {Object} config Configuration options
4052 * @return {Roo.MessageBox} This message box
4054 show : function(options)
4057 // this causes nightmares if you show one dialog after another
4058 // especially on callbacks..
4060 if(this.isVisible()){
4063 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4064 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4065 Roo.log("New Dialog Message:" + options.msg )
4066 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4067 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4070 var d = this.getDialog();
4072 d.setTitle(opt.title || " ");
4073 d.closeEl.setDisplayed(opt.closable !== false);
4074 activeTextEl = textboxEl;
4075 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4080 textareaEl.setHeight(typeof opt.multiline == "number" ?
4081 opt.multiline : this.defaultTextHeight);
4082 activeTextEl = textareaEl;
4091 progressEl.setDisplayed(opt.progress === true);
4093 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4095 this.updateProgress(0);
4096 activeTextEl.dom.value = opt.value || "";
4098 dlg.setDefaultButton(activeTextEl);
4100 var bs = opt.buttons;
4104 }else if(bs && bs.yes){
4105 db = buttons["yes"];
4107 dlg.setDefaultButton(db);
4109 bwidth = updateButtons(opt.buttons);
4110 this.updateText(opt.msg);
4112 d.el.addClass(opt.cls);
4114 d.proxyDrag = opt.proxyDrag === true;
4115 d.modal = opt.modal !== false;
4116 d.mask = opt.modal !== false ? mask : false;
4118 // force it to the end of the z-index stack so it gets a cursor in FF
4119 document.body.appendChild(dlg.el.dom);
4120 d.animateTarget = null;
4121 d.show(options.animEl);
4127 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
4128 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
4129 * and closing the message box when the process is complete.
4130 * @param {String} title The title bar text
4131 * @param {String} msg The message box body text
4132 * @return {Roo.MessageBox} This message box
4134 progress : function(title, msg){
4141 minWidth: this.minProgressWidth,
4148 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
4149 * If a callback function is passed it will be called after the user clicks the button, and the
4150 * id of the button that was clicked will be passed as the only parameter to the callback
4151 * (could also be the top-right close button).
4152 * @param {String} title The title bar text
4153 * @param {String} msg The message box body text
4154 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4155 * @param {Object} scope (optional) The scope of the callback function
4156 * @return {Roo.MessageBox} This message box
4158 alert : function(title, msg, fn, scope)
4173 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
4174 * interaction while waiting for a long-running process to complete that does not have defined intervals.
4175 * You are responsible for closing the message box when the process is complete.
4176 * @param {String} msg The message box body text
4177 * @param {String} title (optional) The title bar text
4178 * @return {Roo.MessageBox} This message box
4180 wait : function(msg, title){
4191 waitTimer = Roo.TaskMgr.start({
4193 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
4201 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
4202 * If a callback function is passed it will be called after the user clicks either button, and the id of the
4203 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
4204 * @param {String} title The title bar text
4205 * @param {String} msg The message box body text
4206 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4207 * @param {Object} scope (optional) The scope of the callback function
4208 * @return {Roo.MessageBox} This message box
4210 confirm : function(title, msg, fn, scope){
4214 buttons: this.YESNO,
4223 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
4224 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
4225 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
4226 * (could also be the top-right close button) and the text that was entered will be passed as the two
4227 * parameters to the callback.
4228 * @param {String} title The title bar text
4229 * @param {String} msg The message box body text
4230 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4231 * @param {Object} scope (optional) The scope of the callback function
4232 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
4233 * property, or the height in pixels to create the textbox (defaults to false / single-line)
4234 * @return {Roo.MessageBox} This message box
4236 prompt : function(title, msg, fn, scope, multiline){
4240 buttons: this.OKCANCEL,
4245 multiline: multiline,
4252 * Button config that displays a single OK button
4257 * Button config that displays Yes and No buttons
4260 YESNO : {yes:true, no:true},
4262 * Button config that displays OK and Cancel buttons
4265 OKCANCEL : {ok:true, cancel:true},
4267 * Button config that displays Yes, No and Cancel buttons
4270 YESNOCANCEL : {yes:true, no:true, cancel:true},
4273 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
4276 defaultTextHeight : 75,
4278 * The maximum width in pixels of the message box (defaults to 600)
4283 * The minimum width in pixels of the message box (defaults to 100)
4288 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
4289 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
4292 minProgressWidth : 250,
4294 * An object containing the default button text strings that can be overriden for localized language support.
4295 * Supported properties are: ok, cancel, yes and no.
4296 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
4309 * Shorthand for {@link Roo.MessageBox}
4311 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
4312 Roo.Msg = Roo.Msg || Roo.MessageBox;
4321 * @class Roo.bootstrap.Navbar
4322 * @extends Roo.bootstrap.Component
4323 * Bootstrap Navbar class
4326 * Create a new Navbar
4327 * @param {Object} config The config object
4331 Roo.bootstrap.Navbar = function(config){
4332 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
4336 * @event beforetoggle
4337 * Fire before toggle the menu
4338 * @param {Roo.EventObject} e
4340 "beforetoggle" : true
4344 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
4353 getAutoCreate : function(){
4356 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
4360 initEvents :function ()
4362 //Roo.log(this.el.select('.navbar-toggle',true));
4363 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
4370 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
4372 var size = this.el.getSize();
4373 this.maskEl.setSize(size.width, size.height);
4374 this.maskEl.enableDisplayMode("block");
4383 getChildContainer : function()
4385 if (this.el && this.el.select('.collapse').getCount()) {
4386 return this.el.select('.collapse',true).first();
4401 onToggle : function()
4404 if(this.fireEvent('beforetoggle', this) === false){
4407 var ce = this.el.select('.navbar-collapse',true).first();
4409 if (!ce.hasClass('show')) {
4419 * Expand the navbar pulldown
4421 expand : function ()
4424 var ce = this.el.select('.navbar-collapse',true).first();
4425 if (ce.hasClass('collapsing')) {
4428 ce.dom.style.height = '';
4430 ce.addClass('in'); // old...
4431 ce.removeClass('collapse');
4432 ce.addClass('show');
4433 var h = ce.getHeight();
4435 ce.removeClass('show');
4436 // at this point we should be able to see it..
4437 ce.addClass('collapsing');
4439 ce.setHeight(0); // resize it ...
4440 ce.on('transitionend', function() {
4441 //Roo.log('done transition');
4442 ce.removeClass('collapsing');
4443 ce.addClass('show');
4444 ce.removeClass('collapse');
4446 ce.dom.style.height = '';
4447 }, this, { single: true} );
4449 ce.dom.scrollTop = 0;
4452 * Collapse the navbar pulldown
4454 collapse : function()
4456 var ce = this.el.select('.navbar-collapse',true).first();
4458 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
4459 // it's collapsed or collapsing..
4462 ce.removeClass('in'); // old...
4463 ce.setHeight(ce.getHeight());
4464 ce.removeClass('show');
4465 ce.addClass('collapsing');
4467 ce.on('transitionend', function() {
4468 ce.dom.style.height = '';
4469 ce.removeClass('collapsing');
4470 ce.addClass('collapse');
4471 }, this, { single: true} );
4491 * @class Roo.bootstrap.NavSimplebar
4492 * @extends Roo.bootstrap.Navbar
4493 * Bootstrap Sidebar class
4495 * @cfg {Boolean} inverse is inverted color
4497 * @cfg {String} type (nav | pills | tabs)
4498 * @cfg {Boolean} arrangement stacked | justified
4499 * @cfg {String} align (left | right) alignment
4501 * @cfg {Boolean} main (true|false) main nav bar? default false
4502 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4504 * @cfg {String} tag (header|footer|nav|div) default is nav
4506 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4510 * Create a new Sidebar
4511 * @param {Object} config The config object
4515 Roo.bootstrap.NavSimplebar = function(config){
4516 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4519 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
4535 getAutoCreate : function(){
4539 tag : this.tag || 'div',
4540 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
4542 if (['light','white'].indexOf(this.weight) > -1) {
4543 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4545 cfg.cls += ' bg-' + this.weight;
4548 cfg.cls += ' navbar-inverse';
4552 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4554 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4563 cls: 'nav nav-' + this.xtype,
4569 this.type = this.type || 'nav';
4570 if (['tabs','pills'].indexOf(this.type) != -1) {
4571 cfg.cn[0].cls += ' nav-' + this.type
4575 if (this.type!=='nav') {
4576 Roo.log('nav type must be nav/tabs/pills')
4578 cfg.cn[0].cls += ' navbar-nav'
4584 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4585 cfg.cn[0].cls += ' nav-' + this.arrangement;
4589 if (this.align === 'right') {
4590 cfg.cn[0].cls += ' navbar-right';
4615 * navbar-expand-md fixed-top
4619 * @class Roo.bootstrap.NavHeaderbar
4620 * @extends Roo.bootstrap.NavSimplebar
4621 * Bootstrap Sidebar class
4623 * @cfg {String} brand what is brand
4624 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4625 * @cfg {String} brand_href href of the brand
4626 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4627 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4628 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4629 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4632 * Create a new Sidebar
4633 * @param {Object} config The config object
4637 Roo.bootstrap.NavHeaderbar = function(config){
4638 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4642 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4649 desktopCenter : false,
4652 getAutoCreate : function(){
4655 tag: this.nav || 'nav',
4656 cls: 'navbar navbar-expand-md',
4662 if (this.desktopCenter) {
4663 cn.push({cls : 'container', cn : []});
4671 cls: 'navbar-toggle navbar-toggler',
4672 'data-toggle': 'collapse',
4677 html: 'Toggle navigation'
4681 cls: 'icon-bar navbar-toggler-icon'
4694 cn.push( Roo.bootstrap.version == 4 ? btn : {
4696 cls: 'navbar-header',
4705 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
4709 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4711 if (['light','white'].indexOf(this.weight) > -1) {
4712 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4714 cfg.cls += ' bg-' + this.weight;
4717 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4718 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4720 // tag can override this..
4722 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4725 if (this.brand !== '') {
4726 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4727 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4729 href: this.brand_href ? this.brand_href : '#',
4730 cls: 'navbar-brand',
4738 cfg.cls += ' main-nav';
4746 getHeaderChildContainer : function()
4748 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4749 return this.el.select('.navbar-header',true).first();
4752 return this.getChildContainer();
4755 getChildContainer : function()
4758 return this.el.select('.roo-navbar-collapse',true).first();
4763 initEvents : function()
4765 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4767 if (this.autohide) {
4772 Roo.get(document).on('scroll',function(e) {
4773 var ns = Roo.get(document).getScroll().top;
4774 var os = prevScroll;
4778 ft.removeClass('slideDown');
4779 ft.addClass('slideUp');
4782 ft.removeClass('slideUp');
4783 ft.addClass('slideDown');
4804 * @class Roo.bootstrap.NavSidebar
4805 * @extends Roo.bootstrap.Navbar
4806 * Bootstrap Sidebar class
4809 * Create a new Sidebar
4810 * @param {Object} config The config object
4814 Roo.bootstrap.NavSidebar = function(config){
4815 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4818 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4820 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4822 getAutoCreate : function(){
4827 cls: 'sidebar sidebar-nav'
4849 * @class Roo.bootstrap.NavGroup
4850 * @extends Roo.bootstrap.Component
4851 * Bootstrap NavGroup class
4852 * @cfg {String} align (left|right)
4853 * @cfg {Boolean} inverse
4854 * @cfg {String} type (nav|pills|tab) default nav
4855 * @cfg {String} navId - reference Id for navbar.
4859 * Create a new nav group
4860 * @param {Object} config The config object
4863 Roo.bootstrap.NavGroup = function(config){
4864 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4867 Roo.bootstrap.NavGroup.register(this);
4871 * Fires when the active item changes
4872 * @param {Roo.bootstrap.NavGroup} this
4873 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4874 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4881 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4892 getAutoCreate : function()
4894 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4900 if (Roo.bootstrap.version == 4) {
4901 if (['tabs','pills'].indexOf(this.type) != -1) {
4902 cfg.cls += ' nav-' + this.type;
4904 // trying to remove so header bar can right align top?
4905 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
4906 // do not use on header bar...
4907 cfg.cls += ' navbar-nav';
4912 if (['tabs','pills'].indexOf(this.type) != -1) {
4913 cfg.cls += ' nav-' + this.type
4915 if (this.type !== 'nav') {
4916 Roo.log('nav type must be nav/tabs/pills')
4918 cfg.cls += ' navbar-nav'
4922 if (this.parent() && this.parent().sidebar) {
4925 cls: 'dashboard-menu sidebar-menu'
4931 if (this.form === true) {
4934 cls: 'navbar-form form-inline'
4936 //nav navbar-right ml-md-auto
4937 if (this.align === 'right') {
4938 cfg.cls += ' navbar-right ml-md-auto';
4940 cfg.cls += ' navbar-left';
4944 if (this.align === 'right') {
4945 cfg.cls += ' navbar-right ml-md-auto';
4947 cfg.cls += ' mr-auto';
4951 cfg.cls += ' navbar-inverse';
4959 * sets the active Navigation item
4960 * @param {Roo.bootstrap.NavItem} the new current navitem
4962 setActiveItem : function(item)
4965 Roo.each(this.navItems, function(v){
4970 v.setActive(false, true);
4977 item.setActive(true, true);
4978 this.fireEvent('changed', this, item, prev);
4983 * gets the active Navigation item
4984 * @return {Roo.bootstrap.NavItem} the current navitem
4986 getActive : function()
4990 Roo.each(this.navItems, function(v){
5001 indexOfNav : function()
5005 Roo.each(this.navItems, function(v,i){
5016 * adds a Navigation item
5017 * @param {Roo.bootstrap.NavItem} the navitem to add
5019 addItem : function(cfg)
5021 if (this.form && Roo.bootstrap.version == 4) {
5024 var cn = new Roo.bootstrap.NavItem(cfg);
5026 cn.parentId = this.id;
5027 cn.onRender(this.el, null);
5031 * register a Navigation item
5032 * @param {Roo.bootstrap.NavItem} the navitem to add
5034 register : function(item)
5036 this.navItems.push( item);
5037 item.navId = this.navId;
5042 * clear all the Navigation item
5045 clearAll : function()
5048 this.el.dom.innerHTML = '';
5051 getNavItem: function(tabId)
5054 Roo.each(this.navItems, function(e) {
5055 if (e.tabId == tabId) {
5065 setActiveNext : function()
5067 var i = this.indexOfNav(this.getActive());
5068 if (i > this.navItems.length) {
5071 this.setActiveItem(this.navItems[i+1]);
5073 setActivePrev : function()
5075 var i = this.indexOfNav(this.getActive());
5079 this.setActiveItem(this.navItems[i-1]);
5081 clearWasActive : function(except) {
5082 Roo.each(this.navItems, function(e) {
5083 if (e.tabId != except.tabId && e.was_active) {
5084 e.was_active = false;
5091 getWasActive : function ()
5094 Roo.each(this.navItems, function(e) {
5109 Roo.apply(Roo.bootstrap.NavGroup, {
5113 * register a Navigation Group
5114 * @param {Roo.bootstrap.NavGroup} the navgroup to add
5116 register : function(navgrp)
5118 this.groups[navgrp.navId] = navgrp;
5122 * fetch a Navigation Group based on the navigation ID
5123 * @param {string} the navgroup to add
5124 * @returns {Roo.bootstrap.NavGroup} the navgroup
5126 get: function(navId) {
5127 if (typeof(this.groups[navId]) == 'undefined') {
5129 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
5131 return this.groups[navId] ;
5146 * @class Roo.bootstrap.NavItem
5147 * @extends Roo.bootstrap.Component
5148 * Bootstrap Navbar.NavItem class
5149 * @cfg {String} href link to
5150 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
5152 * @cfg {String} html content of button
5153 * @cfg {String} badge text inside badge
5154 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
5155 * @cfg {String} glyphicon DEPRICATED - use fa
5156 * @cfg {String} icon DEPRICATED - use fa
5157 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
5158 * @cfg {Boolean} active Is item active
5159 * @cfg {Boolean} disabled Is item disabled
5161 * @cfg {Boolean} preventDefault (true | false) default false
5162 * @cfg {String} tabId the tab that this item activates.
5163 * @cfg {String} tagtype (a|span) render as a href or span?
5164 * @cfg {Boolean} animateRef (true|false) link to element default false
5167 * Create a new Navbar Item
5168 * @param {Object} config The config object
5170 Roo.bootstrap.NavItem = function(config){
5171 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5176 * The raw click event for the entire grid.
5177 * @param {Roo.EventObject} e
5182 * Fires when the active item active state changes
5183 * @param {Roo.bootstrap.NavItem} this
5184 * @param {boolean} state the new state
5190 * Fires when scroll to element
5191 * @param {Roo.bootstrap.NavItem} this
5192 * @param {Object} options
5193 * @param {Roo.EventObject} e
5201 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
5210 preventDefault : false,
5218 button_outline : false,
5222 getAutoCreate : function(){
5230 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
5232 if (this.disabled) {
5233 cfg.cls += ' disabled';
5237 if (this.button_weight.length) {
5238 cfg.tag = this.href ? 'a' : 'button';
5239 cfg.html = this.html || '';
5240 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
5242 cfg.href = this.href;
5245 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
5248 // menu .. should add dropdown-menu class - so no need for carat..
5250 if (this.badge !== '') {
5252 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5257 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
5261 href : this.href || "#",
5262 html: this.html || ''
5265 if (this.tagtype == 'a') {
5266 cfg.cn[0].cls = 'nav-link';
5269 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
5272 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
5274 if(this.glyphicon) {
5275 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
5280 cfg.cn[0].html += " <span class='caret'></span>";
5284 if (this.badge !== '') {
5286 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5294 onRender : function(ct, position)
5296 // Roo.log("Call onRender: " + this.xtype);
5297 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
5301 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
5302 this.navLink = this.el.select('.nav-link',true).first();
5307 initEvents: function()
5309 if (typeof (this.menu) != 'undefined') {
5310 this.menu.parentType = this.xtype;
5311 this.menu.triggerEl = this.el;
5312 this.menu = this.addxtype(Roo.apply({}, this.menu));
5315 this.el.select('a',true).on('click', this.onClick, this);
5317 if(this.tagtype == 'span'){
5318 this.el.select('span',true).on('click', this.onClick, this);
5321 // at this point parent should be available..
5322 this.parent().register(this);
5325 onClick : function(e)
5327 if (e.getTarget('.dropdown-menu-item')) {
5328 // did you click on a menu itemm.... - then don't trigger onclick..
5333 this.preventDefault ||
5336 Roo.log("NavItem - prevent Default?");
5340 if (this.disabled) {
5344 var tg = Roo.bootstrap.TabGroup.get(this.navId);
5345 if (tg && tg.transition) {
5346 Roo.log("waiting for the transitionend");
5352 //Roo.log("fire event clicked");
5353 if(this.fireEvent('click', this, e) === false){
5357 if(this.tagtype == 'span'){
5361 //Roo.log(this.href);
5362 var ael = this.el.select('a',true).first();
5365 if(ael && this.animateRef && this.href.indexOf('#') > -1){
5366 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
5367 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
5368 return; // ignore... - it's a 'hash' to another page.
5370 Roo.log("NavItem - prevent Default?");
5372 this.scrollToElement(e);
5376 var p = this.parent();
5378 if (['tabs','pills'].indexOf(p.type)!==-1) {
5379 if (typeof(p.setActiveItem) !== 'undefined') {
5380 p.setActiveItem(this);
5384 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
5385 if (p.parentType == 'NavHeaderbar' && !this.menu) {
5386 // remove the collapsed menu expand...
5387 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
5391 isActive: function () {
5394 setActive : function(state, fire, is_was_active)
5396 if (this.active && !state && this.navId) {
5397 this.was_active = true;
5398 var nv = Roo.bootstrap.NavGroup.get(this.navId);
5400 nv.clearWasActive(this);
5404 this.active = state;
5407 this.el.removeClass('active');
5408 this.navLink ? this.navLink.removeClass('active') : false;
5409 } else if (!this.el.hasClass('active')) {
5411 this.el.addClass('active');
5412 if (Roo.bootstrap.version == 4 && this.navLink ) {
5413 this.navLink.addClass('active');
5418 this.fireEvent('changed', this, state);
5421 // show a panel if it's registered and related..
5423 if (!this.navId || !this.tabId || !state || is_was_active) {
5427 var tg = Roo.bootstrap.TabGroup.get(this.navId);
5431 var pan = tg.getPanelByName(this.tabId);
5435 // if we can not flip to new panel - go back to old nav highlight..
5436 if (false == tg.showPanel(pan)) {
5437 var nv = Roo.bootstrap.NavGroup.get(this.navId);
5439 var onav = nv.getWasActive();
5441 onav.setActive(true, false, true);
5450 // this should not be here...
5451 setDisabled : function(state)
5453 this.disabled = state;
5455 this.el.removeClass('disabled');
5456 } else if (!this.el.hasClass('disabled')) {
5457 this.el.addClass('disabled');
5463 * Fetch the element to display the tooltip on.
5464 * @return {Roo.Element} defaults to this.el
5466 tooltipEl : function()
5468 return this.el.select('' + this.tagtype + '', true).first();
5471 scrollToElement : function(e)
5473 var c = document.body;
5476 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
5478 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
5479 c = document.documentElement;
5482 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5488 var o = target.calcOffsetsTo(c);
5495 this.fireEvent('scrollto', this, options, e);
5497 Roo.get(c).scrollTo('top', options.value, true);
5510 * <span> icon </span>
5511 * <span> text </span>
5512 * <span>badge </span>
5516 * @class Roo.bootstrap.NavSidebarItem
5517 * @extends Roo.bootstrap.NavItem
5518 * Bootstrap Navbar.NavSidebarItem class
5519 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5520 * {Boolean} open is the menu open
5521 * {Boolean} buttonView use button as the tigger el rather that a (default false)
5522 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5523 * {String} buttonSize (sm|md|lg)the extra classes for the button
5524 * {Boolean} showArrow show arrow next to the text (default true)
5526 * Create a new Navbar Button
5527 * @param {Object} config The config object
5529 Roo.bootstrap.NavSidebarItem = function(config){
5530 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5535 * The raw click event for the entire grid.
5536 * @param {Roo.EventObject} e
5541 * Fires when the active item active state changes
5542 * @param {Roo.bootstrap.NavSidebarItem} this
5543 * @param {boolean} state the new state
5551 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
5553 badgeWeight : 'default',
5559 buttonWeight : 'default',
5565 getAutoCreate : function(){
5570 href : this.href || '#',
5576 if(this.buttonView){
5579 href : this.href || '#',
5580 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5593 cfg.cls += ' active';
5596 if (this.disabled) {
5597 cfg.cls += ' disabled';
5600 cfg.cls += ' open x-open';
5603 if (this.glyphicon || this.icon) {
5604 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
5605 a.cn.push({ tag : 'i', cls : c }) ;
5608 if(!this.buttonView){
5611 html : this.html || ''
5618 if (this.badge !== '') {
5619 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
5625 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5628 a.cls += ' dropdown-toggle treeview' ;
5634 initEvents : function()
5636 if (typeof (this.menu) != 'undefined') {
5637 this.menu.parentType = this.xtype;
5638 this.menu.triggerEl = this.el;
5639 this.menu = this.addxtype(Roo.apply({}, this.menu));
5642 this.el.on('click', this.onClick, this);
5644 if(this.badge !== ''){
5645 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5650 onClick : function(e)
5657 if(this.preventDefault){
5661 this.fireEvent('click', this, e);
5664 disable : function()
5666 this.setDisabled(true);
5671 this.setDisabled(false);
5674 setDisabled : function(state)
5676 if(this.disabled == state){
5680 this.disabled = state;
5683 this.el.addClass('disabled');
5687 this.el.removeClass('disabled');
5692 setActive : function(state)
5694 if(this.active == state){
5698 this.active = state;
5701 this.el.addClass('active');
5705 this.el.removeClass('active');
5710 isActive: function ()
5715 setBadge : function(str)
5721 this.badgeEl.dom.innerHTML = str;
5738 * @class Roo.bootstrap.Row
5739 * @extends Roo.bootstrap.Component
5740 * Bootstrap Row class (contains columns...)
5744 * @param {Object} config The config object
5747 Roo.bootstrap.Row = function(config){
5748 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5751 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5753 getAutoCreate : function(){
5772 * @class Roo.bootstrap.Pagination
5773 * @extends Roo.bootstrap.Component
5774 * Bootstrap Pagination class
5775 * @cfg {String} size xs | sm | md | lg
5776 * @cfg {Boolean} inverse false | true
5779 * Create a new Pagination
5780 * @param {Object} config The config object
5783 Roo.bootstrap.Pagination = function(config){
5784 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5787 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5793 getAutoCreate : function(){
5799 cfg.cls += ' inverse';
5805 cfg.cls += " " + this.cls;
5823 * @class Roo.bootstrap.PaginationItem
5824 * @extends Roo.bootstrap.Component
5825 * Bootstrap PaginationItem class
5826 * @cfg {String} html text
5827 * @cfg {String} href the link
5828 * @cfg {Boolean} preventDefault (true | false) default true
5829 * @cfg {Boolean} active (true | false) default false
5830 * @cfg {Boolean} disabled default false
5834 * Create a new PaginationItem
5835 * @param {Object} config The config object
5839 Roo.bootstrap.PaginationItem = function(config){
5840 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5845 * The raw click event for the entire grid.
5846 * @param {Roo.EventObject} e
5852 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5856 preventDefault: true,
5861 getAutoCreate : function(){
5867 href : this.href ? this.href : '#',
5868 html : this.html ? this.html : ''
5878 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5882 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5888 initEvents: function() {
5890 this.el.on('click', this.onClick, this);
5893 onClick : function(e)
5895 Roo.log('PaginationItem on click ');
5896 if(this.preventDefault){
5904 this.fireEvent('click', this, e);
5920 * @class Roo.bootstrap.Slider
5921 * @extends Roo.bootstrap.Component
5922 * Bootstrap Slider class
5925 * Create a new Slider
5926 * @param {Object} config The config object
5929 Roo.bootstrap.Slider = function(config){
5930 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5933 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5935 getAutoCreate : function(){
5939 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5943 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5955 * Ext JS Library 1.1.1
5956 * Copyright(c) 2006-2007, Ext JS, LLC.
5958 * Originally Released Under LGPL - original licence link has changed is not relivant.
5961 * <script type="text/javascript">
5966 * @class Roo.grid.ColumnModel
5967 * @extends Roo.util.Observable
5968 * This is the default implementation of a ColumnModel used by the Grid. It defines
5969 * the columns in the grid.
5972 var colModel = new Roo.grid.ColumnModel([
5973 {header: "Ticker", width: 60, sortable: true, locked: true},
5974 {header: "Company Name", width: 150, sortable: true},
5975 {header: "Market Cap.", width: 100, sortable: true},
5976 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5977 {header: "Employees", width: 100, sortable: true, resizable: false}
5982 * The config options listed for this class are options which may appear in each
5983 * individual column definition.
5984 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5986 * @param {Object} config An Array of column config objects. See this class's
5987 * config objects for details.
5989 Roo.grid.ColumnModel = function(config){
5991 * The config passed into the constructor
5993 this.config = config;
5996 // if no id, create one
5997 // if the column does not have a dataIndex mapping,
5998 // map it to the order it is in the config
5999 for(var i = 0, len = config.length; i < len; i++){
6001 if(typeof c.dataIndex == "undefined"){
6004 if(typeof c.renderer == "string"){
6005 c.renderer = Roo.util.Format[c.renderer];
6007 if(typeof c.id == "undefined"){
6010 if(c.editor && c.editor.xtype){
6011 c.editor = Roo.factory(c.editor, Roo.grid);
6013 if(c.editor && c.editor.isFormField){
6014 c.editor = new Roo.grid.GridEditor(c.editor);
6016 this.lookup[c.id] = c;
6020 * The width of columns which have no width specified (defaults to 100)
6023 this.defaultWidth = 100;
6026 * Default sortable of columns which have no sortable specified (defaults to false)
6029 this.defaultSortable = false;
6033 * @event widthchange
6034 * Fires when the width of a column changes.
6035 * @param {ColumnModel} this
6036 * @param {Number} columnIndex The column index
6037 * @param {Number} newWidth The new width
6039 "widthchange": true,
6041 * @event headerchange
6042 * Fires when the text of a header changes.
6043 * @param {ColumnModel} this
6044 * @param {Number} columnIndex The column index
6045 * @param {Number} newText The new header text
6047 "headerchange": true,
6049 * @event hiddenchange
6050 * Fires when a column is hidden or "unhidden".
6051 * @param {ColumnModel} this
6052 * @param {Number} columnIndex The column index
6053 * @param {Boolean} hidden true if hidden, false otherwise
6055 "hiddenchange": true,
6057 * @event columnmoved
6058 * Fires when a column is moved.
6059 * @param {ColumnModel} this
6060 * @param {Number} oldIndex
6061 * @param {Number} newIndex
6063 "columnmoved" : true,
6065 * @event columlockchange
6066 * Fires when a column's locked state is changed
6067 * @param {ColumnModel} this
6068 * @param {Number} colIndex
6069 * @param {Boolean} locked true if locked
6071 "columnlockchange" : true
6073 Roo.grid.ColumnModel.superclass.constructor.call(this);
6075 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6077 * @cfg {String} header The header text to display in the Grid view.
6080 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6081 * {@link Roo.data.Record} definition from which to draw the column's value. If not
6082 * specified, the column's index is used as an index into the Record's data Array.
6085 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6086 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6089 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6090 * Defaults to the value of the {@link #defaultSortable} property.
6091 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6094 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
6097 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
6100 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6103 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6106 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6107 * given the cell's data value. See {@link #setRenderer}. If not specified, the
6108 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6109 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6112 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
6115 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
6118 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
6121 * @cfg {String} cursor (Optional)
6124 * @cfg {String} tooltip (Optional)
6127 * @cfg {Number} xs (Optional)
6130 * @cfg {Number} sm (Optional)
6133 * @cfg {Number} md (Optional)
6136 * @cfg {Number} lg (Optional)
6139 * Returns the id of the column at the specified index.
6140 * @param {Number} index The column index
6141 * @return {String} the id
6143 getColumnId : function(index){
6144 return this.config[index].id;
6148 * Returns the column for a specified id.
6149 * @param {String} id The column id
6150 * @return {Object} the column
6152 getColumnById : function(id){
6153 return this.lookup[id];
6158 * Returns the column for a specified dataIndex.
6159 * @param {String} dataIndex The column dataIndex
6160 * @return {Object|Boolean} the column or false if not found
6162 getColumnByDataIndex: function(dataIndex){
6163 var index = this.findColumnIndex(dataIndex);
6164 return index > -1 ? this.config[index] : false;
6168 * Returns the index for a specified column id.
6169 * @param {String} id The column id
6170 * @return {Number} the index, or -1 if not found
6172 getIndexById : function(id){
6173 for(var i = 0, len = this.config.length; i < len; i++){
6174 if(this.config[i].id == id){
6182 * Returns the index for a specified column dataIndex.
6183 * @param {String} dataIndex The column dataIndex
6184 * @return {Number} the index, or -1 if not found
6187 findColumnIndex : function(dataIndex){
6188 for(var i = 0, len = this.config.length; i < len; i++){
6189 if(this.config[i].dataIndex == dataIndex){
6197 moveColumn : function(oldIndex, newIndex){
6198 var c = this.config[oldIndex];
6199 this.config.splice(oldIndex, 1);
6200 this.config.splice(newIndex, 0, c);
6201 this.dataMap = null;
6202 this.fireEvent("columnmoved", this, oldIndex, newIndex);
6205 isLocked : function(colIndex){
6206 return this.config[colIndex].locked === true;
6209 setLocked : function(colIndex, value, suppressEvent){
6210 if(this.isLocked(colIndex) == value){
6213 this.config[colIndex].locked = value;
6215 this.fireEvent("columnlockchange", this, colIndex, value);
6219 getTotalLockedWidth : function(){
6221 for(var i = 0; i < this.config.length; i++){
6222 if(this.isLocked(i) && !this.isHidden(i)){
6223 this.totalWidth += this.getColumnWidth(i);
6229 getLockedCount : function(){
6230 for(var i = 0, len = this.config.length; i < len; i++){
6231 if(!this.isLocked(i)){
6236 return this.config.length;
6240 * Returns the number of columns.
6243 getColumnCount : function(visibleOnly){
6244 if(visibleOnly === true){
6246 for(var i = 0, len = this.config.length; i < len; i++){
6247 if(!this.isHidden(i)){
6253 return this.config.length;
6257 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
6258 * @param {Function} fn
6259 * @param {Object} scope (optional)
6260 * @return {Array} result
6262 getColumnsBy : function(fn, scope){
6264 for(var i = 0, len = this.config.length; i < len; i++){
6265 var c = this.config[i];
6266 if(fn.call(scope||this, c, i) === true){
6274 * Returns true if the specified column is sortable.
6275 * @param {Number} col The column index
6278 isSortable : function(col){
6279 if(typeof this.config[col].sortable == "undefined"){
6280 return this.defaultSortable;
6282 return this.config[col].sortable;
6286 * Returns the rendering (formatting) function defined for the column.
6287 * @param {Number} col The column index.
6288 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
6290 getRenderer : function(col){
6291 if(!this.config[col].renderer){
6292 return Roo.grid.ColumnModel.defaultRenderer;
6294 return this.config[col].renderer;
6298 * Sets the rendering (formatting) function for a column.
6299 * @param {Number} col The column index
6300 * @param {Function} fn The function to use to process the cell's raw data
6301 * to return HTML markup for the grid view. The render function is called with
6302 * the following parameters:<ul>
6303 * <li>Data value.</li>
6304 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
6305 * <li>css A CSS style string to apply to the table cell.</li>
6306 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
6307 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
6308 * <li>Row index</li>
6309 * <li>Column index</li>
6310 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
6312 setRenderer : function(col, fn){
6313 this.config[col].renderer = fn;
6317 * Returns the width for the specified column.
6318 * @param {Number} col The column index
6321 getColumnWidth : function(col){
6322 return this.config[col].width * 1 || this.defaultWidth;
6326 * Sets the width for a column.
6327 * @param {Number} col The column index
6328 * @param {Number} width The new width
6330 setColumnWidth : function(col, width, suppressEvent){
6331 this.config[col].width = width;
6332 this.totalWidth = null;
6334 this.fireEvent("widthchange", this, col, width);
6339 * Returns the total width of all columns.
6340 * @param {Boolean} includeHidden True to include hidden column widths
6343 getTotalWidth : function(includeHidden){
6344 if(!this.totalWidth){
6345 this.totalWidth = 0;
6346 for(var i = 0, len = this.config.length; i < len; i++){
6347 if(includeHidden || !this.isHidden(i)){
6348 this.totalWidth += this.getColumnWidth(i);
6352 return this.totalWidth;
6356 * Returns the header for the specified column.
6357 * @param {Number} col The column index
6360 getColumnHeader : function(col){
6361 return this.config[col].header;
6365 * Sets the header for a column.
6366 * @param {Number} col The column index
6367 * @param {String} header The new header
6369 setColumnHeader : function(col, header){
6370 this.config[col].header = header;
6371 this.fireEvent("headerchange", this, col, header);
6375 * Returns the tooltip for the specified column.
6376 * @param {Number} col The column index
6379 getColumnTooltip : function(col){
6380 return this.config[col].tooltip;
6383 * Sets the tooltip for a column.
6384 * @param {Number} col The column index
6385 * @param {String} tooltip The new tooltip
6387 setColumnTooltip : function(col, tooltip){
6388 this.config[col].tooltip = tooltip;
6392 * Returns the dataIndex for the specified column.
6393 * @param {Number} col The column index
6396 getDataIndex : function(col){
6397 return this.config[col].dataIndex;
6401 * Sets the dataIndex for a column.
6402 * @param {Number} col The column index
6403 * @param {Number} dataIndex The new dataIndex
6405 setDataIndex : function(col, dataIndex){
6406 this.config[col].dataIndex = dataIndex;
6412 * Returns true if the cell is editable.
6413 * @param {Number} colIndex The column index
6414 * @param {Number} rowIndex The row index - this is nto actually used..?
6417 isCellEditable : function(colIndex, rowIndex){
6418 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6422 * Returns the editor defined for the cell/column.
6423 * return false or null to disable editing.
6424 * @param {Number} colIndex The column index
6425 * @param {Number} rowIndex The row index
6428 getCellEditor : function(colIndex, rowIndex){
6429 return this.config[colIndex].editor;
6433 * Sets if a column is editable.
6434 * @param {Number} col The column index
6435 * @param {Boolean} editable True if the column is editable
6437 setEditable : function(col, editable){
6438 this.config[col].editable = editable;
6443 * Returns true if the column is hidden.
6444 * @param {Number} colIndex The column index
6447 isHidden : function(colIndex){
6448 return this.config[colIndex].hidden;
6453 * Returns true if the column width cannot be changed
6455 isFixed : function(colIndex){
6456 return this.config[colIndex].fixed;
6460 * Returns true if the column can be resized
6463 isResizable : function(colIndex){
6464 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6467 * Sets if a column is hidden.
6468 * @param {Number} colIndex The column index
6469 * @param {Boolean} hidden True if the column is hidden
6471 setHidden : function(colIndex, hidden){
6472 this.config[colIndex].hidden = hidden;
6473 this.totalWidth = null;
6474 this.fireEvent("hiddenchange", this, colIndex, hidden);
6478 * Sets the editor for a column.
6479 * @param {Number} col The column index
6480 * @param {Object} editor The editor object
6482 setEditor : function(col, editor){
6483 this.config[col].editor = editor;
6487 Roo.grid.ColumnModel.defaultRenderer = function(value)
6489 if(typeof value == "object") {
6492 if(typeof value == "string" && value.length < 1){
6496 return String.format("{0}", value);
6499 // Alias for backwards compatibility
6500 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6503 * Ext JS Library 1.1.1
6504 * Copyright(c) 2006-2007, Ext JS, LLC.
6506 * Originally Released Under LGPL - original licence link has changed is not relivant.
6509 * <script type="text/javascript">
6513 * @class Roo.LoadMask
6514 * A simple utility class for generically masking elements while loading data. If the element being masked has
6515 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6516 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
6517 * element's UpdateManager load indicator and will be destroyed after the initial load.
6519 * Create a new LoadMask
6520 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6521 * @param {Object} config The config object
6523 Roo.LoadMask = function(el, config){
6524 this.el = Roo.get(el);
6525 Roo.apply(this, config);
6527 this.store.on('beforeload', this.onBeforeLoad, this);
6528 this.store.on('load', this.onLoad, this);
6529 this.store.on('loadexception', this.onLoadException, this);
6530 this.removeMask = false;
6532 var um = this.el.getUpdateManager();
6533 um.showLoadIndicator = false; // disable the default indicator
6534 um.on('beforeupdate', this.onBeforeLoad, this);
6535 um.on('update', this.onLoad, this);
6536 um.on('failure', this.onLoad, this);
6537 this.removeMask = true;
6541 Roo.LoadMask.prototype = {
6543 * @cfg {Boolean} removeMask
6544 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6545 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
6549 * The text to display in a centered loading message box (defaults to 'Loading...')
6553 * @cfg {String} msgCls
6554 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6556 msgCls : 'x-mask-loading',
6559 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6565 * Disables the mask to prevent it from being displayed
6567 disable : function(){
6568 this.disabled = true;
6572 * Enables the mask so that it can be displayed
6574 enable : function(){
6575 this.disabled = false;
6578 onLoadException : function()
6582 if (typeof(arguments[3]) != 'undefined') {
6583 Roo.MessageBox.alert("Error loading",arguments[3]);
6587 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6588 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6595 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6600 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6604 onBeforeLoad : function(){
6606 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6611 destroy : function(){
6613 this.store.un('beforeload', this.onBeforeLoad, this);
6614 this.store.un('load', this.onLoad, this);
6615 this.store.un('loadexception', this.onLoadException, this);
6617 var um = this.el.getUpdateManager();
6618 um.un('beforeupdate', this.onBeforeLoad, this);
6619 um.un('update', this.onLoad, this);
6620 um.un('failure', this.onLoad, this);
6631 * @class Roo.bootstrap.Table
6632 * @extends Roo.bootstrap.Component
6633 * Bootstrap Table class
6634 * @cfg {String} cls table class
6635 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6636 * @cfg {String} bgcolor Specifies the background color for a table
6637 * @cfg {Number} border Specifies whether the table cells should have borders or not
6638 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6639 * @cfg {Number} cellspacing Specifies the space between cells
6640 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6641 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6642 * @cfg {String} sortable Specifies that the table should be sortable
6643 * @cfg {String} summary Specifies a summary of the content of a table
6644 * @cfg {Number} width Specifies the width of a table
6645 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6647 * @cfg {boolean} striped Should the rows be alternative striped
6648 * @cfg {boolean} bordered Add borders to the table
6649 * @cfg {boolean} hover Add hover highlighting
6650 * @cfg {boolean} condensed Format condensed
6651 * @cfg {boolean} responsive Format condensed
6652 * @cfg {Boolean} loadMask (true|false) default false
6653 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6654 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6655 * @cfg {Boolean} rowSelection (true|false) default false
6656 * @cfg {Boolean} cellSelection (true|false) default false
6657 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6658 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6659 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6660 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6664 * Create a new Table
6665 * @param {Object} config The config object
6668 Roo.bootstrap.Table = function(config){
6669 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6674 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6675 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6676 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6677 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6679 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6681 this.sm.grid = this;
6682 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6683 this.sm = this.selModel;
6684 this.sm.xmodule = this.xmodule || false;
6687 if (this.cm && typeof(this.cm.config) == 'undefined') {
6688 this.colModel = new Roo.grid.ColumnModel(this.cm);
6689 this.cm = this.colModel;
6690 this.cm.xmodule = this.xmodule || false;
6693 this.store= Roo.factory(this.store, Roo.data);
6694 this.ds = this.store;
6695 this.ds.xmodule = this.xmodule || false;
6698 if (this.footer && this.store) {
6699 this.footer.dataSource = this.ds;
6700 this.footer = Roo.factory(this.footer);
6707 * Fires when a cell is clicked
6708 * @param {Roo.bootstrap.Table} this
6709 * @param {Roo.Element} el
6710 * @param {Number} rowIndex
6711 * @param {Number} columnIndex
6712 * @param {Roo.EventObject} e
6716 * @event celldblclick
6717 * Fires when a cell is double clicked
6718 * @param {Roo.bootstrap.Table} this
6719 * @param {Roo.Element} el
6720 * @param {Number} rowIndex
6721 * @param {Number} columnIndex
6722 * @param {Roo.EventObject} e
6724 "celldblclick" : true,
6727 * Fires when a row is clicked
6728 * @param {Roo.bootstrap.Table} this
6729 * @param {Roo.Element} el
6730 * @param {Number} rowIndex
6731 * @param {Roo.EventObject} e
6735 * @event rowdblclick
6736 * Fires when a row is double clicked
6737 * @param {Roo.bootstrap.Table} this
6738 * @param {Roo.Element} el
6739 * @param {Number} rowIndex
6740 * @param {Roo.EventObject} e
6742 "rowdblclick" : true,
6745 * Fires when a mouseover occur
6746 * @param {Roo.bootstrap.Table} this
6747 * @param {Roo.Element} el
6748 * @param {Number} rowIndex
6749 * @param {Number} columnIndex
6750 * @param {Roo.EventObject} e
6755 * Fires when a mouseout occur
6756 * @param {Roo.bootstrap.Table} this
6757 * @param {Roo.Element} el
6758 * @param {Number} rowIndex
6759 * @param {Number} columnIndex
6760 * @param {Roo.EventObject} e
6765 * Fires when a row is rendered, so you can change add a style to it.
6766 * @param {Roo.bootstrap.Table} this
6767 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6771 * @event rowsrendered
6772 * Fires when all the rows have been rendered
6773 * @param {Roo.bootstrap.Table} this
6775 'rowsrendered' : true,
6777 * @event contextmenu
6778 * The raw contextmenu event for the entire grid.
6779 * @param {Roo.EventObject} e
6781 "contextmenu" : true,
6783 * @event rowcontextmenu
6784 * Fires when a row is right clicked
6785 * @param {Roo.bootstrap.Table} this
6786 * @param {Number} rowIndex
6787 * @param {Roo.EventObject} e
6789 "rowcontextmenu" : true,
6791 * @event cellcontextmenu
6792 * Fires when a cell is right clicked
6793 * @param {Roo.bootstrap.Table} this
6794 * @param {Number} rowIndex
6795 * @param {Number} cellIndex
6796 * @param {Roo.EventObject} e
6798 "cellcontextmenu" : true,
6800 * @event headercontextmenu
6801 * Fires when a header is right clicked
6802 * @param {Roo.bootstrap.Table} this
6803 * @param {Number} columnIndex
6804 * @param {Roo.EventObject} e
6806 "headercontextmenu" : true
6810 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6836 rowSelection : false,
6837 cellSelection : false,
6840 // Roo.Element - the tbody
6842 // Roo.Element - thead element
6845 container: false, // used by gridpanel...
6851 auto_hide_footer : false,
6853 getAutoCreate : function()
6855 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6862 if (this.scrollBody) {
6863 cfg.cls += ' table-body-fixed';
6866 cfg.cls += ' table-striped';
6870 cfg.cls += ' table-hover';
6872 if (this.bordered) {
6873 cfg.cls += ' table-bordered';
6875 if (this.condensed) {
6876 cfg.cls += ' table-condensed';
6878 if (this.responsive) {
6879 cfg.cls += ' table-responsive';
6883 cfg.cls+= ' ' +this.cls;
6886 // this lot should be simplifed...
6899 ].forEach(function(k) {
6907 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6910 if(this.store || this.cm){
6911 if(this.headerShow){
6912 cfg.cn.push(this.renderHeader());
6915 cfg.cn.push(this.renderBody());
6917 if(this.footerShow){
6918 cfg.cn.push(this.renderFooter());
6920 // where does this come from?
6921 //cfg.cls+= ' TableGrid';
6924 return { cn : [ cfg ] };
6927 initEvents : function()
6929 if(!this.store || !this.cm){
6932 if (this.selModel) {
6933 this.selModel.initEvents();
6937 //Roo.log('initEvents with ds!!!!');
6939 this.mainBody = this.el.select('tbody', true).first();
6940 this.mainHead = this.el.select('thead', true).first();
6941 this.mainFoot = this.el.select('tfoot', true).first();
6947 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6948 e.on('click', _this.sort, _this);
6951 this.mainBody.on("click", this.onClick, this);
6952 this.mainBody.on("dblclick", this.onDblClick, this);
6954 // why is this done????? = it breaks dialogs??
6955 //this.parent().el.setStyle('position', 'relative');
6959 this.footer.parentId = this.id;
6960 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6963 this.el.select('tfoot tr td').first().addClass('hide');
6968 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6971 this.store.on('load', this.onLoad, this);
6972 this.store.on('beforeload', this.onBeforeLoad, this);
6973 this.store.on('update', this.onUpdate, this);
6974 this.store.on('add', this.onAdd, this);
6975 this.store.on("clear", this.clear, this);
6977 this.el.on("contextmenu", this.onContextMenu, this);
6979 this.mainBody.on('scroll', this.onBodyScroll, this);
6981 this.cm.on("headerchange", this.onHeaderChange, this);
6983 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6987 onContextMenu : function(e, t)
6989 this.processEvent("contextmenu", e);
6992 processEvent : function(name, e)
6994 if (name != 'touchstart' ) {
6995 this.fireEvent(name, e);
6998 var t = e.getTarget();
7000 var cell = Roo.get(t);
7006 if(cell.findParent('tfoot', false, true)){
7010 if(cell.findParent('thead', false, true)){
7012 if(e.getTarget().nodeName.toLowerCase() != 'th'){
7013 cell = Roo.get(t).findParent('th', false, true);
7015 Roo.log("failed to find th in thead?");
7016 Roo.log(e.getTarget());
7021 var cellIndex = cell.dom.cellIndex;
7023 var ename = name == 'touchstart' ? 'click' : name;
7024 this.fireEvent("header" + ename, this, cellIndex, e);
7029 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7030 cell = Roo.get(t).findParent('td', false, true);
7032 Roo.log("failed to find th in tbody?");
7033 Roo.log(e.getTarget());
7038 var row = cell.findParent('tr', false, true);
7039 var cellIndex = cell.dom.cellIndex;
7040 var rowIndex = row.dom.rowIndex - 1;
7044 this.fireEvent("row" + name, this, rowIndex, e);
7048 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7054 onMouseover : function(e, el)
7056 var cell = Roo.get(el);
7062 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7063 cell = cell.findParent('td', false, true);
7066 var row = cell.findParent('tr', false, true);
7067 var cellIndex = cell.dom.cellIndex;
7068 var rowIndex = row.dom.rowIndex - 1; // start from 0
7070 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7074 onMouseout : function(e, el)
7076 var cell = Roo.get(el);
7082 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7083 cell = cell.findParent('td', false, true);
7086 var row = cell.findParent('tr', false, true);
7087 var cellIndex = cell.dom.cellIndex;
7088 var rowIndex = row.dom.rowIndex - 1; // start from 0
7090 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7094 onClick : function(e, el)
7096 var cell = Roo.get(el);
7098 if(!cell || (!this.cellSelection && !this.rowSelection)){
7102 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7103 cell = cell.findParent('td', false, true);
7106 if(!cell || typeof(cell) == 'undefined'){
7110 var row = cell.findParent('tr', false, true);
7112 if(!row || typeof(row) == 'undefined'){
7116 var cellIndex = cell.dom.cellIndex;
7117 var rowIndex = this.getRowIndex(row);
7119 // why??? - should these not be based on SelectionModel?
7120 if(this.cellSelection){
7121 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7124 if(this.rowSelection){
7125 this.fireEvent('rowclick', this, row, rowIndex, e);
7131 onDblClick : function(e,el)
7133 var cell = Roo.get(el);
7135 if(!cell || (!this.cellSelection && !this.rowSelection)){
7139 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7140 cell = cell.findParent('td', false, true);
7143 if(!cell || typeof(cell) == 'undefined'){
7147 var row = cell.findParent('tr', false, true);
7149 if(!row || typeof(row) == 'undefined'){
7153 var cellIndex = cell.dom.cellIndex;
7154 var rowIndex = this.getRowIndex(row);
7156 if(this.cellSelection){
7157 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7160 if(this.rowSelection){
7161 this.fireEvent('rowdblclick', this, row, rowIndex, e);
7165 sort : function(e,el)
7167 var col = Roo.get(el);
7169 if(!col.hasClass('sortable')){
7173 var sort = col.attr('sort');
7176 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7180 this.store.sortInfo = {field : sort, direction : dir};
7183 Roo.log("calling footer first");
7184 this.footer.onClick('first');
7187 this.store.load({ params : { start : 0 } });
7191 renderHeader : function()
7199 this.totalWidth = 0;
7201 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7203 var config = cm.config[i];
7207 cls : 'x-hcol-' + i,
7209 html: cm.getColumnHeader(i)
7214 if(typeof(config.sortable) != 'undefined' && config.sortable){
7216 c.html = '<i class="glyphicon"></i>' + c.html;
7219 // could use BS4 hidden-..-down
7221 if(typeof(config.lgHeader) != 'undefined'){
7222 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
7225 if(typeof(config.mdHeader) != 'undefined'){
7226 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
7229 if(typeof(config.smHeader) != 'undefined'){
7230 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
7233 if(typeof(config.xsHeader) != 'undefined'){
7234 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
7241 if(typeof(config.tooltip) != 'undefined'){
7242 c.tooltip = config.tooltip;
7245 if(typeof(config.colspan) != 'undefined'){
7246 c.colspan = config.colspan;
7249 if(typeof(config.hidden) != 'undefined' && config.hidden){
7250 c.style += ' display:none;';
7253 if(typeof(config.dataIndex) != 'undefined'){
7254 c.sort = config.dataIndex;
7259 if(typeof(config.align) != 'undefined' && config.align.length){
7260 c.style += ' text-align:' + config.align + ';';
7263 if(typeof(config.width) != 'undefined'){
7264 c.style += ' width:' + config.width + 'px;';
7265 this.totalWidth += config.width;
7267 this.totalWidth += 100; // assume minimum of 100 per column?
7270 if(typeof(config.cls) != 'undefined'){
7271 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
7274 ['xs','sm','md','lg'].map(function(size){
7276 if(typeof(config[size]) == 'undefined'){
7280 if (!config[size]) { // 0 = hidden
7281 // BS 4 '0' is treated as hide that column and below.
7282 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
7286 c.cls += ' col-' + size + '-' + config[size] + (
7287 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7299 renderBody : function()
7309 colspan : this.cm.getColumnCount()
7319 renderFooter : function()
7329 colspan : this.cm.getColumnCount()
7343 // Roo.log('ds onload');
7348 var ds = this.store;
7350 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7351 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
7352 if (_this.store.sortInfo) {
7354 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
7355 e.select('i', true).addClass(['glyphicon-arrow-up']);
7358 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
7359 e.select('i', true).addClass(['glyphicon-arrow-down']);
7364 var tbody = this.mainBody;
7366 if(ds.getCount() > 0){
7367 ds.data.each(function(d,rowIndex){
7368 var row = this.renderRow(cm, ds, rowIndex);
7370 tbody.createChild(row);
7374 if(row.cellObjects.length){
7375 Roo.each(row.cellObjects, function(r){
7376 _this.renderCellObject(r);
7383 var tfoot = this.el.select('tfoot', true).first();
7385 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7387 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7389 var total = this.ds.getTotalCount();
7391 if(this.footer.pageSize < total){
7392 this.mainFoot.show();
7396 Roo.each(this.el.select('tbody td', true).elements, function(e){
7397 e.on('mouseover', _this.onMouseover, _this);
7400 Roo.each(this.el.select('tbody td', true).elements, function(e){
7401 e.on('mouseout', _this.onMouseout, _this);
7403 this.fireEvent('rowsrendered', this);
7409 onUpdate : function(ds,record)
7411 this.refreshRow(record);
7415 onRemove : function(ds, record, index, isUpdate){
7416 if(isUpdate !== true){
7417 this.fireEvent("beforerowremoved", this, index, record);
7419 var bt = this.mainBody.dom;
7421 var rows = this.el.select('tbody > tr', true).elements;
7423 if(typeof(rows[index]) != 'undefined'){
7424 bt.removeChild(rows[index].dom);
7427 // if(bt.rows[index]){
7428 // bt.removeChild(bt.rows[index]);
7431 if(isUpdate !== true){
7432 //this.stripeRows(index);
7433 //this.syncRowHeights(index, index);
7435 this.fireEvent("rowremoved", this, index, record);
7439 onAdd : function(ds, records, rowIndex)
7441 //Roo.log('on Add called');
7442 // - note this does not handle multiple adding very well..
7443 var bt = this.mainBody.dom;
7444 for (var i =0 ; i < records.length;i++) {
7445 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7446 //Roo.log(records[i]);
7447 //Roo.log(this.store.getAt(rowIndex+i));
7448 this.insertRow(this.store, rowIndex + i, false);
7455 refreshRow : function(record){
7456 var ds = this.store, index;
7457 if(typeof record == 'number'){
7459 record = ds.getAt(index);
7461 index = ds.indexOf(record);
7463 this.insertRow(ds, index, true);
7465 this.onRemove(ds, record, index+1, true);
7467 //this.syncRowHeights(index, index);
7469 this.fireEvent("rowupdated", this, index, record);
7472 insertRow : function(dm, rowIndex, isUpdate){
7475 this.fireEvent("beforerowsinserted", this, rowIndex);
7477 //var s = this.getScrollState();
7478 var row = this.renderRow(this.cm, this.store, rowIndex);
7479 // insert before rowIndex..
7480 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7484 if(row.cellObjects.length){
7485 Roo.each(row.cellObjects, function(r){
7486 _this.renderCellObject(r);
7491 this.fireEvent("rowsinserted", this, rowIndex);
7492 //this.syncRowHeights(firstRow, lastRow);
7493 //this.stripeRows(firstRow);
7500 getRowDom : function(rowIndex)
7502 var rows = this.el.select('tbody > tr', true).elements;
7504 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7507 // returns the object tree for a tr..
7510 renderRow : function(cm, ds, rowIndex)
7512 var d = ds.getAt(rowIndex);
7516 cls : 'x-row-' + rowIndex,
7520 var cellObjects = [];
7522 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7523 var config = cm.config[i];
7525 var renderer = cm.getRenderer(i);
7529 if(typeof(renderer) !== 'undefined'){
7530 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7532 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7533 // and are rendered into the cells after the row is rendered - using the id for the element.
7535 if(typeof(value) === 'object'){
7545 rowIndex : rowIndex,
7550 this.fireEvent('rowclass', this, rowcfg);
7554 cls : rowcfg.rowClass + ' x-col-' + i,
7556 html: (typeof(value) === 'object') ? '' : value
7563 if(typeof(config.colspan) != 'undefined'){
7564 td.colspan = config.colspan;
7567 if(typeof(config.hidden) != 'undefined' && config.hidden){
7568 td.style += ' display:none;';
7571 if(typeof(config.align) != 'undefined' && config.align.length){
7572 td.style += ' text-align:' + config.align + ';';
7574 if(typeof(config.valign) != 'undefined' && config.valign.length){
7575 td.style += ' vertical-align:' + config.valign + ';';
7578 if(typeof(config.width) != 'undefined'){
7579 td.style += ' width:' + config.width + 'px;';
7582 if(typeof(config.cursor) != 'undefined'){
7583 td.style += ' cursor:' + config.cursor + ';';
7586 if(typeof(config.cls) != 'undefined'){
7587 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7590 ['xs','sm','md','lg'].map(function(size){
7592 if(typeof(config[size]) == 'undefined'){
7598 if (!config[size]) { // 0 = hidden
7599 // BS 4 '0' is treated as hide that column and below.
7600 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7604 td.cls += ' col-' + size + '-' + config[size] + (
7605 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7615 row.cellObjects = cellObjects;
7623 onBeforeLoad : function()
7632 this.el.select('tbody', true).first().dom.innerHTML = '';
7635 * Show or hide a row.
7636 * @param {Number} rowIndex to show or hide
7637 * @param {Boolean} state hide
7639 setRowVisibility : function(rowIndex, state)
7641 var bt = this.mainBody.dom;
7643 var rows = this.el.select('tbody > tr', true).elements;
7645 if(typeof(rows[rowIndex]) == 'undefined'){
7648 rows[rowIndex].dom.style.display = state ? '' : 'none';
7652 getSelectionModel : function(){
7654 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7656 return this.selModel;
7659 * Render the Roo.bootstrap object from renderder
7661 renderCellObject : function(r)
7665 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7667 var t = r.cfg.render(r.container);
7670 Roo.each(r.cfg.cn, function(c){
7672 container: t.getChildContainer(),
7675 _this.renderCellObject(child);
7680 getRowIndex : function(row)
7684 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7695 * Returns the grid's underlying element = used by panel.Grid
7696 * @return {Element} The element
7698 getGridEl : function(){
7702 * Forces a resize - used by panel.Grid
7703 * @return {Element} The element
7705 autoSize : function()
7707 //var ctr = Roo.get(this.container.dom.parentElement);
7708 var ctr = Roo.get(this.el.dom);
7710 var thd = this.getGridEl().select('thead',true).first();
7711 var tbd = this.getGridEl().select('tbody', true).first();
7712 var tfd = this.getGridEl().select('tfoot', true).first();
7714 var cw = ctr.getWidth();
7718 tbd.setWidth(ctr.getWidth());
7719 // if the body has a max height - and then scrolls - we should perhaps set up the height here
7720 // this needs fixing for various usage - currently only hydra job advers I think..
7722 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7724 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7727 cw = Math.max(cw, this.totalWidth);
7728 this.getGridEl().select('tr',true).setWidth(cw);
7729 // resize 'expandable coloumn?
7731 return; // we doe not have a view in this design..
7734 onBodyScroll: function()
7736 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7738 this.mainHead.setStyle({
7739 'position' : 'relative',
7740 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7746 var scrollHeight = this.mainBody.dom.scrollHeight;
7748 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7750 var height = this.mainBody.getHeight();
7752 if(scrollHeight - height == scrollTop) {
7754 var total = this.ds.getTotalCount();
7756 if(this.footer.cursor + this.footer.pageSize < total){
7758 this.footer.ds.load({
7760 start : this.footer.cursor + this.footer.pageSize,
7761 limit : this.footer.pageSize
7771 onHeaderChange : function()
7773 var header = this.renderHeader();
7774 var table = this.el.select('table', true).first();
7776 this.mainHead.remove();
7777 this.mainHead = table.createChild(header, this.mainBody, false);
7780 onHiddenChange : function(colModel, colIndex, hidden)
7782 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7783 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7785 this.CSS.updateRule(thSelector, "display", "");
7786 this.CSS.updateRule(tdSelector, "display", "");
7789 this.CSS.updateRule(thSelector, "display", "none");
7790 this.CSS.updateRule(tdSelector, "display", "none");
7793 this.onHeaderChange();
7797 setColumnWidth: function(col_index, width)
7799 // width = "md-2 xs-2..."
7800 if(!this.colModel.config[col_index]) {
7804 var w = width.split(" ");
7806 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7808 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7811 for(var j = 0; j < w.length; j++) {
7817 var size_cls = w[j].split("-");
7819 if(!Number.isInteger(size_cls[1] * 1)) {
7823 if(!this.colModel.config[col_index][size_cls[0]]) {
7827 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7831 h_row[0].classList.replace(
7832 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7833 "col-"+size_cls[0]+"-"+size_cls[1]
7836 for(var i = 0; i < rows.length; i++) {
7838 var size_cls = w[j].split("-");
7840 if(!Number.isInteger(size_cls[1] * 1)) {
7844 if(!this.colModel.config[col_index][size_cls[0]]) {
7848 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7852 rows[i].classList.replace(
7853 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7854 "col-"+size_cls[0]+"-"+size_cls[1]
7858 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7873 * @class Roo.bootstrap.TableCell
7874 * @extends Roo.bootstrap.Component
7875 * Bootstrap TableCell class
7876 * @cfg {String} html cell contain text
7877 * @cfg {String} cls cell class
7878 * @cfg {String} tag cell tag (td|th) default td
7879 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7880 * @cfg {String} align Aligns the content in a cell
7881 * @cfg {String} axis Categorizes cells
7882 * @cfg {String} bgcolor Specifies the background color of a cell
7883 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7884 * @cfg {Number} colspan Specifies the number of columns a cell should span
7885 * @cfg {String} headers Specifies one or more header cells a cell is related to
7886 * @cfg {Number} height Sets the height of a cell
7887 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7888 * @cfg {Number} rowspan Sets the number of rows a cell should span
7889 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7890 * @cfg {String} valign Vertical aligns the content in a cell
7891 * @cfg {Number} width Specifies the width of a cell
7894 * Create a new TableCell
7895 * @param {Object} config The config object
7898 Roo.bootstrap.TableCell = function(config){
7899 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7902 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7922 getAutoCreate : function(){
7923 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7943 cfg.align=this.align
7949 cfg.bgcolor=this.bgcolor
7952 cfg.charoff=this.charoff
7955 cfg.colspan=this.colspan
7958 cfg.headers=this.headers
7961 cfg.height=this.height
7964 cfg.nowrap=this.nowrap
7967 cfg.rowspan=this.rowspan
7970 cfg.scope=this.scope
7973 cfg.valign=this.valign
7976 cfg.width=this.width
7995 * @class Roo.bootstrap.TableRow
7996 * @extends Roo.bootstrap.Component
7997 * Bootstrap TableRow class
7998 * @cfg {String} cls row class
7999 * @cfg {String} align Aligns the content in a table row
8000 * @cfg {String} bgcolor Specifies a background color for a table row
8001 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8002 * @cfg {String} valign Vertical aligns the content in a table row
8005 * Create a new TableRow
8006 * @param {Object} config The config object
8009 Roo.bootstrap.TableRow = function(config){
8010 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8013 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
8021 getAutoCreate : function(){
8022 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8032 cfg.align = this.align;
8035 cfg.bgcolor = this.bgcolor;
8038 cfg.charoff = this.charoff;
8041 cfg.valign = this.valign;
8059 * @class Roo.bootstrap.TableBody
8060 * @extends Roo.bootstrap.Component
8061 * Bootstrap TableBody class
8062 * @cfg {String} cls element class
8063 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8064 * @cfg {String} align Aligns the content inside the element
8065 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8066 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8069 * Create a new TableBody
8070 * @param {Object} config The config object
8073 Roo.bootstrap.TableBody = function(config){
8074 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8077 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
8085 getAutoCreate : function(){
8086 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8100 cfg.align = this.align;
8103 cfg.charoff = this.charoff;
8106 cfg.valign = this.valign;
8113 // initEvents : function()
8120 // this.store = Roo.factory(this.store, Roo.data);
8121 // this.store.on('load', this.onLoad, this);
8123 // this.store.load();
8127 // onLoad: function ()
8129 // this.fireEvent('load', this);
8139 * Ext JS Library 1.1.1
8140 * Copyright(c) 2006-2007, Ext JS, LLC.
8142 * Originally Released Under LGPL - original licence link has changed is not relivant.
8145 * <script type="text/javascript">
8148 // as we use this in bootstrap.
8149 Roo.namespace('Roo.form');
8151 * @class Roo.form.Action
8152 * Internal Class used to handle form actions
8154 * @param {Roo.form.BasicForm} el The form element or its id
8155 * @param {Object} config Configuration options
8160 // define the action interface
8161 Roo.form.Action = function(form, options){
8163 this.options = options || {};
8166 * Client Validation Failed
8169 Roo.form.Action.CLIENT_INVALID = 'client';
8171 * Server Validation Failed
8174 Roo.form.Action.SERVER_INVALID = 'server';
8176 * Connect to Server Failed
8179 Roo.form.Action.CONNECT_FAILURE = 'connect';
8181 * Reading Data from Server Failed
8184 Roo.form.Action.LOAD_FAILURE = 'load';
8186 Roo.form.Action.prototype = {
8188 failureType : undefined,
8189 response : undefined,
8193 run : function(options){
8198 success : function(response){
8203 handleResponse : function(response){
8207 // default connection failure
8208 failure : function(response){
8210 this.response = response;
8211 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8212 this.form.afterAction(this, false);
8215 processResponse : function(response){
8216 this.response = response;
8217 if(!response.responseText){
8220 this.result = this.handleResponse(response);
8224 // utility functions used internally
8225 getUrl : function(appendParams){
8226 var url = this.options.url || this.form.url || this.form.el.dom.action;
8228 var p = this.getParams();
8230 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
8236 getMethod : function(){
8237 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
8240 getParams : function(){
8241 var bp = this.form.baseParams;
8242 var p = this.options.params;
8244 if(typeof p == "object"){
8245 p = Roo.urlEncode(Roo.applyIf(p, bp));
8246 }else if(typeof p == 'string' && bp){
8247 p += '&' + Roo.urlEncode(bp);
8250 p = Roo.urlEncode(bp);
8255 createCallback : function(){
8257 success: this.success,
8258 failure: this.failure,
8260 timeout: (this.form.timeout*1000),
8261 upload: this.form.fileUpload ? this.success : undefined
8266 Roo.form.Action.Submit = function(form, options){
8267 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
8270 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
8273 haveProgress : false,
8274 uploadComplete : false,
8276 // uploadProgress indicator.
8277 uploadProgress : function()
8279 if (!this.form.progressUrl) {
8283 if (!this.haveProgress) {
8284 Roo.MessageBox.progress("Uploading", "Uploading");
8286 if (this.uploadComplete) {
8287 Roo.MessageBox.hide();
8291 this.haveProgress = true;
8293 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
8295 var c = new Roo.data.Connection();
8297 url : this.form.progressUrl,
8302 success : function(req){
8303 //console.log(data);
8307 rdata = Roo.decode(req.responseText)
8309 Roo.log("Invalid data from server..");
8313 if (!rdata || !rdata.success) {
8315 Roo.MessageBox.alert(Roo.encode(rdata));
8318 var data = rdata.data;
8320 if (this.uploadComplete) {
8321 Roo.MessageBox.hide();
8326 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
8327 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
8330 this.uploadProgress.defer(2000,this);
8333 failure: function(data) {
8334 Roo.log('progress url failed ');
8345 // run get Values on the form, so it syncs any secondary forms.
8346 this.form.getValues();
8348 var o = this.options;
8349 var method = this.getMethod();
8350 var isPost = method == 'POST';
8351 if(o.clientValidation === false || this.form.isValid()){
8353 if (this.form.progressUrl) {
8354 this.form.findField('UPLOAD_IDENTIFIER').setValue(
8355 (new Date() * 1) + '' + Math.random());
8360 Roo.Ajax.request(Roo.apply(this.createCallback(), {
8361 form:this.form.el.dom,
8362 url:this.getUrl(!isPost),
8364 params:isPost ? this.getParams() : null,
8365 isUpload: this.form.fileUpload,
8366 formData : this.form.formData
8369 this.uploadProgress();
8371 }else if (o.clientValidation !== false){ // client validation failed
8372 this.failureType = Roo.form.Action.CLIENT_INVALID;
8373 this.form.afterAction(this, false);
8377 success : function(response)
8379 this.uploadComplete= true;
8380 if (this.haveProgress) {
8381 Roo.MessageBox.hide();
8385 var result = this.processResponse(response);
8386 if(result === true || result.success){
8387 this.form.afterAction(this, true);
8391 this.form.markInvalid(result.errors);
8392 this.failureType = Roo.form.Action.SERVER_INVALID;
8394 this.form.afterAction(this, false);
8396 failure : function(response)
8398 this.uploadComplete= true;
8399 if (this.haveProgress) {
8400 Roo.MessageBox.hide();
8403 this.response = response;
8404 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8405 this.form.afterAction(this, false);
8408 handleResponse : function(response){
8409 if(this.form.errorReader){
8410 var rs = this.form.errorReader.read(response);
8413 for(var i = 0, len = rs.records.length; i < len; i++) {
8414 var r = rs.records[i];
8418 if(errors.length < 1){
8422 success : rs.success,
8428 ret = Roo.decode(response.responseText);
8432 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8442 Roo.form.Action.Load = function(form, options){
8443 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8444 this.reader = this.form.reader;
8447 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8452 Roo.Ajax.request(Roo.apply(
8453 this.createCallback(), {
8454 method:this.getMethod(),
8455 url:this.getUrl(false),
8456 params:this.getParams()
8460 success : function(response){
8462 var result = this.processResponse(response);
8463 if(result === true || !result.success || !result.data){
8464 this.failureType = Roo.form.Action.LOAD_FAILURE;
8465 this.form.afterAction(this, false);
8468 this.form.clearInvalid();
8469 this.form.setValues(result.data);
8470 this.form.afterAction(this, true);
8473 handleResponse : function(response){
8474 if(this.form.reader){
8475 var rs = this.form.reader.read(response);
8476 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8478 success : rs.success,
8482 return Roo.decode(response.responseText);
8486 Roo.form.Action.ACTION_TYPES = {
8487 'load' : Roo.form.Action.Load,
8488 'submit' : Roo.form.Action.Submit
8497 * @class Roo.bootstrap.Form
8498 * @extends Roo.bootstrap.Component
8499 * Bootstrap Form class
8500 * @cfg {String} method GET | POST (default POST)
8501 * @cfg {String} labelAlign top | left (default top)
8502 * @cfg {String} align left | right - for navbars
8503 * @cfg {Boolean} loadMask load mask when submit (default true)
8508 * @param {Object} config The config object
8512 Roo.bootstrap.Form = function(config){
8514 Roo.bootstrap.Form.superclass.constructor.call(this, config);
8516 Roo.bootstrap.Form.popover.apply();
8520 * @event clientvalidation
8521 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8522 * @param {Form} this
8523 * @param {Boolean} valid true if the form has passed client-side validation
8525 clientvalidation: true,
8527 * @event beforeaction
8528 * Fires before any action is performed. Return false to cancel the action.
8529 * @param {Form} this
8530 * @param {Action} action The action to be performed
8534 * @event actionfailed
8535 * Fires when an action fails.
8536 * @param {Form} this
8537 * @param {Action} action The action that failed
8539 actionfailed : true,
8541 * @event actioncomplete
8542 * Fires when an action is completed.
8543 * @param {Form} this
8544 * @param {Action} action The action that completed
8546 actioncomplete : true
8550 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
8553 * @cfg {String} method
8554 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8559 * The URL to use for form actions if one isn't supplied in the action options.
8562 * @cfg {Boolean} fileUpload
8563 * Set to true if this form is a file upload.
8567 * @cfg {Object} baseParams
8568 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8572 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8576 * @cfg {Sting} align (left|right) for navbar forms
8581 activeAction : null,
8584 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8585 * element by passing it or its id or mask the form itself by passing in true.
8588 waitMsgTarget : false,
8593 * @cfg {Boolean} errorMask (true|false) default false
8598 * @cfg {Number} maskOffset Default 100
8603 * @cfg {Boolean} maskBody
8607 getAutoCreate : function(){
8611 method : this.method || 'POST',
8612 id : this.id || Roo.id(),
8615 if (this.parent().xtype.match(/^Nav/)) {
8616 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8620 if (this.labelAlign == 'left' ) {
8621 cfg.cls += ' form-horizontal';
8627 initEvents : function()
8629 this.el.on('submit', this.onSubmit, this);
8630 // this was added as random key presses on the form where triggering form submit.
8631 this.el.on('keypress', function(e) {
8632 if (e.getCharCode() != 13) {
8635 // we might need to allow it for textareas.. and some other items.
8636 // check e.getTarget().
8638 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8642 Roo.log("keypress blocked");
8650 onSubmit : function(e){
8655 * Returns true if client-side validation on the form is successful.
8658 isValid : function(){
8659 var items = this.getItems();
8663 items.each(function(f){
8669 Roo.log('invalid field: ' + f.name);
8673 if(!target && f.el.isVisible(true)){
8679 if(this.errorMask && !valid){
8680 Roo.bootstrap.Form.popover.mask(this, target);
8687 * Returns true if any fields in this form have changed since their original load.
8690 isDirty : function(){
8692 var items = this.getItems();
8693 items.each(function(f){
8703 * Performs a predefined action (submit or load) or custom actions you define on this form.
8704 * @param {String} actionName The name of the action type
8705 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8706 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8707 * accept other config options):
8709 Property Type Description
8710 ---------------- --------------- ----------------------------------------------------------------------------------
8711 url String The url for the action (defaults to the form's url)
8712 method String The form method to use (defaults to the form's method, or POST if not defined)
8713 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8714 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8715 validate the form on the client (defaults to false)
8717 * @return {BasicForm} this
8719 doAction : function(action, options){
8720 if(typeof action == 'string'){
8721 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8723 if(this.fireEvent('beforeaction', this, action) !== false){
8724 this.beforeAction(action);
8725 action.run.defer(100, action);
8731 beforeAction : function(action){
8732 var o = action.options;
8737 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8739 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8742 // not really supported yet.. ??
8744 //if(this.waitMsgTarget === true){
8745 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8746 //}else if(this.waitMsgTarget){
8747 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8748 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8750 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8756 afterAction : function(action, success){
8757 this.activeAction = null;
8758 var o = action.options;
8763 Roo.get(document.body).unmask();
8769 //if(this.waitMsgTarget === true){
8770 // this.el.unmask();
8771 //}else if(this.waitMsgTarget){
8772 // this.waitMsgTarget.unmask();
8774 // Roo.MessageBox.updateProgress(1);
8775 // Roo.MessageBox.hide();
8782 Roo.callback(o.success, o.scope, [this, action]);
8783 this.fireEvent('actioncomplete', this, action);
8787 // failure condition..
8788 // we have a scenario where updates need confirming.
8789 // eg. if a locking scenario exists..
8790 // we look for { errors : { needs_confirm : true }} in the response.
8792 (typeof(action.result) != 'undefined') &&
8793 (typeof(action.result.errors) != 'undefined') &&
8794 (typeof(action.result.errors.needs_confirm) != 'undefined')
8797 Roo.log("not supported yet");
8800 Roo.MessageBox.confirm(
8801 "Change requires confirmation",
8802 action.result.errorMsg,
8807 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8817 Roo.callback(o.failure, o.scope, [this, action]);
8818 // show an error message if no failed handler is set..
8819 if (!this.hasListener('actionfailed')) {
8820 Roo.log("need to add dialog support");
8822 Roo.MessageBox.alert("Error",
8823 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8824 action.result.errorMsg :
8825 "Saving Failed, please check your entries or try again"
8830 this.fireEvent('actionfailed', this, action);
8835 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8836 * @param {String} id The value to search for
8839 findField : function(id){
8840 var items = this.getItems();
8841 var field = items.get(id);
8843 items.each(function(f){
8844 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8851 return field || null;
8854 * Mark fields in this form invalid in bulk.
8855 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8856 * @return {BasicForm} this
8858 markInvalid : function(errors){
8859 if(errors instanceof Array){
8860 for(var i = 0, len = errors.length; i < len; i++){
8861 var fieldError = errors[i];
8862 var f = this.findField(fieldError.id);
8864 f.markInvalid(fieldError.msg);
8870 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8871 field.markInvalid(errors[id]);
8875 //Roo.each(this.childForms || [], function (f) {
8876 // f.markInvalid(errors);
8883 * Set values for fields in this form in bulk.
8884 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8885 * @return {BasicForm} this
8887 setValues : function(values){
8888 if(values instanceof Array){ // array of objects
8889 for(var i = 0, len = values.length; i < len; i++){
8891 var f = this.findField(v.id);
8893 f.setValue(v.value);
8894 if(this.trackResetOnLoad){
8895 f.originalValue = f.getValue();
8899 }else{ // object hash
8902 if(typeof values[id] != 'function' && (field = this.findField(id))){
8904 if (field.setFromData &&
8906 field.displayField &&
8907 // combos' with local stores can
8908 // be queried via setValue()
8909 // to set their value..
8910 (field.store && !field.store.isLocal)
8914 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8915 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8916 field.setFromData(sd);
8918 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8920 field.setFromData(values);
8923 field.setValue(values[id]);
8927 if(this.trackResetOnLoad){
8928 field.originalValue = field.getValue();
8934 //Roo.each(this.childForms || [], function (f) {
8935 // f.setValues(values);
8942 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8943 * they are returned as an array.
8944 * @param {Boolean} asString
8947 getValues : function(asString){
8948 //if (this.childForms) {
8949 // copy values from the child forms
8950 // Roo.each(this.childForms, function (f) {
8951 // this.setValues(f.getValues());
8957 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8958 if(asString === true){
8961 return Roo.urlDecode(fs);
8965 * Returns the fields in this form as an object with key/value pairs.
8966 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8969 getFieldValues : function(with_hidden)
8971 var items = this.getItems();
8973 items.each(function(f){
8979 var v = f.getValue();
8981 if (f.inputType =='radio') {
8982 if (typeof(ret[f.getName()]) == 'undefined') {
8983 ret[f.getName()] = ''; // empty..
8986 if (!f.el.dom.checked) {
8994 if(f.xtype == 'MoneyField'){
8995 ret[f.currencyName] = f.getCurrency();
8998 // not sure if this supported any more..
8999 if ((typeof(v) == 'object') && f.getRawValue) {
9000 v = f.getRawValue() ; // dates..
9002 // combo boxes where name != hiddenName...
9003 if (f.name !== false && f.name != '' && f.name != f.getName()) {
9004 ret[f.name] = f.getRawValue();
9006 ret[f.getName()] = v;
9013 * Clears all invalid messages in this form.
9014 * @return {BasicForm} this
9016 clearInvalid : function(){
9017 var items = this.getItems();
9019 items.each(function(f){
9028 * @return {BasicForm} this
9031 var items = this.getItems();
9032 items.each(function(f){
9036 Roo.each(this.childForms || [], function (f) {
9044 getItems : function()
9046 var r=new Roo.util.MixedCollection(false, function(o){
9047 return o.id || (o.id = Roo.id());
9049 var iter = function(el) {
9056 Roo.each(el.items,function(e) {
9065 hideFields : function(items)
9067 Roo.each(items, function(i){
9069 var f = this.findField(i);
9080 showFields : function(items)
9082 Roo.each(items, function(i){
9084 var f = this.findField(i);
9097 Roo.apply(Roo.bootstrap.Form, {
9124 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9125 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9126 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9127 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9130 this.maskEl.top.enableDisplayMode("block");
9131 this.maskEl.left.enableDisplayMode("block");
9132 this.maskEl.bottom.enableDisplayMode("block");
9133 this.maskEl.right.enableDisplayMode("block");
9135 this.toolTip = new Roo.bootstrap.Tooltip({
9136 cls : 'roo-form-error-popover',
9138 'left' : ['r-l', [-2,0], 'right'],
9139 'right' : ['l-r', [2,0], 'left'],
9140 'bottom' : ['tl-bl', [0,2], 'top'],
9141 'top' : [ 'bl-tl', [0,-2], 'bottom']
9145 this.toolTip.render(Roo.get(document.body));
9147 this.toolTip.el.enableDisplayMode("block");
9149 Roo.get(document.body).on('click', function(){
9153 Roo.get(document.body).on('touchstart', function(){
9157 this.isApplied = true
9160 mask : function(form, target)
9164 this.target = target;
9166 if(!this.form.errorMask || !target.el){
9170 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9172 Roo.log(scrollable);
9174 var ot = this.target.el.calcOffsetsTo(scrollable);
9176 var scrollTo = ot[1] - this.form.maskOffset;
9178 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9180 scrollable.scrollTo('top', scrollTo);
9182 var box = this.target.el.getBox();
9184 var zIndex = Roo.bootstrap.Modal.zIndex++;
9187 this.maskEl.top.setStyle('position', 'absolute');
9188 this.maskEl.top.setStyle('z-index', zIndex);
9189 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9190 this.maskEl.top.setLeft(0);
9191 this.maskEl.top.setTop(0);
9192 this.maskEl.top.show();
9194 this.maskEl.left.setStyle('position', 'absolute');
9195 this.maskEl.left.setStyle('z-index', zIndex);
9196 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9197 this.maskEl.left.setLeft(0);
9198 this.maskEl.left.setTop(box.y - this.padding);
9199 this.maskEl.left.show();
9201 this.maskEl.bottom.setStyle('position', 'absolute');
9202 this.maskEl.bottom.setStyle('z-index', zIndex);
9203 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9204 this.maskEl.bottom.setLeft(0);
9205 this.maskEl.bottom.setTop(box.bottom + this.padding);
9206 this.maskEl.bottom.show();
9208 this.maskEl.right.setStyle('position', 'absolute');
9209 this.maskEl.right.setStyle('z-index', zIndex);
9210 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9211 this.maskEl.right.setLeft(box.right + this.padding);
9212 this.maskEl.right.setTop(box.y - this.padding);
9213 this.maskEl.right.show();
9215 this.toolTip.bindEl = this.target.el;
9217 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9219 var tip = this.target.blankText;
9221 if(this.target.getValue() !== '' ) {
9223 if (this.target.invalidText.length) {
9224 tip = this.target.invalidText;
9225 } else if (this.target.regexText.length){
9226 tip = this.target.regexText;
9230 this.toolTip.show(tip);
9232 this.intervalID = window.setInterval(function() {
9233 Roo.bootstrap.Form.popover.unmask();
9236 window.onwheel = function(){ return false;};
9238 (function(){ this.isMasked = true; }).defer(500, this);
9244 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
9248 this.maskEl.top.setStyle('position', 'absolute');
9249 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
9250 this.maskEl.top.hide();
9252 this.maskEl.left.setStyle('position', 'absolute');
9253 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
9254 this.maskEl.left.hide();
9256 this.maskEl.bottom.setStyle('position', 'absolute');
9257 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
9258 this.maskEl.bottom.hide();
9260 this.maskEl.right.setStyle('position', 'absolute');
9261 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
9262 this.maskEl.right.hide();
9264 this.toolTip.hide();
9266 this.toolTip.el.hide();
9268 window.onwheel = function(){ return true;};
9270 if(this.intervalID){
9271 window.clearInterval(this.intervalID);
9272 this.intervalID = false;
9275 this.isMasked = false;
9285 * Ext JS Library 1.1.1
9286 * Copyright(c) 2006-2007, Ext JS, LLC.
9288 * Originally Released Under LGPL - original licence link has changed is not relivant.
9291 * <script type="text/javascript">
9294 * @class Roo.form.VTypes
9295 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
9298 Roo.form.VTypes = function(){
9299 // closure these in so they are only created once.
9300 var alpha = /^[a-zA-Z_]+$/;
9301 var alphanum = /^[a-zA-Z0-9_]+$/;
9302 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
9303 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
9305 // All these messages and functions are configurable
9308 * The function used to validate email addresses
9309 * @param {String} value The email address
9311 'email' : function(v){
9312 return email.test(v);
9315 * The error text to display when the email validation function returns false
9318 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
9320 * The keystroke filter mask to be applied on email input
9323 'emailMask' : /[a-z0-9_\.\-@]/i,
9326 * The function used to validate URLs
9327 * @param {String} value The URL
9329 'url' : function(v){
9333 * The error text to display when the url validation function returns false
9336 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
9339 * The function used to validate alpha values
9340 * @param {String} value The value
9342 'alpha' : function(v){
9343 return alpha.test(v);
9346 * The error text to display when the alpha validation function returns false
9349 'alphaText' : 'This field should only contain letters and _',
9351 * The keystroke filter mask to be applied on alpha input
9354 'alphaMask' : /[a-z_]/i,
9357 * The function used to validate alphanumeric values
9358 * @param {String} value The value
9360 'alphanum' : function(v){
9361 return alphanum.test(v);
9364 * The error text to display when the alphanumeric validation function returns false
9367 'alphanumText' : 'This field should only contain letters, numbers and _',
9369 * The keystroke filter mask to be applied on alphanumeric input
9372 'alphanumMask' : /[a-z0-9_]/i
9382 * @class Roo.bootstrap.Input
9383 * @extends Roo.bootstrap.Component
9384 * Bootstrap Input class
9385 * @cfg {Boolean} disabled is it disabled
9386 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9387 * @cfg {String} name name of the input
9388 * @cfg {string} fieldLabel - the label associated
9389 * @cfg {string} placeholder - placeholder to put in text.
9390 * @cfg {string} before - input group add on before
9391 * @cfg {string} after - input group add on after
9392 * @cfg {string} size - (lg|sm) or leave empty..
9393 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9394 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9395 * @cfg {Number} md colspan out of 12 for computer-sized screens
9396 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9397 * @cfg {string} value default value of the input
9398 * @cfg {Number} labelWidth set the width of label
9399 * @cfg {Number} labellg set the width of label (1-12)
9400 * @cfg {Number} labelmd set the width of label (1-12)
9401 * @cfg {Number} labelsm set the width of label (1-12)
9402 * @cfg {Number} labelxs set the width of label (1-12)
9403 * @cfg {String} labelAlign (top|left)
9404 * @cfg {Boolean} readOnly Specifies that the field should be read-only
9405 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9406 * @cfg {String} indicatorpos (left|right) default left
9407 * @cfg {String} capture (user|camera) use for file input only. (default empty)
9408 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9410 * @cfg {String} align (left|center|right) Default left
9411 * @cfg {Boolean} forceFeedback (true|false) Default false
9414 * Create a new Input
9415 * @param {Object} config The config object
9418 Roo.bootstrap.Input = function(config){
9420 Roo.bootstrap.Input.superclass.constructor.call(this, config);
9425 * Fires when this field receives input focus.
9426 * @param {Roo.form.Field} this
9431 * Fires when this field loses input focus.
9432 * @param {Roo.form.Field} this
9437 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
9438 * {@link Roo.EventObject#getKey} to determine which key was pressed.
9439 * @param {Roo.form.Field} this
9440 * @param {Roo.EventObject} e The event object
9445 * Fires just before the field blurs if the field value has changed.
9446 * @param {Roo.form.Field} this
9447 * @param {Mixed} newValue The new value
9448 * @param {Mixed} oldValue The original value
9453 * Fires after the field has been marked as invalid.
9454 * @param {Roo.form.Field} this
9455 * @param {String} msg The validation message
9460 * Fires after the field has been validated with no errors.
9461 * @param {Roo.form.Field} this
9466 * Fires after the key up
9467 * @param {Roo.form.Field} this
9468 * @param {Roo.EventObject} e The event Object
9474 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
9476 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9477 automatic validation (defaults to "keyup").
9479 validationEvent : "keyup",
9481 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9483 validateOnBlur : true,
9485 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9487 validationDelay : 250,
9489 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9491 focusClass : "x-form-focus", // not needed???
9495 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9497 invalidClass : "has-warning",
9500 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9502 validClass : "has-success",
9505 * @cfg {Boolean} hasFeedback (true|false) default true
9510 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9512 invalidFeedbackClass : "glyphicon-warning-sign",
9515 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9517 validFeedbackClass : "glyphicon-ok",
9520 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9522 selectOnFocus : false,
9525 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9529 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9534 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9536 disableKeyFilter : false,
9539 * @cfg {Boolean} disabled True to disable the field (defaults to false).
9543 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9547 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9549 blankText : "Please complete this mandatory field",
9552 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9556 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9558 maxLength : Number.MAX_VALUE,
9560 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9562 minLengthText : "The minimum length for this field is {0}",
9564 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9566 maxLengthText : "The maximum length for this field is {0}",
9570 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9571 * If available, this function will be called only after the basic validators all return true, and will be passed the
9572 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9576 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9577 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9578 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9582 * @cfg {String} regexText -- Depricated - use Invalid Text
9587 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9593 autocomplete: false,
9612 formatedValue : false,
9613 forceFeedback : false,
9615 indicatorpos : 'left',
9625 parentLabelAlign : function()
9628 while (parent.parent()) {
9629 parent = parent.parent();
9630 if (typeof(parent.labelAlign) !='undefined') {
9631 return parent.labelAlign;
9638 getAutoCreate : function()
9640 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9646 if(this.inputType != 'hidden'){
9647 cfg.cls = 'form-group' //input-group
9653 type : this.inputType,
9655 cls : 'form-control',
9656 placeholder : this.placeholder || '',
9657 autocomplete : this.autocomplete || 'new-password'
9660 if(this.capture.length){
9661 input.capture = this.capture;
9664 if(this.accept.length){
9665 input.accept = this.accept + "/*";
9669 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9672 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9673 input.maxLength = this.maxLength;
9676 if (this.disabled) {
9677 input.disabled=true;
9680 if (this.readOnly) {
9681 input.readonly=true;
9685 input.name = this.name;
9689 input.cls += ' input-' + this.size;
9693 ['xs','sm','md','lg'].map(function(size){
9694 if (settings[size]) {
9695 cfg.cls += ' col-' + size + '-' + settings[size];
9699 var inputblock = input;
9703 cls: 'glyphicon form-control-feedback'
9706 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9709 cls : 'has-feedback',
9717 if (this.before || this.after) {
9720 cls : 'input-group',
9724 if (this.before && typeof(this.before) == 'string') {
9726 inputblock.cn.push({
9728 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9732 if (this.before && typeof(this.before) == 'object') {
9733 this.before = Roo.factory(this.before);
9735 inputblock.cn.push({
9737 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9738 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9742 inputblock.cn.push(input);
9744 if (this.after && typeof(this.after) == 'string') {
9745 inputblock.cn.push({
9747 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9751 if (this.after && typeof(this.after) == 'object') {
9752 this.after = Roo.factory(this.after);
9754 inputblock.cn.push({
9756 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9757 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9761 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9762 inputblock.cls += ' has-feedback';
9763 inputblock.cn.push(feedback);
9768 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9769 tooltip : 'This field is required'
9771 if (Roo.bootstrap.version == 4) {
9774 style : 'display-none'
9777 if (align ==='left' && this.fieldLabel.length) {
9779 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
9786 cls : 'control-label col-form-label',
9787 html : this.fieldLabel
9798 var labelCfg = cfg.cn[1];
9799 var contentCfg = cfg.cn[2];
9801 if(this.indicatorpos == 'right'){
9806 cls : 'control-label col-form-label',
9810 html : this.fieldLabel
9824 labelCfg = cfg.cn[0];
9825 contentCfg = cfg.cn[1];
9829 if(this.labelWidth > 12){
9830 labelCfg.style = "width: " + this.labelWidth + 'px';
9833 if(this.labelWidth < 13 && this.labelmd == 0){
9834 this.labelmd = this.labelWidth;
9837 if(this.labellg > 0){
9838 labelCfg.cls += ' col-lg-' + this.labellg;
9839 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9842 if(this.labelmd > 0){
9843 labelCfg.cls += ' col-md-' + this.labelmd;
9844 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9847 if(this.labelsm > 0){
9848 labelCfg.cls += ' col-sm-' + this.labelsm;
9849 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9852 if(this.labelxs > 0){
9853 labelCfg.cls += ' col-xs-' + this.labelxs;
9854 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9858 } else if ( this.fieldLabel.length) {
9863 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9864 tooltip : 'This field is required'
9868 //cls : 'input-group-addon',
9869 html : this.fieldLabel
9877 if(this.indicatorpos == 'right'){
9882 //cls : 'input-group-addon',
9883 html : this.fieldLabel
9888 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9889 tooltip : 'This field is required'
9909 if (this.parentType === 'Navbar' && this.parent().bar) {
9910 cfg.cls += ' navbar-form';
9913 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9914 // on BS4 we do this only if not form
9915 cfg.cls += ' navbar-form';
9923 * return the real input element.
9925 inputEl: function ()
9927 return this.el.select('input.form-control',true).first();
9930 tooltipEl : function()
9932 return this.inputEl();
9935 indicatorEl : function()
9937 if (Roo.bootstrap.version == 4) {
9938 return false; // not enabled in v4 yet.
9941 var indicator = this.el.select('i.roo-required-indicator',true).first();
9951 setDisabled : function(v)
9953 var i = this.inputEl().dom;
9955 i.removeAttribute('disabled');
9959 i.setAttribute('disabled','true');
9961 initEvents : function()
9964 this.inputEl().on("keydown" , this.fireKey, this);
9965 this.inputEl().on("focus", this.onFocus, this);
9966 this.inputEl().on("blur", this.onBlur, this);
9968 this.inputEl().relayEvent('keyup', this);
9970 this.indicator = this.indicatorEl();
9973 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9976 // reference to original value for reset
9977 this.originalValue = this.getValue();
9978 //Roo.form.TextField.superclass.initEvents.call(this);
9979 if(this.validationEvent == 'keyup'){
9980 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9981 this.inputEl().on('keyup', this.filterValidation, this);
9983 else if(this.validationEvent !== false){
9984 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9987 if(this.selectOnFocus){
9988 this.on("focus", this.preFocus, this);
9991 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9992 this.inputEl().on("keypress", this.filterKeys, this);
9994 this.inputEl().relayEvent('keypress', this);
9997 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9998 this.el.on("click", this.autoSize, this);
10001 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10002 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10005 if (typeof(this.before) == 'object') {
10006 this.before.render(this.el.select('.roo-input-before',true).first());
10008 if (typeof(this.after) == 'object') {
10009 this.after.render(this.el.select('.roo-input-after',true).first());
10012 this.inputEl().on('change', this.onChange, this);
10015 filterValidation : function(e){
10016 if(!e.isNavKeyPress()){
10017 this.validationTask.delay(this.validationDelay);
10021 * Validates the field value
10022 * @return {Boolean} True if the value is valid, else false
10024 validate : function(){
10025 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10026 if(this.disabled || this.validateValue(this.getRawValue())){
10031 this.markInvalid();
10037 * Validates a value according to the field's validation rules and marks the field as invalid
10038 * if the validation fails
10039 * @param {Mixed} value The value to validate
10040 * @return {Boolean} True if the value is valid, else false
10042 validateValue : function(value)
10044 if(this.getVisibilityEl().hasClass('hidden')){
10048 if(value.length < 1) { // if it's blank
10049 if(this.allowBlank){
10055 if(value.length < this.minLength){
10058 if(value.length > this.maxLength){
10062 var vt = Roo.form.VTypes;
10063 if(!vt[this.vtype](value, this)){
10067 if(typeof this.validator == "function"){
10068 var msg = this.validator(value);
10072 if (typeof(msg) == 'string') {
10073 this.invalidText = msg;
10077 if(this.regex && !this.regex.test(value)){
10085 fireKey : function(e){
10086 //Roo.log('field ' + e.getKey());
10087 if(e.isNavKeyPress()){
10088 this.fireEvent("specialkey", this, e);
10091 focus : function (selectText){
10093 this.inputEl().focus();
10094 if(selectText === true){
10095 this.inputEl().dom.select();
10101 onFocus : function(){
10102 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10103 // this.el.addClass(this.focusClass);
10105 if(!this.hasFocus){
10106 this.hasFocus = true;
10107 this.startValue = this.getValue();
10108 this.fireEvent("focus", this);
10112 beforeBlur : Roo.emptyFn,
10116 onBlur : function(){
10118 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10119 //this.el.removeClass(this.focusClass);
10121 this.hasFocus = false;
10122 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10125 var v = this.getValue();
10126 if(String(v) !== String(this.startValue)){
10127 this.fireEvent('change', this, v, this.startValue);
10129 this.fireEvent("blur", this);
10132 onChange : function(e)
10134 var v = this.getValue();
10135 if(String(v) !== String(this.startValue)){
10136 this.fireEvent('change', this, v, this.startValue);
10142 * Resets the current field value to the originally loaded value and clears any validation messages
10144 reset : function(){
10145 this.setValue(this.originalValue);
10149 * Returns the name of the field
10150 * @return {Mixed} name The name field
10152 getName: function(){
10156 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
10157 * @return {Mixed} value The field value
10159 getValue : function(){
10161 var v = this.inputEl().getValue();
10166 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
10167 * @return {Mixed} value The field value
10169 getRawValue : function(){
10170 var v = this.inputEl().getValue();
10176 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
10177 * @param {Mixed} value The value to set
10179 setRawValue : function(v){
10180 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10183 selectText : function(start, end){
10184 var v = this.getRawValue();
10186 start = start === undefined ? 0 : start;
10187 end = end === undefined ? v.length : end;
10188 var d = this.inputEl().dom;
10189 if(d.setSelectionRange){
10190 d.setSelectionRange(start, end);
10191 }else if(d.createTextRange){
10192 var range = d.createTextRange();
10193 range.moveStart("character", start);
10194 range.moveEnd("character", v.length-end);
10201 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
10202 * @param {Mixed} value The value to set
10204 setValue : function(v){
10207 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10213 processValue : function(value){
10214 if(this.stripCharsRe){
10215 var newValue = value.replace(this.stripCharsRe, '');
10216 if(newValue !== value){
10217 this.setRawValue(newValue);
10224 preFocus : function(){
10226 if(this.selectOnFocus){
10227 this.inputEl().dom.select();
10230 filterKeys : function(e){
10231 var k = e.getKey();
10232 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
10235 var c = e.getCharCode(), cc = String.fromCharCode(c);
10236 if(Roo.isIE && (e.isSpecialKey() || !cc)){
10239 if(!this.maskRe.test(cc)){
10244 * Clear any invalid styles/messages for this field
10246 clearInvalid : function(){
10248 if(!this.el || this.preventMark){ // not rendered
10253 this.el.removeClass([this.invalidClass, 'is-invalid']);
10255 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10257 var feedback = this.el.select('.form-control-feedback', true).first();
10260 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10265 if(this.indicator){
10266 this.indicator.removeClass('visible');
10267 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10270 this.fireEvent('valid', this);
10274 * Mark this field as valid
10276 markValid : function()
10278 if(!this.el || this.preventMark){ // not rendered...
10282 this.el.removeClass([this.invalidClass, this.validClass]);
10283 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10285 var feedback = this.el.select('.form-control-feedback', true).first();
10288 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10291 if(this.indicator){
10292 this.indicator.removeClass('visible');
10293 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10300 if(this.allowBlank && !this.getRawValue().length){
10303 if (Roo.bootstrap.version == 3) {
10304 this.el.addClass(this.validClass);
10306 this.inputEl().addClass('is-valid');
10309 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10311 var feedback = this.el.select('.form-control-feedback', true).first();
10314 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10315 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10320 this.fireEvent('valid', this);
10324 * Mark this field as invalid
10325 * @param {String} msg The validation message
10327 markInvalid : function(msg)
10329 if(!this.el || this.preventMark){ // not rendered
10333 this.el.removeClass([this.invalidClass, this.validClass]);
10334 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10336 var feedback = this.el.select('.form-control-feedback', true).first();
10339 this.el.select('.form-control-feedback', true).first().removeClass(
10340 [this.invalidFeedbackClass, this.validFeedbackClass]);
10347 if(this.allowBlank && !this.getRawValue().length){
10351 if(this.indicator){
10352 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10353 this.indicator.addClass('visible');
10355 if (Roo.bootstrap.version == 3) {
10356 this.el.addClass(this.invalidClass);
10358 this.inputEl().addClass('is-invalid');
10363 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10365 var feedback = this.el.select('.form-control-feedback', true).first();
10368 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10370 if(this.getValue().length || this.forceFeedback){
10371 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10378 this.fireEvent('invalid', this, msg);
10381 SafariOnKeyDown : function(event)
10383 // this is a workaround for a password hang bug on chrome/ webkit.
10384 if (this.inputEl().dom.type != 'password') {
10388 var isSelectAll = false;
10390 if(this.inputEl().dom.selectionEnd > 0){
10391 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10393 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10394 event.preventDefault();
10399 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10401 event.preventDefault();
10402 // this is very hacky as keydown always get's upper case.
10404 var cc = String.fromCharCode(event.getCharCode());
10405 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
10409 adjustWidth : function(tag, w){
10410 tag = tag.toLowerCase();
10411 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10412 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10413 if(tag == 'input'){
10416 if(tag == 'textarea'){
10419 }else if(Roo.isOpera){
10420 if(tag == 'input'){
10423 if(tag == 'textarea'){
10431 setFieldLabel : function(v)
10433 if(!this.rendered){
10437 if(this.indicatorEl()){
10438 var ar = this.el.select('label > span',true);
10440 if (ar.elements.length) {
10441 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10442 this.fieldLabel = v;
10446 var br = this.el.select('label',true);
10448 if(br.elements.length) {
10449 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10450 this.fieldLabel = v;
10454 Roo.log('Cannot Found any of label > span || label in input');
10458 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10459 this.fieldLabel = v;
10474 * @class Roo.bootstrap.TextArea
10475 * @extends Roo.bootstrap.Input
10476 * Bootstrap TextArea class
10477 * @cfg {Number} cols Specifies the visible width of a text area
10478 * @cfg {Number} rows Specifies the visible number of lines in a text area
10479 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10480 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10481 * @cfg {string} html text
10484 * Create a new TextArea
10485 * @param {Object} config The config object
10488 Roo.bootstrap.TextArea = function(config){
10489 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10493 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
10503 getAutoCreate : function(){
10505 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10511 if(this.inputType != 'hidden'){
10512 cfg.cls = 'form-group' //input-group
10520 value : this.value || '',
10521 html: this.html || '',
10522 cls : 'form-control',
10523 placeholder : this.placeholder || ''
10527 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10528 input.maxLength = this.maxLength;
10532 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10536 input.cols = this.cols;
10539 if (this.readOnly) {
10540 input.readonly = true;
10544 input.name = this.name;
10548 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10552 ['xs','sm','md','lg'].map(function(size){
10553 if (settings[size]) {
10554 cfg.cls += ' col-' + size + '-' + settings[size];
10558 var inputblock = input;
10560 if(this.hasFeedback && !this.allowBlank){
10564 cls: 'glyphicon form-control-feedback'
10568 cls : 'has-feedback',
10577 if (this.before || this.after) {
10580 cls : 'input-group',
10584 inputblock.cn.push({
10586 cls : 'input-group-addon',
10591 inputblock.cn.push(input);
10593 if(this.hasFeedback && !this.allowBlank){
10594 inputblock.cls += ' has-feedback';
10595 inputblock.cn.push(feedback);
10599 inputblock.cn.push({
10601 cls : 'input-group-addon',
10608 if (align ==='left' && this.fieldLabel.length) {
10613 cls : 'control-label',
10614 html : this.fieldLabel
10625 if(this.labelWidth > 12){
10626 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10629 if(this.labelWidth < 13 && this.labelmd == 0){
10630 this.labelmd = this.labelWidth;
10633 if(this.labellg > 0){
10634 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10635 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10638 if(this.labelmd > 0){
10639 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10640 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10643 if(this.labelsm > 0){
10644 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10645 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10648 if(this.labelxs > 0){
10649 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10650 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10653 } else if ( this.fieldLabel.length) {
10658 //cls : 'input-group-addon',
10659 html : this.fieldLabel
10677 if (this.disabled) {
10678 input.disabled=true;
10685 * return the real textarea element.
10687 inputEl: function ()
10689 return this.el.select('textarea.form-control',true).first();
10693 * Clear any invalid styles/messages for this field
10695 clearInvalid : function()
10698 if(!this.el || this.preventMark){ // not rendered
10702 var label = this.el.select('label', true).first();
10703 var icon = this.el.select('i.fa-star', true).first();
10708 this.el.removeClass( this.validClass);
10709 this.inputEl().removeClass('is-invalid');
10711 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10713 var feedback = this.el.select('.form-control-feedback', true).first();
10716 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10721 this.fireEvent('valid', this);
10725 * Mark this field as valid
10727 markValid : function()
10729 if(!this.el || this.preventMark){ // not rendered
10733 this.el.removeClass([this.invalidClass, this.validClass]);
10734 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10736 var feedback = this.el.select('.form-control-feedback', true).first();
10739 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10742 if(this.disabled || this.allowBlank){
10746 var label = this.el.select('label', true).first();
10747 var icon = this.el.select('i.fa-star', true).first();
10752 if (Roo.bootstrap.version == 3) {
10753 this.el.addClass(this.validClass);
10755 this.inputEl().addClass('is-valid');
10759 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10761 var feedback = this.el.select('.form-control-feedback', true).first();
10764 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10765 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10770 this.fireEvent('valid', this);
10774 * Mark this field as invalid
10775 * @param {String} msg The validation message
10777 markInvalid : function(msg)
10779 if(!this.el || this.preventMark){ // not rendered
10783 this.el.removeClass([this.invalidClass, this.validClass]);
10784 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10786 var feedback = this.el.select('.form-control-feedback', true).first();
10789 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10792 if(this.disabled || this.allowBlank){
10796 var label = this.el.select('label', true).first();
10797 var icon = this.el.select('i.fa-star', true).first();
10799 if(!this.getValue().length && label && !icon){
10800 this.el.createChild({
10802 cls : 'text-danger fa fa-lg fa-star',
10803 tooltip : 'This field is required',
10804 style : 'margin-right:5px;'
10808 if (Roo.bootstrap.version == 3) {
10809 this.el.addClass(this.invalidClass);
10811 this.inputEl().addClass('is-invalid');
10814 // fixme ... this may be depricated need to test..
10815 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10817 var feedback = this.el.select('.form-control-feedback', true).first();
10820 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10822 if(this.getValue().length || this.forceFeedback){
10823 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10830 this.fireEvent('invalid', this, msg);
10838 * trigger field - base class for combo..
10843 * @class Roo.bootstrap.TriggerField
10844 * @extends Roo.bootstrap.Input
10845 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10846 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10847 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10848 * for which you can provide a custom implementation. For example:
10850 var trigger = new Roo.bootstrap.TriggerField();
10851 trigger.onTriggerClick = myTriggerFn;
10852 trigger.applyTo('my-field');
10855 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10856 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10857 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10858 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10859 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10862 * Create a new TriggerField.
10863 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10864 * to the base TextField)
10866 Roo.bootstrap.TriggerField = function(config){
10867 this.mimicing = false;
10868 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10871 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10873 * @cfg {String} triggerClass A CSS class to apply to the trigger
10876 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10881 * @cfg {Boolean} removable (true|false) special filter default false
10885 /** @cfg {Boolean} grow @hide */
10886 /** @cfg {Number} growMin @hide */
10887 /** @cfg {Number} growMax @hide */
10893 autoSize: Roo.emptyFn,
10897 deferHeight : true,
10900 actionMode : 'wrap',
10905 getAutoCreate : function(){
10907 var align = this.labelAlign || this.parentLabelAlign();
10912 cls: 'form-group' //input-group
10919 type : this.inputType,
10920 cls : 'form-control',
10921 autocomplete: 'new-password',
10922 placeholder : this.placeholder || ''
10926 input.name = this.name;
10929 input.cls += ' input-' + this.size;
10932 if (this.disabled) {
10933 input.disabled=true;
10936 var inputblock = input;
10938 if(this.hasFeedback && !this.allowBlank){
10942 cls: 'glyphicon form-control-feedback'
10945 if(this.removable && !this.editable && !this.tickable){
10947 cls : 'has-feedback',
10953 cls : 'roo-combo-removable-btn close'
10960 cls : 'has-feedback',
10969 if(this.removable && !this.editable && !this.tickable){
10971 cls : 'roo-removable',
10977 cls : 'roo-combo-removable-btn close'
10984 if (this.before || this.after) {
10987 cls : 'input-group',
10991 inputblock.cn.push({
10993 cls : 'input-group-addon input-group-prepend input-group-text',
10998 inputblock.cn.push(input);
11000 if(this.hasFeedback && !this.allowBlank){
11001 inputblock.cls += ' has-feedback';
11002 inputblock.cn.push(feedback);
11006 inputblock.cn.push({
11008 cls : 'input-group-addon input-group-append input-group-text',
11017 var ibwrap = inputblock;
11022 cls: 'roo-select2-choices',
11026 cls: 'roo-select2-search-field',
11038 cls: 'roo-select2-container input-group',
11043 cls: 'form-hidden-field'
11049 if(!this.multiple && this.showToggleBtn){
11055 if (this.caret != false) {
11058 cls: 'fa fa-' + this.caret
11065 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11067 Roo.bootstrap.version == 3 ? caret : '',
11070 cls: 'combobox-clear',
11084 combobox.cls += ' roo-select2-container-multi';
11088 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11089 tooltip : 'This field is required'
11091 if (Roo.bootstrap.version == 4) {
11094 style : 'display:none'
11099 if (align ==='left' && this.fieldLabel.length) {
11101 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11108 cls : 'control-label',
11109 html : this.fieldLabel
11121 var labelCfg = cfg.cn[1];
11122 var contentCfg = cfg.cn[2];
11124 if(this.indicatorpos == 'right'){
11129 cls : 'control-label',
11133 html : this.fieldLabel
11147 labelCfg = cfg.cn[0];
11148 contentCfg = cfg.cn[1];
11151 if(this.labelWidth > 12){
11152 labelCfg.style = "width: " + this.labelWidth + 'px';
11155 if(this.labelWidth < 13 && this.labelmd == 0){
11156 this.labelmd = this.labelWidth;
11159 if(this.labellg > 0){
11160 labelCfg.cls += ' col-lg-' + this.labellg;
11161 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11164 if(this.labelmd > 0){
11165 labelCfg.cls += ' col-md-' + this.labelmd;
11166 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11169 if(this.labelsm > 0){
11170 labelCfg.cls += ' col-sm-' + this.labelsm;
11171 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11174 if(this.labelxs > 0){
11175 labelCfg.cls += ' col-xs-' + this.labelxs;
11176 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11179 } else if ( this.fieldLabel.length) {
11180 // Roo.log(" label");
11185 //cls : 'input-group-addon',
11186 html : this.fieldLabel
11194 if(this.indicatorpos == 'right'){
11202 html : this.fieldLabel
11216 // Roo.log(" no label && no align");
11223 ['xs','sm','md','lg'].map(function(size){
11224 if (settings[size]) {
11225 cfg.cls += ' col-' + size + '-' + settings[size];
11236 onResize : function(w, h){
11237 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
11238 // if(typeof w == 'number'){
11239 // var x = w - this.trigger.getWidth();
11240 // this.inputEl().setWidth(this.adjustWidth('input', x));
11241 // this.trigger.setStyle('left', x+'px');
11246 adjustSize : Roo.BoxComponent.prototype.adjustSize,
11249 getResizeEl : function(){
11250 return this.inputEl();
11254 getPositionEl : function(){
11255 return this.inputEl();
11259 alignErrorIcon : function(){
11260 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
11264 initEvents : function(){
11268 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
11269 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
11270 if(!this.multiple && this.showToggleBtn){
11271 this.trigger = this.el.select('span.dropdown-toggle',true).first();
11272 if(this.hideTrigger){
11273 this.trigger.setDisplayed(false);
11275 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
11279 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
11282 if(this.removable && !this.editable && !this.tickable){
11283 var close = this.closeTriggerEl();
11286 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
11287 close.on('click', this.removeBtnClick, this, close);
11291 //this.trigger.addClassOnOver('x-form-trigger-over');
11292 //this.trigger.addClassOnClick('x-form-trigger-click');
11295 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
11299 closeTriggerEl : function()
11301 var close = this.el.select('.roo-combo-removable-btn', true).first();
11302 return close ? close : false;
11305 removeBtnClick : function(e, h, el)
11307 e.preventDefault();
11309 if(this.fireEvent("remove", this) !== false){
11311 this.fireEvent("afterremove", this)
11315 createList : function()
11317 this.list = Roo.get(document.body).createChild({
11318 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
11319 cls: 'typeahead typeahead-long dropdown-menu',
11320 style: 'display:none'
11323 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
11328 initTrigger : function(){
11333 onDestroy : function(){
11335 this.trigger.removeAllListeners();
11336 // this.trigger.remove();
11339 // this.wrap.remove();
11341 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
11345 onFocus : function(){
11346 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
11348 if(!this.mimicing){
11349 this.wrap.addClass('x-trigger-wrap-focus');
11350 this.mimicing = true;
11351 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
11352 if(this.monitorTab){
11353 this.el.on("keydown", this.checkTab, this);
11360 checkTab : function(e){
11361 if(e.getKey() == e.TAB){
11362 this.triggerBlur();
11367 onBlur : function(){
11372 mimicBlur : function(e, t){
11374 if(!this.wrap.contains(t) && this.validateBlur()){
11375 this.triggerBlur();
11381 triggerBlur : function(){
11382 this.mimicing = false;
11383 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
11384 if(this.monitorTab){
11385 this.el.un("keydown", this.checkTab, this);
11387 //this.wrap.removeClass('x-trigger-wrap-focus');
11388 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11392 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11393 validateBlur : function(e, t){
11398 onDisable : function(){
11399 this.inputEl().dom.disabled = true;
11400 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11402 // this.wrap.addClass('x-item-disabled');
11407 onEnable : function(){
11408 this.inputEl().dom.disabled = false;
11409 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11411 // this.el.removeClass('x-item-disabled');
11416 onShow : function(){
11417 var ae = this.getActionEl();
11420 ae.dom.style.display = '';
11421 ae.dom.style.visibility = 'visible';
11427 onHide : function(){
11428 var ae = this.getActionEl();
11429 ae.dom.style.display = 'none';
11433 * The function that should handle the trigger's click event. This method does nothing by default until overridden
11434 * by an implementing function.
11436 * @param {EventObject} e
11438 onTriggerClick : Roo.emptyFn
11442 * Ext JS Library 1.1.1
11443 * Copyright(c) 2006-2007, Ext JS, LLC.
11445 * Originally Released Under LGPL - original licence link has changed is not relivant.
11448 * <script type="text/javascript">
11453 * @class Roo.data.SortTypes
11455 * Defines the default sorting (casting?) comparison functions used when sorting data.
11457 Roo.data.SortTypes = {
11459 * Default sort that does nothing
11460 * @param {Mixed} s The value being converted
11461 * @return {Mixed} The comparison value
11463 none : function(s){
11468 * The regular expression used to strip tags
11472 stripTagsRE : /<\/?[^>]+>/gi,
11475 * Strips all HTML tags to sort on text only
11476 * @param {Mixed} s The value being converted
11477 * @return {String} The comparison value
11479 asText : function(s){
11480 return String(s).replace(this.stripTagsRE, "");
11484 * Strips all HTML tags to sort on text only - Case insensitive
11485 * @param {Mixed} s The value being converted
11486 * @return {String} The comparison value
11488 asUCText : function(s){
11489 return String(s).toUpperCase().replace(this.stripTagsRE, "");
11493 * Case insensitive string
11494 * @param {Mixed} s The value being converted
11495 * @return {String} The comparison value
11497 asUCString : function(s) {
11498 return String(s).toUpperCase();
11503 * @param {Mixed} s The value being converted
11504 * @return {Number} The comparison value
11506 asDate : function(s) {
11510 if(s instanceof Date){
11511 return s.getTime();
11513 return Date.parse(String(s));
11518 * @param {Mixed} s The value being converted
11519 * @return {Float} The comparison value
11521 asFloat : function(s) {
11522 var val = parseFloat(String(s).replace(/,/g, ""));
11531 * @param {Mixed} s The value being converted
11532 * @return {Number} The comparison value
11534 asInt : function(s) {
11535 var val = parseInt(String(s).replace(/,/g, ""));
11543 * Ext JS Library 1.1.1
11544 * Copyright(c) 2006-2007, Ext JS, LLC.
11546 * Originally Released Under LGPL - original licence link has changed is not relivant.
11549 * <script type="text/javascript">
11553 * @class Roo.data.Record
11554 * Instances of this class encapsulate both record <em>definition</em> information, and record
11555 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11556 * to access Records cached in an {@link Roo.data.Store} object.<br>
11558 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11559 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11562 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11564 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11565 * {@link #create}. The parameters are the same.
11566 * @param {Array} data An associative Array of data values keyed by the field name.
11567 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11568 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11569 * not specified an integer id is generated.
11571 Roo.data.Record = function(data, id){
11572 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11577 * Generate a constructor for a specific record layout.
11578 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11579 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11580 * Each field definition object may contain the following properties: <ul>
11581 * <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,
11582 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11583 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11584 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11585 * is being used, then this is a string containing the javascript expression to reference the data relative to
11586 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11587 * to the data item relative to the record element. If the mapping expression is the same as the field name,
11588 * this may be omitted.</p></li>
11589 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11590 * <ul><li>auto (Default, implies no conversion)</li>
11595 * <li>date</li></ul></p></li>
11596 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11597 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11598 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11599 * by the Reader into an object that will be stored in the Record. It is passed the
11600 * following parameters:<ul>
11601 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11603 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11605 * <br>usage:<br><pre><code>
11606 var TopicRecord = Roo.data.Record.create(
11607 {name: 'title', mapping: 'topic_title'},
11608 {name: 'author', mapping: 'username'},
11609 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11610 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11611 {name: 'lastPoster', mapping: 'user2'},
11612 {name: 'excerpt', mapping: 'post_text'}
11615 var myNewRecord = new TopicRecord({
11616 title: 'Do my job please',
11619 lastPost: new Date(),
11620 lastPoster: 'Animal',
11621 excerpt: 'No way dude!'
11623 myStore.add(myNewRecord);
11628 Roo.data.Record.create = function(o){
11629 var f = function(){
11630 f.superclass.constructor.apply(this, arguments);
11632 Roo.extend(f, Roo.data.Record);
11633 var p = f.prototype;
11634 p.fields = new Roo.util.MixedCollection(false, function(field){
11637 for(var i = 0, len = o.length; i < len; i++){
11638 p.fields.add(new Roo.data.Field(o[i]));
11640 f.getField = function(name){
11641 return p.fields.get(name);
11646 Roo.data.Record.AUTO_ID = 1000;
11647 Roo.data.Record.EDIT = 'edit';
11648 Roo.data.Record.REJECT = 'reject';
11649 Roo.data.Record.COMMIT = 'commit';
11651 Roo.data.Record.prototype = {
11653 * Readonly flag - true if this record has been modified.
11662 join : function(store){
11663 this.store = store;
11667 * Set the named field to the specified value.
11668 * @param {String} name The name of the field to set.
11669 * @param {Object} value The value to set the field to.
11671 set : function(name, value){
11672 if(this.data[name] == value){
11676 if(!this.modified){
11677 this.modified = {};
11679 if(typeof this.modified[name] == 'undefined'){
11680 this.modified[name] = this.data[name];
11682 this.data[name] = value;
11683 if(!this.editing && this.store){
11684 this.store.afterEdit(this);
11689 * Get the value of the named field.
11690 * @param {String} name The name of the field to get the value of.
11691 * @return {Object} The value of the field.
11693 get : function(name){
11694 return this.data[name];
11698 beginEdit : function(){
11699 this.editing = true;
11700 this.modified = {};
11704 cancelEdit : function(){
11705 this.editing = false;
11706 delete this.modified;
11710 endEdit : function(){
11711 this.editing = false;
11712 if(this.dirty && this.store){
11713 this.store.afterEdit(this);
11718 * Usually called by the {@link Roo.data.Store} which owns the Record.
11719 * Rejects all changes made to the Record since either creation, or the last commit operation.
11720 * Modified fields are reverted to their original values.
11722 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11723 * of reject operations.
11725 reject : function(){
11726 var m = this.modified;
11728 if(typeof m[n] != "function"){
11729 this.data[n] = m[n];
11732 this.dirty = false;
11733 delete this.modified;
11734 this.editing = false;
11736 this.store.afterReject(this);
11741 * Usually called by the {@link Roo.data.Store} which owns the Record.
11742 * Commits all changes made to the Record since either creation, or the last commit operation.
11744 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11745 * of commit operations.
11747 commit : function(){
11748 this.dirty = false;
11749 delete this.modified;
11750 this.editing = false;
11752 this.store.afterCommit(this);
11757 hasError : function(){
11758 return this.error != null;
11762 clearError : function(){
11767 * Creates a copy of this record.
11768 * @param {String} id (optional) A new record id if you don't want to use this record's id
11771 copy : function(newId) {
11772 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11776 * Ext JS Library 1.1.1
11777 * Copyright(c) 2006-2007, Ext JS, LLC.
11779 * Originally Released Under LGPL - original licence link has changed is not relivant.
11782 * <script type="text/javascript">
11788 * @class Roo.data.Store
11789 * @extends Roo.util.Observable
11790 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11791 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11793 * 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
11794 * has no knowledge of the format of the data returned by the Proxy.<br>
11796 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11797 * instances from the data object. These records are cached and made available through accessor functions.
11799 * Creates a new Store.
11800 * @param {Object} config A config object containing the objects needed for the Store to access data,
11801 * and read the data into Records.
11803 Roo.data.Store = function(config){
11804 this.data = new Roo.util.MixedCollection(false);
11805 this.data.getKey = function(o){
11808 this.baseParams = {};
11810 this.paramNames = {
11815 "multisort" : "_multisort"
11818 if(config && config.data){
11819 this.inlineData = config.data;
11820 delete config.data;
11823 Roo.apply(this, config);
11825 if(this.reader){ // reader passed
11826 this.reader = Roo.factory(this.reader, Roo.data);
11827 this.reader.xmodule = this.xmodule || false;
11828 if(!this.recordType){
11829 this.recordType = this.reader.recordType;
11831 if(this.reader.onMetaChange){
11832 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11836 if(this.recordType){
11837 this.fields = this.recordType.prototype.fields;
11839 this.modified = [];
11843 * @event datachanged
11844 * Fires when the data cache has changed, and a widget which is using this Store
11845 * as a Record cache should refresh its view.
11846 * @param {Store} this
11848 datachanged : true,
11850 * @event metachange
11851 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11852 * @param {Store} this
11853 * @param {Object} meta The JSON metadata
11858 * Fires when Records have been added to the Store
11859 * @param {Store} this
11860 * @param {Roo.data.Record[]} records The array of Records added
11861 * @param {Number} index The index at which the record(s) were added
11866 * Fires when a Record has been removed from the Store
11867 * @param {Store} this
11868 * @param {Roo.data.Record} record The Record that was removed
11869 * @param {Number} index The index at which the record was removed
11874 * Fires when a Record has been updated
11875 * @param {Store} this
11876 * @param {Roo.data.Record} record The Record that was updated
11877 * @param {String} operation The update operation being performed. Value may be one of:
11879 Roo.data.Record.EDIT
11880 Roo.data.Record.REJECT
11881 Roo.data.Record.COMMIT
11887 * Fires when the data cache has been cleared.
11888 * @param {Store} this
11892 * @event beforeload
11893 * Fires before a request is made for a new data object. If the beforeload handler returns false
11894 * the load action will be canceled.
11895 * @param {Store} this
11896 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11900 * @event beforeloadadd
11901 * Fires after a new set of Records has been loaded.
11902 * @param {Store} this
11903 * @param {Roo.data.Record[]} records The Records that were loaded
11904 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11906 beforeloadadd : true,
11909 * Fires after a new set of Records has been loaded, before they are added to the store.
11910 * @param {Store} this
11911 * @param {Roo.data.Record[]} records The Records that were loaded
11912 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11913 * @params {Object} return from reader
11917 * @event loadexception
11918 * Fires if an exception occurs in the Proxy during loading.
11919 * Called with the signature of the Proxy's "loadexception" event.
11920 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11923 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11924 * @param {Object} load options
11925 * @param {Object} jsonData from your request (normally this contains the Exception)
11927 loadexception : true
11931 this.proxy = Roo.factory(this.proxy, Roo.data);
11932 this.proxy.xmodule = this.xmodule || false;
11933 this.relayEvents(this.proxy, ["loadexception"]);
11935 this.sortToggle = {};
11936 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11938 Roo.data.Store.superclass.constructor.call(this);
11940 if(this.inlineData){
11941 this.loadData(this.inlineData);
11942 delete this.inlineData;
11946 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11948 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11949 * without a remote query - used by combo/forms at present.
11953 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11956 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11959 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11960 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11963 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11964 * on any HTTP request
11967 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11970 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11974 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11975 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11977 remoteSort : false,
11980 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11981 * loaded or when a record is removed. (defaults to false).
11983 pruneModifiedRecords : false,
11986 lastOptions : null,
11989 * Add Records to the Store and fires the add event.
11990 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11992 add : function(records){
11993 records = [].concat(records);
11994 for(var i = 0, len = records.length; i < len; i++){
11995 records[i].join(this);
11997 var index = this.data.length;
11998 this.data.addAll(records);
11999 this.fireEvent("add", this, records, index);
12003 * Remove a Record from the Store and fires the remove event.
12004 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
12006 remove : function(record){
12007 var index = this.data.indexOf(record);
12008 this.data.removeAt(index);
12010 if(this.pruneModifiedRecords){
12011 this.modified.remove(record);
12013 this.fireEvent("remove", this, record, index);
12017 * Remove all Records from the Store and fires the clear event.
12019 removeAll : function(){
12021 if(this.pruneModifiedRecords){
12022 this.modified = [];
12024 this.fireEvent("clear", this);
12028 * Inserts Records to the Store at the given index and fires the add event.
12029 * @param {Number} index The start index at which to insert the passed Records.
12030 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
12032 insert : function(index, records){
12033 records = [].concat(records);
12034 for(var i = 0, len = records.length; i < len; i++){
12035 this.data.insert(index, records[i]);
12036 records[i].join(this);
12038 this.fireEvent("add", this, records, index);
12042 * Get the index within the cache of the passed Record.
12043 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
12044 * @return {Number} The index of the passed Record. Returns -1 if not found.
12046 indexOf : function(record){
12047 return this.data.indexOf(record);
12051 * Get the index within the cache of the Record with the passed id.
12052 * @param {String} id The id of the Record to find.
12053 * @return {Number} The index of the Record. Returns -1 if not found.
12055 indexOfId : function(id){
12056 return this.data.indexOfKey(id);
12060 * Get the Record with the specified id.
12061 * @param {String} id The id of the Record to find.
12062 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
12064 getById : function(id){
12065 return this.data.key(id);
12069 * Get the Record at the specified index.
12070 * @param {Number} index The index of the Record to find.
12071 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
12073 getAt : function(index){
12074 return this.data.itemAt(index);
12078 * Returns a range of Records between specified indices.
12079 * @param {Number} startIndex (optional) The starting index (defaults to 0)
12080 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
12081 * @return {Roo.data.Record[]} An array of Records
12083 getRange : function(start, end){
12084 return this.data.getRange(start, end);
12088 storeOptions : function(o){
12089 o = Roo.apply({}, o);
12092 this.lastOptions = o;
12096 * Loads the Record cache from the configured Proxy using the configured Reader.
12098 * If using remote paging, then the first load call must specify the <em>start</em>
12099 * and <em>limit</em> properties in the options.params property to establish the initial
12100 * position within the dataset, and the number of Records to cache on each read from the Proxy.
12102 * <strong>It is important to note that for remote data sources, loading is asynchronous,
12103 * and this call will return before the new data has been loaded. Perform any post-processing
12104 * in a callback function, or in a "load" event handler.</strong>
12106 * @param {Object} options An object containing properties which control loading options:<ul>
12107 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
12108 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
12109 * passed the following arguments:<ul>
12110 * <li>r : Roo.data.Record[]</li>
12111 * <li>options: Options object from the load call</li>
12112 * <li>success: Boolean success indicator</li></ul></li>
12113 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
12114 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
12117 load : function(options){
12118 options = options || {};
12119 if(this.fireEvent("beforeload", this, options) !== false){
12120 this.storeOptions(options);
12121 var p = Roo.apply(options.params || {}, this.baseParams);
12122 // if meta was not loaded from remote source.. try requesting it.
12123 if (!this.reader.metaFromRemote) {
12124 p._requestMeta = 1;
12126 if(this.sortInfo && this.remoteSort){
12127 var pn = this.paramNames;
12128 p[pn["sort"]] = this.sortInfo.field;
12129 p[pn["dir"]] = this.sortInfo.direction;
12131 if (this.multiSort) {
12132 var pn = this.paramNames;
12133 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
12136 this.proxy.load(p, this.reader, this.loadRecords, this, options);
12141 * Reloads the Record cache from the configured Proxy using the configured Reader and
12142 * the options from the last load operation performed.
12143 * @param {Object} options (optional) An object containing properties which may override the options
12144 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
12145 * the most recently used options are reused).
12147 reload : function(options){
12148 this.load(Roo.applyIf(options||{}, this.lastOptions));
12152 // Called as a callback by the Reader during a load operation.
12153 loadRecords : function(o, options, success){
12154 if(!o || success === false){
12155 if(success !== false){
12156 this.fireEvent("load", this, [], options, o);
12158 if(options.callback){
12159 options.callback.call(options.scope || this, [], options, false);
12163 // if data returned failure - throw an exception.
12164 if (o.success === false) {
12165 // show a message if no listener is registered.
12166 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
12167 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
12169 // loadmask wil be hooked into this..
12170 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
12173 var r = o.records, t = o.totalRecords || r.length;
12175 this.fireEvent("beforeloadadd", this, r, options, o);
12177 if(!options || options.add !== true){
12178 if(this.pruneModifiedRecords){
12179 this.modified = [];
12181 for(var i = 0, len = r.length; i < len; i++){
12185 this.data = this.snapshot;
12186 delete this.snapshot;
12189 this.data.addAll(r);
12190 this.totalLength = t;
12192 this.fireEvent("datachanged", this);
12194 this.totalLength = Math.max(t, this.data.length+r.length);
12198 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
12200 var e = new Roo.data.Record({});
12202 e.set(this.parent.displayField, this.parent.emptyTitle);
12203 e.set(this.parent.valueField, '');
12208 this.fireEvent("load", this, r, options, o);
12209 if(options.callback){
12210 options.callback.call(options.scope || this, r, options, true);
12216 * Loads data from a passed data block. A Reader which understands the format of the data
12217 * must have been configured in the constructor.
12218 * @param {Object} data The data block from which to read the Records. The format of the data expected
12219 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
12220 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
12222 loadData : function(o, append){
12223 var r = this.reader.readRecords(o);
12224 this.loadRecords(r, {add: append}, true);
12228 * using 'cn' the nested child reader read the child array into it's child stores.
12229 * @param {Object} rec The record with a 'children array
12231 loadDataFromChildren : function(rec)
12233 this.loadData(this.reader.toLoadData(rec));
12238 * Gets the number of cached records.
12240 * <em>If using paging, this may not be the total size of the dataset. If the data object
12241 * used by the Reader contains the dataset size, then the getTotalCount() function returns
12242 * the data set size</em>
12244 getCount : function(){
12245 return this.data.length || 0;
12249 * Gets the total number of records in the dataset as returned by the server.
12251 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
12252 * the dataset size</em>
12254 getTotalCount : function(){
12255 return this.totalLength || 0;
12259 * Returns the sort state of the Store as an object with two properties:
12261 field {String} The name of the field by which the Records are sorted
12262 direction {String} The sort order, "ASC" or "DESC"
12265 getSortState : function(){
12266 return this.sortInfo;
12270 applySort : function(){
12271 if(this.sortInfo && !this.remoteSort){
12272 var s = this.sortInfo, f = s.field;
12273 var st = this.fields.get(f).sortType;
12274 var fn = function(r1, r2){
12275 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
12276 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12278 this.data.sort(s.direction, fn);
12279 if(this.snapshot && this.snapshot != this.data){
12280 this.snapshot.sort(s.direction, fn);
12286 * Sets the default sort column and order to be used by the next load operation.
12287 * @param {String} fieldName The name of the field to sort by.
12288 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12290 setDefaultSort : function(field, dir){
12291 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
12295 * Sort the Records.
12296 * If remote sorting is used, the sort is performed on the server, and the cache is
12297 * reloaded. If local sorting is used, the cache is sorted internally.
12298 * @param {String} fieldName The name of the field to sort by.
12299 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12301 sort : function(fieldName, dir){
12302 var f = this.fields.get(fieldName);
12304 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
12306 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
12307 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
12312 this.sortToggle[f.name] = dir;
12313 this.sortInfo = {field: f.name, direction: dir};
12314 if(!this.remoteSort){
12316 this.fireEvent("datachanged", this);
12318 this.load(this.lastOptions);
12323 * Calls the specified function for each of the Records in the cache.
12324 * @param {Function} fn The function to call. The Record is passed as the first parameter.
12325 * Returning <em>false</em> aborts and exits the iteration.
12326 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
12328 each : function(fn, scope){
12329 this.data.each(fn, scope);
12333 * Gets all records modified since the last commit. Modified records are persisted across load operations
12334 * (e.g., during paging).
12335 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
12337 getModifiedRecords : function(){
12338 return this.modified;
12342 createFilterFn : function(property, value, anyMatch){
12343 if(!value.exec){ // not a regex
12344 value = String(value);
12345 if(value.length == 0){
12348 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
12350 return function(r){
12351 return value.test(r.data[property]);
12356 * Sums the value of <i>property</i> for each record between start and end and returns the result.
12357 * @param {String} property A field on your records
12358 * @param {Number} start The record index to start at (defaults to 0)
12359 * @param {Number} end The last record index to include (defaults to length - 1)
12360 * @return {Number} The sum
12362 sum : function(property, start, end){
12363 var rs = this.data.items, v = 0;
12364 start = start || 0;
12365 end = (end || end === 0) ? end : rs.length-1;
12367 for(var i = start; i <= end; i++){
12368 v += (rs[i].data[property] || 0);
12374 * Filter the records by a specified property.
12375 * @param {String} field A field on your records
12376 * @param {String/RegExp} value Either a string that the field
12377 * should start with or a RegExp to test against the field
12378 * @param {Boolean} anyMatch True to match any part not just the beginning
12380 filter : function(property, value, anyMatch){
12381 var fn = this.createFilterFn(property, value, anyMatch);
12382 return fn ? this.filterBy(fn) : this.clearFilter();
12386 * Filter by a function. The specified function will be called with each
12387 * record in this data source. If the function returns true the record is included,
12388 * otherwise it is filtered.
12389 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12390 * @param {Object} scope (optional) The scope of the function (defaults to this)
12392 filterBy : function(fn, scope){
12393 this.snapshot = this.snapshot || this.data;
12394 this.data = this.queryBy(fn, scope||this);
12395 this.fireEvent("datachanged", this);
12399 * Query the records by a specified property.
12400 * @param {String} field A field on your records
12401 * @param {String/RegExp} value Either a string that the field
12402 * should start with or a RegExp to test against the field
12403 * @param {Boolean} anyMatch True to match any part not just the beginning
12404 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12406 query : function(property, value, anyMatch){
12407 var fn = this.createFilterFn(property, value, anyMatch);
12408 return fn ? this.queryBy(fn) : this.data.clone();
12412 * Query by a function. The specified function will be called with each
12413 * record in this data source. If the function returns true the record is included
12415 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12416 * @param {Object} scope (optional) The scope of the function (defaults to this)
12417 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12419 queryBy : function(fn, scope){
12420 var data = this.snapshot || this.data;
12421 return data.filterBy(fn, scope||this);
12425 * Collects unique values for a particular dataIndex from this store.
12426 * @param {String} dataIndex The property to collect
12427 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12428 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12429 * @return {Array} An array of the unique values
12431 collect : function(dataIndex, allowNull, bypassFilter){
12432 var d = (bypassFilter === true && this.snapshot) ?
12433 this.snapshot.items : this.data.items;
12434 var v, sv, r = [], l = {};
12435 for(var i = 0, len = d.length; i < len; i++){
12436 v = d[i].data[dataIndex];
12438 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12447 * Revert to a view of the Record cache with no filtering applied.
12448 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12450 clearFilter : function(suppressEvent){
12451 if(this.snapshot && this.snapshot != this.data){
12452 this.data = this.snapshot;
12453 delete this.snapshot;
12454 if(suppressEvent !== true){
12455 this.fireEvent("datachanged", this);
12461 afterEdit : function(record){
12462 if(this.modified.indexOf(record) == -1){
12463 this.modified.push(record);
12465 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12469 afterReject : function(record){
12470 this.modified.remove(record);
12471 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12475 afterCommit : function(record){
12476 this.modified.remove(record);
12477 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12481 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12482 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12484 commitChanges : function(){
12485 var m = this.modified.slice(0);
12486 this.modified = [];
12487 for(var i = 0, len = m.length; i < len; i++){
12493 * Cancel outstanding changes on all changed records.
12495 rejectChanges : function(){
12496 var m = this.modified.slice(0);
12497 this.modified = [];
12498 for(var i = 0, len = m.length; i < len; i++){
12503 onMetaChange : function(meta, rtype, o){
12504 this.recordType = rtype;
12505 this.fields = rtype.prototype.fields;
12506 delete this.snapshot;
12507 this.sortInfo = meta.sortInfo || this.sortInfo;
12508 this.modified = [];
12509 this.fireEvent('metachange', this, this.reader.meta);
12512 moveIndex : function(data, type)
12514 var index = this.indexOf(data);
12516 var newIndex = index + type;
12520 this.insert(newIndex, data);
12525 * Ext JS Library 1.1.1
12526 * Copyright(c) 2006-2007, Ext JS, LLC.
12528 * Originally Released Under LGPL - original licence link has changed is not relivant.
12531 * <script type="text/javascript">
12535 * @class Roo.data.SimpleStore
12536 * @extends Roo.data.Store
12537 * Small helper class to make creating Stores from Array data easier.
12538 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12539 * @cfg {Array} fields An array of field definition objects, or field name strings.
12540 * @cfg {Object} an existing reader (eg. copied from another store)
12541 * @cfg {Array} data The multi-dimensional array of data
12543 * @param {Object} config
12545 Roo.data.SimpleStore = function(config)
12547 Roo.data.SimpleStore.superclass.constructor.call(this, {
12549 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
12552 Roo.data.Record.create(config.fields)
12554 proxy : new Roo.data.MemoryProxy(config.data)
12558 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12560 * Ext JS Library 1.1.1
12561 * Copyright(c) 2006-2007, Ext JS, LLC.
12563 * Originally Released Under LGPL - original licence link has changed is not relivant.
12566 * <script type="text/javascript">
12571 * @extends Roo.data.Store
12572 * @class Roo.data.JsonStore
12573 * Small helper class to make creating Stores for JSON data easier. <br/>
12575 var store = new Roo.data.JsonStore({
12576 url: 'get-images.php',
12578 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12581 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12582 * JsonReader and HttpProxy (unless inline data is provided).</b>
12583 * @cfg {Array} fields An array of field definition objects, or field name strings.
12585 * @param {Object} config
12587 Roo.data.JsonStore = function(c){
12588 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12589 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12590 reader: new Roo.data.JsonReader(c, c.fields)
12593 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12595 * Ext JS Library 1.1.1
12596 * Copyright(c) 2006-2007, Ext JS, LLC.
12598 * Originally Released Under LGPL - original licence link has changed is not relivant.
12601 * <script type="text/javascript">
12605 Roo.data.Field = function(config){
12606 if(typeof config == "string"){
12607 config = {name: config};
12609 Roo.apply(this, config);
12612 this.type = "auto";
12615 var st = Roo.data.SortTypes;
12616 // named sortTypes are supported, here we look them up
12617 if(typeof this.sortType == "string"){
12618 this.sortType = st[this.sortType];
12621 // set default sortType for strings and dates
12622 if(!this.sortType){
12625 this.sortType = st.asUCString;
12628 this.sortType = st.asDate;
12631 this.sortType = st.none;
12636 var stripRe = /[\$,%]/g;
12638 // prebuilt conversion function for this field, instead of
12639 // switching every time we're reading a value
12641 var cv, dateFormat = this.dateFormat;
12646 cv = function(v){ return v; };
12649 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12653 return v !== undefined && v !== null && v !== '' ?
12654 parseInt(String(v).replace(stripRe, ""), 10) : '';
12659 return v !== undefined && v !== null && v !== '' ?
12660 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12665 cv = function(v){ return v === true || v === "true" || v == 1; };
12672 if(v instanceof Date){
12676 if(dateFormat == "timestamp"){
12677 return new Date(v*1000);
12679 return Date.parseDate(v, dateFormat);
12681 var parsed = Date.parse(v);
12682 return parsed ? new Date(parsed) : null;
12691 Roo.data.Field.prototype = {
12699 * Ext JS Library 1.1.1
12700 * Copyright(c) 2006-2007, Ext JS, LLC.
12702 * Originally Released Under LGPL - original licence link has changed is not relivant.
12705 * <script type="text/javascript">
12708 // Base class for reading structured data from a data source. This class is intended to be
12709 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12712 * @class Roo.data.DataReader
12713 * Base class for reading structured data from a data source. This class is intended to be
12714 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12717 Roo.data.DataReader = function(meta, recordType){
12721 this.recordType = recordType instanceof Array ?
12722 Roo.data.Record.create(recordType) : recordType;
12725 Roo.data.DataReader.prototype = {
12728 readerType : 'Data',
12730 * Create an empty record
12731 * @param {Object} data (optional) - overlay some values
12732 * @return {Roo.data.Record} record created.
12734 newRow : function(d) {
12736 this.recordType.prototype.fields.each(function(c) {
12738 case 'int' : da[c.name] = 0; break;
12739 case 'date' : da[c.name] = new Date(); break;
12740 case 'float' : da[c.name] = 0.0; break;
12741 case 'boolean' : da[c.name] = false; break;
12742 default : da[c.name] = ""; break;
12746 return new this.recordType(Roo.apply(da, d));
12752 * Ext JS Library 1.1.1
12753 * Copyright(c) 2006-2007, Ext JS, LLC.
12755 * Originally Released Under LGPL - original licence link has changed is not relivant.
12758 * <script type="text/javascript">
12762 * @class Roo.data.DataProxy
12763 * @extends Roo.data.Observable
12764 * This class is an abstract base class for implementations which provide retrieval of
12765 * unformatted data objects.<br>
12767 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12768 * (of the appropriate type which knows how to parse the data object) to provide a block of
12769 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12771 * Custom implementations must implement the load method as described in
12772 * {@link Roo.data.HttpProxy#load}.
12774 Roo.data.DataProxy = function(){
12777 * @event beforeload
12778 * Fires before a network request is made to retrieve a data object.
12779 * @param {Object} This DataProxy object.
12780 * @param {Object} params The params parameter to the load function.
12785 * Fires before the load method's callback is called.
12786 * @param {Object} This DataProxy object.
12787 * @param {Object} o The data object.
12788 * @param {Object} arg The callback argument object passed to the load function.
12792 * @event loadexception
12793 * Fires if an Exception occurs during data retrieval.
12794 * @param {Object} This DataProxy object.
12795 * @param {Object} o The data object.
12796 * @param {Object} arg The callback argument object passed to the load function.
12797 * @param {Object} e The Exception.
12799 loadexception : true
12801 Roo.data.DataProxy.superclass.constructor.call(this);
12804 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12807 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12811 * Ext JS Library 1.1.1
12812 * Copyright(c) 2006-2007, Ext JS, LLC.
12814 * Originally Released Under LGPL - original licence link has changed is not relivant.
12817 * <script type="text/javascript">
12820 * @class Roo.data.MemoryProxy
12821 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12822 * to the Reader when its load method is called.
12824 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12826 Roo.data.MemoryProxy = function(data){
12830 Roo.data.MemoryProxy.superclass.constructor.call(this);
12834 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12837 * Load data from the requested source (in this case an in-memory
12838 * data object passed to the constructor), read the data object into
12839 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12840 * process that block using the passed callback.
12841 * @param {Object} params This parameter is not used by the MemoryProxy class.
12842 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12843 * object into a block of Roo.data.Records.
12844 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12845 * The function must be passed <ul>
12846 * <li>The Record block object</li>
12847 * <li>The "arg" argument from the load function</li>
12848 * <li>A boolean success indicator</li>
12850 * @param {Object} scope The scope in which to call the callback
12851 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12853 load : function(params, reader, callback, scope, arg){
12854 params = params || {};
12857 result = reader.readRecords(params.data ? params.data :this.data);
12859 this.fireEvent("loadexception", this, arg, null, e);
12860 callback.call(scope, null, arg, false);
12863 callback.call(scope, result, arg, true);
12867 update : function(params, records){
12872 * Ext JS Library 1.1.1
12873 * Copyright(c) 2006-2007, Ext JS, LLC.
12875 * Originally Released Under LGPL - original licence link has changed is not relivant.
12878 * <script type="text/javascript">
12881 * @class Roo.data.HttpProxy
12882 * @extends Roo.data.DataProxy
12883 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12884 * configured to reference a certain URL.<br><br>
12886 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12887 * from which the running page was served.<br><br>
12889 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12891 * Be aware that to enable the browser to parse an XML document, the server must set
12892 * the Content-Type header in the HTTP response to "text/xml".
12894 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12895 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12896 * will be used to make the request.
12898 Roo.data.HttpProxy = function(conn){
12899 Roo.data.HttpProxy.superclass.constructor.call(this);
12900 // is conn a conn config or a real conn?
12902 this.useAjax = !conn || !conn.events;
12906 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12907 // thse are take from connection...
12910 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12913 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12914 * extra parameters to each request made by this object. (defaults to undefined)
12917 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12918 * to each request made by this object. (defaults to undefined)
12921 * @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)
12924 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12927 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12933 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12937 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12938 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12939 * a finer-grained basis than the DataProxy events.
12941 getConnection : function(){
12942 return this.useAjax ? Roo.Ajax : this.conn;
12946 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12947 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12948 * process that block using the passed callback.
12949 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12950 * for the request to the remote server.
12951 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12952 * object into a block of Roo.data.Records.
12953 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12954 * The function must be passed <ul>
12955 * <li>The Record block object</li>
12956 * <li>The "arg" argument from the load function</li>
12957 * <li>A boolean success indicator</li>
12959 * @param {Object} scope The scope in which to call the callback
12960 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12962 load : function(params, reader, callback, scope, arg){
12963 if(this.fireEvent("beforeload", this, params) !== false){
12965 params : params || {},
12967 callback : callback,
12972 callback : this.loadResponse,
12976 Roo.applyIf(o, this.conn);
12977 if(this.activeRequest){
12978 Roo.Ajax.abort(this.activeRequest);
12980 this.activeRequest = Roo.Ajax.request(o);
12982 this.conn.request(o);
12985 callback.call(scope||this, null, arg, false);
12990 loadResponse : function(o, success, response){
12991 delete this.activeRequest;
12993 this.fireEvent("loadexception", this, o, response);
12994 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12999 result = o.reader.read(response);
13001 this.fireEvent("loadexception", this, o, response, e);
13002 o.request.callback.call(o.request.scope, null, o.request.arg, false);
13006 this.fireEvent("load", this, o, o.request.arg);
13007 o.request.callback.call(o.request.scope, result, o.request.arg, true);
13011 update : function(dataSet){
13016 updateResponse : function(dataSet){
13021 * Ext JS Library 1.1.1
13022 * Copyright(c) 2006-2007, Ext JS, LLC.
13024 * Originally Released Under LGPL - original licence link has changed is not relivant.
13027 * <script type="text/javascript">
13031 * @class Roo.data.ScriptTagProxy
13032 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
13033 * other than the originating domain of the running page.<br><br>
13035 * <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
13036 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
13038 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
13039 * source code that is used as the source inside a <script> tag.<br><br>
13041 * In order for the browser to process the returned data, the server must wrap the data object
13042 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
13043 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
13044 * depending on whether the callback name was passed:
13047 boolean scriptTag = false;
13048 String cb = request.getParameter("callback");
13051 response.setContentType("text/javascript");
13053 response.setContentType("application/x-json");
13055 Writer out = response.getWriter();
13057 out.write(cb + "(");
13059 out.print(dataBlock.toJsonString());
13066 * @param {Object} config A configuration object.
13068 Roo.data.ScriptTagProxy = function(config){
13069 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
13070 Roo.apply(this, config);
13071 this.head = document.getElementsByTagName("head")[0];
13074 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
13076 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
13078 * @cfg {String} url The URL from which to request the data object.
13081 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
13085 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
13086 * the server the name of the callback function set up by the load call to process the returned data object.
13087 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
13088 * javascript output which calls this named function passing the data object as its only parameter.
13090 callbackParam : "callback",
13092 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
13093 * name to the request.
13098 * Load data from the configured URL, read the data object into
13099 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13100 * process that block using the passed callback.
13101 * @param {Object} params An object containing properties which are to be used as HTTP parameters
13102 * for the request to the remote server.
13103 * @param {Roo.data.DataReader} reader The Reader object which converts the data
13104 * object into a block of Roo.data.Records.
13105 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
13106 * The function must be passed <ul>
13107 * <li>The Record block object</li>
13108 * <li>The "arg" argument from the load function</li>
13109 * <li>A boolean success indicator</li>
13111 * @param {Object} scope The scope in which to call the callback
13112 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13114 load : function(params, reader, callback, scope, arg){
13115 if(this.fireEvent("beforeload", this, params) !== false){
13117 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
13119 var url = this.url;
13120 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
13122 url += "&_dc=" + (new Date().getTime());
13124 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
13127 cb : "stcCallback"+transId,
13128 scriptId : "stcScript"+transId,
13132 callback : callback,
13138 window[trans.cb] = function(o){
13139 conn.handleResponse(o, trans);
13142 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
13144 if(this.autoAbort !== false){
13148 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
13150 var script = document.createElement("script");
13151 script.setAttribute("src", url);
13152 script.setAttribute("type", "text/javascript");
13153 script.setAttribute("id", trans.scriptId);
13154 this.head.appendChild(script);
13156 this.trans = trans;
13158 callback.call(scope||this, null, arg, false);
13163 isLoading : function(){
13164 return this.trans ? true : false;
13168 * Abort the current server request.
13170 abort : function(){
13171 if(this.isLoading()){
13172 this.destroyTrans(this.trans);
13177 destroyTrans : function(trans, isLoaded){
13178 this.head.removeChild(document.getElementById(trans.scriptId));
13179 clearTimeout(trans.timeoutId);
13181 window[trans.cb] = undefined;
13183 delete window[trans.cb];
13186 // if hasn't been loaded, wait for load to remove it to prevent script error
13187 window[trans.cb] = function(){
13188 window[trans.cb] = undefined;
13190 delete window[trans.cb];
13197 handleResponse : function(o, trans){
13198 this.trans = false;
13199 this.destroyTrans(trans, true);
13202 result = trans.reader.readRecords(o);
13204 this.fireEvent("loadexception", this, o, trans.arg, e);
13205 trans.callback.call(trans.scope||window, null, trans.arg, false);
13208 this.fireEvent("load", this, o, trans.arg);
13209 trans.callback.call(trans.scope||window, result, trans.arg, true);
13213 handleFailure : function(trans){
13214 this.trans = false;
13215 this.destroyTrans(trans, false);
13216 this.fireEvent("loadexception", this, null, trans.arg);
13217 trans.callback.call(trans.scope||window, null, trans.arg, false);
13221 * Ext JS Library 1.1.1
13222 * Copyright(c) 2006-2007, Ext JS, LLC.
13224 * Originally Released Under LGPL - original licence link has changed is not relivant.
13227 * <script type="text/javascript">
13231 * @class Roo.data.JsonReader
13232 * @extends Roo.data.DataReader
13233 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
13234 * based on mappings in a provided Roo.data.Record constructor.
13236 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
13237 * in the reply previously.
13242 var RecordDef = Roo.data.Record.create([
13243 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
13244 {name: 'occupation'} // This field will use "occupation" as the mapping.
13246 var myReader = new Roo.data.JsonReader({
13247 totalProperty: "results", // The property which contains the total dataset size (optional)
13248 root: "rows", // The property which contains an Array of row objects
13249 id: "id" // The property within each row object that provides an ID for the record (optional)
13253 * This would consume a JSON file like this:
13255 { 'results': 2, 'rows': [
13256 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
13257 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
13260 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
13261 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
13262 * paged from the remote server.
13263 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
13264 * @cfg {String} root name of the property which contains the Array of row objects.
13265 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13266 * @cfg {Array} fields Array of field definition objects
13268 * Create a new JsonReader
13269 * @param {Object} meta Metadata configuration options
13270 * @param {Object} recordType Either an Array of field definition objects,
13271 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
13273 Roo.data.JsonReader = function(meta, recordType){
13276 // set some defaults:
13277 Roo.applyIf(meta, {
13278 totalProperty: 'total',
13279 successProperty : 'success',
13284 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13286 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
13288 readerType : 'Json',
13291 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
13292 * Used by Store query builder to append _requestMeta to params.
13295 metaFromRemote : false,
13297 * This method is only used by a DataProxy which has retrieved data from a remote server.
13298 * @param {Object} response The XHR object which contains the JSON data in its responseText.
13299 * @return {Object} data A data block which is used by an Roo.data.Store object as
13300 * a cache of Roo.data.Records.
13302 read : function(response){
13303 var json = response.responseText;
13305 var o = /* eval:var:o */ eval("("+json+")");
13307 throw {message: "JsonReader.read: Json object not found"};
13313 this.metaFromRemote = true;
13314 this.meta = o.metaData;
13315 this.recordType = Roo.data.Record.create(o.metaData.fields);
13316 this.onMetaChange(this.meta, this.recordType, o);
13318 return this.readRecords(o);
13321 // private function a store will implement
13322 onMetaChange : function(meta, recordType, o){
13329 simpleAccess: function(obj, subsc) {
13336 getJsonAccessor: function(){
13338 return function(expr) {
13340 return(re.test(expr))
13341 ? new Function("obj", "return obj." + expr)
13346 return Roo.emptyFn;
13351 * Create a data block containing Roo.data.Records from an XML document.
13352 * @param {Object} o An object which contains an Array of row objects in the property specified
13353 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
13354 * which contains the total size of the dataset.
13355 * @return {Object} data A data block which is used by an Roo.data.Store object as
13356 * a cache of Roo.data.Records.
13358 readRecords : function(o){
13360 * After any data loads, the raw JSON data is available for further custom processing.
13364 var s = this.meta, Record = this.recordType,
13365 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
13367 // Generate extraction functions for the totalProperty, the root, the id, and for each field
13369 if(s.totalProperty) {
13370 this.getTotal = this.getJsonAccessor(s.totalProperty);
13372 if(s.successProperty) {
13373 this.getSuccess = this.getJsonAccessor(s.successProperty);
13375 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
13377 var g = this.getJsonAccessor(s.id);
13378 this.getId = function(rec) {
13380 return (r === undefined || r === "") ? null : r;
13383 this.getId = function(){return null;};
13386 for(var jj = 0; jj < fl; jj++){
13388 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
13389 this.ef[jj] = this.getJsonAccessor(map);
13393 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
13394 if(s.totalProperty){
13395 var vt = parseInt(this.getTotal(o), 10);
13400 if(s.successProperty){
13401 var vs = this.getSuccess(o);
13402 if(vs === false || vs === 'false'){
13407 for(var i = 0; i < c; i++){
13410 var id = this.getId(n);
13411 for(var j = 0; j < fl; j++){
13413 var v = this.ef[j](n);
13415 Roo.log('missing convert for ' + f.name);
13419 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13421 var record = new Record(values, id);
13423 records[i] = record;
13429 totalRecords : totalRecords
13432 // used when loading children.. @see loadDataFromChildren
13433 toLoadData: function(rec)
13435 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13436 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13437 return { data : data, total : data.length };
13442 * Ext JS Library 1.1.1
13443 * Copyright(c) 2006-2007, Ext JS, LLC.
13445 * Originally Released Under LGPL - original licence link has changed is not relivant.
13448 * <script type="text/javascript">
13452 * @class Roo.data.ArrayReader
13453 * @extends Roo.data.DataReader
13454 * Data reader class to create an Array of Roo.data.Record objects from an Array.
13455 * Each element of that Array represents a row of data fields. The
13456 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13457 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13461 var RecordDef = Roo.data.Record.create([
13462 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
13463 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
13465 var myReader = new Roo.data.ArrayReader({
13466 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
13470 * This would consume an Array like this:
13472 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13476 * Create a new JsonReader
13477 * @param {Object} meta Metadata configuration options.
13478 * @param {Object|Array} recordType Either an Array of field definition objects
13480 * @cfg {Array} fields Array of field definition objects
13481 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13482 * as specified to {@link Roo.data.Record#create},
13483 * or an {@link Roo.data.Record} object
13486 * created using {@link Roo.data.Record#create}.
13488 Roo.data.ArrayReader = function(meta, recordType)
13490 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13493 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13496 * Create a data block containing Roo.data.Records from an XML document.
13497 * @param {Object} o An Array of row objects which represents the dataset.
13498 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13499 * a cache of Roo.data.Records.
13501 readRecords : function(o)
13503 var sid = this.meta ? this.meta.id : null;
13504 var recordType = this.recordType, fields = recordType.prototype.fields;
13507 for(var i = 0; i < root.length; i++){
13510 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13511 for(var j = 0, jlen = fields.length; j < jlen; j++){
13512 var f = fields.items[j];
13513 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13514 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13516 values[f.name] = v;
13518 var record = new recordType(values, id);
13520 records[records.length] = record;
13524 totalRecords : records.length
13527 // used when loading children.. @see loadDataFromChildren
13528 toLoadData: function(rec)
13530 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13531 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13542 * @class Roo.bootstrap.ComboBox
13543 * @extends Roo.bootstrap.TriggerField
13544 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13545 * @cfg {Boolean} append (true|false) default false
13546 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13547 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13548 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13549 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13550 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13551 * @cfg {Boolean} animate default true
13552 * @cfg {Boolean} emptyResultText only for touch device
13553 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13554 * @cfg {String} emptyTitle default ''
13556 * Create a new ComboBox.
13557 * @param {Object} config Configuration options
13559 Roo.bootstrap.ComboBox = function(config){
13560 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13564 * Fires when the dropdown list is expanded
13565 * @param {Roo.bootstrap.ComboBox} combo This combo box
13570 * Fires when the dropdown list is collapsed
13571 * @param {Roo.bootstrap.ComboBox} combo This combo box
13575 * @event beforeselect
13576 * Fires before a list item is selected. Return false to cancel the selection.
13577 * @param {Roo.bootstrap.ComboBox} combo This combo box
13578 * @param {Roo.data.Record} record The data record returned from the underlying store
13579 * @param {Number} index The index of the selected item in the dropdown list
13581 'beforeselect' : true,
13584 * Fires when a list item is selected
13585 * @param {Roo.bootstrap.ComboBox} combo This combo box
13586 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13587 * @param {Number} index The index of the selected item in the dropdown list
13591 * @event beforequery
13592 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13593 * The event object passed has these properties:
13594 * @param {Roo.bootstrap.ComboBox} combo This combo box
13595 * @param {String} query The query
13596 * @param {Boolean} forceAll true to force "all" query
13597 * @param {Boolean} cancel true to cancel the query
13598 * @param {Object} e The query event object
13600 'beforequery': true,
13603 * Fires when the 'add' icon is pressed (add a listener to enable add button)
13604 * @param {Roo.bootstrap.ComboBox} combo This combo box
13609 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13610 * @param {Roo.bootstrap.ComboBox} combo This combo box
13611 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13616 * Fires when the remove value from the combobox array
13617 * @param {Roo.bootstrap.ComboBox} combo This combo box
13621 * @event afterremove
13622 * Fires when the remove value from the combobox array
13623 * @param {Roo.bootstrap.ComboBox} combo This combo box
13625 'afterremove' : true,
13627 * @event specialfilter
13628 * Fires when specialfilter
13629 * @param {Roo.bootstrap.ComboBox} combo This combo box
13631 'specialfilter' : true,
13634 * Fires when tick the element
13635 * @param {Roo.bootstrap.ComboBox} combo This combo box
13639 * @event touchviewdisplay
13640 * Fires when touch view require special display (default is using displayField)
13641 * @param {Roo.bootstrap.ComboBox} combo This combo box
13642 * @param {Object} cfg set html .
13644 'touchviewdisplay' : true
13649 this.tickItems = [];
13651 this.selectedIndex = -1;
13652 if(this.mode == 'local'){
13653 if(config.queryDelay === undefined){
13654 this.queryDelay = 10;
13656 if(config.minChars === undefined){
13662 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13665 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13666 * rendering into an Roo.Editor, defaults to false)
13669 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13670 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13673 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13676 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13677 * the dropdown list (defaults to undefined, with no header element)
13681 * @cfg {String/Roo.Template} tpl The template to use to render the output
13685 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13687 listWidth: undefined,
13689 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13690 * mode = 'remote' or 'text' if mode = 'local')
13692 displayField: undefined,
13695 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13696 * mode = 'remote' or 'value' if mode = 'local').
13697 * Note: use of a valueField requires the user make a selection
13698 * in order for a value to be mapped.
13700 valueField: undefined,
13702 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13707 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13708 * field's data value (defaults to the underlying DOM element's name)
13710 hiddenName: undefined,
13712 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13716 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13718 selectedClass: 'active',
13721 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13725 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13726 * anchor positions (defaults to 'tl-bl')
13728 listAlign: 'tl-bl?',
13730 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13734 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13735 * query specified by the allQuery config option (defaults to 'query')
13737 triggerAction: 'query',
13739 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13740 * (defaults to 4, does not apply if editable = false)
13744 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13745 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13749 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13750 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13754 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13755 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13759 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13760 * when editable = true (defaults to false)
13762 selectOnFocus:false,
13764 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13766 queryParam: 'query',
13768 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13769 * when mode = 'remote' (defaults to 'Loading...')
13771 loadingText: 'Loading...',
13773 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13777 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13781 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13782 * traditional select (defaults to true)
13786 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13790 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13794 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13795 * listWidth has a higher value)
13799 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13800 * allow the user to set arbitrary text into the field (defaults to false)
13802 forceSelection:false,
13804 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13805 * if typeAhead = true (defaults to 250)
13807 typeAheadDelay : 250,
13809 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13810 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13812 valueNotFoundText : undefined,
13814 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13816 blockFocus : false,
13819 * @cfg {Boolean} disableClear Disable showing of clear button.
13821 disableClear : false,
13823 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13825 alwaysQuery : false,
13828 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13833 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13835 invalidClass : "has-warning",
13838 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13840 validClass : "has-success",
13843 * @cfg {Boolean} specialFilter (true|false) special filter default false
13845 specialFilter : false,
13848 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13850 mobileTouchView : true,
13853 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13855 useNativeIOS : false,
13858 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13860 mobile_restrict_height : false,
13862 ios_options : false,
13874 btnPosition : 'right',
13875 triggerList : true,
13876 showToggleBtn : true,
13878 emptyResultText: 'Empty',
13879 triggerText : 'Select',
13882 // element that contains real text value.. (when hidden is used..)
13884 getAutoCreate : function()
13889 * Render classic select for iso
13892 if(Roo.isIOS && this.useNativeIOS){
13893 cfg = this.getAutoCreateNativeIOS();
13901 if(Roo.isTouch && this.mobileTouchView){
13902 cfg = this.getAutoCreateTouchView();
13909 if(!this.tickable){
13910 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13915 * ComboBox with tickable selections
13918 var align = this.labelAlign || this.parentLabelAlign();
13921 cls : 'form-group roo-combobox-tickable' //input-group
13924 var btn_text_select = '';
13925 var btn_text_done = '';
13926 var btn_text_cancel = '';
13928 if (this.btn_text_show) {
13929 btn_text_select = 'Select';
13930 btn_text_done = 'Done';
13931 btn_text_cancel = 'Cancel';
13936 cls : 'tickable-buttons',
13941 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13942 //html : this.triggerText
13943 html: btn_text_select
13949 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13951 html: btn_text_done
13957 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13959 html: btn_text_cancel
13965 buttons.cn.unshift({
13967 cls: 'roo-select2-search-field-input'
13973 Roo.each(buttons.cn, function(c){
13975 c.cls += ' btn-' + _this.size;
13978 if (_this.disabled) {
13985 style : 'display: contents',
13990 cls: 'form-hidden-field'
13994 cls: 'roo-select2-choices',
13998 cls: 'roo-select2-search-field',
14009 cls: 'roo-select2-container input-group roo-select2-container-multi',
14015 // cls: 'typeahead typeahead-long dropdown-menu',
14016 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
14021 if(this.hasFeedback && !this.allowBlank){
14025 cls: 'glyphicon form-control-feedback'
14028 combobox.cn.push(feedback);
14033 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
14034 tooltip : 'This field is required'
14036 if (Roo.bootstrap.version == 4) {
14039 style : 'display:none'
14042 if (align ==='left' && this.fieldLabel.length) {
14044 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
14051 cls : 'control-label col-form-label',
14052 html : this.fieldLabel
14064 var labelCfg = cfg.cn[1];
14065 var contentCfg = cfg.cn[2];
14068 if(this.indicatorpos == 'right'){
14074 cls : 'control-label col-form-label',
14078 html : this.fieldLabel
14094 labelCfg = cfg.cn[0];
14095 contentCfg = cfg.cn[1];
14099 if(this.labelWidth > 12){
14100 labelCfg.style = "width: " + this.labelWidth + 'px';
14103 if(this.labelWidth < 13 && this.labelmd == 0){
14104 this.labelmd = this.labelWidth;
14107 if(this.labellg > 0){
14108 labelCfg.cls += ' col-lg-' + this.labellg;
14109 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14112 if(this.labelmd > 0){
14113 labelCfg.cls += ' col-md-' + this.labelmd;
14114 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14117 if(this.labelsm > 0){
14118 labelCfg.cls += ' col-sm-' + this.labelsm;
14119 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14122 if(this.labelxs > 0){
14123 labelCfg.cls += ' col-xs-' + this.labelxs;
14124 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14128 } else if ( this.fieldLabel.length) {
14129 // Roo.log(" label");
14134 //cls : 'input-group-addon',
14135 html : this.fieldLabel
14140 if(this.indicatorpos == 'right'){
14144 //cls : 'input-group-addon',
14145 html : this.fieldLabel
14155 // Roo.log(" no label && no align");
14162 ['xs','sm','md','lg'].map(function(size){
14163 if (settings[size]) {
14164 cfg.cls += ' col-' + size + '-' + settings[size];
14172 _initEventsCalled : false,
14175 initEvents: function()
14177 if (this._initEventsCalled) { // as we call render... prevent looping...
14180 this._initEventsCalled = true;
14183 throw "can not find store for combo";
14186 this.indicator = this.indicatorEl();
14188 this.store = Roo.factory(this.store, Roo.data);
14189 this.store.parent = this;
14191 // if we are building from html. then this element is so complex, that we can not really
14192 // use the rendered HTML.
14193 // so we have to trash and replace the previous code.
14194 if (Roo.XComponent.build_from_html) {
14195 // remove this element....
14196 var e = this.el.dom, k=0;
14197 while (e ) { e = e.previousSibling; ++k;}
14202 this.rendered = false;
14204 this.render(this.parent().getChildContainer(true), k);
14207 if(Roo.isIOS && this.useNativeIOS){
14208 this.initIOSView();
14216 if(Roo.isTouch && this.mobileTouchView){
14217 this.initTouchView();
14222 this.initTickableEvents();
14226 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
14228 if(this.hiddenName){
14230 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14232 this.hiddenField.dom.value =
14233 this.hiddenValue !== undefined ? this.hiddenValue :
14234 this.value !== undefined ? this.value : '';
14236 // prevent input submission
14237 this.el.dom.removeAttribute('name');
14238 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14243 // this.el.dom.setAttribute('autocomplete', 'off');
14246 var cls = 'x-combo-list';
14248 //this.list = new Roo.Layer({
14249 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
14255 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14256 _this.list.setWidth(lw);
14259 this.list.on('mouseover', this.onViewOver, this);
14260 this.list.on('mousemove', this.onViewMove, this);
14261 this.list.on('scroll', this.onViewScroll, this);
14264 this.list.swallowEvent('mousewheel');
14265 this.assetHeight = 0;
14268 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
14269 this.assetHeight += this.header.getHeight();
14272 this.innerList = this.list.createChild({cls:cls+'-inner'});
14273 this.innerList.on('mouseover', this.onViewOver, this);
14274 this.innerList.on('mousemove', this.onViewMove, this);
14275 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14277 if(this.allowBlank && !this.pageSize && !this.disableClear){
14278 this.footer = this.list.createChild({cls:cls+'-ft'});
14279 this.pageTb = new Roo.Toolbar(this.footer);
14283 this.footer = this.list.createChild({cls:cls+'-ft'});
14284 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
14285 {pageSize: this.pageSize});
14289 if (this.pageTb && this.allowBlank && !this.disableClear) {
14291 this.pageTb.add(new Roo.Toolbar.Fill(), {
14292 cls: 'x-btn-icon x-btn-clear',
14294 handler: function()
14297 _this.clearValue();
14298 _this.onSelect(false, -1);
14303 this.assetHeight += this.footer.getHeight();
14308 this.tpl = Roo.bootstrap.version == 4 ?
14309 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
14310 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
14313 this.view = new Roo.View(this.list, this.tpl, {
14314 singleSelect:true, store: this.store, selectedClass: this.selectedClass
14316 //this.view.wrapEl.setDisplayed(false);
14317 this.view.on('click', this.onViewClick, this);
14320 this.store.on('beforeload', this.onBeforeLoad, this);
14321 this.store.on('load', this.onLoad, this);
14322 this.store.on('loadexception', this.onLoadException, this);
14324 if(this.resizable){
14325 this.resizer = new Roo.Resizable(this.list, {
14326 pinned:true, handles:'se'
14328 this.resizer.on('resize', function(r, w, h){
14329 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
14330 this.listWidth = w;
14331 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
14332 this.restrictHeight();
14334 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
14337 if(!this.editable){
14338 this.editable = true;
14339 this.setEditable(false);
14344 if (typeof(this.events.add.listeners) != 'undefined') {
14346 this.addicon = this.wrap.createChild(
14347 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
14349 this.addicon.on('click', function(e) {
14350 this.fireEvent('add', this);
14353 if (typeof(this.events.edit.listeners) != 'undefined') {
14355 this.editicon = this.wrap.createChild(
14356 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
14357 if (this.addicon) {
14358 this.editicon.setStyle('margin-left', '40px');
14360 this.editicon.on('click', function(e) {
14362 // we fire even if inothing is selected..
14363 this.fireEvent('edit', this, this.lastData );
14369 this.keyNav = new Roo.KeyNav(this.inputEl(), {
14370 "up" : function(e){
14371 this.inKeyMode = true;
14375 "down" : function(e){
14376 if(!this.isExpanded()){
14377 this.onTriggerClick();
14379 this.inKeyMode = true;
14384 "enter" : function(e){
14385 // this.onViewClick();
14389 if(this.fireEvent("specialkey", this, e)){
14390 this.onViewClick(false);
14396 "esc" : function(e){
14400 "tab" : function(e){
14403 if(this.fireEvent("specialkey", this, e)){
14404 this.onViewClick(false);
14412 doRelay : function(foo, bar, hname){
14413 if(hname == 'down' || this.scope.isExpanded()){
14414 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14423 this.queryDelay = Math.max(this.queryDelay || 10,
14424 this.mode == 'local' ? 10 : 250);
14427 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14429 if(this.typeAhead){
14430 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14432 if(this.editable !== false){
14433 this.inputEl().on("keyup", this.onKeyUp, this);
14435 if(this.forceSelection){
14436 this.inputEl().on('blur', this.doForce, this);
14440 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14441 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14445 initTickableEvents: function()
14449 if(this.hiddenName){
14451 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14453 this.hiddenField.dom.value =
14454 this.hiddenValue !== undefined ? this.hiddenValue :
14455 this.value !== undefined ? this.value : '';
14457 // prevent input submission
14458 this.el.dom.removeAttribute('name');
14459 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14464 // this.list = this.el.select('ul.dropdown-menu',true).first();
14466 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14467 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14468 if(this.triggerList){
14469 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14472 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14473 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14475 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14476 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14478 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14479 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14481 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14482 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14483 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14486 this.cancelBtn.hide();
14491 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14492 _this.list.setWidth(lw);
14495 this.list.on('mouseover', this.onViewOver, this);
14496 this.list.on('mousemove', this.onViewMove, this);
14498 this.list.on('scroll', this.onViewScroll, this);
14501 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
14502 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14505 this.view = new Roo.View(this.list, this.tpl, {
14510 selectedClass: this.selectedClass
14513 //this.view.wrapEl.setDisplayed(false);
14514 this.view.on('click', this.onViewClick, this);
14518 this.store.on('beforeload', this.onBeforeLoad, this);
14519 this.store.on('load', this.onLoad, this);
14520 this.store.on('loadexception', this.onLoadException, this);
14523 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14524 "up" : function(e){
14525 this.inKeyMode = true;
14529 "down" : function(e){
14530 this.inKeyMode = true;
14534 "enter" : function(e){
14535 if(this.fireEvent("specialkey", this, e)){
14536 this.onViewClick(false);
14542 "esc" : function(e){
14543 this.onTickableFooterButtonClick(e, false, false);
14546 "tab" : function(e){
14547 this.fireEvent("specialkey", this, e);
14549 this.onTickableFooterButtonClick(e, false, false);
14556 doRelay : function(e, fn, key){
14557 if(this.scope.isExpanded()){
14558 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14567 this.queryDelay = Math.max(this.queryDelay || 10,
14568 this.mode == 'local' ? 10 : 250);
14571 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14573 if(this.typeAhead){
14574 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14577 if(this.editable !== false){
14578 this.tickableInputEl().on("keyup", this.onKeyUp, this);
14581 this.indicator = this.indicatorEl();
14583 if(this.indicator){
14584 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14585 this.indicator.hide();
14590 onDestroy : function(){
14592 this.view.setStore(null);
14593 this.view.el.removeAllListeners();
14594 this.view.el.remove();
14595 this.view.purgeListeners();
14598 this.list.dom.innerHTML = '';
14602 this.store.un('beforeload', this.onBeforeLoad, this);
14603 this.store.un('load', this.onLoad, this);
14604 this.store.un('loadexception', this.onLoadException, this);
14606 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14610 fireKey : function(e){
14611 if(e.isNavKeyPress() && !this.list.isVisible()){
14612 this.fireEvent("specialkey", this, e);
14617 onResize: function(w, h){
14618 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14620 // if(typeof w != 'number'){
14621 // // we do not handle it!?!?
14624 // var tw = this.trigger.getWidth();
14625 // // tw += this.addicon ? this.addicon.getWidth() : 0;
14626 // // tw += this.editicon ? this.editicon.getWidth() : 0;
14628 // this.inputEl().setWidth( this.adjustWidth('input', x));
14630 // //this.trigger.setStyle('left', x+'px');
14632 // if(this.list && this.listWidth === undefined){
14633 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14634 // this.list.setWidth(lw);
14635 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14643 * Allow or prevent the user from directly editing the field text. If false is passed,
14644 * the user will only be able to select from the items defined in the dropdown list. This method
14645 * is the runtime equivalent of setting the 'editable' config option at config time.
14646 * @param {Boolean} value True to allow the user to directly edit the field text
14648 setEditable : function(value){
14649 if(value == this.editable){
14652 this.editable = value;
14654 this.inputEl().dom.setAttribute('readOnly', true);
14655 this.inputEl().on('mousedown', this.onTriggerClick, this);
14656 this.inputEl().addClass('x-combo-noedit');
14658 this.inputEl().dom.setAttribute('readOnly', false);
14659 this.inputEl().un('mousedown', this.onTriggerClick, this);
14660 this.inputEl().removeClass('x-combo-noedit');
14666 onBeforeLoad : function(combo,opts){
14667 if(!this.hasFocus){
14671 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14673 this.restrictHeight();
14674 this.selectedIndex = -1;
14678 onLoad : function(){
14680 this.hasQuery = false;
14682 if(!this.hasFocus){
14686 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14687 this.loading.hide();
14690 if(this.store.getCount() > 0){
14693 this.restrictHeight();
14694 if(this.lastQuery == this.allQuery){
14695 if(this.editable && !this.tickable){
14696 this.inputEl().dom.select();
14700 !this.selectByValue(this.value, true) &&
14703 !this.store.lastOptions ||
14704 typeof(this.store.lastOptions.add) == 'undefined' ||
14705 this.store.lastOptions.add != true
14708 this.select(0, true);
14711 if(this.autoFocus){
14714 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14715 this.taTask.delay(this.typeAheadDelay);
14719 this.onEmptyResults();
14725 onLoadException : function()
14727 this.hasQuery = false;
14729 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14730 this.loading.hide();
14733 if(this.tickable && this.editable){
14738 // only causes errors at present
14739 //Roo.log(this.store.reader.jsonData);
14740 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14742 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14748 onTypeAhead : function(){
14749 if(this.store.getCount() > 0){
14750 var r = this.store.getAt(0);
14751 var newValue = r.data[this.displayField];
14752 var len = newValue.length;
14753 var selStart = this.getRawValue().length;
14755 if(selStart != len){
14756 this.setRawValue(newValue);
14757 this.selectText(selStart, newValue.length);
14763 onSelect : function(record, index){
14765 if(this.fireEvent('beforeselect', this, record, index) !== false){
14767 this.setFromData(index > -1 ? record.data : false);
14770 this.fireEvent('select', this, record, index);
14775 * Returns the currently selected field value or empty string if no value is set.
14776 * @return {String} value The selected value
14778 getValue : function()
14780 if(Roo.isIOS && this.useNativeIOS){
14781 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14785 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14788 if(this.valueField){
14789 return typeof this.value != 'undefined' ? this.value : '';
14791 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14795 getRawValue : function()
14797 if(Roo.isIOS && this.useNativeIOS){
14798 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14801 var v = this.inputEl().getValue();
14807 * Clears any text/value currently set in the field
14809 clearValue : function(){
14811 if(this.hiddenField){
14812 this.hiddenField.dom.value = '';
14815 this.setRawValue('');
14816 this.lastSelectionText = '';
14817 this.lastData = false;
14819 var close = this.closeTriggerEl();
14830 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14831 * will be displayed in the field. If the value does not match the data value of an existing item,
14832 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14833 * Otherwise the field will be blank (although the value will still be set).
14834 * @param {String} value The value to match
14836 setValue : function(v)
14838 if(Roo.isIOS && this.useNativeIOS){
14839 this.setIOSValue(v);
14849 if(this.valueField){
14850 var r = this.findRecord(this.valueField, v);
14852 text = r.data[this.displayField];
14853 }else if(this.valueNotFoundText !== undefined){
14854 text = this.valueNotFoundText;
14857 this.lastSelectionText = text;
14858 if(this.hiddenField){
14859 this.hiddenField.dom.value = v;
14861 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14864 var close = this.closeTriggerEl();
14867 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14873 * @property {Object} the last set data for the element
14878 * Sets the value of the field based on a object which is related to the record format for the store.
14879 * @param {Object} value the value to set as. or false on reset?
14881 setFromData : function(o){
14888 var dv = ''; // display value
14889 var vv = ''; // value value..
14891 if (this.displayField) {
14892 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14894 // this is an error condition!!!
14895 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14898 if(this.valueField){
14899 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14902 var close = this.closeTriggerEl();
14905 if(dv.length || vv * 1 > 0){
14907 this.blockFocus=true;
14913 if(this.hiddenField){
14914 this.hiddenField.dom.value = vv;
14916 this.lastSelectionText = dv;
14917 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14921 // no hidden field.. - we store the value in 'value', but still display
14922 // display field!!!!
14923 this.lastSelectionText = dv;
14924 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14931 reset : function(){
14932 // overridden so that last data is reset..
14939 this.setValue(this.originalValue);
14940 //this.clearInvalid();
14941 this.lastData = false;
14943 this.view.clearSelections();
14949 findRecord : function(prop, value){
14951 if(this.store.getCount() > 0){
14952 this.store.each(function(r){
14953 if(r.data[prop] == value){
14963 getName: function()
14965 // returns hidden if it's set..
14966 if (!this.rendered) {return ''};
14967 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14971 onViewMove : function(e, t){
14972 this.inKeyMode = false;
14976 onViewOver : function(e, t){
14977 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14980 var item = this.view.findItemFromChild(t);
14983 var index = this.view.indexOf(item);
14984 this.select(index, false);
14989 onViewClick : function(view, doFocus, el, e)
14991 var index = this.view.getSelectedIndexes()[0];
14993 var r = this.store.getAt(index);
14997 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
15004 Roo.each(this.tickItems, function(v,k){
15006 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
15008 _this.tickItems.splice(k, 1);
15010 if(typeof(e) == 'undefined' && view == false){
15011 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
15023 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
15024 this.tickItems.push(r.data);
15027 if(typeof(e) == 'undefined' && view == false){
15028 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
15035 this.onSelect(r, index);
15037 if(doFocus !== false && !this.blockFocus){
15038 this.inputEl().focus();
15043 restrictHeight : function(){
15044 //this.innerList.dom.style.height = '';
15045 //var inner = this.innerList.dom;
15046 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
15047 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
15048 //this.list.beginUpdate();
15049 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
15050 this.list.alignTo(this.inputEl(), this.listAlign);
15051 this.list.alignTo(this.inputEl(), this.listAlign);
15052 //this.list.endUpdate();
15056 onEmptyResults : function(){
15058 if(this.tickable && this.editable){
15059 this.hasFocus = false;
15060 this.restrictHeight();
15068 * Returns true if the dropdown list is expanded, else false.
15070 isExpanded : function(){
15071 return this.list.isVisible();
15075 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
15076 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15077 * @param {String} value The data value of the item to select
15078 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15079 * selected item if it is not currently in view (defaults to true)
15080 * @return {Boolean} True if the value matched an item in the list, else false
15082 selectByValue : function(v, scrollIntoView){
15083 if(v !== undefined && v !== null){
15084 var r = this.findRecord(this.valueField || this.displayField, v);
15086 this.select(this.store.indexOf(r), scrollIntoView);
15094 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
15095 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15096 * @param {Number} index The zero-based index of the list item to select
15097 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15098 * selected item if it is not currently in view (defaults to true)
15100 select : function(index, scrollIntoView){
15101 this.selectedIndex = index;
15102 this.view.select(index);
15103 if(scrollIntoView !== false){
15104 var el = this.view.getNode(index);
15106 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
15109 this.list.scrollChildIntoView(el, false);
15115 selectNext : function(){
15116 var ct = this.store.getCount();
15118 if(this.selectedIndex == -1){
15120 }else if(this.selectedIndex < ct-1){
15121 this.select(this.selectedIndex+1);
15127 selectPrev : function(){
15128 var ct = this.store.getCount();
15130 if(this.selectedIndex == -1){
15132 }else if(this.selectedIndex != 0){
15133 this.select(this.selectedIndex-1);
15139 onKeyUp : function(e){
15140 if(this.editable !== false && !e.isSpecialKey()){
15141 this.lastKey = e.getKey();
15142 this.dqTask.delay(this.queryDelay);
15147 validateBlur : function(){
15148 return !this.list || !this.list.isVisible();
15152 initQuery : function(){
15154 var v = this.getRawValue();
15156 if(this.tickable && this.editable){
15157 v = this.tickableInputEl().getValue();
15164 doForce : function(){
15165 if(this.inputEl().dom.value.length > 0){
15166 this.inputEl().dom.value =
15167 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
15173 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
15174 * query allowing the query action to be canceled if needed.
15175 * @param {String} query The SQL query to execute
15176 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
15177 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
15178 * saved in the current store (defaults to false)
15180 doQuery : function(q, forceAll){
15182 if(q === undefined || q === null){
15187 forceAll: forceAll,
15191 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
15196 forceAll = qe.forceAll;
15197 if(forceAll === true || (q.length >= this.minChars)){
15199 this.hasQuery = true;
15201 if(this.lastQuery != q || this.alwaysQuery){
15202 this.lastQuery = q;
15203 if(this.mode == 'local'){
15204 this.selectedIndex = -1;
15206 this.store.clearFilter();
15209 if(this.specialFilter){
15210 this.fireEvent('specialfilter', this);
15215 this.store.filter(this.displayField, q);
15218 this.store.fireEvent("datachanged", this.store);
15225 this.store.baseParams[this.queryParam] = q;
15227 var options = {params : this.getParams(q)};
15230 options.add = true;
15231 options.params.start = this.page * this.pageSize;
15234 this.store.load(options);
15237 * this code will make the page width larger, at the beginning, the list not align correctly,
15238 * we should expand the list on onLoad
15239 * so command out it
15244 this.selectedIndex = -1;
15249 this.loadNext = false;
15253 getParams : function(q){
15255 //p[this.queryParam] = q;
15259 p.limit = this.pageSize;
15265 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
15267 collapse : function(){
15268 if(!this.isExpanded()){
15274 this.hasFocus = false;
15278 this.cancelBtn.hide();
15279 this.trigger.show();
15282 this.tickableInputEl().dom.value = '';
15283 this.tickableInputEl().blur();
15288 Roo.get(document).un('mousedown', this.collapseIf, this);
15289 Roo.get(document).un('mousewheel', this.collapseIf, this);
15290 if (!this.editable) {
15291 Roo.get(document).un('keydown', this.listKeyPress, this);
15293 this.fireEvent('collapse', this);
15299 collapseIf : function(e){
15300 var in_combo = e.within(this.el);
15301 var in_list = e.within(this.list);
15302 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
15304 if (in_combo || in_list || is_list) {
15305 //e.stopPropagation();
15310 this.onTickableFooterButtonClick(e, false, false);
15318 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
15320 expand : function(){
15322 if(this.isExpanded() || !this.hasFocus){
15326 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
15327 this.list.setWidth(lw);
15333 this.restrictHeight();
15337 this.tickItems = Roo.apply([], this.item);
15340 this.cancelBtn.show();
15341 this.trigger.hide();
15344 this.tickableInputEl().focus();
15349 Roo.get(document).on('mousedown', this.collapseIf, this);
15350 Roo.get(document).on('mousewheel', this.collapseIf, this);
15351 if (!this.editable) {
15352 Roo.get(document).on('keydown', this.listKeyPress, this);
15355 this.fireEvent('expand', this);
15359 // Implements the default empty TriggerField.onTriggerClick function
15360 onTriggerClick : function(e)
15362 Roo.log('trigger click');
15364 if(this.disabled || !this.triggerList){
15369 this.loadNext = false;
15371 if(this.isExpanded()){
15373 if (!this.blockFocus) {
15374 this.inputEl().focus();
15378 this.hasFocus = true;
15379 if(this.triggerAction == 'all') {
15380 this.doQuery(this.allQuery, true);
15382 this.doQuery(this.getRawValue());
15384 if (!this.blockFocus) {
15385 this.inputEl().focus();
15390 onTickableTriggerClick : function(e)
15397 this.loadNext = false;
15398 this.hasFocus = true;
15400 if(this.triggerAction == 'all') {
15401 this.doQuery(this.allQuery, true);
15403 this.doQuery(this.getRawValue());
15407 onSearchFieldClick : function(e)
15409 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15410 this.onTickableFooterButtonClick(e, false, false);
15414 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15419 this.loadNext = false;
15420 this.hasFocus = true;
15422 if(this.triggerAction == 'all') {
15423 this.doQuery(this.allQuery, true);
15425 this.doQuery(this.getRawValue());
15429 listKeyPress : function(e)
15431 //Roo.log('listkeypress');
15432 // scroll to first matching element based on key pres..
15433 if (e.isSpecialKey()) {
15436 var k = String.fromCharCode(e.getKey()).toUpperCase();
15439 var csel = this.view.getSelectedNodes();
15440 var cselitem = false;
15442 var ix = this.view.indexOf(csel[0]);
15443 cselitem = this.store.getAt(ix);
15444 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15450 this.store.each(function(v) {
15452 // start at existing selection.
15453 if (cselitem.id == v.id) {
15459 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15460 match = this.store.indexOf(v);
15466 if (match === false) {
15467 return true; // no more action?
15470 this.view.select(match);
15471 var sn = Roo.get(this.view.getSelectedNodes()[0]);
15472 sn.scrollIntoView(sn.dom.parentNode, false);
15475 onViewScroll : function(e, t){
15477 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){
15481 this.hasQuery = true;
15483 this.loading = this.list.select('.loading', true).first();
15485 if(this.loading === null){
15486 this.list.createChild({
15488 cls: 'loading roo-select2-more-results roo-select2-active',
15489 html: 'Loading more results...'
15492 this.loading = this.list.select('.loading', true).first();
15494 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15496 this.loading.hide();
15499 this.loading.show();
15504 this.loadNext = true;
15506 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15511 addItem : function(o)
15513 var dv = ''; // display value
15515 if (this.displayField) {
15516 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15518 // this is an error condition!!!
15519 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
15526 var choice = this.choices.createChild({
15528 cls: 'roo-select2-search-choice',
15537 cls: 'roo-select2-search-choice-close fa fa-times',
15542 }, this.searchField);
15544 var close = choice.select('a.roo-select2-search-choice-close', true).first();
15546 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15554 this.inputEl().dom.value = '';
15559 onRemoveItem : function(e, _self, o)
15561 e.preventDefault();
15563 this.lastItem = Roo.apply([], this.item);
15565 var index = this.item.indexOf(o.data) * 1;
15568 Roo.log('not this item?!');
15572 this.item.splice(index, 1);
15577 this.fireEvent('remove', this, e);
15583 syncValue : function()
15585 if(!this.item.length){
15592 Roo.each(this.item, function(i){
15593 if(_this.valueField){
15594 value.push(i[_this.valueField]);
15601 this.value = value.join(',');
15603 if(this.hiddenField){
15604 this.hiddenField.dom.value = this.value;
15607 this.store.fireEvent("datachanged", this.store);
15612 clearItem : function()
15614 if(!this.multiple){
15620 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15628 if(this.tickable && !Roo.isTouch){
15629 this.view.refresh();
15633 inputEl: function ()
15635 if(Roo.isIOS && this.useNativeIOS){
15636 return this.el.select('select.roo-ios-select', true).first();
15639 if(Roo.isTouch && this.mobileTouchView){
15640 return this.el.select('input.form-control',true).first();
15644 return this.searchField;
15647 return this.el.select('input.form-control',true).first();
15650 onTickableFooterButtonClick : function(e, btn, el)
15652 e.preventDefault();
15654 this.lastItem = Roo.apply([], this.item);
15656 if(btn && btn.name == 'cancel'){
15657 this.tickItems = Roo.apply([], this.item);
15666 Roo.each(this.tickItems, function(o){
15674 validate : function()
15676 if(this.getVisibilityEl().hasClass('hidden')){
15680 var v = this.getRawValue();
15683 v = this.getValue();
15686 if(this.disabled || this.allowBlank || v.length){
15691 this.markInvalid();
15695 tickableInputEl : function()
15697 if(!this.tickable || !this.editable){
15698 return this.inputEl();
15701 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15705 getAutoCreateTouchView : function()
15710 cls: 'form-group' //input-group
15716 type : this.inputType,
15717 cls : 'form-control x-combo-noedit',
15718 autocomplete: 'new-password',
15719 placeholder : this.placeholder || '',
15724 input.name = this.name;
15728 input.cls += ' input-' + this.size;
15731 if (this.disabled) {
15732 input.disabled = true;
15743 inputblock.cls += ' input-group';
15745 inputblock.cn.unshift({
15747 cls : 'input-group-addon input-group-prepend input-group-text',
15752 if(this.removable && !this.multiple){
15753 inputblock.cls += ' roo-removable';
15755 inputblock.cn.push({
15758 cls : 'roo-combo-removable-btn close'
15762 if(this.hasFeedback && !this.allowBlank){
15764 inputblock.cls += ' has-feedback';
15766 inputblock.cn.push({
15768 cls: 'glyphicon form-control-feedback'
15775 inputblock.cls += (this.before) ? '' : ' input-group';
15777 inputblock.cn.push({
15779 cls : 'input-group-addon input-group-append input-group-text',
15785 var ibwrap = inputblock;
15790 cls: 'roo-select2-choices',
15794 cls: 'roo-select2-search-field',
15807 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15812 cls: 'form-hidden-field'
15818 if(!this.multiple && this.showToggleBtn){
15824 if (this.caret != false) {
15827 cls: 'fa fa-' + this.caret
15834 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15836 Roo.bootstrap.version == 3 ? caret : '',
15839 cls: 'combobox-clear',
15853 combobox.cls += ' roo-select2-container-multi';
15856 var align = this.labelAlign || this.parentLabelAlign();
15858 if (align ==='left' && this.fieldLabel.length) {
15863 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15864 tooltip : 'This field is required'
15868 cls : 'control-label col-form-label',
15869 html : this.fieldLabel
15880 var labelCfg = cfg.cn[1];
15881 var contentCfg = cfg.cn[2];
15884 if(this.indicatorpos == 'right'){
15889 cls : 'control-label col-form-label',
15893 html : this.fieldLabel
15897 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15898 tooltip : 'This field is required'
15911 labelCfg = cfg.cn[0];
15912 contentCfg = cfg.cn[1];
15917 if(this.labelWidth > 12){
15918 labelCfg.style = "width: " + this.labelWidth + 'px';
15921 if(this.labelWidth < 13 && this.labelmd == 0){
15922 this.labelmd = this.labelWidth;
15925 if(this.labellg > 0){
15926 labelCfg.cls += ' col-lg-' + this.labellg;
15927 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15930 if(this.labelmd > 0){
15931 labelCfg.cls += ' col-md-' + this.labelmd;
15932 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15935 if(this.labelsm > 0){
15936 labelCfg.cls += ' col-sm-' + this.labelsm;
15937 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15940 if(this.labelxs > 0){
15941 labelCfg.cls += ' col-xs-' + this.labelxs;
15942 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15946 } else if ( this.fieldLabel.length) {
15950 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15951 tooltip : 'This field is required'
15955 cls : 'control-label',
15956 html : this.fieldLabel
15967 if(this.indicatorpos == 'right'){
15971 cls : 'control-label',
15972 html : this.fieldLabel,
15976 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15977 tooltip : 'This field is required'
15994 var settings = this;
15996 ['xs','sm','md','lg'].map(function(size){
15997 if (settings[size]) {
15998 cfg.cls += ' col-' + size + '-' + settings[size];
16005 initTouchView : function()
16007 this.renderTouchView();
16009 this.touchViewEl.on('scroll', function(){
16010 this.el.dom.scrollTop = 0;
16013 this.originalValue = this.getValue();
16015 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
16017 this.inputEl().on("click", this.showTouchView, this);
16018 if (this.triggerEl) {
16019 this.triggerEl.on("click", this.showTouchView, this);
16023 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
16024 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
16026 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
16028 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
16029 this.store.on('load', this.onTouchViewLoad, this);
16030 this.store.on('loadexception', this.onTouchViewLoadException, this);
16032 if(this.hiddenName){
16034 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16036 this.hiddenField.dom.value =
16037 this.hiddenValue !== undefined ? this.hiddenValue :
16038 this.value !== undefined ? this.value : '';
16040 this.el.dom.removeAttribute('name');
16041 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16045 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16046 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16049 if(this.removable && !this.multiple){
16050 var close = this.closeTriggerEl();
16052 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
16053 close.on('click', this.removeBtnClick, this, close);
16057 * fix the bug in Safari iOS8
16059 this.inputEl().on("focus", function(e){
16060 document.activeElement.blur();
16063 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
16070 renderTouchView : function()
16072 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
16073 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16075 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
16076 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16078 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
16079 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16080 this.touchViewBodyEl.setStyle('overflow', 'auto');
16082 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
16083 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16085 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
16086 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16090 showTouchView : function()
16096 this.touchViewHeaderEl.hide();
16098 if(this.modalTitle.length){
16099 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
16100 this.touchViewHeaderEl.show();
16103 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
16104 this.touchViewEl.show();
16106 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
16108 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
16109 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
16111 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16113 if(this.modalTitle.length){
16114 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16117 this.touchViewBodyEl.setHeight(bodyHeight);
16121 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
16123 this.touchViewEl.addClass('in');
16126 if(this._touchViewMask){
16127 Roo.get(document.body).addClass("x-body-masked");
16128 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
16129 this._touchViewMask.setStyle('z-index', 10000);
16130 this._touchViewMask.addClass('show');
16133 this.doTouchViewQuery();
16137 hideTouchView : function()
16139 this.touchViewEl.removeClass('in');
16143 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
16145 this.touchViewEl.setStyle('display', 'none');
16148 if(this._touchViewMask){
16149 this._touchViewMask.removeClass('show');
16150 Roo.get(document.body).removeClass("x-body-masked");
16154 setTouchViewValue : function()
16161 Roo.each(this.tickItems, function(o){
16166 this.hideTouchView();
16169 doTouchViewQuery : function()
16178 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
16182 if(!this.alwaysQuery || this.mode == 'local'){
16183 this.onTouchViewLoad();
16190 onTouchViewBeforeLoad : function(combo,opts)
16196 onTouchViewLoad : function()
16198 if(this.store.getCount() < 1){
16199 this.onTouchViewEmptyResults();
16203 this.clearTouchView();
16205 var rawValue = this.getRawValue();
16207 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
16209 this.tickItems = [];
16211 this.store.data.each(function(d, rowIndex){
16212 var row = this.touchViewListGroup.createChild(template);
16214 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
16215 row.addClass(d.data.cls);
16218 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16221 html : d.data[this.displayField]
16224 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
16225 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
16228 row.removeClass('selected');
16229 if(!this.multiple && this.valueField &&
16230 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
16233 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16234 row.addClass('selected');
16237 if(this.multiple && this.valueField &&
16238 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
16242 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16243 this.tickItems.push(d.data);
16246 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
16250 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
16252 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16254 if(this.modalTitle.length){
16255 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16258 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
16260 if(this.mobile_restrict_height && listHeight < bodyHeight){
16261 this.touchViewBodyEl.setHeight(listHeight);
16266 if(firstChecked && listHeight > bodyHeight){
16267 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
16272 onTouchViewLoadException : function()
16274 this.hideTouchView();
16277 onTouchViewEmptyResults : function()
16279 this.clearTouchView();
16281 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
16283 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
16287 clearTouchView : function()
16289 this.touchViewListGroup.dom.innerHTML = '';
16292 onTouchViewClick : function(e, el, o)
16294 e.preventDefault();
16297 var rowIndex = o.rowIndex;
16299 var r = this.store.getAt(rowIndex);
16301 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
16303 if(!this.multiple){
16304 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
16305 c.dom.removeAttribute('checked');
16308 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16310 this.setFromData(r.data);
16312 var close = this.closeTriggerEl();
16318 this.hideTouchView();
16320 this.fireEvent('select', this, r, rowIndex);
16325 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
16326 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
16327 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
16331 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16332 this.addItem(r.data);
16333 this.tickItems.push(r.data);
16337 getAutoCreateNativeIOS : function()
16340 cls: 'form-group' //input-group,
16345 cls : 'roo-ios-select'
16349 combobox.name = this.name;
16352 if (this.disabled) {
16353 combobox.disabled = true;
16356 var settings = this;
16358 ['xs','sm','md','lg'].map(function(size){
16359 if (settings[size]) {
16360 cfg.cls += ' col-' + size + '-' + settings[size];
16370 initIOSView : function()
16372 this.store.on('load', this.onIOSViewLoad, this);
16377 onIOSViewLoad : function()
16379 if(this.store.getCount() < 1){
16383 this.clearIOSView();
16385 if(this.allowBlank) {
16387 var default_text = '-- SELECT --';
16389 if(this.placeholder.length){
16390 default_text = this.placeholder;
16393 if(this.emptyTitle.length){
16394 default_text += ' - ' + this.emptyTitle + ' -';
16397 var opt = this.inputEl().createChild({
16400 html : default_text
16404 o[this.valueField] = 0;
16405 o[this.displayField] = default_text;
16407 this.ios_options.push({
16414 this.store.data.each(function(d, rowIndex){
16418 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16419 html = d.data[this.displayField];
16424 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16425 value = d.data[this.valueField];
16434 if(this.value == d.data[this.valueField]){
16435 option['selected'] = true;
16438 var opt = this.inputEl().createChild(option);
16440 this.ios_options.push({
16447 this.inputEl().on('change', function(){
16448 this.fireEvent('select', this);
16453 clearIOSView: function()
16455 this.inputEl().dom.innerHTML = '';
16457 this.ios_options = [];
16460 setIOSValue: function(v)
16464 if(!this.ios_options){
16468 Roo.each(this.ios_options, function(opts){
16470 opts.el.dom.removeAttribute('selected');
16472 if(opts.data[this.valueField] != v){
16476 opts.el.dom.setAttribute('selected', true);
16482 * @cfg {Boolean} grow
16486 * @cfg {Number} growMin
16490 * @cfg {Number} growMax
16499 Roo.apply(Roo.bootstrap.ComboBox, {
16503 cls: 'modal-header',
16525 cls: 'list-group-item',
16529 cls: 'roo-combobox-list-group-item-value'
16533 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16547 listItemCheckbox : {
16549 cls: 'list-group-item',
16553 cls: 'roo-combobox-list-group-item-value'
16557 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16573 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16578 cls: 'modal-footer',
16586 cls: 'col-xs-6 text-left',
16589 cls: 'btn btn-danger roo-touch-view-cancel',
16595 cls: 'col-xs-6 text-right',
16598 cls: 'btn btn-success roo-touch-view-ok',
16609 Roo.apply(Roo.bootstrap.ComboBox, {
16611 touchViewTemplate : {
16613 cls: 'modal fade roo-combobox-touch-view',
16617 cls: 'modal-dialog',
16618 style : 'position:fixed', // we have to fix position....
16622 cls: 'modal-content',
16624 Roo.bootstrap.ComboBox.header,
16625 Roo.bootstrap.ComboBox.body,
16626 Roo.bootstrap.ComboBox.footer
16635 * Ext JS Library 1.1.1
16636 * Copyright(c) 2006-2007, Ext JS, LLC.
16638 * Originally Released Under LGPL - original licence link has changed is not relivant.
16641 * <script type="text/javascript">
16646 * @extends Roo.util.Observable
16647 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16648 * This class also supports single and multi selection modes. <br>
16649 * Create a data model bound view:
16651 var store = new Roo.data.Store(...);
16653 var view = new Roo.View({
16655 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16657 singleSelect: true,
16658 selectedClass: "ydataview-selected",
16662 // listen for node click?
16663 view.on("click", function(vw, index, node, e){
16664 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16668 dataModel.load("foobar.xml");
16670 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16672 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16673 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16675 * Note: old style constructor is still suported (container, template, config)
16678 * Create a new View
16679 * @param {Object} config The config object
16682 Roo.View = function(config, depreciated_tpl, depreciated_config){
16684 this.parent = false;
16686 if (typeof(depreciated_tpl) == 'undefined') {
16687 // new way.. - universal constructor.
16688 Roo.apply(this, config);
16689 this.el = Roo.get(this.el);
16692 this.el = Roo.get(config);
16693 this.tpl = depreciated_tpl;
16694 Roo.apply(this, depreciated_config);
16696 this.wrapEl = this.el.wrap().wrap();
16697 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16700 if(typeof(this.tpl) == "string"){
16701 this.tpl = new Roo.Template(this.tpl);
16703 // support xtype ctors..
16704 this.tpl = new Roo.factory(this.tpl, Roo);
16708 this.tpl.compile();
16713 * @event beforeclick
16714 * Fires before a click is processed. Returns false to cancel the default action.
16715 * @param {Roo.View} this
16716 * @param {Number} index The index of the target node
16717 * @param {HTMLElement} node The target node
16718 * @param {Roo.EventObject} e The raw event object
16720 "beforeclick" : true,
16723 * Fires when a template node is clicked.
16724 * @param {Roo.View} this
16725 * @param {Number} index The index of the target node
16726 * @param {HTMLElement} node The target node
16727 * @param {Roo.EventObject} e The raw event object
16732 * Fires when a template node is double clicked.
16733 * @param {Roo.View} this
16734 * @param {Number} index The index of the target node
16735 * @param {HTMLElement} node The target node
16736 * @param {Roo.EventObject} e The raw event object
16740 * @event contextmenu
16741 * Fires when a template node is right clicked.
16742 * @param {Roo.View} this
16743 * @param {Number} index The index of the target node
16744 * @param {HTMLElement} node The target node
16745 * @param {Roo.EventObject} e The raw event object
16747 "contextmenu" : true,
16749 * @event selectionchange
16750 * Fires when the selected nodes change.
16751 * @param {Roo.View} this
16752 * @param {Array} selections Array of the selected nodes
16754 "selectionchange" : true,
16757 * @event beforeselect
16758 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16759 * @param {Roo.View} this
16760 * @param {HTMLElement} node The node to be selected
16761 * @param {Array} selections Array of currently selected nodes
16763 "beforeselect" : true,
16765 * @event preparedata
16766 * Fires on every row to render, to allow you to change the data.
16767 * @param {Roo.View} this
16768 * @param {Object} data to be rendered (change this)
16770 "preparedata" : true
16778 "click": this.onClick,
16779 "dblclick": this.onDblClick,
16780 "contextmenu": this.onContextMenu,
16784 this.selections = [];
16786 this.cmp = new Roo.CompositeElementLite([]);
16788 this.store = Roo.factory(this.store, Roo.data);
16789 this.setStore(this.store, true);
16792 if ( this.footer && this.footer.xtype) {
16794 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16796 this.footer.dataSource = this.store;
16797 this.footer.container = fctr;
16798 this.footer = Roo.factory(this.footer, Roo);
16799 fctr.insertFirst(this.el);
16801 // this is a bit insane - as the paging toolbar seems to detach the el..
16802 // dom.parentNode.parentNode.parentNode
16803 // they get detached?
16807 Roo.View.superclass.constructor.call(this);
16812 Roo.extend(Roo.View, Roo.util.Observable, {
16815 * @cfg {Roo.data.Store} store Data store to load data from.
16820 * @cfg {String|Roo.Element} el The container element.
16825 * @cfg {String|Roo.Template} tpl The template used by this View
16829 * @cfg {String} dataName the named area of the template to use as the data area
16830 * Works with domtemplates roo-name="name"
16834 * @cfg {String} selectedClass The css class to add to selected nodes
16836 selectedClass : "x-view-selected",
16838 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16843 * @cfg {String} text to display on mask (default Loading)
16847 * @cfg {Boolean} multiSelect Allow multiple selection
16849 multiSelect : false,
16851 * @cfg {Boolean} singleSelect Allow single selection
16853 singleSelect: false,
16856 * @cfg {Boolean} toggleSelect - selecting
16858 toggleSelect : false,
16861 * @cfg {Boolean} tickable - selecting
16866 * Returns the element this view is bound to.
16867 * @return {Roo.Element}
16869 getEl : function(){
16870 return this.wrapEl;
16876 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16878 refresh : function(){
16879 //Roo.log('refresh');
16882 // if we are using something like 'domtemplate', then
16883 // the what gets used is:
16884 // t.applySubtemplate(NAME, data, wrapping data..)
16885 // the outer template then get' applied with
16886 // the store 'extra data'
16887 // and the body get's added to the
16888 // roo-name="data" node?
16889 // <span class='roo-tpl-{name}'></span> ?????
16893 this.clearSelections();
16894 this.el.update("");
16896 var records = this.store.getRange();
16897 if(records.length < 1) {
16899 // is this valid?? = should it render a template??
16901 this.el.update(this.emptyText);
16905 if (this.dataName) {
16906 this.el.update(t.apply(this.store.meta)); //????
16907 el = this.el.child('.roo-tpl-' + this.dataName);
16910 for(var i = 0, len = records.length; i < len; i++){
16911 var data = this.prepareData(records[i].data, i, records[i]);
16912 this.fireEvent("preparedata", this, data, i, records[i]);
16914 var d = Roo.apply({}, data);
16917 Roo.apply(d, {'roo-id' : Roo.id()});
16921 Roo.each(this.parent.item, function(item){
16922 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16925 Roo.apply(d, {'roo-data-checked' : 'checked'});
16929 html[html.length] = Roo.util.Format.trim(
16931 t.applySubtemplate(this.dataName, d, this.store.meta) :
16938 el.update(html.join(""));
16939 this.nodes = el.dom.childNodes;
16940 this.updateIndexes(0);
16945 * Function to override to reformat the data that is sent to
16946 * the template for each node.
16947 * DEPRICATED - use the preparedata event handler.
16948 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16949 * a JSON object for an UpdateManager bound view).
16951 prepareData : function(data, index, record)
16953 this.fireEvent("preparedata", this, data, index, record);
16957 onUpdate : function(ds, record){
16958 // Roo.log('on update');
16959 this.clearSelections();
16960 var index = this.store.indexOf(record);
16961 var n = this.nodes[index];
16962 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16963 n.parentNode.removeChild(n);
16964 this.updateIndexes(index, index);
16970 onAdd : function(ds, records, index)
16972 //Roo.log(['on Add', ds, records, index] );
16973 this.clearSelections();
16974 if(this.nodes.length == 0){
16978 var n = this.nodes[index];
16979 for(var i = 0, len = records.length; i < len; i++){
16980 var d = this.prepareData(records[i].data, i, records[i]);
16982 this.tpl.insertBefore(n, d);
16985 this.tpl.append(this.el, d);
16988 this.updateIndexes(index);
16991 onRemove : function(ds, record, index){
16992 // Roo.log('onRemove');
16993 this.clearSelections();
16994 var el = this.dataName ?
16995 this.el.child('.roo-tpl-' + this.dataName) :
16998 el.dom.removeChild(this.nodes[index]);
16999 this.updateIndexes(index);
17003 * Refresh an individual node.
17004 * @param {Number} index
17006 refreshNode : function(index){
17007 this.onUpdate(this.store, this.store.getAt(index));
17010 updateIndexes : function(startIndex, endIndex){
17011 var ns = this.nodes;
17012 startIndex = startIndex || 0;
17013 endIndex = endIndex || ns.length - 1;
17014 for(var i = startIndex; i <= endIndex; i++){
17015 ns[i].nodeIndex = i;
17020 * Changes the data store this view uses and refresh the view.
17021 * @param {Store} store
17023 setStore : function(store, initial){
17024 if(!initial && this.store){
17025 this.store.un("datachanged", this.refresh);
17026 this.store.un("add", this.onAdd);
17027 this.store.un("remove", this.onRemove);
17028 this.store.un("update", this.onUpdate);
17029 this.store.un("clear", this.refresh);
17030 this.store.un("beforeload", this.onBeforeLoad);
17031 this.store.un("load", this.onLoad);
17032 this.store.un("loadexception", this.onLoad);
17036 store.on("datachanged", this.refresh, this);
17037 store.on("add", this.onAdd, this);
17038 store.on("remove", this.onRemove, this);
17039 store.on("update", this.onUpdate, this);
17040 store.on("clear", this.refresh, this);
17041 store.on("beforeload", this.onBeforeLoad, this);
17042 store.on("load", this.onLoad, this);
17043 store.on("loadexception", this.onLoad, this);
17051 * onbeforeLoad - masks the loading area.
17054 onBeforeLoad : function(store,opts)
17056 //Roo.log('onBeforeLoad');
17058 this.el.update("");
17060 this.el.mask(this.mask ? this.mask : "Loading" );
17062 onLoad : function ()
17069 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
17070 * @param {HTMLElement} node
17071 * @return {HTMLElement} The template node
17073 findItemFromChild : function(node){
17074 var el = this.dataName ?
17075 this.el.child('.roo-tpl-' + this.dataName,true) :
17078 if(!node || node.parentNode == el){
17081 var p = node.parentNode;
17082 while(p && p != el){
17083 if(p.parentNode == el){
17092 onClick : function(e){
17093 var item = this.findItemFromChild(e.getTarget());
17095 var index = this.indexOf(item);
17096 if(this.onItemClick(item, index, e) !== false){
17097 this.fireEvent("click", this, index, item, e);
17100 this.clearSelections();
17105 onContextMenu : function(e){
17106 var item = this.findItemFromChild(e.getTarget());
17108 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
17113 onDblClick : function(e){
17114 var item = this.findItemFromChild(e.getTarget());
17116 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
17120 onItemClick : function(item, index, e)
17122 if(this.fireEvent("beforeclick", this, index, item, e) === false){
17125 if (this.toggleSelect) {
17126 var m = this.isSelected(item) ? 'unselect' : 'select';
17129 _t[m](item, true, false);
17132 if(this.multiSelect || this.singleSelect){
17133 if(this.multiSelect && e.shiftKey && this.lastSelection){
17134 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
17136 this.select(item, this.multiSelect && e.ctrlKey);
17137 this.lastSelection = item;
17140 if(!this.tickable){
17141 e.preventDefault();
17149 * Get the number of selected nodes.
17152 getSelectionCount : function(){
17153 return this.selections.length;
17157 * Get the currently selected nodes.
17158 * @return {Array} An array of HTMLElements
17160 getSelectedNodes : function(){
17161 return this.selections;
17165 * Get the indexes of the selected nodes.
17168 getSelectedIndexes : function(){
17169 var indexes = [], s = this.selections;
17170 for(var i = 0, len = s.length; i < len; i++){
17171 indexes.push(s[i].nodeIndex);
17177 * Clear all selections
17178 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
17180 clearSelections : function(suppressEvent){
17181 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
17182 this.cmp.elements = this.selections;
17183 this.cmp.removeClass(this.selectedClass);
17184 this.selections = [];
17185 if(!suppressEvent){
17186 this.fireEvent("selectionchange", this, this.selections);
17192 * Returns true if the passed node is selected
17193 * @param {HTMLElement/Number} node The node or node index
17194 * @return {Boolean}
17196 isSelected : function(node){
17197 var s = this.selections;
17201 node = this.getNode(node);
17202 return s.indexOf(node) !== -1;
17207 * @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
17208 * @param {Boolean} keepExisting (optional) true to keep existing selections
17209 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17211 select : function(nodeInfo, keepExisting, suppressEvent){
17212 if(nodeInfo instanceof Array){
17214 this.clearSelections(true);
17216 for(var i = 0, len = nodeInfo.length; i < len; i++){
17217 this.select(nodeInfo[i], true, true);
17221 var node = this.getNode(nodeInfo);
17222 if(!node || this.isSelected(node)){
17223 return; // already selected.
17226 this.clearSelections(true);
17229 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
17230 Roo.fly(node).addClass(this.selectedClass);
17231 this.selections.push(node);
17232 if(!suppressEvent){
17233 this.fireEvent("selectionchange", this, this.selections);
17241 * @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
17242 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
17243 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17245 unselect : function(nodeInfo, keepExisting, suppressEvent)
17247 if(nodeInfo instanceof Array){
17248 Roo.each(this.selections, function(s) {
17249 this.unselect(s, nodeInfo);
17253 var node = this.getNode(nodeInfo);
17254 if(!node || !this.isSelected(node)){
17255 //Roo.log("not selected");
17256 return; // not selected.
17260 Roo.each(this.selections, function(s) {
17262 Roo.fly(node).removeClass(this.selectedClass);
17269 this.selections= ns;
17270 this.fireEvent("selectionchange", this, this.selections);
17274 * Gets a template node.
17275 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17276 * @return {HTMLElement} The node or null if it wasn't found
17278 getNode : function(nodeInfo){
17279 if(typeof nodeInfo == "string"){
17280 return document.getElementById(nodeInfo);
17281 }else if(typeof nodeInfo == "number"){
17282 return this.nodes[nodeInfo];
17288 * Gets a range template nodes.
17289 * @param {Number} startIndex
17290 * @param {Number} endIndex
17291 * @return {Array} An array of nodes
17293 getNodes : function(start, end){
17294 var ns = this.nodes;
17295 start = start || 0;
17296 end = typeof end == "undefined" ? ns.length - 1 : end;
17299 for(var i = start; i <= end; i++){
17303 for(var i = start; i >= end; i--){
17311 * Finds the index of the passed node
17312 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17313 * @return {Number} The index of the node or -1
17315 indexOf : function(node){
17316 node = this.getNode(node);
17317 if(typeof node.nodeIndex == "number"){
17318 return node.nodeIndex;
17320 var ns = this.nodes;
17321 for(var i = 0, len = ns.length; i < len; i++){
17332 * based on jquery fullcalendar
17336 Roo.bootstrap = Roo.bootstrap || {};
17338 * @class Roo.bootstrap.Calendar
17339 * @extends Roo.bootstrap.Component
17340 * Bootstrap Calendar class
17341 * @cfg {Boolean} loadMask (true|false) default false
17342 * @cfg {Object} header generate the user specific header of the calendar, default false
17345 * Create a new Container
17346 * @param {Object} config The config object
17351 Roo.bootstrap.Calendar = function(config){
17352 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
17356 * Fires when a date is selected
17357 * @param {DatePicker} this
17358 * @param {Date} date The selected date
17362 * @event monthchange
17363 * Fires when the displayed month changes
17364 * @param {DatePicker} this
17365 * @param {Date} date The selected month
17367 'monthchange': true,
17369 * @event evententer
17370 * Fires when mouse over an event
17371 * @param {Calendar} this
17372 * @param {event} Event
17374 'evententer': true,
17376 * @event eventleave
17377 * Fires when the mouse leaves an
17378 * @param {Calendar} this
17381 'eventleave': true,
17383 * @event eventclick
17384 * Fires when the mouse click an
17385 * @param {Calendar} this
17394 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
17397 * @cfg {Number} startDay
17398 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17406 getAutoCreate : function(){
17409 var fc_button = function(name, corner, style, content ) {
17410 return Roo.apply({},{
17412 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
17414 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17417 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17428 style : 'width:100%',
17435 cls : 'fc-header-left',
17437 fc_button('prev', 'left', 'arrow', '‹' ),
17438 fc_button('next', 'right', 'arrow', '›' ),
17439 { tag: 'span', cls: 'fc-header-space' },
17440 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
17448 cls : 'fc-header-center',
17452 cls: 'fc-header-title',
17455 html : 'month / year'
17463 cls : 'fc-header-right',
17465 /* fc_button('month', 'left', '', 'month' ),
17466 fc_button('week', '', '', 'week' ),
17467 fc_button('day', 'right', '', 'day' )
17479 header = this.header;
17482 var cal_heads = function() {
17484 // fixme - handle this.
17486 for (var i =0; i < Date.dayNames.length; i++) {
17487 var d = Date.dayNames[i];
17490 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17491 html : d.substring(0,3)
17495 ret[0].cls += ' fc-first';
17496 ret[6].cls += ' fc-last';
17499 var cal_cell = function(n) {
17502 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17507 cls: 'fc-day-number',
17511 cls: 'fc-day-content',
17515 style: 'position: relative;' // height: 17px;
17527 var cal_rows = function() {
17530 for (var r = 0; r < 6; r++) {
17537 for (var i =0; i < Date.dayNames.length; i++) {
17538 var d = Date.dayNames[i];
17539 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17542 row.cn[0].cls+=' fc-first';
17543 row.cn[0].cn[0].style = 'min-height:90px';
17544 row.cn[6].cls+=' fc-last';
17548 ret[0].cls += ' fc-first';
17549 ret[4].cls += ' fc-prev-last';
17550 ret[5].cls += ' fc-last';
17557 cls: 'fc-border-separate',
17558 style : 'width:100%',
17566 cls : 'fc-first fc-last',
17584 cls : 'fc-content',
17585 style : "position: relative;",
17588 cls : 'fc-view fc-view-month fc-grid',
17589 style : 'position: relative',
17590 unselectable : 'on',
17593 cls : 'fc-event-container',
17594 style : 'position:absolute;z-index:8;top:0;left:0;'
17612 initEvents : function()
17615 throw "can not find store for calendar";
17621 style: "text-align:center",
17625 style: "background-color:white;width:50%;margin:250 auto",
17629 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17640 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17642 var size = this.el.select('.fc-content', true).first().getSize();
17643 this.maskEl.setSize(size.width, size.height);
17644 this.maskEl.enableDisplayMode("block");
17645 if(!this.loadMask){
17646 this.maskEl.hide();
17649 this.store = Roo.factory(this.store, Roo.data);
17650 this.store.on('load', this.onLoad, this);
17651 this.store.on('beforeload', this.onBeforeLoad, this);
17655 this.cells = this.el.select('.fc-day',true);
17656 //Roo.log(this.cells);
17657 this.textNodes = this.el.query('.fc-day-number');
17658 this.cells.addClassOnOver('fc-state-hover');
17660 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17661 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17662 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17663 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17665 this.on('monthchange', this.onMonthChange, this);
17667 this.update(new Date().clearTime());
17670 resize : function() {
17671 var sz = this.el.getSize();
17673 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17674 this.el.select('.fc-day-content div',true).setHeight(34);
17679 showPrevMonth : function(e){
17680 this.update(this.activeDate.add("mo", -1));
17682 showToday : function(e){
17683 this.update(new Date().clearTime());
17686 showNextMonth : function(e){
17687 this.update(this.activeDate.add("mo", 1));
17691 showPrevYear : function(){
17692 this.update(this.activeDate.add("y", -1));
17696 showNextYear : function(){
17697 this.update(this.activeDate.add("y", 1));
17702 update : function(date)
17704 var vd = this.activeDate;
17705 this.activeDate = date;
17706 // if(vd && this.el){
17707 // var t = date.getTime();
17708 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17709 // Roo.log('using add remove');
17711 // this.fireEvent('monthchange', this, date);
17713 // this.cells.removeClass("fc-state-highlight");
17714 // this.cells.each(function(c){
17715 // if(c.dateValue == t){
17716 // c.addClass("fc-state-highlight");
17717 // setTimeout(function(){
17718 // try{c.dom.firstChild.focus();}catch(e){}
17728 var days = date.getDaysInMonth();
17730 var firstOfMonth = date.getFirstDateOfMonth();
17731 var startingPos = firstOfMonth.getDay()-this.startDay;
17733 if(startingPos < this.startDay){
17737 var pm = date.add(Date.MONTH, -1);
17738 var prevStart = pm.getDaysInMonth()-startingPos;
17740 this.cells = this.el.select('.fc-day',true);
17741 this.textNodes = this.el.query('.fc-day-number');
17742 this.cells.addClassOnOver('fc-state-hover');
17744 var cells = this.cells.elements;
17745 var textEls = this.textNodes;
17747 Roo.each(cells, function(cell){
17748 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17751 days += startingPos;
17753 // convert everything to numbers so it's fast
17754 var day = 86400000;
17755 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17758 //Roo.log(prevStart);
17760 var today = new Date().clearTime().getTime();
17761 var sel = date.clearTime().getTime();
17762 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17763 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17764 var ddMatch = this.disabledDatesRE;
17765 var ddText = this.disabledDatesText;
17766 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17767 var ddaysText = this.disabledDaysText;
17768 var format = this.format;
17770 var setCellClass = function(cal, cell){
17774 //Roo.log('set Cell Class');
17776 var t = d.getTime();
17780 cell.dateValue = t;
17782 cell.className += " fc-today";
17783 cell.className += " fc-state-highlight";
17784 cell.title = cal.todayText;
17787 // disable highlight in other month..
17788 //cell.className += " fc-state-highlight";
17793 cell.className = " fc-state-disabled";
17794 cell.title = cal.minText;
17798 cell.className = " fc-state-disabled";
17799 cell.title = cal.maxText;
17803 if(ddays.indexOf(d.getDay()) != -1){
17804 cell.title = ddaysText;
17805 cell.className = " fc-state-disabled";
17808 if(ddMatch && format){
17809 var fvalue = d.dateFormat(format);
17810 if(ddMatch.test(fvalue)){
17811 cell.title = ddText.replace("%0", fvalue);
17812 cell.className = " fc-state-disabled";
17816 if (!cell.initialClassName) {
17817 cell.initialClassName = cell.dom.className;
17820 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17825 for(; i < startingPos; i++) {
17826 textEls[i].innerHTML = (++prevStart);
17827 d.setDate(d.getDate()+1);
17829 cells[i].className = "fc-past fc-other-month";
17830 setCellClass(this, cells[i]);
17835 for(; i < days; i++){
17836 intDay = i - startingPos + 1;
17837 textEls[i].innerHTML = (intDay);
17838 d.setDate(d.getDate()+1);
17840 cells[i].className = ''; // "x-date-active";
17841 setCellClass(this, cells[i]);
17845 for(; i < 42; i++) {
17846 textEls[i].innerHTML = (++extraDays);
17847 d.setDate(d.getDate()+1);
17849 cells[i].className = "fc-future fc-other-month";
17850 setCellClass(this, cells[i]);
17853 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17855 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17857 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17858 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17860 if(totalRows != 6){
17861 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17862 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17865 this.fireEvent('monthchange', this, date);
17869 if(!this.internalRender){
17870 var main = this.el.dom.firstChild;
17871 var w = main.offsetWidth;
17872 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17873 Roo.fly(main).setWidth(w);
17874 this.internalRender = true;
17875 // opera does not respect the auto grow header center column
17876 // then, after it gets a width opera refuses to recalculate
17877 // without a second pass
17878 if(Roo.isOpera && !this.secondPass){
17879 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17880 this.secondPass = true;
17881 this.update.defer(10, this, [date]);
17888 findCell : function(dt) {
17889 dt = dt.clearTime().getTime();
17891 this.cells.each(function(c){
17892 //Roo.log("check " +c.dateValue + '?=' + dt);
17893 if(c.dateValue == dt){
17903 findCells : function(ev) {
17904 var s = ev.start.clone().clearTime().getTime();
17906 var e= ev.end.clone().clearTime().getTime();
17909 this.cells.each(function(c){
17910 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17912 if(c.dateValue > e){
17915 if(c.dateValue < s){
17924 // findBestRow: function(cells)
17928 // for (var i =0 ; i < cells.length;i++) {
17929 // ret = Math.max(cells[i].rows || 0,ret);
17936 addItem : function(ev)
17938 // look for vertical location slot in
17939 var cells = this.findCells(ev);
17941 // ev.row = this.findBestRow(cells);
17943 // work out the location.
17947 for(var i =0; i < cells.length; i++) {
17949 cells[i].row = cells[0].row;
17952 cells[i].row = cells[i].row + 1;
17962 if (crow.start.getY() == cells[i].getY()) {
17964 crow.end = cells[i];
17981 cells[0].events.push(ev);
17983 this.calevents.push(ev);
17986 clearEvents: function() {
17988 if(!this.calevents){
17992 Roo.each(this.cells.elements, function(c){
17998 Roo.each(this.calevents, function(e) {
17999 Roo.each(e.els, function(el) {
18000 el.un('mouseenter' ,this.onEventEnter, this);
18001 el.un('mouseleave' ,this.onEventLeave, this);
18006 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
18012 renderEvents: function()
18016 this.cells.each(function(c) {
18025 if(c.row != c.events.length){
18026 r = 4 - (4 - (c.row - c.events.length));
18029 c.events = ev.slice(0, r);
18030 c.more = ev.slice(r);
18032 if(c.more.length && c.more.length == 1){
18033 c.events.push(c.more.pop());
18036 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
18040 this.cells.each(function(c) {
18042 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
18045 for (var e = 0; e < c.events.length; e++){
18046 var ev = c.events[e];
18047 var rows = ev.rows;
18049 for(var i = 0; i < rows.length; i++) {
18051 // how many rows should it span..
18054 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
18055 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
18057 unselectable : "on",
18060 cls: 'fc-event-inner',
18064 // cls: 'fc-event-time',
18065 // html : cells.length > 1 ? '' : ev.time
18069 cls: 'fc-event-title',
18070 html : String.format('{0}', ev.title)
18077 cls: 'ui-resizable-handle ui-resizable-e',
18078 html : '  '
18085 cfg.cls += ' fc-event-start';
18087 if ((i+1) == rows.length) {
18088 cfg.cls += ' fc-event-end';
18091 var ctr = _this.el.select('.fc-event-container',true).first();
18092 var cg = ctr.createChild(cfg);
18094 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
18095 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
18097 var r = (c.more.length) ? 1 : 0;
18098 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
18099 cg.setWidth(ebox.right - sbox.x -2);
18101 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
18102 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
18103 cg.on('click', _this.onEventClick, _this, ev);
18114 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
18115 style : 'position: absolute',
18116 unselectable : "on",
18119 cls: 'fc-event-inner',
18123 cls: 'fc-event-title',
18131 cls: 'ui-resizable-handle ui-resizable-e',
18132 html : '  '
18138 var ctr = _this.el.select('.fc-event-container',true).first();
18139 var cg = ctr.createChild(cfg);
18141 var sbox = c.select('.fc-day-content',true).first().getBox();
18142 var ebox = c.select('.fc-day-content',true).first().getBox();
18144 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
18145 cg.setWidth(ebox.right - sbox.x -2);
18147 cg.on('click', _this.onMoreEventClick, _this, c.more);
18157 onEventEnter: function (e, el,event,d) {
18158 this.fireEvent('evententer', this, el, event);
18161 onEventLeave: function (e, el,event,d) {
18162 this.fireEvent('eventleave', this, el, event);
18165 onEventClick: function (e, el,event,d) {
18166 this.fireEvent('eventclick', this, el, event);
18169 onMonthChange: function () {
18173 onMoreEventClick: function(e, el, more)
18177 this.calpopover.placement = 'right';
18178 this.calpopover.setTitle('More');
18180 this.calpopover.setContent('');
18182 var ctr = this.calpopover.el.select('.popover-content', true).first();
18184 Roo.each(more, function(m){
18186 cls : 'fc-event-hori fc-event-draggable',
18189 var cg = ctr.createChild(cfg);
18191 cg.on('click', _this.onEventClick, _this, m);
18194 this.calpopover.show(el);
18199 onLoad: function ()
18201 this.calevents = [];
18204 if(this.store.getCount() > 0){
18205 this.store.data.each(function(d){
18208 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
18209 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
18210 time : d.data.start_time,
18211 title : d.data.title,
18212 description : d.data.description,
18213 venue : d.data.venue
18218 this.renderEvents();
18220 if(this.calevents.length && this.loadMask){
18221 this.maskEl.hide();
18225 onBeforeLoad: function()
18227 this.clearEvents();
18229 this.maskEl.show();
18243 * @class Roo.bootstrap.Popover
18244 * @extends Roo.bootstrap.Component
18245 * Bootstrap Popover class
18246 * @cfg {String} html contents of the popover (or false to use children..)
18247 * @cfg {String} title of popover (or false to hide)
18248 * @cfg {String} placement how it is placed
18249 * @cfg {String} trigger click || hover (or false to trigger manually)
18250 * @cfg {String} over what (parent or false to trigger manually.)
18251 * @cfg {Number} delay - delay before showing
18254 * Create a new Popover
18255 * @param {Object} config The config object
18258 Roo.bootstrap.Popover = function(config){
18259 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
18265 * After the popover show
18267 * @param {Roo.bootstrap.Popover} this
18272 * After the popover hide
18274 * @param {Roo.bootstrap.Popover} this
18280 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
18282 title: 'Fill in a title',
18285 placement : 'right',
18286 trigger : 'hover', // hover
18292 can_build_overlaid : false,
18294 getChildContainer : function()
18296 return this.el.select('.popover-content',true).first();
18299 getAutoCreate : function(){
18302 cls : 'popover roo-dynamic',
18303 style: 'display:block',
18309 cls : 'popover-inner',
18313 cls: 'popover-title popover-header',
18317 cls : 'popover-content popover-body',
18328 setTitle: function(str)
18331 this.el.select('.popover-title',true).first().dom.innerHTML = str;
18333 setContent: function(str)
18336 this.el.select('.popover-content',true).first().dom.innerHTML = str;
18338 // as it get's added to the bottom of the page.
18339 onRender : function(ct, position)
18341 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18343 var cfg = Roo.apply({}, this.getAutoCreate());
18347 cfg.cls += ' ' + this.cls;
18350 cfg.style = this.style;
18352 //Roo.log("adding to ");
18353 this.el = Roo.get(document.body).createChild(cfg, position);
18354 // Roo.log(this.el);
18359 initEvents : function()
18361 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
18362 this.el.enableDisplayMode('block');
18364 if (this.over === false) {
18367 if (this.triggers === false) {
18370 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18371 var triggers = this.trigger ? this.trigger.split(' ') : [];
18372 Roo.each(triggers, function(trigger) {
18374 if (trigger == 'click') {
18375 on_el.on('click', this.toggle, this);
18376 } else if (trigger != 'manual') {
18377 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
18378 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
18380 on_el.on(eventIn ,this.enter, this);
18381 on_el.on(eventOut, this.leave, this);
18392 toggle : function () {
18393 this.hoverState == 'in' ? this.leave() : this.enter();
18396 enter : function () {
18398 clearTimeout(this.timeout);
18400 this.hoverState = 'in';
18402 if (!this.delay || !this.delay.show) {
18407 this.timeout = setTimeout(function () {
18408 if (_t.hoverState == 'in') {
18411 }, this.delay.show)
18414 leave : function() {
18415 clearTimeout(this.timeout);
18417 this.hoverState = 'out';
18419 if (!this.delay || !this.delay.hide) {
18424 this.timeout = setTimeout(function () {
18425 if (_t.hoverState == 'out') {
18428 }, this.delay.hide)
18431 show : function (on_el)
18434 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18438 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18439 if (this.html !== false) {
18440 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18442 this.el.removeClass([
18443 'fade','top','bottom', 'left', 'right','in',
18444 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18446 if (!this.title.length) {
18447 this.el.select('.popover-title',true).hide();
18450 var placement = typeof this.placement == 'function' ?
18451 this.placement.call(this, this.el, on_el) :
18454 var autoToken = /\s?auto?\s?/i;
18455 var autoPlace = autoToken.test(placement);
18457 placement = placement.replace(autoToken, '') || 'top';
18461 //this.el.setXY([0,0]);
18463 this.el.dom.style.display='block';
18464 this.el.addClass(placement);
18466 //this.el.appendTo(on_el);
18468 var p = this.getPosition();
18469 var box = this.el.getBox();
18474 var align = Roo.bootstrap.Popover.alignment[placement];
18477 this.el.alignTo(on_el, align[0],align[1]);
18478 //var arrow = this.el.select('.arrow',true).first();
18479 //arrow.set(align[2],
18481 this.el.addClass('in');
18484 if (this.el.hasClass('fade')) {
18488 this.hoverState = 'in';
18490 this.fireEvent('show', this);
18495 this.el.setXY([0,0]);
18496 this.el.removeClass('in');
18498 this.hoverState = null;
18500 this.fireEvent('hide', this);
18505 Roo.bootstrap.Popover.alignment = {
18506 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18507 'right' : ['l-r', [10,0], 'left bs-popover-left'],
18508 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18509 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18520 * @class Roo.bootstrap.Progress
18521 * @extends Roo.bootstrap.Component
18522 * Bootstrap Progress class
18523 * @cfg {Boolean} striped striped of the progress bar
18524 * @cfg {Boolean} active animated of the progress bar
18528 * Create a new Progress
18529 * @param {Object} config The config object
18532 Roo.bootstrap.Progress = function(config){
18533 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18536 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
18541 getAutoCreate : function(){
18549 cfg.cls += ' progress-striped';
18553 cfg.cls += ' active';
18572 * @class Roo.bootstrap.ProgressBar
18573 * @extends Roo.bootstrap.Component
18574 * Bootstrap ProgressBar class
18575 * @cfg {Number} aria_valuenow aria-value now
18576 * @cfg {Number} aria_valuemin aria-value min
18577 * @cfg {Number} aria_valuemax aria-value max
18578 * @cfg {String} label label for the progress bar
18579 * @cfg {String} panel (success | info | warning | danger )
18580 * @cfg {String} role role of the progress bar
18581 * @cfg {String} sr_only text
18585 * Create a new ProgressBar
18586 * @param {Object} config The config object
18589 Roo.bootstrap.ProgressBar = function(config){
18590 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18593 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
18597 aria_valuemax : 100,
18603 getAutoCreate : function()
18608 cls: 'progress-bar',
18609 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18621 cfg.role = this.role;
18624 if(this.aria_valuenow){
18625 cfg['aria-valuenow'] = this.aria_valuenow;
18628 if(this.aria_valuemin){
18629 cfg['aria-valuemin'] = this.aria_valuemin;
18632 if(this.aria_valuemax){
18633 cfg['aria-valuemax'] = this.aria_valuemax;
18636 if(this.label && !this.sr_only){
18637 cfg.html = this.label;
18641 cfg.cls += ' progress-bar-' + this.panel;
18647 update : function(aria_valuenow)
18649 this.aria_valuenow = aria_valuenow;
18651 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18666 * @class Roo.bootstrap.TabGroup
18667 * @extends Roo.bootstrap.Column
18668 * Bootstrap Column class
18669 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18670 * @cfg {Boolean} carousel true to make the group behave like a carousel
18671 * @cfg {Boolean} bullets show bullets for the panels
18672 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18673 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18674 * @cfg {Boolean} showarrow (true|false) show arrow default true
18677 * Create a new TabGroup
18678 * @param {Object} config The config object
18681 Roo.bootstrap.TabGroup = function(config){
18682 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18684 this.navId = Roo.id();
18687 Roo.bootstrap.TabGroup.register(this);
18691 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18694 transition : false,
18699 slideOnTouch : false,
18702 getAutoCreate : function()
18704 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18706 cfg.cls += ' tab-content';
18708 if (this.carousel) {
18709 cfg.cls += ' carousel slide';
18712 cls : 'carousel-inner',
18716 if(this.bullets && !Roo.isTouch){
18719 cls : 'carousel-bullets',
18723 if(this.bullets_cls){
18724 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18731 cfg.cn[0].cn.push(bullets);
18734 if(this.showarrow){
18735 cfg.cn[0].cn.push({
18737 class : 'carousel-arrow',
18741 class : 'carousel-prev',
18745 class : 'fa fa-chevron-left'
18751 class : 'carousel-next',
18755 class : 'fa fa-chevron-right'
18768 initEvents: function()
18770 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18771 // this.el.on("touchstart", this.onTouchStart, this);
18774 if(this.autoslide){
18777 this.slideFn = window.setInterval(function() {
18778 _this.showPanelNext();
18782 if(this.showarrow){
18783 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18784 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18790 // onTouchStart : function(e, el, o)
18792 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18796 // this.showPanelNext();
18800 getChildContainer : function()
18802 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18806 * register a Navigation item
18807 * @param {Roo.bootstrap.NavItem} the navitem to add
18809 register : function(item)
18811 this.tabs.push( item);
18812 item.navId = this.navId; // not really needed..
18817 getActivePanel : function()
18820 Roo.each(this.tabs, function(t) {
18830 getPanelByName : function(n)
18833 Roo.each(this.tabs, function(t) {
18834 if (t.tabId == n) {
18842 indexOfPanel : function(p)
18845 Roo.each(this.tabs, function(t,i) {
18846 if (t.tabId == p.tabId) {
18855 * show a specific panel
18856 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18857 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18859 showPanel : function (pan)
18861 if(this.transition || typeof(pan) == 'undefined'){
18862 Roo.log("waiting for the transitionend");
18866 if (typeof(pan) == 'number') {
18867 pan = this.tabs[pan];
18870 if (typeof(pan) == 'string') {
18871 pan = this.getPanelByName(pan);
18874 var cur = this.getActivePanel();
18877 Roo.log('pan or acitve pan is undefined');
18881 if (pan.tabId == this.getActivePanel().tabId) {
18885 if (false === cur.fireEvent('beforedeactivate')) {
18889 if(this.bullets > 0 && !Roo.isTouch){
18890 this.setActiveBullet(this.indexOfPanel(pan));
18893 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18895 //class="carousel-item carousel-item-next carousel-item-left"
18897 this.transition = true;
18898 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18899 var lr = dir == 'next' ? 'left' : 'right';
18900 pan.el.addClass(dir); // or prev
18901 pan.el.addClass('carousel-item-' + dir); // or prev
18902 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18903 cur.el.addClass(lr); // or right
18904 pan.el.addClass(lr);
18905 cur.el.addClass('carousel-item-' +lr); // or right
18906 pan.el.addClass('carousel-item-' +lr);
18910 cur.el.on('transitionend', function() {
18911 Roo.log("trans end?");
18913 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18914 pan.setActive(true);
18916 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18917 cur.setActive(false);
18919 _this.transition = false;
18921 }, this, { single: true } );
18926 cur.setActive(false);
18927 pan.setActive(true);
18932 showPanelNext : function()
18934 var i = this.indexOfPanel(this.getActivePanel());
18936 if (i >= this.tabs.length - 1 && !this.autoslide) {
18940 if (i >= this.tabs.length - 1 && this.autoslide) {
18944 this.showPanel(this.tabs[i+1]);
18947 showPanelPrev : function()
18949 var i = this.indexOfPanel(this.getActivePanel());
18951 if (i < 1 && !this.autoslide) {
18955 if (i < 1 && this.autoslide) {
18956 i = this.tabs.length;
18959 this.showPanel(this.tabs[i-1]);
18963 addBullet: function()
18965 if(!this.bullets || Roo.isTouch){
18968 var ctr = this.el.select('.carousel-bullets',true).first();
18969 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18970 var bullet = ctr.createChild({
18971 cls : 'bullet bullet-' + i
18972 },ctr.dom.lastChild);
18977 bullet.on('click', (function(e, el, o, ii, t){
18979 e.preventDefault();
18981 this.showPanel(ii);
18983 if(this.autoslide && this.slideFn){
18984 clearInterval(this.slideFn);
18985 this.slideFn = window.setInterval(function() {
18986 _this.showPanelNext();
18990 }).createDelegate(this, [i, bullet], true));
18995 setActiveBullet : function(i)
19001 Roo.each(this.el.select('.bullet', true).elements, function(el){
19002 el.removeClass('selected');
19005 var bullet = this.el.select('.bullet-' + i, true).first();
19011 bullet.addClass('selected');
19022 Roo.apply(Roo.bootstrap.TabGroup, {
19026 * register a Navigation Group
19027 * @param {Roo.bootstrap.NavGroup} the navgroup to add
19029 register : function(navgrp)
19031 this.groups[navgrp.navId] = navgrp;
19035 * fetch a Navigation Group based on the navigation ID
19036 * if one does not exist , it will get created.
19037 * @param {string} the navgroup to add
19038 * @returns {Roo.bootstrap.NavGroup} the navgroup
19040 get: function(navId) {
19041 if (typeof(this.groups[navId]) == 'undefined') {
19042 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
19044 return this.groups[navId] ;
19059 * @class Roo.bootstrap.TabPanel
19060 * @extends Roo.bootstrap.Component
19061 * Bootstrap TabPanel class
19062 * @cfg {Boolean} active panel active
19063 * @cfg {String} html panel content
19064 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
19065 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
19066 * @cfg {String} href click to link..
19070 * Create a new TabPanel
19071 * @param {Object} config The config object
19074 Roo.bootstrap.TabPanel = function(config){
19075 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
19079 * Fires when the active status changes
19080 * @param {Roo.bootstrap.TabPanel} this
19081 * @param {Boolean} state the new state
19086 * @event beforedeactivate
19087 * Fires before a tab is de-activated - can be used to do validation on a form.
19088 * @param {Roo.bootstrap.TabPanel} this
19089 * @return {Boolean} false if there is an error
19092 'beforedeactivate': true
19095 this.tabId = this.tabId || Roo.id();
19099 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
19107 getAutoCreate : function(){
19112 // item is needed for carousel - not sure if it has any effect otherwise
19113 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
19114 html: this.html || ''
19118 cfg.cls += ' active';
19122 cfg.tabId = this.tabId;
19130 initEvents: function()
19132 var p = this.parent();
19134 this.navId = this.navId || p.navId;
19136 if (typeof(this.navId) != 'undefined') {
19137 // not really needed.. but just in case.. parent should be a NavGroup.
19138 var tg = Roo.bootstrap.TabGroup.get(this.navId);
19142 var i = tg.tabs.length - 1;
19144 if(this.active && tg.bullets > 0 && i < tg.bullets){
19145 tg.setActiveBullet(i);
19149 this.el.on('click', this.onClick, this);
19152 this.el.on("touchstart", this.onTouchStart, this);
19153 this.el.on("touchmove", this.onTouchMove, this);
19154 this.el.on("touchend", this.onTouchEnd, this);
19159 onRender : function(ct, position)
19161 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
19164 setActive : function(state)
19166 Roo.log("panel - set active " + this.tabId + "=" + state);
19168 this.active = state;
19170 this.el.removeClass('active');
19172 } else if (!this.el.hasClass('active')) {
19173 this.el.addClass('active');
19176 this.fireEvent('changed', this, state);
19179 onClick : function(e)
19181 e.preventDefault();
19183 if(!this.href.length){
19187 window.location.href = this.href;
19196 onTouchStart : function(e)
19198 this.swiping = false;
19200 this.startX = e.browserEvent.touches[0].clientX;
19201 this.startY = e.browserEvent.touches[0].clientY;
19204 onTouchMove : function(e)
19206 this.swiping = true;
19208 this.endX = e.browserEvent.touches[0].clientX;
19209 this.endY = e.browserEvent.touches[0].clientY;
19212 onTouchEnd : function(e)
19219 var tabGroup = this.parent();
19221 if(this.endX > this.startX){ // swiping right
19222 tabGroup.showPanelPrev();
19226 if(this.startX > this.endX){ // swiping left
19227 tabGroup.showPanelNext();
19246 * @class Roo.bootstrap.DateField
19247 * @extends Roo.bootstrap.Input
19248 * Bootstrap DateField class
19249 * @cfg {Number} weekStart default 0
19250 * @cfg {String} viewMode default empty, (months|years)
19251 * @cfg {String} minViewMode default empty, (months|years)
19252 * @cfg {Number} startDate default -Infinity
19253 * @cfg {Number} endDate default Infinity
19254 * @cfg {Boolean} todayHighlight default false
19255 * @cfg {Boolean} todayBtn default false
19256 * @cfg {Boolean} calendarWeeks default false
19257 * @cfg {Object} daysOfWeekDisabled default empty
19258 * @cfg {Boolean} singleMode default false (true | false)
19260 * @cfg {Boolean} keyboardNavigation default true
19261 * @cfg {String} language default en
19264 * Create a new DateField
19265 * @param {Object} config The config object
19268 Roo.bootstrap.DateField = function(config){
19269 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
19273 * Fires when this field show.
19274 * @param {Roo.bootstrap.DateField} this
19275 * @param {Mixed} date The date value
19280 * Fires when this field hide.
19281 * @param {Roo.bootstrap.DateField} this
19282 * @param {Mixed} date The date value
19287 * Fires when select a date.
19288 * @param {Roo.bootstrap.DateField} this
19289 * @param {Mixed} date The date value
19293 * @event beforeselect
19294 * Fires when before select a date.
19295 * @param {Roo.bootstrap.DateField} this
19296 * @param {Mixed} date The date value
19298 beforeselect : true
19302 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
19305 * @cfg {String} format
19306 * The default date format string which can be overriden for localization support. The format must be
19307 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
19311 * @cfg {String} altFormats
19312 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
19313 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
19315 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
19323 todayHighlight : false,
19329 keyboardNavigation: true,
19331 calendarWeeks: false,
19333 startDate: -Infinity,
19337 daysOfWeekDisabled: [],
19341 singleMode : false,
19343 UTCDate: function()
19345 return new Date(Date.UTC.apply(Date, arguments));
19348 UTCToday: function()
19350 var today = new Date();
19351 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
19354 getDate: function() {
19355 var d = this.getUTCDate();
19356 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
19359 getUTCDate: function() {
19363 setDate: function(d) {
19364 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
19367 setUTCDate: function(d) {
19369 this.setValue(this.formatDate(this.date));
19372 onRender: function(ct, position)
19375 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
19377 this.language = this.language || 'en';
19378 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
19379 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
19381 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
19382 this.format = this.format || 'm/d/y';
19383 this.isInline = false;
19384 this.isInput = true;
19385 this.component = this.el.select('.add-on', true).first() || false;
19386 this.component = (this.component && this.component.length === 0) ? false : this.component;
19387 this.hasInput = this.component && this.inputEl().length;
19389 if (typeof(this.minViewMode === 'string')) {
19390 switch (this.minViewMode) {
19392 this.minViewMode = 1;
19395 this.minViewMode = 2;
19398 this.minViewMode = 0;
19403 if (typeof(this.viewMode === 'string')) {
19404 switch (this.viewMode) {
19417 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19419 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19421 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19423 this.picker().on('mousedown', this.onMousedown, this);
19424 this.picker().on('click', this.onClick, this);
19426 this.picker().addClass('datepicker-dropdown');
19428 this.startViewMode = this.viewMode;
19430 if(this.singleMode){
19431 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19432 v.setVisibilityMode(Roo.Element.DISPLAY);
19436 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19437 v.setStyle('width', '189px');
19441 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19442 if(!this.calendarWeeks){
19447 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19448 v.attr('colspan', function(i, val){
19449 return parseInt(val) + 1;
19454 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19456 this.setStartDate(this.startDate);
19457 this.setEndDate(this.endDate);
19459 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19466 if(this.isInline) {
19471 picker : function()
19473 return this.pickerEl;
19474 // return this.el.select('.datepicker', true).first();
19477 fillDow: function()
19479 var dowCnt = this.weekStart;
19488 if(this.calendarWeeks){
19496 while (dowCnt < this.weekStart + 7) {
19500 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19504 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19507 fillMonths: function()
19510 var months = this.picker().select('>.datepicker-months td', true).first();
19512 months.dom.innerHTML = '';
19518 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19521 months.createChild(month);
19528 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;
19530 if (this.date < this.startDate) {
19531 this.viewDate = new Date(this.startDate);
19532 } else if (this.date > this.endDate) {
19533 this.viewDate = new Date(this.endDate);
19535 this.viewDate = new Date(this.date);
19543 var d = new Date(this.viewDate),
19544 year = d.getUTCFullYear(),
19545 month = d.getUTCMonth(),
19546 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19547 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19548 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19549 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19550 currentDate = this.date && this.date.valueOf(),
19551 today = this.UTCToday();
19553 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19555 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19557 // this.picker.select('>tfoot th.today').
19558 // .text(dates[this.language].today)
19559 // .toggle(this.todayBtn !== false);
19561 this.updateNavArrows();
19564 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19566 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19568 prevMonth.setUTCDate(day);
19570 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19572 var nextMonth = new Date(prevMonth);
19574 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19576 nextMonth = nextMonth.valueOf();
19578 var fillMonths = false;
19580 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19582 while(prevMonth.valueOf() <= nextMonth) {
19585 if (prevMonth.getUTCDay() === this.weekStart) {
19587 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19595 if(this.calendarWeeks){
19596 // ISO 8601: First week contains first thursday.
19597 // ISO also states week starts on Monday, but we can be more abstract here.
19599 // Start of current week: based on weekstart/current date
19600 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19601 // Thursday of this week
19602 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19603 // First Thursday of year, year from thursday
19604 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19605 // Calendar week: ms between thursdays, div ms per day, div 7 days
19606 calWeek = (th - yth) / 864e5 / 7 + 1;
19608 fillMonths.cn.push({
19616 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19618 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19621 if (this.todayHighlight &&
19622 prevMonth.getUTCFullYear() == today.getFullYear() &&
19623 prevMonth.getUTCMonth() == today.getMonth() &&
19624 prevMonth.getUTCDate() == today.getDate()) {
19625 clsName += ' today';
19628 if (currentDate && prevMonth.valueOf() === currentDate) {
19629 clsName += ' active';
19632 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19633 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19634 clsName += ' disabled';
19637 fillMonths.cn.push({
19639 cls: 'day ' + clsName,
19640 html: prevMonth.getDate()
19643 prevMonth.setDate(prevMonth.getDate()+1);
19646 var currentYear = this.date && this.date.getUTCFullYear();
19647 var currentMonth = this.date && this.date.getUTCMonth();
19649 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19651 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19652 v.removeClass('active');
19654 if(currentYear === year && k === currentMonth){
19655 v.addClass('active');
19658 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19659 v.addClass('disabled');
19665 year = parseInt(year/10, 10) * 10;
19667 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19669 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19672 for (var i = -1; i < 11; i++) {
19673 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19675 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19683 showMode: function(dir)
19686 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19689 Roo.each(this.picker().select('>div',true).elements, function(v){
19690 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19693 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19698 if(this.isInline) {
19702 this.picker().removeClass(['bottom', 'top']);
19704 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19706 * place to the top of element!
19710 this.picker().addClass('top');
19711 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19716 this.picker().addClass('bottom');
19718 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19721 parseDate : function(value)
19723 if(!value || value instanceof Date){
19726 var v = Date.parseDate(value, this.format);
19727 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19728 v = Date.parseDate(value, 'Y-m-d');
19730 if(!v && this.altFormats){
19731 if(!this.altFormatsArray){
19732 this.altFormatsArray = this.altFormats.split("|");
19734 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19735 v = Date.parseDate(value, this.altFormatsArray[i]);
19741 formatDate : function(date, fmt)
19743 return (!date || !(date instanceof Date)) ?
19744 date : date.dateFormat(fmt || this.format);
19747 onFocus : function()
19749 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19753 onBlur : function()
19755 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19757 var d = this.inputEl().getValue();
19764 showPopup : function()
19766 this.picker().show();
19770 this.fireEvent('showpopup', this, this.date);
19773 hidePopup : function()
19775 if(this.isInline) {
19778 this.picker().hide();
19779 this.viewMode = this.startViewMode;
19782 this.fireEvent('hidepopup', this, this.date);
19786 onMousedown: function(e)
19788 e.stopPropagation();
19789 e.preventDefault();
19794 Roo.bootstrap.DateField.superclass.keyup.call(this);
19798 setValue: function(v)
19800 if(this.fireEvent('beforeselect', this, v) !== false){
19801 var d = new Date(this.parseDate(v) ).clearTime();
19803 if(isNaN(d.getTime())){
19804 this.date = this.viewDate = '';
19805 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19809 v = this.formatDate(d);
19811 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19813 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19817 this.fireEvent('select', this, this.date);
19821 getValue: function()
19823 return this.formatDate(this.date);
19826 fireKey: function(e)
19828 if (!this.picker().isVisible()){
19829 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19835 var dateChanged = false,
19837 newDate, newViewDate;
19842 e.preventDefault();
19846 if (!this.keyboardNavigation) {
19849 dir = e.keyCode == 37 ? -1 : 1;
19852 newDate = this.moveYear(this.date, dir);
19853 newViewDate = this.moveYear(this.viewDate, dir);
19854 } else if (e.shiftKey){
19855 newDate = this.moveMonth(this.date, dir);
19856 newViewDate = this.moveMonth(this.viewDate, dir);
19858 newDate = new Date(this.date);
19859 newDate.setUTCDate(this.date.getUTCDate() + dir);
19860 newViewDate = new Date(this.viewDate);
19861 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19863 if (this.dateWithinRange(newDate)){
19864 this.date = newDate;
19865 this.viewDate = newViewDate;
19866 this.setValue(this.formatDate(this.date));
19868 e.preventDefault();
19869 dateChanged = true;
19874 if (!this.keyboardNavigation) {
19877 dir = e.keyCode == 38 ? -1 : 1;
19879 newDate = this.moveYear(this.date, dir);
19880 newViewDate = this.moveYear(this.viewDate, dir);
19881 } else if (e.shiftKey){
19882 newDate = this.moveMonth(this.date, dir);
19883 newViewDate = this.moveMonth(this.viewDate, dir);
19885 newDate = new Date(this.date);
19886 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19887 newViewDate = new Date(this.viewDate);
19888 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19890 if (this.dateWithinRange(newDate)){
19891 this.date = newDate;
19892 this.viewDate = newViewDate;
19893 this.setValue(this.formatDate(this.date));
19895 e.preventDefault();
19896 dateChanged = true;
19900 this.setValue(this.formatDate(this.date));
19902 e.preventDefault();
19905 this.setValue(this.formatDate(this.date));
19919 onClick: function(e)
19921 e.stopPropagation();
19922 e.preventDefault();
19924 var target = e.getTarget();
19926 if(target.nodeName.toLowerCase() === 'i'){
19927 target = Roo.get(target).dom.parentNode;
19930 var nodeName = target.nodeName;
19931 var className = target.className;
19932 var html = target.innerHTML;
19933 //Roo.log(nodeName);
19935 switch(nodeName.toLowerCase()) {
19937 switch(className) {
19943 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19944 switch(this.viewMode){
19946 this.viewDate = this.moveMonth(this.viewDate, dir);
19950 this.viewDate = this.moveYear(this.viewDate, dir);
19956 var date = new Date();
19957 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19959 this.setValue(this.formatDate(this.date));
19966 if (className.indexOf('disabled') < 0) {
19967 this.viewDate.setUTCDate(1);
19968 if (className.indexOf('month') > -1) {
19969 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19971 var year = parseInt(html, 10) || 0;
19972 this.viewDate.setUTCFullYear(year);
19976 if(this.singleMode){
19977 this.setValue(this.formatDate(this.viewDate));
19988 //Roo.log(className);
19989 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19990 var day = parseInt(html, 10) || 1;
19991 var year = this.viewDate.getUTCFullYear(),
19992 month = this.viewDate.getUTCMonth();
19994 if (className.indexOf('old') > -1) {
20001 } else if (className.indexOf('new') > -1) {
20009 //Roo.log([year,month,day]);
20010 this.date = this.UTCDate(year, month, day,0,0,0,0);
20011 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
20013 //Roo.log(this.formatDate(this.date));
20014 this.setValue(this.formatDate(this.date));
20021 setStartDate: function(startDate)
20023 this.startDate = startDate || -Infinity;
20024 if (this.startDate !== -Infinity) {
20025 this.startDate = this.parseDate(this.startDate);
20028 this.updateNavArrows();
20031 setEndDate: function(endDate)
20033 this.endDate = endDate || Infinity;
20034 if (this.endDate !== Infinity) {
20035 this.endDate = this.parseDate(this.endDate);
20038 this.updateNavArrows();
20041 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
20043 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
20044 if (typeof(this.daysOfWeekDisabled) !== 'object') {
20045 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
20047 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
20048 return parseInt(d, 10);
20051 this.updateNavArrows();
20054 updateNavArrows: function()
20056 if(this.singleMode){
20060 var d = new Date(this.viewDate),
20061 year = d.getUTCFullYear(),
20062 month = d.getUTCMonth();
20064 Roo.each(this.picker().select('.prev', true).elements, function(v){
20066 switch (this.viewMode) {
20069 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
20075 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
20082 Roo.each(this.picker().select('.next', true).elements, function(v){
20084 switch (this.viewMode) {
20087 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
20093 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
20101 moveMonth: function(date, dir)
20106 var new_date = new Date(date.valueOf()),
20107 day = new_date.getUTCDate(),
20108 month = new_date.getUTCMonth(),
20109 mag = Math.abs(dir),
20111 dir = dir > 0 ? 1 : -1;
20114 // If going back one month, make sure month is not current month
20115 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
20117 return new_date.getUTCMonth() == month;
20119 // If going forward one month, make sure month is as expected
20120 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
20122 return new_date.getUTCMonth() != new_month;
20124 new_month = month + dir;
20125 new_date.setUTCMonth(new_month);
20126 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
20127 if (new_month < 0 || new_month > 11) {
20128 new_month = (new_month + 12) % 12;
20131 // For magnitudes >1, move one month at a time...
20132 for (var i=0; i<mag; i++) {
20133 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
20134 new_date = this.moveMonth(new_date, dir);
20136 // ...then reset the day, keeping it in the new month
20137 new_month = new_date.getUTCMonth();
20138 new_date.setUTCDate(day);
20140 return new_month != new_date.getUTCMonth();
20143 // Common date-resetting loop -- if date is beyond end of month, make it
20146 new_date.setUTCDate(--day);
20147 new_date.setUTCMonth(new_month);
20152 moveYear: function(date, dir)
20154 return this.moveMonth(date, dir*12);
20157 dateWithinRange: function(date)
20159 return date >= this.startDate && date <= this.endDate;
20165 this.picker().remove();
20168 validateValue : function(value)
20170 if(this.getVisibilityEl().hasClass('hidden')){
20174 if(value.length < 1) {
20175 if(this.allowBlank){
20181 if(value.length < this.minLength){
20184 if(value.length > this.maxLength){
20188 var vt = Roo.form.VTypes;
20189 if(!vt[this.vtype](value, this)){
20193 if(typeof this.validator == "function"){
20194 var msg = this.validator(value);
20200 if(this.regex && !this.regex.test(value)){
20204 if(typeof(this.parseDate(value)) == 'undefined'){
20208 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
20212 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
20222 this.date = this.viewDate = '';
20224 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20229 Roo.apply(Roo.bootstrap.DateField, {
20240 html: '<i class="fa fa-arrow-left"/>'
20250 html: '<i class="fa fa-arrow-right"/>'
20292 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
20293 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
20294 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
20295 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20296 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
20309 navFnc: 'FullYear',
20314 navFnc: 'FullYear',
20319 Roo.apply(Roo.bootstrap.DateField, {
20323 cls: 'datepicker dropdown-menu roo-dynamic',
20327 cls: 'datepicker-days',
20331 cls: 'table-condensed',
20333 Roo.bootstrap.DateField.head,
20337 Roo.bootstrap.DateField.footer
20344 cls: 'datepicker-months',
20348 cls: 'table-condensed',
20350 Roo.bootstrap.DateField.head,
20351 Roo.bootstrap.DateField.content,
20352 Roo.bootstrap.DateField.footer
20359 cls: 'datepicker-years',
20363 cls: 'table-condensed',
20365 Roo.bootstrap.DateField.head,
20366 Roo.bootstrap.DateField.content,
20367 Roo.bootstrap.DateField.footer
20386 * @class Roo.bootstrap.TimeField
20387 * @extends Roo.bootstrap.Input
20388 * Bootstrap DateField class
20392 * Create a new TimeField
20393 * @param {Object} config The config object
20396 Roo.bootstrap.TimeField = function(config){
20397 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20401 * Fires when this field show.
20402 * @param {Roo.bootstrap.DateField} thisthis
20403 * @param {Mixed} date The date value
20408 * Fires when this field hide.
20409 * @param {Roo.bootstrap.DateField} this
20410 * @param {Mixed} date The date value
20415 * Fires when select a date.
20416 * @param {Roo.bootstrap.DateField} this
20417 * @param {Mixed} date The date value
20423 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
20426 * @cfg {String} format
20427 * The default time format string which can be overriden for localization support. The format must be
20428 * valid according to {@link Date#parseDate} (defaults to 'H:i').
20432 onRender: function(ct, position)
20435 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20437 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20439 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20441 this.pop = this.picker().select('>.datepicker-time',true).first();
20442 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20444 this.picker().on('mousedown', this.onMousedown, this);
20445 this.picker().on('click', this.onClick, this);
20447 this.picker().addClass('datepicker-dropdown');
20452 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20453 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20454 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20455 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20456 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20457 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20461 fireKey: function(e){
20462 if (!this.picker().isVisible()){
20463 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20469 e.preventDefault();
20477 this.onTogglePeriod();
20480 this.onIncrementMinutes();
20483 this.onDecrementMinutes();
20492 onClick: function(e) {
20493 e.stopPropagation();
20494 e.preventDefault();
20497 picker : function()
20499 return this.el.select('.datepicker', true).first();
20502 fillTime: function()
20504 var time = this.pop.select('tbody', true).first();
20506 time.dom.innerHTML = '';
20521 cls: 'hours-up glyphicon glyphicon-chevron-up'
20541 cls: 'minutes-up glyphicon glyphicon-chevron-up'
20562 cls: 'timepicker-hour',
20577 cls: 'timepicker-minute',
20592 cls: 'btn btn-primary period',
20614 cls: 'hours-down glyphicon glyphicon-chevron-down'
20634 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20652 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20659 var hours = this.time.getHours();
20660 var minutes = this.time.getMinutes();
20673 hours = hours - 12;
20677 hours = '0' + hours;
20681 minutes = '0' + minutes;
20684 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20685 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20686 this.pop.select('button', true).first().dom.innerHTML = period;
20692 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20694 var cls = ['bottom'];
20696 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20703 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20708 this.picker().addClass(cls.join('-'));
20712 Roo.each(cls, function(c){
20714 _this.picker().setTop(_this.inputEl().getHeight());
20718 _this.picker().setTop(0 - _this.picker().getHeight());
20723 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20727 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20734 onFocus : function()
20736 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20740 onBlur : function()
20742 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20748 this.picker().show();
20753 this.fireEvent('show', this, this.date);
20758 this.picker().hide();
20761 this.fireEvent('hide', this, this.date);
20764 setTime : function()
20767 this.setValue(this.time.format(this.format));
20769 this.fireEvent('select', this, this.date);
20774 onMousedown: function(e){
20775 e.stopPropagation();
20776 e.preventDefault();
20779 onIncrementHours: function()
20781 Roo.log('onIncrementHours');
20782 this.time = this.time.add(Date.HOUR, 1);
20787 onDecrementHours: function()
20789 Roo.log('onDecrementHours');
20790 this.time = this.time.add(Date.HOUR, -1);
20794 onIncrementMinutes: function()
20796 Roo.log('onIncrementMinutes');
20797 this.time = this.time.add(Date.MINUTE, 1);
20801 onDecrementMinutes: function()
20803 Roo.log('onDecrementMinutes');
20804 this.time = this.time.add(Date.MINUTE, -1);
20808 onTogglePeriod: function()
20810 Roo.log('onTogglePeriod');
20811 this.time = this.time.add(Date.HOUR, 12);
20818 Roo.apply(Roo.bootstrap.TimeField, {
20848 cls: 'btn btn-info ok',
20860 Roo.apply(Roo.bootstrap.TimeField, {
20864 cls: 'datepicker dropdown-menu',
20868 cls: 'datepicker-time',
20872 cls: 'table-condensed',
20874 Roo.bootstrap.TimeField.content,
20875 Roo.bootstrap.TimeField.footer
20894 * @class Roo.bootstrap.MonthField
20895 * @extends Roo.bootstrap.Input
20896 * Bootstrap MonthField class
20898 * @cfg {String} language default en
20901 * Create a new MonthField
20902 * @param {Object} config The config object
20905 Roo.bootstrap.MonthField = function(config){
20906 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20911 * Fires when this field show.
20912 * @param {Roo.bootstrap.MonthField} this
20913 * @param {Mixed} date The date value
20918 * Fires when this field hide.
20919 * @param {Roo.bootstrap.MonthField} this
20920 * @param {Mixed} date The date value
20925 * Fires when select a date.
20926 * @param {Roo.bootstrap.MonthField} this
20927 * @param {String} oldvalue The old value
20928 * @param {String} newvalue The new value
20934 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20936 onRender: function(ct, position)
20939 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20941 this.language = this.language || 'en';
20942 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20943 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20945 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20946 this.isInline = false;
20947 this.isInput = true;
20948 this.component = this.el.select('.add-on', true).first() || false;
20949 this.component = (this.component && this.component.length === 0) ? false : this.component;
20950 this.hasInput = this.component && this.inputEL().length;
20952 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20954 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20956 this.picker().on('mousedown', this.onMousedown, this);
20957 this.picker().on('click', this.onClick, this);
20959 this.picker().addClass('datepicker-dropdown');
20961 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20962 v.setStyle('width', '189px');
20969 if(this.isInline) {
20975 setValue: function(v, suppressEvent)
20977 var o = this.getValue();
20979 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20983 if(suppressEvent !== true){
20984 this.fireEvent('select', this, o, v);
20989 getValue: function()
20994 onClick: function(e)
20996 e.stopPropagation();
20997 e.preventDefault();
20999 var target = e.getTarget();
21001 if(target.nodeName.toLowerCase() === 'i'){
21002 target = Roo.get(target).dom.parentNode;
21005 var nodeName = target.nodeName;
21006 var className = target.className;
21007 var html = target.innerHTML;
21009 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
21013 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
21015 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21021 picker : function()
21023 return this.pickerEl;
21026 fillMonths: function()
21029 var months = this.picker().select('>.datepicker-months td', true).first();
21031 months.dom.innerHTML = '';
21037 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
21040 months.createChild(month);
21049 if(typeof(this.vIndex) == 'undefined' && this.value.length){
21050 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
21053 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
21054 e.removeClass('active');
21056 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
21057 e.addClass('active');
21064 if(this.isInline) {
21068 this.picker().removeClass(['bottom', 'top']);
21070 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21072 * place to the top of element!
21076 this.picker().addClass('top');
21077 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21082 this.picker().addClass('bottom');
21084 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21087 onFocus : function()
21089 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
21093 onBlur : function()
21095 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
21097 var d = this.inputEl().getValue();
21106 this.picker().show();
21107 this.picker().select('>.datepicker-months', true).first().show();
21111 this.fireEvent('show', this, this.date);
21116 if(this.isInline) {
21119 this.picker().hide();
21120 this.fireEvent('hide', this, this.date);
21124 onMousedown: function(e)
21126 e.stopPropagation();
21127 e.preventDefault();
21132 Roo.bootstrap.MonthField.superclass.keyup.call(this);
21136 fireKey: function(e)
21138 if (!this.picker().isVisible()){
21139 if (e.keyCode == 27) {// allow escape to hide and re-show picker
21150 e.preventDefault();
21154 dir = e.keyCode == 37 ? -1 : 1;
21156 this.vIndex = this.vIndex + dir;
21158 if(this.vIndex < 0){
21162 if(this.vIndex > 11){
21166 if(isNaN(this.vIndex)){
21170 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21176 dir = e.keyCode == 38 ? -1 : 1;
21178 this.vIndex = this.vIndex + dir * 4;
21180 if(this.vIndex < 0){
21184 if(this.vIndex > 11){
21188 if(isNaN(this.vIndex)){
21192 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21197 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21198 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21202 e.preventDefault();
21205 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21206 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21222 this.picker().remove();
21227 Roo.apply(Roo.bootstrap.MonthField, {
21246 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21247 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
21252 Roo.apply(Roo.bootstrap.MonthField, {
21256 cls: 'datepicker dropdown-menu roo-dynamic',
21260 cls: 'datepicker-months',
21264 cls: 'table-condensed',
21266 Roo.bootstrap.DateField.content
21286 * @class Roo.bootstrap.CheckBox
21287 * @extends Roo.bootstrap.Input
21288 * Bootstrap CheckBox class
21290 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
21291 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
21292 * @cfg {String} boxLabel The text that appears beside the checkbox
21293 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
21294 * @cfg {Boolean} checked initnal the element
21295 * @cfg {Boolean} inline inline the element (default false)
21296 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
21297 * @cfg {String} tooltip label tooltip
21300 * Create a new CheckBox
21301 * @param {Object} config The config object
21304 Roo.bootstrap.CheckBox = function(config){
21305 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
21310 * Fires when the element is checked or unchecked.
21311 * @param {Roo.bootstrap.CheckBox} this This input
21312 * @param {Boolean} checked The new checked value
21317 * Fires when the element is click.
21318 * @param {Roo.bootstrap.CheckBox} this This input
21325 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
21327 inputType: 'checkbox',
21336 // checkbox success does not make any sense really..
21341 getAutoCreate : function()
21343 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
21349 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
21352 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
21358 type : this.inputType,
21359 value : this.inputValue,
21360 cls : 'roo-' + this.inputType, //'form-box',
21361 placeholder : this.placeholder || ''
21365 if(this.inputType != 'radio'){
21369 cls : 'roo-hidden-value',
21370 value : this.checked ? this.inputValue : this.valueOff
21375 if (this.weight) { // Validity check?
21376 cfg.cls += " " + this.inputType + "-" + this.weight;
21379 if (this.disabled) {
21380 input.disabled=true;
21384 input.checked = this.checked;
21389 input.name = this.name;
21391 if(this.inputType != 'radio'){
21392 hidden.name = this.name;
21393 input.name = '_hidden_' + this.name;
21398 input.cls += ' input-' + this.size;
21403 ['xs','sm','md','lg'].map(function(size){
21404 if (settings[size]) {
21405 cfg.cls += ' col-' + size + '-' + settings[size];
21409 var inputblock = input;
21411 if (this.before || this.after) {
21414 cls : 'input-group',
21419 inputblock.cn.push({
21421 cls : 'input-group-addon',
21426 inputblock.cn.push(input);
21428 if(this.inputType != 'radio'){
21429 inputblock.cn.push(hidden);
21433 inputblock.cn.push({
21435 cls : 'input-group-addon',
21441 var boxLabelCfg = false;
21447 //'for': id, // box label is handled by onclick - so no for...
21449 html: this.boxLabel
21452 boxLabelCfg.tooltip = this.tooltip;
21458 if (align ==='left' && this.fieldLabel.length) {
21459 // Roo.log("left and has label");
21464 cls : 'control-label',
21465 html : this.fieldLabel
21476 cfg.cn[1].cn.push(boxLabelCfg);
21479 if(this.labelWidth > 12){
21480 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21483 if(this.labelWidth < 13 && this.labelmd == 0){
21484 this.labelmd = this.labelWidth;
21487 if(this.labellg > 0){
21488 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21489 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21492 if(this.labelmd > 0){
21493 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21494 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21497 if(this.labelsm > 0){
21498 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21499 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21502 if(this.labelxs > 0){
21503 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21504 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21507 } else if ( this.fieldLabel.length) {
21508 // Roo.log(" label");
21512 tag: this.boxLabel ? 'span' : 'label',
21514 cls: 'control-label box-input-label',
21515 //cls : 'input-group-addon',
21516 html : this.fieldLabel
21523 cfg.cn.push(boxLabelCfg);
21528 // Roo.log(" no label && no align");
21529 cfg.cn = [ inputblock ] ;
21531 cfg.cn.push(boxLabelCfg);
21539 if(this.inputType != 'radio'){
21540 cfg.cn.push(hidden);
21548 * return the real input element.
21550 inputEl: function ()
21552 return this.el.select('input.roo-' + this.inputType,true).first();
21554 hiddenEl: function ()
21556 return this.el.select('input.roo-hidden-value',true).first();
21559 labelEl: function()
21561 return this.el.select('label.control-label',true).first();
21563 /* depricated... */
21567 return this.labelEl();
21570 boxLabelEl: function()
21572 return this.el.select('label.box-label',true).first();
21575 initEvents : function()
21577 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21579 this.inputEl().on('click', this.onClick, this);
21581 if (this.boxLabel) {
21582 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
21585 this.startValue = this.getValue();
21588 Roo.bootstrap.CheckBox.register(this);
21592 onClick : function(e)
21594 if(this.fireEvent('click', this, e) !== false){
21595 this.setChecked(!this.checked);
21600 setChecked : function(state,suppressEvent)
21602 this.startValue = this.getValue();
21604 if(this.inputType == 'radio'){
21606 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21607 e.dom.checked = false;
21610 this.inputEl().dom.checked = true;
21612 this.inputEl().dom.value = this.inputValue;
21614 if(suppressEvent !== true){
21615 this.fireEvent('check', this, true);
21623 this.checked = state;
21625 this.inputEl().dom.checked = state;
21628 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21630 if(suppressEvent !== true){
21631 this.fireEvent('check', this, state);
21637 getValue : function()
21639 if(this.inputType == 'radio'){
21640 return this.getGroupValue();
21643 return this.hiddenEl().dom.value;
21647 getGroupValue : function()
21649 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21653 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21656 setValue : function(v,suppressEvent)
21658 if(this.inputType == 'radio'){
21659 this.setGroupValue(v, suppressEvent);
21663 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21668 setGroupValue : function(v, suppressEvent)
21670 this.startValue = this.getValue();
21672 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21673 e.dom.checked = false;
21675 if(e.dom.value == v){
21676 e.dom.checked = true;
21680 if(suppressEvent !== true){
21681 this.fireEvent('check', this, true);
21689 validate : function()
21691 if(this.getVisibilityEl().hasClass('hidden')){
21697 (this.inputType == 'radio' && this.validateRadio()) ||
21698 (this.inputType == 'checkbox' && this.validateCheckbox())
21704 this.markInvalid();
21708 validateRadio : function()
21710 if(this.getVisibilityEl().hasClass('hidden')){
21714 if(this.allowBlank){
21720 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21721 if(!e.dom.checked){
21733 validateCheckbox : function()
21736 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21737 //return (this.getValue() == this.inputValue) ? true : false;
21740 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21748 for(var i in group){
21749 if(group[i].el.isVisible(true)){
21757 for(var i in group){
21762 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21769 * Mark this field as valid
21771 markValid : function()
21775 this.fireEvent('valid', this);
21777 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21780 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21787 if(this.inputType == 'radio'){
21788 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21789 var fg = e.findParent('.form-group', false, true);
21790 if (Roo.bootstrap.version == 3) {
21791 fg.removeClass([_this.invalidClass, _this.validClass]);
21792 fg.addClass(_this.validClass);
21794 fg.removeClass(['is-valid', 'is-invalid']);
21795 fg.addClass('is-valid');
21803 var fg = this.el.findParent('.form-group', false, true);
21804 if (Roo.bootstrap.version == 3) {
21805 fg.removeClass([this.invalidClass, this.validClass]);
21806 fg.addClass(this.validClass);
21808 fg.removeClass(['is-valid', 'is-invalid']);
21809 fg.addClass('is-valid');
21814 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21820 for(var i in group){
21821 var fg = group[i].el.findParent('.form-group', false, true);
21822 if (Roo.bootstrap.version == 3) {
21823 fg.removeClass([this.invalidClass, this.validClass]);
21824 fg.addClass(this.validClass);
21826 fg.removeClass(['is-valid', 'is-invalid']);
21827 fg.addClass('is-valid');
21833 * Mark this field as invalid
21834 * @param {String} msg The validation message
21836 markInvalid : function(msg)
21838 if(this.allowBlank){
21844 this.fireEvent('invalid', this, msg);
21846 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21849 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21853 label.markInvalid();
21856 if(this.inputType == 'radio'){
21858 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21859 var fg = e.findParent('.form-group', false, true);
21860 if (Roo.bootstrap.version == 3) {
21861 fg.removeClass([_this.invalidClass, _this.validClass]);
21862 fg.addClass(_this.invalidClass);
21864 fg.removeClass(['is-invalid', 'is-valid']);
21865 fg.addClass('is-invalid');
21873 var fg = this.el.findParent('.form-group', false, true);
21874 if (Roo.bootstrap.version == 3) {
21875 fg.removeClass([_this.invalidClass, _this.validClass]);
21876 fg.addClass(_this.invalidClass);
21878 fg.removeClass(['is-invalid', 'is-valid']);
21879 fg.addClass('is-invalid');
21884 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21890 for(var i in group){
21891 var fg = group[i].el.findParent('.form-group', false, true);
21892 if (Roo.bootstrap.version == 3) {
21893 fg.removeClass([_this.invalidClass, _this.validClass]);
21894 fg.addClass(_this.invalidClass);
21896 fg.removeClass(['is-invalid', 'is-valid']);
21897 fg.addClass('is-invalid');
21903 clearInvalid : function()
21905 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21907 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21909 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21911 if (label && label.iconEl) {
21912 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21913 label.iconEl.removeClass(['is-invalid', 'is-valid']);
21917 disable : function()
21919 if(this.inputType != 'radio'){
21920 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21927 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21928 _this.getActionEl().addClass(this.disabledClass);
21929 e.dom.disabled = true;
21933 this.disabled = true;
21934 this.fireEvent("disable", this);
21938 enable : function()
21940 if(this.inputType != 'radio'){
21941 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21948 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21949 _this.getActionEl().removeClass(this.disabledClass);
21950 e.dom.disabled = false;
21954 this.disabled = false;
21955 this.fireEvent("enable", this);
21959 setBoxLabel : function(v)
21964 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21970 Roo.apply(Roo.bootstrap.CheckBox, {
21975 * register a CheckBox Group
21976 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21978 register : function(checkbox)
21980 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21981 this.groups[checkbox.groupId] = {};
21984 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21988 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21992 * fetch a CheckBox Group based on the group ID
21993 * @param {string} the group ID
21994 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21996 get: function(groupId) {
21997 if (typeof(this.groups[groupId]) == 'undefined') {
22001 return this.groups[groupId] ;
22014 * @class Roo.bootstrap.Radio
22015 * @extends Roo.bootstrap.Component
22016 * Bootstrap Radio class
22017 * @cfg {String} boxLabel - the label associated
22018 * @cfg {String} value - the value of radio
22021 * Create a new Radio
22022 * @param {Object} config The config object
22024 Roo.bootstrap.Radio = function(config){
22025 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
22029 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
22035 getAutoCreate : function()
22039 cls : 'form-group radio',
22044 html : this.boxLabel
22052 initEvents : function()
22054 this.parent().register(this);
22056 this.el.on('click', this.onClick, this);
22060 onClick : function(e)
22062 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
22063 this.setChecked(true);
22067 setChecked : function(state, suppressEvent)
22069 this.parent().setValue(this.value, suppressEvent);
22073 setBoxLabel : function(v)
22078 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
22093 * @class Roo.bootstrap.SecurePass
22094 * @extends Roo.bootstrap.Input
22095 * Bootstrap SecurePass class
22099 * Create a new SecurePass
22100 * @param {Object} config The config object
22103 Roo.bootstrap.SecurePass = function (config) {
22104 // these go here, so the translation tool can replace them..
22106 PwdEmpty: "Please type a password, and then retype it to confirm.",
22107 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22108 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22109 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22110 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22111 FNInPwd: "Your password can't contain your first name. Please type a different password.",
22112 LNInPwd: "Your password can't contain your last name. Please type a different password.",
22113 TooWeak: "Your password is Too Weak."
22115 this.meterLabel = "Password strength:";
22116 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
22117 this.meterClass = [
22118 "roo-password-meter-tooweak",
22119 "roo-password-meter-weak",
22120 "roo-password-meter-medium",
22121 "roo-password-meter-strong",
22122 "roo-password-meter-grey"
22127 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
22130 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
22132 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
22134 * PwdEmpty: "Please type a password, and then retype it to confirm.",
22135 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22136 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22137 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22138 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22139 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
22140 * LNInPwd: "Your password can't contain your last name. Please type a different password."
22150 * @cfg {String/Object} Label for the strength meter (defaults to
22151 * 'Password strength:')
22156 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
22157 * ['Weak', 'Medium', 'Strong'])
22160 pwdStrengths: false,
22173 initEvents: function ()
22175 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
22177 if (this.el.is('input[type=password]') && Roo.isSafari) {
22178 this.el.on('keydown', this.SafariOnKeyDown, this);
22181 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
22184 onRender: function (ct, position)
22186 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
22187 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
22188 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
22190 this.trigger.createChild({
22195 cls: 'roo-password-meter-grey col-xs-12',
22198 //width: this.meterWidth + 'px'
22202 cls: 'roo-password-meter-text'
22208 if (this.hideTrigger) {
22209 this.trigger.setDisplayed(false);
22211 this.setSize(this.width || '', this.height || '');
22214 onDestroy: function ()
22216 if (this.trigger) {
22217 this.trigger.removeAllListeners();
22218 this.trigger.remove();
22221 this.wrap.remove();
22223 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
22226 checkStrength: function ()
22228 var pwd = this.inputEl().getValue();
22229 if (pwd == this._lastPwd) {
22234 if (this.ClientSideStrongPassword(pwd)) {
22236 } else if (this.ClientSideMediumPassword(pwd)) {
22238 } else if (this.ClientSideWeakPassword(pwd)) {
22244 Roo.log('strength1: ' + strength);
22246 //var pm = this.trigger.child('div/div/div').dom;
22247 var pm = this.trigger.child('div/div');
22248 pm.removeClass(this.meterClass);
22249 pm.addClass(this.meterClass[strength]);
22252 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22254 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
22256 this._lastPwd = pwd;
22260 Roo.bootstrap.SecurePass.superclass.reset.call(this);
22262 this._lastPwd = '';
22264 var pm = this.trigger.child('div/div');
22265 pm.removeClass(this.meterClass);
22266 pm.addClass('roo-password-meter-grey');
22269 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22272 this.inputEl().dom.type='password';
22275 validateValue: function (value)
22278 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
22281 if (value.length == 0) {
22282 if (this.allowBlank) {
22283 this.clearInvalid();
22287 this.markInvalid(this.errors.PwdEmpty);
22288 this.errorMsg = this.errors.PwdEmpty;
22296 if ('[\x21-\x7e]*'.match(value)) {
22297 this.markInvalid(this.errors.PwdBadChar);
22298 this.errorMsg = this.errors.PwdBadChar;
22301 if (value.length < 6) {
22302 this.markInvalid(this.errors.PwdShort);
22303 this.errorMsg = this.errors.PwdShort;
22306 if (value.length > 16) {
22307 this.markInvalid(this.errors.PwdLong);
22308 this.errorMsg = this.errors.PwdLong;
22312 if (this.ClientSideStrongPassword(value)) {
22314 } else if (this.ClientSideMediumPassword(value)) {
22316 } else if (this.ClientSideWeakPassword(value)) {
22323 if (strength < 2) {
22324 //this.markInvalid(this.errors.TooWeak);
22325 this.errorMsg = this.errors.TooWeak;
22330 console.log('strength2: ' + strength);
22332 //var pm = this.trigger.child('div/div/div').dom;
22334 var pm = this.trigger.child('div/div');
22335 pm.removeClass(this.meterClass);
22336 pm.addClass(this.meterClass[strength]);
22338 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22340 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
22342 this.errorMsg = '';
22346 CharacterSetChecks: function (type)
22349 this.fResult = false;
22352 isctype: function (character, type)
22355 case this.kCapitalLetter:
22356 if (character >= 'A' && character <= 'Z') {
22361 case this.kSmallLetter:
22362 if (character >= 'a' && character <= 'z') {
22368 if (character >= '0' && character <= '9') {
22373 case this.kPunctuation:
22374 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
22385 IsLongEnough: function (pwd, size)
22387 return !(pwd == null || isNaN(size) || pwd.length < size);
22390 SpansEnoughCharacterSets: function (word, nb)
22392 if (!this.IsLongEnough(word, nb))
22397 var characterSetChecks = new Array(
22398 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22399 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22402 for (var index = 0; index < word.length; ++index) {
22403 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22404 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22405 characterSetChecks[nCharSet].fResult = true;
22412 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22413 if (characterSetChecks[nCharSet].fResult) {
22418 if (nCharSets < nb) {
22424 ClientSideStrongPassword: function (pwd)
22426 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22429 ClientSideMediumPassword: function (pwd)
22431 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22434 ClientSideWeakPassword: function (pwd)
22436 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22439 })//<script type="text/javascript">
22442 * Based Ext JS Library 1.1.1
22443 * Copyright(c) 2006-2007, Ext JS, LLC.
22449 * @class Roo.HtmlEditorCore
22450 * @extends Roo.Component
22451 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22453 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22456 Roo.HtmlEditorCore = function(config){
22459 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22464 * @event initialize
22465 * Fires when the editor is fully initialized (including the iframe)
22466 * @param {Roo.HtmlEditorCore} this
22471 * Fires when the editor is first receives the focus. Any insertion must wait
22472 * until after this event.
22473 * @param {Roo.HtmlEditorCore} this
22477 * @event beforesync
22478 * Fires before the textarea is updated with content from the editor iframe. Return false
22479 * to cancel the sync.
22480 * @param {Roo.HtmlEditorCore} this
22481 * @param {String} html
22485 * @event beforepush
22486 * Fires before the iframe editor is updated with content from the textarea. Return false
22487 * to cancel the push.
22488 * @param {Roo.HtmlEditorCore} this
22489 * @param {String} html
22494 * Fires when the textarea is updated with content from the editor iframe.
22495 * @param {Roo.HtmlEditorCore} this
22496 * @param {String} html
22501 * Fires when the iframe editor is updated with content from the textarea.
22502 * @param {Roo.HtmlEditorCore} this
22503 * @param {String} html
22508 * @event editorevent
22509 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22510 * @param {Roo.HtmlEditorCore} this
22516 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22518 // defaults : white / black...
22519 this.applyBlacklists();
22526 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
22530 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
22536 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22541 * @cfg {Number} height (in pixels)
22545 * @cfg {Number} width (in pixels)
22550 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22553 stylesheets: false,
22558 // private properties
22559 validationEvent : false,
22561 initialized : false,
22563 sourceEditMode : false,
22564 onFocus : Roo.emptyFn,
22566 hideMode:'offsets',
22570 // blacklist + whitelisted elements..
22577 * Protected method that will not generally be called directly. It
22578 * is called when the editor initializes the iframe with HTML contents. Override this method if you
22579 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22581 getDocMarkup : function(){
22585 // inherit styels from page...??
22586 if (this.stylesheets === false) {
22588 Roo.get(document.head).select('style').each(function(node) {
22589 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22592 Roo.get(document.head).select('link').each(function(node) {
22593 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22596 } else if (!this.stylesheets.length) {
22598 st = '<style type="text/css">' +
22599 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22602 st = '<style type="text/css">' +
22607 st += '<style type="text/css">' +
22608 'IMG { cursor: pointer } ' +
22611 var cls = 'roo-htmleditor-body';
22613 if(this.bodyCls.length){
22614 cls += ' ' + this.bodyCls;
22617 return '<html><head>' + st +
22618 //<style type="text/css">' +
22619 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22621 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
22625 onRender : function(ct, position)
22628 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22629 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22632 this.el.dom.style.border = '0 none';
22633 this.el.dom.setAttribute('tabIndex', -1);
22634 this.el.addClass('x-hidden hide');
22638 if(Roo.isIE){ // fix IE 1px bogus margin
22639 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22643 this.frameId = Roo.id();
22647 var iframe = this.owner.wrap.createChild({
22649 cls: 'form-control', // bootstrap..
22651 name: this.frameId,
22652 frameBorder : 'no',
22653 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
22658 this.iframe = iframe.dom;
22660 this.assignDocWin();
22662 this.doc.designMode = 'on';
22665 this.doc.write(this.getDocMarkup());
22669 var task = { // must defer to wait for browser to be ready
22671 //console.log("run task?" + this.doc.readyState);
22672 this.assignDocWin();
22673 if(this.doc.body || this.doc.readyState == 'complete'){
22675 this.doc.designMode="on";
22679 Roo.TaskMgr.stop(task);
22680 this.initEditor.defer(10, this);
22687 Roo.TaskMgr.start(task);
22692 onResize : function(w, h)
22694 Roo.log('resize: ' +w + ',' + h );
22695 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22699 if(typeof w == 'number'){
22701 this.iframe.style.width = w + 'px';
22703 if(typeof h == 'number'){
22705 this.iframe.style.height = h + 'px';
22707 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22714 * Toggles the editor between standard and source edit mode.
22715 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22717 toggleSourceEdit : function(sourceEditMode){
22719 this.sourceEditMode = sourceEditMode === true;
22721 if(this.sourceEditMode){
22723 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22726 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22727 //this.iframe.className = '';
22730 //this.setSize(this.owner.wrap.getSize());
22731 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22738 * Protected method that will not generally be called directly. If you need/want
22739 * custom HTML cleanup, this is the method you should override.
22740 * @param {String} html The HTML to be cleaned
22741 * return {String} The cleaned HTML
22743 cleanHtml : function(html){
22744 html = String(html);
22745 if(html.length > 5){
22746 if(Roo.isSafari){ // strip safari nonsense
22747 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22750 if(html == ' '){
22757 * HTML Editor -> Textarea
22758 * Protected method that will not generally be called directly. Syncs the contents
22759 * of the editor iframe with the textarea.
22761 syncValue : function(){
22762 if(this.initialized){
22763 var bd = (this.doc.body || this.doc.documentElement);
22764 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22765 var html = bd.innerHTML;
22767 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22768 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22770 html = '<div style="'+m[0]+'">' + html + '</div>';
22773 html = this.cleanHtml(html);
22774 // fix up the special chars.. normaly like back quotes in word...
22775 // however we do not want to do this with chinese..
22776 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
22778 var cc = match.charCodeAt();
22780 // Get the character value, handling surrogate pairs
22781 if (match.length == 2) {
22782 // It's a surrogate pair, calculate the Unicode code point
22783 var high = match.charCodeAt(0) - 0xD800;
22784 var low = match.charCodeAt(1) - 0xDC00;
22785 cc = (high * 0x400) + low + 0x10000;
22787 (cc >= 0x4E00 && cc < 0xA000 ) ||
22788 (cc >= 0x3400 && cc < 0x4E00 ) ||
22789 (cc >= 0xf900 && cc < 0xfb00 )
22794 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
22795 return "&#" + cc + ";";
22802 if(this.owner.fireEvent('beforesync', this, html) !== false){
22803 this.el.dom.value = html;
22804 this.owner.fireEvent('sync', this, html);
22810 * Protected method that will not generally be called directly. Pushes the value of the textarea
22811 * into the iframe editor.
22813 pushValue : function(){
22814 if(this.initialized){
22815 var v = this.el.dom.value.trim();
22817 // if(v.length < 1){
22821 if(this.owner.fireEvent('beforepush', this, v) !== false){
22822 var d = (this.doc.body || this.doc.documentElement);
22824 this.cleanUpPaste();
22825 this.el.dom.value = d.innerHTML;
22826 this.owner.fireEvent('push', this, v);
22832 deferFocus : function(){
22833 this.focus.defer(10, this);
22837 focus : function(){
22838 if(this.win && !this.sourceEditMode){
22845 assignDocWin: function()
22847 var iframe = this.iframe;
22850 this.doc = iframe.contentWindow.document;
22851 this.win = iframe.contentWindow;
22853 // if (!Roo.get(this.frameId)) {
22856 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22857 // this.win = Roo.get(this.frameId).dom.contentWindow;
22859 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22863 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22864 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22869 initEditor : function(){
22870 //console.log("INIT EDITOR");
22871 this.assignDocWin();
22875 this.doc.designMode="on";
22877 this.doc.write(this.getDocMarkup());
22880 var dbody = (this.doc.body || this.doc.documentElement);
22881 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22882 // this copies styles from the containing element into thsi one..
22883 // not sure why we need all of this..
22884 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22886 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22887 //ss['background-attachment'] = 'fixed'; // w3c
22888 dbody.bgProperties = 'fixed'; // ie
22889 //Roo.DomHelper.applyStyles(dbody, ss);
22890 Roo.EventManager.on(this.doc, {
22891 //'mousedown': this.onEditorEvent,
22892 'mouseup': this.onEditorEvent,
22893 'dblclick': this.onEditorEvent,
22894 'click': this.onEditorEvent,
22895 'keyup': this.onEditorEvent,
22900 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22902 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22903 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22905 this.initialized = true;
22907 this.owner.fireEvent('initialize', this);
22912 onDestroy : function(){
22918 //for (var i =0; i < this.toolbars.length;i++) {
22919 // // fixme - ask toolbars for heights?
22920 // this.toolbars[i].onDestroy();
22923 //this.wrap.dom.innerHTML = '';
22924 //this.wrap.remove();
22929 onFirstFocus : function(){
22931 this.assignDocWin();
22934 this.activated = true;
22937 if(Roo.isGecko){ // prevent silly gecko errors
22939 var s = this.win.getSelection();
22940 if(!s.focusNode || s.focusNode.nodeType != 3){
22941 var r = s.getRangeAt(0);
22942 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22947 this.execCmd('useCSS', true);
22948 this.execCmd('styleWithCSS', false);
22951 this.owner.fireEvent('activate', this);
22955 adjustFont: function(btn){
22956 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22957 //if(Roo.isSafari){ // safari
22960 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22961 if(Roo.isSafari){ // safari
22962 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22963 v = (v < 10) ? 10 : v;
22964 v = (v > 48) ? 48 : v;
22965 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22970 v = Math.max(1, v+adjust);
22972 this.execCmd('FontSize', v );
22975 onEditorEvent : function(e)
22977 this.owner.fireEvent('editorevent', this, e);
22978 // this.updateToolbar();
22979 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22982 insertTag : function(tg)
22984 // could be a bit smarter... -> wrap the current selected tRoo..
22985 if (tg.toLowerCase() == 'span' ||
22986 tg.toLowerCase() == 'code' ||
22987 tg.toLowerCase() == 'sup' ||
22988 tg.toLowerCase() == 'sub'
22991 range = this.createRange(this.getSelection());
22992 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22993 wrappingNode.appendChild(range.extractContents());
22994 range.insertNode(wrappingNode);
23001 this.execCmd("formatblock", tg);
23005 insertText : function(txt)
23009 var range = this.createRange();
23010 range.deleteContents();
23011 //alert(Sender.getAttribute('label'));
23013 range.insertNode(this.doc.createTextNode(txt));
23019 * Executes a Midas editor command on the editor document and performs necessary focus and
23020 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
23021 * @param {String} cmd The Midas command
23022 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
23024 relayCmd : function(cmd, value){
23026 this.execCmd(cmd, value);
23027 this.owner.fireEvent('editorevent', this);
23028 //this.updateToolbar();
23029 this.owner.deferFocus();
23033 * Executes a Midas editor command directly on the editor document.
23034 * For visual commands, you should use {@link #relayCmd} instead.
23035 * <b>This should only be called after the editor is initialized.</b>
23036 * @param {String} cmd The Midas command
23037 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
23039 execCmd : function(cmd, value){
23040 this.doc.execCommand(cmd, false, value === undefined ? null : value);
23047 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
23049 * @param {String} text | dom node..
23051 insertAtCursor : function(text)
23054 if(!this.activated){
23060 var r = this.doc.selection.createRange();
23071 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
23075 // from jquery ui (MIT licenced)
23077 var win = this.win;
23079 if (win.getSelection && win.getSelection().getRangeAt) {
23080 range = win.getSelection().getRangeAt(0);
23081 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
23082 range.insertNode(node);
23083 } else if (win.document.selection && win.document.selection.createRange) {
23084 // no firefox support
23085 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23086 win.document.selection.createRange().pasteHTML(txt);
23088 // no firefox support
23089 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23090 this.execCmd('InsertHTML', txt);
23099 mozKeyPress : function(e){
23101 var c = e.getCharCode(), cmd;
23104 c = String.fromCharCode(c).toLowerCase();
23118 this.cleanUpPaste.defer(100, this);
23126 e.preventDefault();
23134 fixKeys : function(){ // load time branching for fastest keydown performance
23136 return function(e){
23137 var k = e.getKey(), r;
23140 r = this.doc.selection.createRange();
23143 r.pasteHTML('    ');
23150 r = this.doc.selection.createRange();
23152 var target = r.parentElement();
23153 if(!target || target.tagName.toLowerCase() != 'li'){
23155 r.pasteHTML('<br />');
23161 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23162 this.cleanUpPaste.defer(100, this);
23168 }else if(Roo.isOpera){
23169 return function(e){
23170 var k = e.getKey();
23174 this.execCmd('InsertHTML','    ');
23177 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23178 this.cleanUpPaste.defer(100, this);
23183 }else if(Roo.isSafari){
23184 return function(e){
23185 var k = e.getKey();
23189 this.execCmd('InsertText','\t');
23193 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23194 this.cleanUpPaste.defer(100, this);
23202 getAllAncestors: function()
23204 var p = this.getSelectedNode();
23207 a.push(p); // push blank onto stack..
23208 p = this.getParentElement();
23212 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
23216 a.push(this.doc.body);
23220 lastSelNode : false,
23223 getSelection : function()
23225 this.assignDocWin();
23226 return Roo.isIE ? this.doc.selection : this.win.getSelection();
23229 getSelectedNode: function()
23231 // this may only work on Gecko!!!
23233 // should we cache this!!!!
23238 var range = this.createRange(this.getSelection()).cloneRange();
23241 var parent = range.parentElement();
23243 var testRange = range.duplicate();
23244 testRange.moveToElementText(parent);
23245 if (testRange.inRange(range)) {
23248 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
23251 parent = parent.parentElement;
23256 // is ancestor a text element.
23257 var ac = range.commonAncestorContainer;
23258 if (ac.nodeType == 3) {
23259 ac = ac.parentNode;
23262 var ar = ac.childNodes;
23265 var other_nodes = [];
23266 var has_other_nodes = false;
23267 for (var i=0;i<ar.length;i++) {
23268 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
23271 // fullly contained node.
23273 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
23278 // probably selected..
23279 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
23280 other_nodes.push(ar[i]);
23284 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
23289 has_other_nodes = true;
23291 if (!nodes.length && other_nodes.length) {
23292 nodes= other_nodes;
23294 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
23300 createRange: function(sel)
23302 // this has strange effects when using with
23303 // top toolbar - not sure if it's a great idea.
23304 //this.editor.contentWindow.focus();
23305 if (typeof sel != "undefined") {
23307 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
23309 return this.doc.createRange();
23312 return this.doc.createRange();
23315 getParentElement: function()
23318 this.assignDocWin();
23319 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
23321 var range = this.createRange(sel);
23324 var p = range.commonAncestorContainer;
23325 while (p.nodeType == 3) { // text node
23336 * Range intersection.. the hard stuff...
23340 * [ -- selected range --- ]
23344 * if end is before start or hits it. fail.
23345 * if start is after end or hits it fail.
23347 * if either hits (but other is outside. - then it's not
23353 // @see http://www.thismuchiknow.co.uk/?p=64.
23354 rangeIntersectsNode : function(range, node)
23356 var nodeRange = node.ownerDocument.createRange();
23358 nodeRange.selectNode(node);
23360 nodeRange.selectNodeContents(node);
23363 var rangeStartRange = range.cloneRange();
23364 rangeStartRange.collapse(true);
23366 var rangeEndRange = range.cloneRange();
23367 rangeEndRange.collapse(false);
23369 var nodeStartRange = nodeRange.cloneRange();
23370 nodeStartRange.collapse(true);
23372 var nodeEndRange = nodeRange.cloneRange();
23373 nodeEndRange.collapse(false);
23375 return rangeStartRange.compareBoundaryPoints(
23376 Range.START_TO_START, nodeEndRange) == -1 &&
23377 rangeEndRange.compareBoundaryPoints(
23378 Range.START_TO_START, nodeStartRange) == 1;
23382 rangeCompareNode : function(range, node)
23384 var nodeRange = node.ownerDocument.createRange();
23386 nodeRange.selectNode(node);
23388 nodeRange.selectNodeContents(node);
23392 range.collapse(true);
23394 nodeRange.collapse(true);
23396 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23397 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
23399 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23401 var nodeIsBefore = ss == 1;
23402 var nodeIsAfter = ee == -1;
23404 if (nodeIsBefore && nodeIsAfter) {
23407 if (!nodeIsBefore && nodeIsAfter) {
23408 return 1; //right trailed.
23411 if (nodeIsBefore && !nodeIsAfter) {
23412 return 2; // left trailed.
23418 // private? - in a new class?
23419 cleanUpPaste : function()
23421 // cleans up the whole document..
23422 Roo.log('cleanuppaste');
23424 this.cleanUpChildren(this.doc.body);
23425 var clean = this.cleanWordChars(this.doc.body.innerHTML);
23426 if (clean != this.doc.body.innerHTML) {
23427 this.doc.body.innerHTML = clean;
23432 cleanWordChars : function(input) {// change the chars to hex code
23433 var he = Roo.HtmlEditorCore;
23435 var output = input;
23436 Roo.each(he.swapCodes, function(sw) {
23437 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23439 output = output.replace(swapper, sw[1]);
23446 cleanUpChildren : function (n)
23448 if (!n.childNodes.length) {
23451 for (var i = n.childNodes.length-1; i > -1 ; i--) {
23452 this.cleanUpChild(n.childNodes[i]);
23459 cleanUpChild : function (node)
23462 //console.log(node);
23463 if (node.nodeName == "#text") {
23464 // clean up silly Windows -- stuff?
23467 if (node.nodeName == "#comment") {
23468 node.parentNode.removeChild(node);
23469 // clean up silly Windows -- stuff?
23472 var lcname = node.tagName.toLowerCase();
23473 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23474 // whitelist of tags..
23476 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23478 node.parentNode.removeChild(node);
23483 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23485 // spans with no attributes - just remove them..
23486 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
23487 remove_keep_children = true;
23490 // remove <a name=....> as rendering on yahoo mailer is borked with this.
23491 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23493 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23494 // remove_keep_children = true;
23497 if (remove_keep_children) {
23498 this.cleanUpChildren(node);
23499 // inserts everything just before this node...
23500 while (node.childNodes.length) {
23501 var cn = node.childNodes[0];
23502 node.removeChild(cn);
23503 node.parentNode.insertBefore(cn, node);
23505 node.parentNode.removeChild(node);
23509 if (!node.attributes || !node.attributes.length) {
23514 this.cleanUpChildren(node);
23518 function cleanAttr(n,v)
23521 if (v.match(/^\./) || v.match(/^\//)) {
23524 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23527 if (v.match(/^#/)) {
23530 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23531 node.removeAttribute(n);
23535 var cwhite = this.cwhite;
23536 var cblack = this.cblack;
23538 function cleanStyle(n,v)
23540 if (v.match(/expression/)) { //XSS?? should we even bother..
23541 node.removeAttribute(n);
23545 var parts = v.split(/;/);
23548 Roo.each(parts, function(p) {
23549 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23553 var l = p.split(':').shift().replace(/\s+/g,'');
23554 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23556 if ( cwhite.length && cblack.indexOf(l) > -1) {
23557 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23558 //node.removeAttribute(n);
23562 // only allow 'c whitelisted system attributes'
23563 if ( cwhite.length && cwhite.indexOf(l) < 0) {
23564 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23565 //node.removeAttribute(n);
23575 if (clean.length) {
23576 node.setAttribute(n, clean.join(';'));
23578 node.removeAttribute(n);
23584 for (var i = node.attributes.length-1; i > -1 ; i--) {
23585 var a = node.attributes[i];
23588 if (a.name.toLowerCase().substr(0,2)=='on') {
23589 node.removeAttribute(a.name);
23592 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23593 node.removeAttribute(a.name);
23596 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23597 cleanAttr(a.name,a.value); // fixme..
23600 if (a.name == 'style') {
23601 cleanStyle(a.name,a.value);
23604 /// clean up MS crap..
23605 // tecnically this should be a list of valid class'es..
23608 if (a.name == 'class') {
23609 if (a.value.match(/^Mso/)) {
23610 node.removeAttribute('class');
23613 if (a.value.match(/^body$/)) {
23614 node.removeAttribute('class');
23625 this.cleanUpChildren(node);
23631 * Clean up MS wordisms...
23633 cleanWord : function(node)
23636 this.cleanWord(this.doc.body);
23641 node.nodeName == 'SPAN' &&
23642 !node.hasAttributes() &&
23643 node.childNodes.length == 1 &&
23644 node.firstChild.nodeName == "#text"
23646 var textNode = node.firstChild;
23647 node.removeChild(textNode);
23648 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
23649 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
23651 node.parentNode.insertBefore(textNode, node);
23652 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
23653 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
23655 node.parentNode.removeChild(node);
23658 if (node.nodeName == "#text") {
23659 // clean up silly Windows -- stuff?
23662 if (node.nodeName == "#comment") {
23663 node.parentNode.removeChild(node);
23664 // clean up silly Windows -- stuff?
23668 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23669 node.parentNode.removeChild(node);
23672 //Roo.log(node.tagName);
23673 // remove - but keep children..
23674 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
23675 //Roo.log('-- removed');
23676 while (node.childNodes.length) {
23677 var cn = node.childNodes[0];
23678 node.removeChild(cn);
23679 node.parentNode.insertBefore(cn, node);
23680 // move node to parent - and clean it..
23681 this.cleanWord(cn);
23683 node.parentNode.removeChild(node);
23684 /// no need to iterate chidlren = it's got none..
23685 //this.iterateChildren(node, this.cleanWord);
23689 if (node.className.length) {
23691 var cn = node.className.split(/\W+/);
23693 Roo.each(cn, function(cls) {
23694 if (cls.match(/Mso[a-zA-Z]+/)) {
23699 node.className = cna.length ? cna.join(' ') : '';
23701 node.removeAttribute("class");
23705 if (node.hasAttribute("lang")) {
23706 node.removeAttribute("lang");
23709 if (node.hasAttribute("style")) {
23711 var styles = node.getAttribute("style").split(";");
23713 Roo.each(styles, function(s) {
23714 if (!s.match(/:/)) {
23717 var kv = s.split(":");
23718 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23721 // what ever is left... we allow.
23724 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23725 if (!nstyle.length) {
23726 node.removeAttribute('style');
23729 this.iterateChildren(node, this.cleanWord);
23735 * iterateChildren of a Node, calling fn each time, using this as the scole..
23736 * @param {DomNode} node node to iterate children of.
23737 * @param {Function} fn method of this class to call on each item.
23739 iterateChildren : function(node, fn)
23741 if (!node.childNodes.length) {
23744 for (var i = node.childNodes.length-1; i > -1 ; i--) {
23745 fn.call(this, node.childNodes[i])
23751 * cleanTableWidths.
23753 * Quite often pasting from word etc.. results in tables with column and widths.
23754 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23757 cleanTableWidths : function(node)
23762 this.cleanTableWidths(this.doc.body);
23767 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23770 Roo.log(node.tagName);
23771 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23772 this.iterateChildren(node, this.cleanTableWidths);
23775 if (node.hasAttribute('width')) {
23776 node.removeAttribute('width');
23780 if (node.hasAttribute("style")) {
23783 var styles = node.getAttribute("style").split(";");
23785 Roo.each(styles, function(s) {
23786 if (!s.match(/:/)) {
23789 var kv = s.split(":");
23790 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23793 // what ever is left... we allow.
23796 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23797 if (!nstyle.length) {
23798 node.removeAttribute('style');
23802 this.iterateChildren(node, this.cleanTableWidths);
23810 domToHTML : function(currentElement, depth, nopadtext) {
23812 depth = depth || 0;
23813 nopadtext = nopadtext || false;
23815 if (!currentElement) {
23816 return this.domToHTML(this.doc.body);
23819 //Roo.log(currentElement);
23821 var allText = false;
23822 var nodeName = currentElement.nodeName;
23823 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23825 if (nodeName == '#text') {
23827 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23832 if (nodeName != 'BODY') {
23835 // Prints the node tagName, such as <A>, <IMG>, etc
23838 for(i = 0; i < currentElement.attributes.length;i++) {
23840 var aname = currentElement.attributes.item(i).name;
23841 if (!currentElement.attributes.item(i).value.length) {
23844 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23847 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23856 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23859 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23864 // Traverse the tree
23866 var currentElementChild = currentElement.childNodes.item(i);
23867 var allText = true;
23868 var innerHTML = '';
23870 while (currentElementChild) {
23871 // Formatting code (indent the tree so it looks nice on the screen)
23872 var nopad = nopadtext;
23873 if (lastnode == 'SPAN') {
23877 if (currentElementChild.nodeName == '#text') {
23878 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23879 toadd = nopadtext ? toadd : toadd.trim();
23880 if (!nopad && toadd.length > 80) {
23881 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23883 innerHTML += toadd;
23886 currentElementChild = currentElement.childNodes.item(i);
23892 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23894 // Recursively traverse the tree structure of the child node
23895 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23896 lastnode = currentElementChild.nodeName;
23898 currentElementChild=currentElement.childNodes.item(i);
23904 // The remaining code is mostly for formatting the tree
23905 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23910 ret+= "</"+tagName+">";
23916 applyBlacklists : function()
23918 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23919 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23923 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23924 if (b.indexOf(tag) > -1) {
23927 this.white.push(tag);
23931 Roo.each(w, function(tag) {
23932 if (b.indexOf(tag) > -1) {
23935 if (this.white.indexOf(tag) > -1) {
23938 this.white.push(tag);
23943 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23944 if (w.indexOf(tag) > -1) {
23947 this.black.push(tag);
23951 Roo.each(b, function(tag) {
23952 if (w.indexOf(tag) > -1) {
23955 if (this.black.indexOf(tag) > -1) {
23958 this.black.push(tag);
23963 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23964 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23968 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23969 if (b.indexOf(tag) > -1) {
23972 this.cwhite.push(tag);
23976 Roo.each(w, function(tag) {
23977 if (b.indexOf(tag) > -1) {
23980 if (this.cwhite.indexOf(tag) > -1) {
23983 this.cwhite.push(tag);
23988 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23989 if (w.indexOf(tag) > -1) {
23992 this.cblack.push(tag);
23996 Roo.each(b, function(tag) {
23997 if (w.indexOf(tag) > -1) {
24000 if (this.cblack.indexOf(tag) > -1) {
24003 this.cblack.push(tag);
24008 setStylesheets : function(stylesheets)
24010 if(typeof(stylesheets) == 'string'){
24011 Roo.get(this.iframe.contentDocument.head).createChild({
24013 rel : 'stylesheet',
24022 Roo.each(stylesheets, function(s) {
24027 Roo.get(_this.iframe.contentDocument.head).createChild({
24029 rel : 'stylesheet',
24038 removeStylesheets : function()
24042 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
24047 setStyle : function(style)
24049 Roo.get(this.iframe.contentDocument.head).createChild({
24058 // hide stuff that is not compatible
24072 * @event specialkey
24076 * @cfg {String} fieldClass @hide
24079 * @cfg {String} focusClass @hide
24082 * @cfg {String} autoCreate @hide
24085 * @cfg {String} inputType @hide
24088 * @cfg {String} invalidClass @hide
24091 * @cfg {String} invalidText @hide
24094 * @cfg {String} msgFx @hide
24097 * @cfg {String} validateOnBlur @hide
24101 Roo.HtmlEditorCore.white = [
24102 'area', 'br', 'img', 'input', 'hr', 'wbr',
24104 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
24105 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
24106 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
24107 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
24108 'table', 'ul', 'xmp',
24110 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
24113 'dir', 'menu', 'ol', 'ul', 'dl',
24119 Roo.HtmlEditorCore.black = [
24120 // 'embed', 'object', // enable - backend responsiblity to clean thiese
24122 'base', 'basefont', 'bgsound', 'blink', 'body',
24123 'frame', 'frameset', 'head', 'html', 'ilayer',
24124 'iframe', 'layer', 'link', 'meta', 'object',
24125 'script', 'style' ,'title', 'xml' // clean later..
24127 Roo.HtmlEditorCore.clean = [
24128 'script', 'style', 'title', 'xml'
24130 Roo.HtmlEditorCore.remove = [
24135 Roo.HtmlEditorCore.ablack = [
24139 Roo.HtmlEditorCore.aclean = [
24140 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24144 Roo.HtmlEditorCore.pwhite= [
24145 'http', 'https', 'mailto'
24148 // white listed style attributes.
24149 Roo.HtmlEditorCore.cwhite= [
24150 // 'text-align', /// default is to allow most things..
24156 // black listed style attributes.
24157 Roo.HtmlEditorCore.cblack= [
24158 // 'font-size' -- this can be set by the project
24162 Roo.HtmlEditorCore.swapCodes =[
24181 * @class Roo.bootstrap.HtmlEditor
24182 * @extends Roo.bootstrap.TextArea
24183 * Bootstrap HtmlEditor class
24186 * Create a new HtmlEditor
24187 * @param {Object} config The config object
24190 Roo.bootstrap.HtmlEditor = function(config){
24191 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
24192 if (!this.toolbars) {
24193 this.toolbars = [];
24196 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
24199 * @event initialize
24200 * Fires when the editor is fully initialized (including the iframe)
24201 * @param {HtmlEditor} this
24206 * Fires when the editor is first receives the focus. Any insertion must wait
24207 * until after this event.
24208 * @param {HtmlEditor} this
24212 * @event beforesync
24213 * Fires before the textarea is updated with content from the editor iframe. Return false
24214 * to cancel the sync.
24215 * @param {HtmlEditor} this
24216 * @param {String} html
24220 * @event beforepush
24221 * Fires before the iframe editor is updated with content from the textarea. Return false
24222 * to cancel the push.
24223 * @param {HtmlEditor} this
24224 * @param {String} html
24229 * Fires when the textarea is updated with content from the editor iframe.
24230 * @param {HtmlEditor} this
24231 * @param {String} html
24236 * Fires when the iframe editor is updated with content from the textarea.
24237 * @param {HtmlEditor} this
24238 * @param {String} html
24242 * @event editmodechange
24243 * Fires when the editor switches edit modes
24244 * @param {HtmlEditor} this
24245 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24247 editmodechange: true,
24249 * @event editorevent
24250 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24251 * @param {HtmlEditor} this
24255 * @event firstfocus
24256 * Fires when on first focus - needed by toolbars..
24257 * @param {HtmlEditor} this
24262 * Auto save the htmlEditor value as a file into Events
24263 * @param {HtmlEditor} this
24267 * @event savedpreview
24268 * preview the saved version of htmlEditor
24269 * @param {HtmlEditor} this
24276 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
24280 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24285 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
24290 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24295 * @cfg {Number} height (in pixels)
24299 * @cfg {Number} width (in pixels)
24304 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24307 stylesheets: false,
24312 // private properties
24313 validationEvent : false,
24315 initialized : false,
24318 onFocus : Roo.emptyFn,
24320 hideMode:'offsets',
24322 tbContainer : false,
24326 toolbarContainer :function() {
24327 return this.wrap.select('.x-html-editor-tb',true).first();
24331 * Protected method that will not generally be called directly. It
24332 * is called when the editor creates its toolbar. Override this method if you need to
24333 * add custom toolbar buttons.
24334 * @param {HtmlEditor} editor
24336 createToolbar : function(){
24337 Roo.log('renewing');
24338 Roo.log("create toolbars");
24340 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
24341 this.toolbars[0].render(this.toolbarContainer());
24345 // if (!editor.toolbars || !editor.toolbars.length) {
24346 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
24349 // for (var i =0 ; i < editor.toolbars.length;i++) {
24350 // editor.toolbars[i] = Roo.factory(
24351 // typeof(editor.toolbars[i]) == 'string' ?
24352 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
24353 // Roo.bootstrap.HtmlEditor);
24354 // editor.toolbars[i].init(editor);
24360 onRender : function(ct, position)
24362 // Roo.log("Call onRender: " + this.xtype);
24364 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
24366 this.wrap = this.inputEl().wrap({
24367 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24370 this.editorcore.onRender(ct, position);
24372 if (this.resizable) {
24373 this.resizeEl = new Roo.Resizable(this.wrap, {
24377 minHeight : this.height,
24378 height: this.height,
24379 handles : this.resizable,
24382 resize : function(r, w, h) {
24383 _t.onResize(w,h); // -something
24389 this.createToolbar(this);
24392 if(!this.width && this.resizable){
24393 this.setSize(this.wrap.getSize());
24395 if (this.resizeEl) {
24396 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24397 // should trigger onReize..
24403 onResize : function(w, h)
24405 Roo.log('resize: ' +w + ',' + h );
24406 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
24410 if(this.inputEl() ){
24411 if(typeof w == 'number'){
24412 var aw = w - this.wrap.getFrameWidth('lr');
24413 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24416 if(typeof h == 'number'){
24417 var tbh = -11; // fixme it needs to tool bar size!
24418 for (var i =0; i < this.toolbars.length;i++) {
24419 // fixme - ask toolbars for heights?
24420 tbh += this.toolbars[i].el.getHeight();
24421 //if (this.toolbars[i].footer) {
24422 // tbh += this.toolbars[i].footer.el.getHeight();
24430 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24431 ah -= 5; // knock a few pixes off for look..
24432 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24436 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24437 this.editorcore.onResize(ew,eh);
24442 * Toggles the editor between standard and source edit mode.
24443 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24445 toggleSourceEdit : function(sourceEditMode)
24447 this.editorcore.toggleSourceEdit(sourceEditMode);
24449 if(this.editorcore.sourceEditMode){
24450 Roo.log('editor - showing textarea');
24453 // Roo.log(this.syncValue());
24455 this.inputEl().removeClass(['hide', 'x-hidden']);
24456 this.inputEl().dom.removeAttribute('tabIndex');
24457 this.inputEl().focus();
24459 Roo.log('editor - hiding textarea');
24461 // Roo.log(this.pushValue());
24464 this.inputEl().addClass(['hide', 'x-hidden']);
24465 this.inputEl().dom.setAttribute('tabIndex', -1);
24466 //this.deferFocus();
24469 if(this.resizable){
24470 this.setSize(this.wrap.getSize());
24473 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24476 // private (for BoxComponent)
24477 adjustSize : Roo.BoxComponent.prototype.adjustSize,
24479 // private (for BoxComponent)
24480 getResizeEl : function(){
24484 // private (for BoxComponent)
24485 getPositionEl : function(){
24490 initEvents : function(){
24491 this.originalValue = this.getValue();
24495 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24498 // markInvalid : Roo.emptyFn,
24500 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24503 // clearInvalid : Roo.emptyFn,
24505 setValue : function(v){
24506 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24507 this.editorcore.pushValue();
24512 deferFocus : function(){
24513 this.focus.defer(10, this);
24517 focus : function(){
24518 this.editorcore.focus();
24524 onDestroy : function(){
24530 for (var i =0; i < this.toolbars.length;i++) {
24531 // fixme - ask toolbars for heights?
24532 this.toolbars[i].onDestroy();
24535 this.wrap.dom.innerHTML = '';
24536 this.wrap.remove();
24541 onFirstFocus : function(){
24542 //Roo.log("onFirstFocus");
24543 this.editorcore.onFirstFocus();
24544 for (var i =0; i < this.toolbars.length;i++) {
24545 this.toolbars[i].onFirstFocus();
24551 syncValue : function()
24553 this.editorcore.syncValue();
24556 pushValue : function()
24558 this.editorcore.pushValue();
24562 // hide stuff that is not compatible
24576 * @event specialkey
24580 * @cfg {String} fieldClass @hide
24583 * @cfg {String} focusClass @hide
24586 * @cfg {String} autoCreate @hide
24589 * @cfg {String} inputType @hide
24593 * @cfg {String} invalidText @hide
24596 * @cfg {String} msgFx @hide
24599 * @cfg {String} validateOnBlur @hide
24608 Roo.namespace('Roo.bootstrap.htmleditor');
24610 * @class Roo.bootstrap.HtmlEditorToolbar1
24616 new Roo.bootstrap.HtmlEditor({
24619 new Roo.bootstrap.HtmlEditorToolbar1({
24620 disable : { fonts: 1 , format: 1, ..., ... , ...],
24626 * @cfg {Object} disable List of elements to disable..
24627 * @cfg {Array} btns List of additional buttons.
24631 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24634 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24637 Roo.apply(this, config);
24639 // default disabled, based on 'good practice'..
24640 this.disable = this.disable || {};
24641 Roo.applyIf(this.disable, {
24644 specialElements : true
24646 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24648 this.editor = config.editor;
24649 this.editorcore = config.editor.editorcore;
24651 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24653 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24654 // dont call parent... till later.
24656 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
24661 editorcore : false,
24666 "h1","h2","h3","h4","h5","h6",
24668 "abbr", "acronym", "address", "cite", "samp", "var",
24672 onRender : function(ct, position)
24674 // Roo.log("Call onRender: " + this.xtype);
24676 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24678 this.el.dom.style.marginBottom = '0';
24680 var editorcore = this.editorcore;
24681 var editor= this.editor;
24684 var btn = function(id,cmd , toggle, handler, html){
24686 var event = toggle ? 'toggle' : 'click';
24691 xns: Roo.bootstrap,
24695 enableToggle:toggle !== false,
24697 pressed : toggle ? false : null,
24700 a.listeners[toggle ? 'toggle' : 'click'] = function() {
24701 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
24707 // var cb_box = function...
24712 xns: Roo.bootstrap,
24717 xns: Roo.bootstrap,
24721 Roo.each(this.formats, function(f) {
24722 style.menu.items.push({
24724 xns: Roo.bootstrap,
24725 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24730 editorcore.insertTag(this.tagname);
24737 children.push(style);
24739 btn('bold',false,true);
24740 btn('italic',false,true);
24741 btn('align-left', 'justifyleft',true);
24742 btn('align-center', 'justifycenter',true);
24743 btn('align-right' , 'justifyright',true);
24744 btn('link', false, false, function(btn) {
24745 //Roo.log("create link?");
24746 var url = prompt(this.createLinkText, this.defaultLinkValue);
24747 if(url && url != 'http:/'+'/'){
24748 this.editorcore.relayCmd('createlink', url);
24751 btn('list','insertunorderedlist',true);
24752 btn('pencil', false,true, function(btn){
24754 this.toggleSourceEdit(btn.pressed);
24757 if (this.editor.btns.length > 0) {
24758 for (var i = 0; i<this.editor.btns.length; i++) {
24759 children.push(this.editor.btns[i]);
24767 xns: Roo.bootstrap,
24772 xns: Roo.bootstrap,
24777 cog.menu.items.push({
24779 xns: Roo.bootstrap,
24780 html : Clean styles,
24785 editorcore.insertTag(this.tagname);
24794 this.xtype = 'NavSimplebar';
24796 for(var i=0;i< children.length;i++) {
24798 this.buttons.add(this.addxtypeChild(children[i]));
24802 editor.on('editorevent', this.updateToolbar, this);
24804 onBtnClick : function(id)
24806 this.editorcore.relayCmd(id);
24807 this.editorcore.focus();
24811 * Protected method that will not generally be called directly. It triggers
24812 * a toolbar update by reading the markup state of the current selection in the editor.
24814 updateToolbar: function(){
24816 if(!this.editorcore.activated){
24817 this.editor.onFirstFocus(); // is this neeed?
24821 var btns = this.buttons;
24822 var doc = this.editorcore.doc;
24823 btns.get('bold').setActive(doc.queryCommandState('bold'));
24824 btns.get('italic').setActive(doc.queryCommandState('italic'));
24825 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24827 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24828 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24829 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24831 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24832 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24835 var ans = this.editorcore.getAllAncestors();
24836 if (this.formatCombo) {
24839 var store = this.formatCombo.store;
24840 this.formatCombo.setValue("");
24841 for (var i =0; i < ans.length;i++) {
24842 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24844 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24852 // hides menus... - so this cant be on a menu...
24853 Roo.bootstrap.MenuMgr.hideAll();
24855 Roo.bootstrap.MenuMgr.hideAll();
24856 //this.editorsyncValue();
24858 onFirstFocus: function() {
24859 this.buttons.each(function(item){
24863 toggleSourceEdit : function(sourceEditMode){
24866 if(sourceEditMode){
24867 Roo.log("disabling buttons");
24868 this.buttons.each( function(item){
24869 if(item.cmd != 'pencil'){
24875 Roo.log("enabling buttons");
24876 if(this.editorcore.initialized){
24877 this.buttons.each( function(item){
24883 Roo.log("calling toggole on editor");
24884 // tell the editor that it's been pressed..
24885 this.editor.toggleSourceEdit(sourceEditMode);
24895 * @class Roo.bootstrap.Table.AbstractSelectionModel
24896 * @extends Roo.util.Observable
24897 * Abstract base class for grid SelectionModels. It provides the interface that should be
24898 * implemented by descendant classes. This class should not be directly instantiated.
24901 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24902 this.locked = false;
24903 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24907 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24908 /** @ignore Called by the grid automatically. Do not call directly. */
24909 init : function(grid){
24915 * Locks the selections.
24918 this.locked = true;
24922 * Unlocks the selections.
24924 unlock : function(){
24925 this.locked = false;
24929 * Returns true if the selections are locked.
24930 * @return {Boolean}
24932 isLocked : function(){
24933 return this.locked;
24937 initEvents : function ()
24943 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24944 * @class Roo.bootstrap.Table.RowSelectionModel
24945 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24946 * It supports multiple selections and keyboard selection/navigation.
24948 * @param {Object} config
24951 Roo.bootstrap.Table.RowSelectionModel = function(config){
24952 Roo.apply(this, config);
24953 this.selections = new Roo.util.MixedCollection(false, function(o){
24958 this.lastActive = false;
24962 * @event selectionchange
24963 * Fires when the selection changes
24964 * @param {SelectionModel} this
24966 "selectionchange" : true,
24968 * @event afterselectionchange
24969 * Fires after the selection changes (eg. by key press or clicking)
24970 * @param {SelectionModel} this
24972 "afterselectionchange" : true,
24974 * @event beforerowselect
24975 * Fires when a row is selected being selected, return false to cancel.
24976 * @param {SelectionModel} this
24977 * @param {Number} rowIndex The selected index
24978 * @param {Boolean} keepExisting False if other selections will be cleared
24980 "beforerowselect" : true,
24983 * Fires when a row is selected.
24984 * @param {SelectionModel} this
24985 * @param {Number} rowIndex The selected index
24986 * @param {Roo.data.Record} r The record
24988 "rowselect" : true,
24990 * @event rowdeselect
24991 * Fires when a row is deselected.
24992 * @param {SelectionModel} this
24993 * @param {Number} rowIndex The selected index
24995 "rowdeselect" : true
24997 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24998 this.locked = false;
25001 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
25003 * @cfg {Boolean} singleSelect
25004 * True to allow selection of only one row at a time (defaults to false)
25006 singleSelect : false,
25009 initEvents : function()
25012 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
25013 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
25014 //}else{ // allow click to work like normal
25015 // this.grid.on("rowclick", this.handleDragableRowClick, this);
25017 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
25018 this.grid.on("rowclick", this.handleMouseDown, this);
25020 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
25021 "up" : function(e){
25023 this.selectPrevious(e.shiftKey);
25024 }else if(this.last !== false && this.lastActive !== false){
25025 var last = this.last;
25026 this.selectRange(this.last, this.lastActive-1);
25027 this.grid.getView().focusRow(this.lastActive);
25028 if(last !== false){
25032 this.selectFirstRow();
25034 this.fireEvent("afterselectionchange", this);
25036 "down" : function(e){
25038 this.selectNext(e.shiftKey);
25039 }else if(this.last !== false && this.lastActive !== false){
25040 var last = this.last;
25041 this.selectRange(this.last, this.lastActive+1);
25042 this.grid.getView().focusRow(this.lastActive);
25043 if(last !== false){
25047 this.selectFirstRow();
25049 this.fireEvent("afterselectionchange", this);
25053 this.grid.store.on('load', function(){
25054 this.selections.clear();
25057 var view = this.grid.view;
25058 view.on("refresh", this.onRefresh, this);
25059 view.on("rowupdated", this.onRowUpdated, this);
25060 view.on("rowremoved", this.onRemove, this);
25065 onRefresh : function()
25067 var ds = this.grid.store, i, v = this.grid.view;
25068 var s = this.selections;
25069 s.each(function(r){
25070 if((i = ds.indexOfId(r.id)) != -1){
25079 onRemove : function(v, index, r){
25080 this.selections.remove(r);
25084 onRowUpdated : function(v, index, r){
25085 if(this.isSelected(r)){
25086 v.onRowSelect(index);
25092 * @param {Array} records The records to select
25093 * @param {Boolean} keepExisting (optional) True to keep existing selections
25095 selectRecords : function(records, keepExisting)
25098 this.clearSelections();
25100 var ds = this.grid.store;
25101 for(var i = 0, len = records.length; i < len; i++){
25102 this.selectRow(ds.indexOf(records[i]), true);
25107 * Gets the number of selected rows.
25110 getCount : function(){
25111 return this.selections.length;
25115 * Selects the first row in the grid.
25117 selectFirstRow : function(){
25122 * Select the last row.
25123 * @param {Boolean} keepExisting (optional) True to keep existing selections
25125 selectLastRow : function(keepExisting){
25126 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
25127 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
25131 * Selects the row immediately following the last selected row.
25132 * @param {Boolean} keepExisting (optional) True to keep existing selections
25134 selectNext : function(keepExisting)
25136 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
25137 this.selectRow(this.last+1, keepExisting);
25138 this.grid.getView().focusRow(this.last);
25143 * Selects the row that precedes the last selected row.
25144 * @param {Boolean} keepExisting (optional) True to keep existing selections
25146 selectPrevious : function(keepExisting){
25148 this.selectRow(this.last-1, keepExisting);
25149 this.grid.getView().focusRow(this.last);
25154 * Returns the selected records
25155 * @return {Array} Array of selected records
25157 getSelections : function(){
25158 return [].concat(this.selections.items);
25162 * Returns the first selected record.
25165 getSelected : function(){
25166 return this.selections.itemAt(0);
25171 * Clears all selections.
25173 clearSelections : function(fast)
25179 var ds = this.grid.store;
25180 var s = this.selections;
25181 s.each(function(r){
25182 this.deselectRow(ds.indexOfId(r.id));
25186 this.selections.clear();
25193 * Selects all rows.
25195 selectAll : function(){
25199 this.selections.clear();
25200 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
25201 this.selectRow(i, true);
25206 * Returns True if there is a selection.
25207 * @return {Boolean}
25209 hasSelection : function(){
25210 return this.selections.length > 0;
25214 * Returns True if the specified row is selected.
25215 * @param {Number/Record} record The record or index of the record to check
25216 * @return {Boolean}
25218 isSelected : function(index){
25219 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
25220 return (r && this.selections.key(r.id) ? true : false);
25224 * Returns True if the specified record id is selected.
25225 * @param {String} id The id of record to check
25226 * @return {Boolean}
25228 isIdSelected : function(id){
25229 return (this.selections.key(id) ? true : false);
25234 handleMouseDBClick : function(e, t){
25238 handleMouseDown : function(e, t)
25240 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
25241 if(this.isLocked() || rowIndex < 0 ){
25244 if(e.shiftKey && this.last !== false){
25245 var last = this.last;
25246 this.selectRange(last, rowIndex, e.ctrlKey);
25247 this.last = last; // reset the last
25251 var isSelected = this.isSelected(rowIndex);
25252 //Roo.log("select row:" + rowIndex);
25254 this.deselectRow(rowIndex);
25256 this.selectRow(rowIndex, true);
25260 if(e.button !== 0 && isSelected){
25261 alert('rowIndex 2: ' + rowIndex);
25262 view.focusRow(rowIndex);
25263 }else if(e.ctrlKey && isSelected){
25264 this.deselectRow(rowIndex);
25265 }else if(!isSelected){
25266 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
25267 view.focusRow(rowIndex);
25271 this.fireEvent("afterselectionchange", this);
25274 handleDragableRowClick : function(grid, rowIndex, e)
25276 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
25277 this.selectRow(rowIndex, false);
25278 grid.view.focusRow(rowIndex);
25279 this.fireEvent("afterselectionchange", this);
25284 * Selects multiple rows.
25285 * @param {Array} rows Array of the indexes of the row to select
25286 * @param {Boolean} keepExisting (optional) True to keep existing selections
25288 selectRows : function(rows, keepExisting){
25290 this.clearSelections();
25292 for(var i = 0, len = rows.length; i < len; i++){
25293 this.selectRow(rows[i], true);
25298 * Selects a range of rows. All rows in between startRow and endRow are also selected.
25299 * @param {Number} startRow The index of the first row in the range
25300 * @param {Number} endRow The index of the last row in the range
25301 * @param {Boolean} keepExisting (optional) True to retain existing selections
25303 selectRange : function(startRow, endRow, keepExisting){
25308 this.clearSelections();
25310 if(startRow <= endRow){
25311 for(var i = startRow; i <= endRow; i++){
25312 this.selectRow(i, true);
25315 for(var i = startRow; i >= endRow; i--){
25316 this.selectRow(i, true);
25322 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
25323 * @param {Number} startRow The index of the first row in the range
25324 * @param {Number} endRow The index of the last row in the range
25326 deselectRange : function(startRow, endRow, preventViewNotify){
25330 for(var i = startRow; i <= endRow; i++){
25331 this.deselectRow(i, preventViewNotify);
25337 * @param {Number} row The index of the row to select
25338 * @param {Boolean} keepExisting (optional) True to keep existing selections
25340 selectRow : function(index, keepExisting, preventViewNotify)
25342 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
25345 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
25346 if(!keepExisting || this.singleSelect){
25347 this.clearSelections();
25350 var r = this.grid.store.getAt(index);
25351 //console.log('selectRow - record id :' + r.id);
25353 this.selections.add(r);
25354 this.last = this.lastActive = index;
25355 if(!preventViewNotify){
25356 var proxy = new Roo.Element(
25357 this.grid.getRowDom(index)
25359 proxy.addClass('bg-info info');
25361 this.fireEvent("rowselect", this, index, r);
25362 this.fireEvent("selectionchange", this);
25368 * @param {Number} row The index of the row to deselect
25370 deselectRow : function(index, preventViewNotify)
25375 if(this.last == index){
25378 if(this.lastActive == index){
25379 this.lastActive = false;
25382 var r = this.grid.store.getAt(index);
25387 this.selections.remove(r);
25388 //.console.log('deselectRow - record id :' + r.id);
25389 if(!preventViewNotify){
25391 var proxy = new Roo.Element(
25392 this.grid.getRowDom(index)
25394 proxy.removeClass('bg-info info');
25396 this.fireEvent("rowdeselect", this, index);
25397 this.fireEvent("selectionchange", this);
25401 restoreLast : function(){
25403 this.last = this._last;
25408 acceptsNav : function(row, col, cm){
25409 return !cm.isHidden(col) && cm.isCellEditable(col, row);
25413 onEditorKey : function(field, e){
25414 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25419 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25421 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25423 }else if(k == e.ENTER && !e.ctrlKey){
25427 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25429 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25431 }else if(k == e.ESC){
25435 g.startEditing(newCell[0], newCell[1]);
25441 * Ext JS Library 1.1.1
25442 * Copyright(c) 2006-2007, Ext JS, LLC.
25444 * Originally Released Under LGPL - original licence link has changed is not relivant.
25447 * <script type="text/javascript">
25451 * @class Roo.bootstrap.PagingToolbar
25452 * @extends Roo.bootstrap.NavSimplebar
25453 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25455 * Create a new PagingToolbar
25456 * @param {Object} config The config object
25457 * @param {Roo.data.Store} store
25459 Roo.bootstrap.PagingToolbar = function(config)
25461 // old args format still supported... - xtype is prefered..
25462 // created from xtype...
25464 this.ds = config.dataSource;
25466 if (config.store && !this.ds) {
25467 this.store= Roo.factory(config.store, Roo.data);
25468 this.ds = this.store;
25469 this.ds.xmodule = this.xmodule || false;
25472 this.toolbarItems = [];
25473 if (config.items) {
25474 this.toolbarItems = config.items;
25477 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25482 this.bind(this.ds);
25485 if (Roo.bootstrap.version == 4) {
25486 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25488 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25493 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25495 * @cfg {Roo.data.Store} dataSource
25496 * The underlying data store providing the paged data
25499 * @cfg {String/HTMLElement/Element} container
25500 * container The id or element that will contain the toolbar
25503 * @cfg {Boolean} displayInfo
25504 * True to display the displayMsg (defaults to false)
25507 * @cfg {Number} pageSize
25508 * The number of records to display per page (defaults to 20)
25512 * @cfg {String} displayMsg
25513 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25515 displayMsg : 'Displaying {0} - {1} of {2}',
25517 * @cfg {String} emptyMsg
25518 * The message to display when no records are found (defaults to "No data to display")
25520 emptyMsg : 'No data to display',
25522 * Customizable piece of the default paging text (defaults to "Page")
25525 beforePageText : "Page",
25527 * Customizable piece of the default paging text (defaults to "of %0")
25530 afterPageText : "of {0}",
25532 * Customizable piece of the default paging text (defaults to "First Page")
25535 firstText : "First Page",
25537 * Customizable piece of the default paging text (defaults to "Previous Page")
25540 prevText : "Previous Page",
25542 * Customizable piece of the default paging text (defaults to "Next Page")
25545 nextText : "Next Page",
25547 * Customizable piece of the default paging text (defaults to "Last Page")
25550 lastText : "Last Page",
25552 * Customizable piece of the default paging text (defaults to "Refresh")
25555 refreshText : "Refresh",
25559 onRender : function(ct, position)
25561 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25562 this.navgroup.parentId = this.id;
25563 this.navgroup.onRender(this.el, null);
25564 // add the buttons to the navgroup
25566 if(this.displayInfo){
25567 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25568 this.displayEl = this.el.select('.x-paging-info', true).first();
25569 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25570 // this.displayEl = navel.el.select('span',true).first();
25576 Roo.each(_this.buttons, function(e){ // this might need to use render????
25577 Roo.factory(e).render(_this.el);
25581 Roo.each(_this.toolbarItems, function(e) {
25582 _this.navgroup.addItem(e);
25586 this.first = this.navgroup.addItem({
25587 tooltip: this.firstText,
25588 cls: "prev btn-outline-secondary",
25589 html : ' <i class="fa fa-step-backward"></i>',
25591 preventDefault: true,
25592 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25595 this.prev = this.navgroup.addItem({
25596 tooltip: this.prevText,
25597 cls: "prev btn-outline-secondary",
25598 html : ' <i class="fa fa-backward"></i>',
25600 preventDefault: true,
25601 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
25603 //this.addSeparator();
25606 var field = this.navgroup.addItem( {
25608 cls : 'x-paging-position btn-outline-secondary',
25610 html : this.beforePageText +
25611 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25612 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
25615 this.field = field.el.select('input', true).first();
25616 this.field.on("keydown", this.onPagingKeydown, this);
25617 this.field.on("focus", function(){this.dom.select();});
25620 this.afterTextEl = field.el.select('.x-paging-after',true).first();
25621 //this.field.setHeight(18);
25622 //this.addSeparator();
25623 this.next = this.navgroup.addItem({
25624 tooltip: this.nextText,
25625 cls: "next btn-outline-secondary",
25626 html : ' <i class="fa fa-forward"></i>',
25628 preventDefault: true,
25629 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
25631 this.last = this.navgroup.addItem({
25632 tooltip: this.lastText,
25633 html : ' <i class="fa fa-step-forward"></i>',
25634 cls: "next btn-outline-secondary",
25636 preventDefault: true,
25637 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
25639 //this.addSeparator();
25640 this.loading = this.navgroup.addItem({
25641 tooltip: this.refreshText,
25642 cls: "btn-outline-secondary",
25643 html : ' <i class="fa fa-refresh"></i>',
25644 preventDefault: true,
25645 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25651 updateInfo : function(){
25652 if(this.displayEl){
25653 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25654 var msg = count == 0 ?
25658 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
25660 this.displayEl.update(msg);
25665 onLoad : function(ds, r, o)
25667 this.cursor = o.params.start ? o.params.start : 0;
25669 var d = this.getPageData(),
25674 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25675 this.field.dom.value = ap;
25676 this.first.setDisabled(ap == 1);
25677 this.prev.setDisabled(ap == 1);
25678 this.next.setDisabled(ap == ps);
25679 this.last.setDisabled(ap == ps);
25680 this.loading.enable();
25685 getPageData : function(){
25686 var total = this.ds.getTotalCount();
25689 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25690 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25695 onLoadError : function(){
25696 this.loading.enable();
25700 onPagingKeydown : function(e){
25701 var k = e.getKey();
25702 var d = this.getPageData();
25704 var v = this.field.dom.value, pageNum;
25705 if(!v || isNaN(pageNum = parseInt(v, 10))){
25706 this.field.dom.value = d.activePage;
25709 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25710 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25713 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))
25715 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25716 this.field.dom.value = pageNum;
25717 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25720 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25722 var v = this.field.dom.value, pageNum;
25723 var increment = (e.shiftKey) ? 10 : 1;
25724 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25727 if(!v || isNaN(pageNum = parseInt(v, 10))) {
25728 this.field.dom.value = d.activePage;
25731 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25733 this.field.dom.value = parseInt(v, 10) + increment;
25734 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25735 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25742 beforeLoad : function(){
25744 this.loading.disable();
25749 onClick : function(which){
25758 ds.load({params:{start: 0, limit: this.pageSize}});
25761 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25764 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25767 var total = ds.getTotalCount();
25768 var extra = total % this.pageSize;
25769 var lastStart = extra ? (total - extra) : total-this.pageSize;
25770 ds.load({params:{start: lastStart, limit: this.pageSize}});
25773 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25779 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25780 * @param {Roo.data.Store} store The data store to unbind
25782 unbind : function(ds){
25783 ds.un("beforeload", this.beforeLoad, this);
25784 ds.un("load", this.onLoad, this);
25785 ds.un("loadexception", this.onLoadError, this);
25786 ds.un("remove", this.updateInfo, this);
25787 ds.un("add", this.updateInfo, this);
25788 this.ds = undefined;
25792 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25793 * @param {Roo.data.Store} store The data store to bind
25795 bind : function(ds){
25796 ds.on("beforeload", this.beforeLoad, this);
25797 ds.on("load", this.onLoad, this);
25798 ds.on("loadexception", this.onLoadError, this);
25799 ds.on("remove", this.updateInfo, this);
25800 ds.on("add", this.updateInfo, this);
25811 * @class Roo.bootstrap.MessageBar
25812 * @extends Roo.bootstrap.Component
25813 * Bootstrap MessageBar class
25814 * @cfg {String} html contents of the MessageBar
25815 * @cfg {String} weight (info | success | warning | danger) default info
25816 * @cfg {String} beforeClass insert the bar before the given class
25817 * @cfg {Boolean} closable (true | false) default false
25818 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25821 * Create a new Element
25822 * @param {Object} config The config object
25825 Roo.bootstrap.MessageBar = function(config){
25826 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25829 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25835 beforeClass: 'bootstrap-sticky-wrap',
25837 getAutoCreate : function(){
25841 cls: 'alert alert-dismissable alert-' + this.weight,
25846 html: this.html || ''
25852 cfg.cls += ' alert-messages-fixed';
25866 onRender : function(ct, position)
25868 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25871 var cfg = Roo.apply({}, this.getAutoCreate());
25875 cfg.cls += ' ' + this.cls;
25878 cfg.style = this.style;
25880 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25882 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25885 this.el.select('>button.close').on('click', this.hide, this);
25891 if (!this.rendered) {
25897 this.fireEvent('show', this);
25903 if (!this.rendered) {
25909 this.fireEvent('hide', this);
25912 update : function()
25914 // var e = this.el.dom.firstChild;
25916 // if(this.closable){
25917 // e = e.nextSibling;
25920 // e.data = this.html || '';
25922 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25938 * @class Roo.bootstrap.Graph
25939 * @extends Roo.bootstrap.Component
25940 * Bootstrap Graph class
25944 @cfg {String} graphtype bar | vbar | pie
25945 @cfg {number} g_x coodinator | centre x (pie)
25946 @cfg {number} g_y coodinator | centre y (pie)
25947 @cfg {number} g_r radius (pie)
25948 @cfg {number} g_height height of the chart (respected by all elements in the set)
25949 @cfg {number} g_width width of the chart (respected by all elements in the set)
25950 @cfg {Object} title The title of the chart
25953 -opts (object) options for the chart
25955 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25956 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25958 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.
25959 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25961 o stretch (boolean)
25963 -opts (object) options for the pie
25966 o startAngle (number)
25967 o endAngle (number)
25971 * Create a new Input
25972 * @param {Object} config The config object
25975 Roo.bootstrap.Graph = function(config){
25976 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25982 * The img click event for the img.
25983 * @param {Roo.EventObject} e
25989 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
26000 //g_colors: this.colors,
26007 getAutoCreate : function(){
26018 onRender : function(ct,position){
26021 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
26023 if (typeof(Raphael) == 'undefined') {
26024 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
26028 this.raphael = Raphael(this.el.dom);
26030 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26031 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26032 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26033 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
26035 r.text(160, 10, "Single Series Chart").attr(txtattr);
26036 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
26037 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
26038 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
26040 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
26041 r.barchart(330, 10, 300, 220, data1);
26042 r.barchart(10, 250, 300, 220, data2, {stacked: true});
26043 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
26046 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
26047 // r.barchart(30, 30, 560, 250, xdata, {
26048 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
26049 // axis : "0 0 1 1",
26050 // axisxlabels : xdata
26051 // //yvalues : cols,
26054 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
26056 // this.load(null,xdata,{
26057 // axis : "0 0 1 1",
26058 // axisxlabels : xdata
26063 load : function(graphtype,xdata,opts)
26065 this.raphael.clear();
26067 graphtype = this.graphtype;
26072 var r = this.raphael,
26073 fin = function () {
26074 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
26076 fout = function () {
26077 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
26079 pfin = function() {
26080 this.sector.stop();
26081 this.sector.scale(1.1, 1.1, this.cx, this.cy);
26084 this.label[0].stop();
26085 this.label[0].attr({ r: 7.5 });
26086 this.label[1].attr({ "font-weight": 800 });
26089 pfout = function() {
26090 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
26093 this.label[0].animate({ r: 5 }, 500, "bounce");
26094 this.label[1].attr({ "font-weight": 400 });
26100 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26103 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26106 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
26107 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
26109 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
26116 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
26121 setTitle: function(o)
26126 initEvents: function() {
26129 this.el.on('click', this.onClick, this);
26133 onClick : function(e)
26135 Roo.log('img onclick');
26136 this.fireEvent('click', this, e);
26148 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26151 * @class Roo.bootstrap.dash.NumberBox
26152 * @extends Roo.bootstrap.Component
26153 * Bootstrap NumberBox class
26154 * @cfg {String} headline Box headline
26155 * @cfg {String} content Box content
26156 * @cfg {String} icon Box icon
26157 * @cfg {String} footer Footer text
26158 * @cfg {String} fhref Footer href
26161 * Create a new NumberBox
26162 * @param {Object} config The config object
26166 Roo.bootstrap.dash.NumberBox = function(config){
26167 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
26171 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
26180 getAutoCreate : function(){
26184 cls : 'small-box ',
26192 cls : 'roo-headline',
26193 html : this.headline
26197 cls : 'roo-content',
26198 html : this.content
26212 cls : 'ion ' + this.icon
26221 cls : 'small-box-footer',
26222 href : this.fhref || '#',
26226 cfg.cn.push(footer);
26233 onRender : function(ct,position){
26234 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
26241 setHeadline: function (value)
26243 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
26246 setFooter: function (value, href)
26248 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
26251 this.el.select('a.small-box-footer',true).first().attr('href', href);
26256 setContent: function (value)
26258 this.el.select('.roo-content',true).first().dom.innerHTML = value;
26261 initEvents: function()
26275 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26278 * @class Roo.bootstrap.dash.TabBox
26279 * @extends Roo.bootstrap.Component
26280 * Bootstrap TabBox class
26281 * @cfg {String} title Title of the TabBox
26282 * @cfg {String} icon Icon of the TabBox
26283 * @cfg {Boolean} showtabs (true|false) show the tabs default true
26284 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
26287 * Create a new TabBox
26288 * @param {Object} config The config object
26292 Roo.bootstrap.dash.TabBox = function(config){
26293 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
26298 * When a pane is added
26299 * @param {Roo.bootstrap.dash.TabPane} pane
26303 * @event activatepane
26304 * When a pane is activated
26305 * @param {Roo.bootstrap.dash.TabPane} pane
26307 "activatepane" : true
26315 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
26320 tabScrollable : false,
26322 getChildContainer : function()
26324 return this.el.select('.tab-content', true).first();
26327 getAutoCreate : function(){
26331 cls: 'pull-left header',
26339 cls: 'fa ' + this.icon
26345 cls: 'nav nav-tabs pull-right',
26351 if(this.tabScrollable){
26358 cls: 'nav nav-tabs pull-right',
26369 cls: 'nav-tabs-custom',
26374 cls: 'tab-content no-padding',
26382 initEvents : function()
26384 //Roo.log('add add pane handler');
26385 this.on('addpane', this.onAddPane, this);
26388 * Updates the box title
26389 * @param {String} html to set the title to.
26391 setTitle : function(value)
26393 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
26395 onAddPane : function(pane)
26397 this.panes.push(pane);
26398 //Roo.log('addpane');
26400 // tabs are rendere left to right..
26401 if(!this.showtabs){
26405 var ctr = this.el.select('.nav-tabs', true).first();
26408 var existing = ctr.select('.nav-tab',true);
26409 var qty = existing.getCount();;
26412 var tab = ctr.createChild({
26414 cls : 'nav-tab' + (qty ? '' : ' active'),
26422 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26425 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26427 pane.el.addClass('active');
26432 onTabClick : function(ev,un,ob,pane)
26434 //Roo.log('tab - prev default');
26435 ev.preventDefault();
26438 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26439 pane.tab.addClass('active');
26440 //Roo.log(pane.title);
26441 this.getChildContainer().select('.tab-pane',true).removeClass('active');
26442 // technically we should have a deactivate event.. but maybe add later.
26443 // and it should not de-activate the selected tab...
26444 this.fireEvent('activatepane', pane);
26445 pane.el.addClass('active');
26446 pane.fireEvent('activate');
26451 getActivePane : function()
26454 Roo.each(this.panes, function(p) {
26455 if(p.el.hasClass('active')){
26476 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26478 * @class Roo.bootstrap.TabPane
26479 * @extends Roo.bootstrap.Component
26480 * Bootstrap TabPane class
26481 * @cfg {Boolean} active (false | true) Default false
26482 * @cfg {String} title title of panel
26486 * Create a new TabPane
26487 * @param {Object} config The config object
26490 Roo.bootstrap.dash.TabPane = function(config){
26491 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26497 * When a pane is activated
26498 * @param {Roo.bootstrap.dash.TabPane} pane
26505 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
26510 // the tabBox that this is attached to.
26513 getAutoCreate : function()
26521 cfg.cls += ' active';
26526 initEvents : function()
26528 //Roo.log('trigger add pane handler');
26529 this.parent().fireEvent('addpane', this)
26533 * Updates the tab title
26534 * @param {String} html to set the title to.
26536 setTitle: function(str)
26542 this.tab.select('a', true).first().dom.innerHTML = str;
26559 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26562 * @class Roo.bootstrap.menu.Menu
26563 * @extends Roo.bootstrap.Component
26564 * Bootstrap Menu class - container for Menu
26565 * @cfg {String} html Text of the menu
26566 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26567 * @cfg {String} icon Font awesome icon
26568 * @cfg {String} pos Menu align to (top | bottom) default bottom
26572 * Create a new Menu
26573 * @param {Object} config The config object
26577 Roo.bootstrap.menu.Menu = function(config){
26578 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26582 * @event beforeshow
26583 * Fires before this menu is displayed
26584 * @param {Roo.bootstrap.menu.Menu} this
26588 * @event beforehide
26589 * Fires before this menu is hidden
26590 * @param {Roo.bootstrap.menu.Menu} this
26595 * Fires after this menu is displayed
26596 * @param {Roo.bootstrap.menu.Menu} this
26601 * Fires after this menu is hidden
26602 * @param {Roo.bootstrap.menu.Menu} this
26607 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26608 * @param {Roo.bootstrap.menu.Menu} this
26609 * @param {Roo.EventObject} e
26616 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
26620 weight : 'default',
26625 getChildContainer : function() {
26626 if(this.isSubMenu){
26630 return this.el.select('ul.dropdown-menu', true).first();
26633 getAutoCreate : function()
26638 cls : 'roo-menu-text',
26646 cls : 'fa ' + this.icon
26657 cls : 'dropdown-button btn btn-' + this.weight,
26662 cls : 'dropdown-toggle btn btn-' + this.weight,
26672 cls : 'dropdown-menu'
26678 if(this.pos == 'top'){
26679 cfg.cls += ' dropup';
26682 if(this.isSubMenu){
26685 cls : 'dropdown-menu'
26692 onRender : function(ct, position)
26694 this.isSubMenu = ct.hasClass('dropdown-submenu');
26696 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26699 initEvents : function()
26701 if(this.isSubMenu){
26705 this.hidden = true;
26707 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26708 this.triggerEl.on('click', this.onTriggerPress, this);
26710 this.buttonEl = this.el.select('button.dropdown-button', true).first();
26711 this.buttonEl.on('click', this.onClick, this);
26717 if(this.isSubMenu){
26721 return this.el.select('ul.dropdown-menu', true).first();
26724 onClick : function(e)
26726 this.fireEvent("click", this, e);
26729 onTriggerPress : function(e)
26731 if (this.isVisible()) {
26738 isVisible : function(){
26739 return !this.hidden;
26744 this.fireEvent("beforeshow", this);
26746 this.hidden = false;
26747 this.el.addClass('open');
26749 Roo.get(document).on("mouseup", this.onMouseUp, this);
26751 this.fireEvent("show", this);
26758 this.fireEvent("beforehide", this);
26760 this.hidden = true;
26761 this.el.removeClass('open');
26763 Roo.get(document).un("mouseup", this.onMouseUp);
26765 this.fireEvent("hide", this);
26768 onMouseUp : function()
26782 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26785 * @class Roo.bootstrap.menu.Item
26786 * @extends Roo.bootstrap.Component
26787 * Bootstrap MenuItem class
26788 * @cfg {Boolean} submenu (true | false) default false
26789 * @cfg {String} html text of the item
26790 * @cfg {String} href the link
26791 * @cfg {Boolean} disable (true | false) default false
26792 * @cfg {Boolean} preventDefault (true | false) default true
26793 * @cfg {String} icon Font awesome icon
26794 * @cfg {String} pos Submenu align to (left | right) default right
26798 * Create a new Item
26799 * @param {Object} config The config object
26803 Roo.bootstrap.menu.Item = function(config){
26804 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26808 * Fires when the mouse is hovering over this menu
26809 * @param {Roo.bootstrap.menu.Item} this
26810 * @param {Roo.EventObject} e
26815 * Fires when the mouse exits this menu
26816 * @param {Roo.bootstrap.menu.Item} this
26817 * @param {Roo.EventObject} e
26823 * The raw click event for the entire grid.
26824 * @param {Roo.EventObject} e
26830 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26835 preventDefault: true,
26840 getAutoCreate : function()
26845 cls : 'roo-menu-item-text',
26853 cls : 'fa ' + this.icon
26862 href : this.href || '#',
26869 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26873 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26875 if(this.pos == 'left'){
26876 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26883 initEvents : function()
26885 this.el.on('mouseover', this.onMouseOver, this);
26886 this.el.on('mouseout', this.onMouseOut, this);
26888 this.el.select('a', true).first().on('click', this.onClick, this);
26892 onClick : function(e)
26894 if(this.preventDefault){
26895 e.preventDefault();
26898 this.fireEvent("click", this, e);
26901 onMouseOver : function(e)
26903 if(this.submenu && this.pos == 'left'){
26904 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26907 this.fireEvent("mouseover", this, e);
26910 onMouseOut : function(e)
26912 this.fireEvent("mouseout", this, e);
26924 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26927 * @class Roo.bootstrap.menu.Separator
26928 * @extends Roo.bootstrap.Component
26929 * Bootstrap Separator class
26932 * Create a new Separator
26933 * @param {Object} config The config object
26937 Roo.bootstrap.menu.Separator = function(config){
26938 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26941 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26943 getAutoCreate : function(){
26964 * @class Roo.bootstrap.Tooltip
26965 * Bootstrap Tooltip class
26966 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26967 * to determine which dom element triggers the tooltip.
26969 * It needs to add support for additional attributes like tooltip-position
26972 * Create a new Toolti
26973 * @param {Object} config The config object
26976 Roo.bootstrap.Tooltip = function(config){
26977 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26979 this.alignment = Roo.bootstrap.Tooltip.alignment;
26981 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26982 this.alignment = config.alignment;
26987 Roo.apply(Roo.bootstrap.Tooltip, {
26989 * @function init initialize tooltip monitoring.
26993 currentTip : false,
26994 currentRegion : false,
27000 Roo.get(document).on('mouseover', this.enter ,this);
27001 Roo.get(document).on('mouseout', this.leave, this);
27004 this.currentTip = new Roo.bootstrap.Tooltip();
27007 enter : function(ev)
27009 var dom = ev.getTarget();
27011 //Roo.log(['enter',dom]);
27012 var el = Roo.fly(dom);
27013 if (this.currentEl) {
27015 //Roo.log(this.currentEl);
27016 //Roo.log(this.currentEl.contains(dom));
27017 if (this.currentEl == el) {
27020 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
27026 if (this.currentTip.el) {
27027 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
27031 if(!el || el.dom == document){
27037 // you can not look for children, as if el is the body.. then everythign is the child..
27038 if (!el.attr('tooltip')) { //
27039 if (!el.select("[tooltip]").elements.length) {
27042 // is the mouse over this child...?
27043 bindEl = el.select("[tooltip]").first();
27044 var xy = ev.getXY();
27045 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
27046 //Roo.log("not in region.");
27049 //Roo.log("child element over..");
27052 this.currentEl = bindEl;
27053 this.currentTip.bind(bindEl);
27054 this.currentRegion = Roo.lib.Region.getRegion(dom);
27055 this.currentTip.enter();
27058 leave : function(ev)
27060 var dom = ev.getTarget();
27061 //Roo.log(['leave',dom]);
27062 if (!this.currentEl) {
27067 if (dom != this.currentEl.dom) {
27070 var xy = ev.getXY();
27071 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
27074 // only activate leave if mouse cursor is outside... bounding box..
27079 if (this.currentTip) {
27080 this.currentTip.leave();
27082 //Roo.log('clear currentEl');
27083 this.currentEl = false;
27088 'left' : ['r-l', [-2,0], 'right'],
27089 'right' : ['l-r', [2,0], 'left'],
27090 'bottom' : ['t-b', [0,2], 'top'],
27091 'top' : [ 'b-t', [0,-2], 'bottom']
27097 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
27102 delay : null, // can be { show : 300 , hide: 500}
27106 hoverState : null, //???
27108 placement : 'bottom',
27112 getAutoCreate : function(){
27119 cls : 'tooltip-arrow'
27122 cls : 'tooltip-inner'
27129 bind : function(el)
27135 enter : function () {
27137 if (this.timeout != null) {
27138 clearTimeout(this.timeout);
27141 this.hoverState = 'in';
27142 //Roo.log("enter - show");
27143 if (!this.delay || !this.delay.show) {
27148 this.timeout = setTimeout(function () {
27149 if (_t.hoverState == 'in') {
27152 }, this.delay.show);
27156 clearTimeout(this.timeout);
27158 this.hoverState = 'out';
27159 if (!this.delay || !this.delay.hide) {
27165 this.timeout = setTimeout(function () {
27166 //Roo.log("leave - timeout");
27168 if (_t.hoverState == 'out') {
27170 Roo.bootstrap.Tooltip.currentEl = false;
27175 show : function (msg)
27178 this.render(document.body);
27181 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
27183 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
27185 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
27187 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
27189 var placement = typeof this.placement == 'function' ?
27190 this.placement.call(this, this.el, on_el) :
27193 var autoToken = /\s?auto?\s?/i;
27194 var autoPlace = autoToken.test(placement);
27196 placement = placement.replace(autoToken, '') || 'top';
27200 //this.el.setXY([0,0]);
27202 //this.el.dom.style.display='block';
27204 //this.el.appendTo(on_el);
27206 var p = this.getPosition();
27207 var box = this.el.getBox();
27213 var align = this.alignment[placement];
27215 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
27217 if(placement == 'top' || placement == 'bottom'){
27219 placement = 'right';
27222 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
27223 placement = 'left';
27226 var scroll = Roo.select('body', true).first().getScroll();
27228 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
27232 align = this.alignment[placement];
27235 this.el.alignTo(this.bindEl, align[0],align[1]);
27236 //var arrow = this.el.select('.arrow',true).first();
27237 //arrow.set(align[2],
27239 this.el.addClass(placement);
27241 this.el.addClass('in fade');
27243 this.hoverState = null;
27245 if (this.el.hasClass('fade')) {
27256 //this.el.setXY([0,0]);
27257 this.el.removeClass('in');
27273 * @class Roo.bootstrap.LocationPicker
27274 * @extends Roo.bootstrap.Component
27275 * Bootstrap LocationPicker class
27276 * @cfg {Number} latitude Position when init default 0
27277 * @cfg {Number} longitude Position when init default 0
27278 * @cfg {Number} zoom default 15
27279 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
27280 * @cfg {Boolean} mapTypeControl default false
27281 * @cfg {Boolean} disableDoubleClickZoom default false
27282 * @cfg {Boolean} scrollwheel default true
27283 * @cfg {Boolean} streetViewControl default false
27284 * @cfg {Number} radius default 0
27285 * @cfg {String} locationName
27286 * @cfg {Boolean} draggable default true
27287 * @cfg {Boolean} enableAutocomplete default false
27288 * @cfg {Boolean} enableReverseGeocode default true
27289 * @cfg {String} markerTitle
27292 * Create a new LocationPicker
27293 * @param {Object} config The config object
27297 Roo.bootstrap.LocationPicker = function(config){
27299 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
27304 * Fires when the picker initialized.
27305 * @param {Roo.bootstrap.LocationPicker} this
27306 * @param {Google Location} location
27310 * @event positionchanged
27311 * Fires when the picker position changed.
27312 * @param {Roo.bootstrap.LocationPicker} this
27313 * @param {Google Location} location
27315 positionchanged : true,
27318 * Fires when the map resize.
27319 * @param {Roo.bootstrap.LocationPicker} this
27324 * Fires when the map show.
27325 * @param {Roo.bootstrap.LocationPicker} this
27330 * Fires when the map hide.
27331 * @param {Roo.bootstrap.LocationPicker} this
27336 * Fires when click the map.
27337 * @param {Roo.bootstrap.LocationPicker} this
27338 * @param {Map event} e
27342 * @event mapRightClick
27343 * Fires when right click the map.
27344 * @param {Roo.bootstrap.LocationPicker} this
27345 * @param {Map event} e
27347 mapRightClick : true,
27349 * @event markerClick
27350 * Fires when click the marker.
27351 * @param {Roo.bootstrap.LocationPicker} this
27352 * @param {Map event} e
27354 markerClick : true,
27356 * @event markerRightClick
27357 * Fires when right click the marker.
27358 * @param {Roo.bootstrap.LocationPicker} this
27359 * @param {Map event} e
27361 markerRightClick : true,
27363 * @event OverlayViewDraw
27364 * Fires when OverlayView Draw
27365 * @param {Roo.bootstrap.LocationPicker} this
27367 OverlayViewDraw : true,
27369 * @event OverlayViewOnAdd
27370 * Fires when OverlayView Draw
27371 * @param {Roo.bootstrap.LocationPicker} this
27373 OverlayViewOnAdd : true,
27375 * @event OverlayViewOnRemove
27376 * Fires when OverlayView Draw
27377 * @param {Roo.bootstrap.LocationPicker} this
27379 OverlayViewOnRemove : true,
27381 * @event OverlayViewShow
27382 * Fires when OverlayView Draw
27383 * @param {Roo.bootstrap.LocationPicker} this
27384 * @param {Pixel} cpx
27386 OverlayViewShow : true,
27388 * @event OverlayViewHide
27389 * Fires when OverlayView Draw
27390 * @param {Roo.bootstrap.LocationPicker} this
27392 OverlayViewHide : true,
27394 * @event loadexception
27395 * Fires when load google lib failed.
27396 * @param {Roo.bootstrap.LocationPicker} this
27398 loadexception : true
27403 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
27405 gMapContext: false,
27411 mapTypeControl: false,
27412 disableDoubleClickZoom: false,
27414 streetViewControl: false,
27418 enableAutocomplete: false,
27419 enableReverseGeocode: true,
27422 getAutoCreate: function()
27427 cls: 'roo-location-picker'
27433 initEvents: function(ct, position)
27435 if(!this.el.getWidth() || this.isApplied()){
27439 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27444 initial: function()
27446 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27447 this.fireEvent('loadexception', this);
27451 if(!this.mapTypeId){
27452 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27455 this.gMapContext = this.GMapContext();
27457 this.initOverlayView();
27459 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27463 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27464 _this.setPosition(_this.gMapContext.marker.position);
27467 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27468 _this.fireEvent('mapClick', this, event);
27472 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27473 _this.fireEvent('mapRightClick', this, event);
27477 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27478 _this.fireEvent('markerClick', this, event);
27482 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27483 _this.fireEvent('markerRightClick', this, event);
27487 this.setPosition(this.gMapContext.location);
27489 this.fireEvent('initial', this, this.gMapContext.location);
27492 initOverlayView: function()
27496 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27500 _this.fireEvent('OverlayViewDraw', _this);
27505 _this.fireEvent('OverlayViewOnAdd', _this);
27508 onRemove: function()
27510 _this.fireEvent('OverlayViewOnRemove', _this);
27513 show: function(cpx)
27515 _this.fireEvent('OverlayViewShow', _this, cpx);
27520 _this.fireEvent('OverlayViewHide', _this);
27526 fromLatLngToContainerPixel: function(event)
27528 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27531 isApplied: function()
27533 return this.getGmapContext() == false ? false : true;
27536 getGmapContext: function()
27538 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27541 GMapContext: function()
27543 var position = new google.maps.LatLng(this.latitude, this.longitude);
27545 var _map = new google.maps.Map(this.el.dom, {
27548 mapTypeId: this.mapTypeId,
27549 mapTypeControl: this.mapTypeControl,
27550 disableDoubleClickZoom: this.disableDoubleClickZoom,
27551 scrollwheel: this.scrollwheel,
27552 streetViewControl: this.streetViewControl,
27553 locationName: this.locationName,
27554 draggable: this.draggable,
27555 enableAutocomplete: this.enableAutocomplete,
27556 enableReverseGeocode: this.enableReverseGeocode
27559 var _marker = new google.maps.Marker({
27560 position: position,
27562 title: this.markerTitle,
27563 draggable: this.draggable
27570 location: position,
27571 radius: this.radius,
27572 locationName: this.locationName,
27573 addressComponents: {
27574 formatted_address: null,
27575 addressLine1: null,
27576 addressLine2: null,
27578 streetNumber: null,
27582 stateOrProvince: null
27585 domContainer: this.el.dom,
27586 geodecoder: new google.maps.Geocoder()
27590 drawCircle: function(center, radius, options)
27592 if (this.gMapContext.circle != null) {
27593 this.gMapContext.circle.setMap(null);
27597 options = Roo.apply({}, options, {
27598 strokeColor: "#0000FF",
27599 strokeOpacity: .35,
27601 fillColor: "#0000FF",
27605 options.map = this.gMapContext.map;
27606 options.radius = radius;
27607 options.center = center;
27608 this.gMapContext.circle = new google.maps.Circle(options);
27609 return this.gMapContext.circle;
27615 setPosition: function(location)
27617 this.gMapContext.location = location;
27618 this.gMapContext.marker.setPosition(location);
27619 this.gMapContext.map.panTo(location);
27620 this.drawCircle(location, this.gMapContext.radius, {});
27624 if (this.gMapContext.settings.enableReverseGeocode) {
27625 this.gMapContext.geodecoder.geocode({
27626 latLng: this.gMapContext.location
27627 }, function(results, status) {
27629 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27630 _this.gMapContext.locationName = results[0].formatted_address;
27631 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27633 _this.fireEvent('positionchanged', this, location);
27640 this.fireEvent('positionchanged', this, location);
27645 google.maps.event.trigger(this.gMapContext.map, "resize");
27647 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27649 this.fireEvent('resize', this);
27652 setPositionByLatLng: function(latitude, longitude)
27654 this.setPosition(new google.maps.LatLng(latitude, longitude));
27657 getCurrentPosition: function()
27660 latitude: this.gMapContext.location.lat(),
27661 longitude: this.gMapContext.location.lng()
27665 getAddressName: function()
27667 return this.gMapContext.locationName;
27670 getAddressComponents: function()
27672 return this.gMapContext.addressComponents;
27675 address_component_from_google_geocode: function(address_components)
27679 for (var i = 0; i < address_components.length; i++) {
27680 var component = address_components[i];
27681 if (component.types.indexOf("postal_code") >= 0) {
27682 result.postalCode = component.short_name;
27683 } else if (component.types.indexOf("street_number") >= 0) {
27684 result.streetNumber = component.short_name;
27685 } else if (component.types.indexOf("route") >= 0) {
27686 result.streetName = component.short_name;
27687 } else if (component.types.indexOf("neighborhood") >= 0) {
27688 result.city = component.short_name;
27689 } else if (component.types.indexOf("locality") >= 0) {
27690 result.city = component.short_name;
27691 } else if (component.types.indexOf("sublocality") >= 0) {
27692 result.district = component.short_name;
27693 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27694 result.stateOrProvince = component.short_name;
27695 } else if (component.types.indexOf("country") >= 0) {
27696 result.country = component.short_name;
27700 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27701 result.addressLine2 = "";
27705 setZoomLevel: function(zoom)
27707 this.gMapContext.map.setZoom(zoom);
27720 this.fireEvent('show', this);
27731 this.fireEvent('hide', this);
27736 Roo.apply(Roo.bootstrap.LocationPicker, {
27738 OverlayView : function(map, options)
27740 options = options || {};
27747 * @class Roo.bootstrap.Alert
27748 * @extends Roo.bootstrap.Component
27749 * Bootstrap Alert class - shows an alert area box
27751 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27752 Enter a valid email address
27755 * @cfg {String} title The title of alert
27756 * @cfg {String} html The content of alert
27757 * @cfg {String} weight ( success | info | warning | danger )
27758 * @cfg {String} faicon font-awesomeicon
27761 * Create a new alert
27762 * @param {Object} config The config object
27766 Roo.bootstrap.Alert = function(config){
27767 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27771 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27778 getAutoCreate : function()
27787 cls : 'roo-alert-icon'
27792 cls : 'roo-alert-title',
27797 cls : 'roo-alert-text',
27804 cfg.cn[0].cls += ' fa ' + this.faicon;
27808 cfg.cls += ' alert-' + this.weight;
27814 initEvents: function()
27816 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27819 setTitle : function(str)
27821 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27824 setText : function(str)
27826 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27829 setWeight : function(weight)
27832 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27835 this.weight = weight;
27837 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27840 setIcon : function(icon)
27843 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27846 this.faicon = icon;
27848 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27869 * @class Roo.bootstrap.UploadCropbox
27870 * @extends Roo.bootstrap.Component
27871 * Bootstrap UploadCropbox class
27872 * @cfg {String} emptyText show when image has been loaded
27873 * @cfg {String} rotateNotify show when image too small to rotate
27874 * @cfg {Number} errorTimeout default 3000
27875 * @cfg {Number} minWidth default 300
27876 * @cfg {Number} minHeight default 300
27877 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27878 * @cfg {Boolean} isDocument (true|false) default false
27879 * @cfg {String} url action url
27880 * @cfg {String} paramName default 'imageUpload'
27881 * @cfg {String} method default POST
27882 * @cfg {Boolean} loadMask (true|false) default true
27883 * @cfg {Boolean} loadingText default 'Loading...'
27886 * Create a new UploadCropbox
27887 * @param {Object} config The config object
27890 Roo.bootstrap.UploadCropbox = function(config){
27891 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27895 * @event beforeselectfile
27896 * Fire before select file
27897 * @param {Roo.bootstrap.UploadCropbox} this
27899 "beforeselectfile" : true,
27902 * Fire after initEvent
27903 * @param {Roo.bootstrap.UploadCropbox} this
27908 * Fire after initEvent
27909 * @param {Roo.bootstrap.UploadCropbox} this
27910 * @param {String} data
27915 * Fire when preparing the file data
27916 * @param {Roo.bootstrap.UploadCropbox} this
27917 * @param {Object} file
27922 * Fire when get exception
27923 * @param {Roo.bootstrap.UploadCropbox} this
27924 * @param {XMLHttpRequest} xhr
27926 "exception" : true,
27928 * @event beforeloadcanvas
27929 * Fire before load the canvas
27930 * @param {Roo.bootstrap.UploadCropbox} this
27931 * @param {String} src
27933 "beforeloadcanvas" : true,
27936 * Fire when trash image
27937 * @param {Roo.bootstrap.UploadCropbox} this
27942 * Fire when download the image
27943 * @param {Roo.bootstrap.UploadCropbox} this
27947 * @event footerbuttonclick
27948 * Fire when footerbuttonclick
27949 * @param {Roo.bootstrap.UploadCropbox} this
27950 * @param {String} type
27952 "footerbuttonclick" : true,
27956 * @param {Roo.bootstrap.UploadCropbox} this
27961 * Fire when rotate the image
27962 * @param {Roo.bootstrap.UploadCropbox} this
27963 * @param {String} pos
27968 * Fire when inspect the file
27969 * @param {Roo.bootstrap.UploadCropbox} this
27970 * @param {Object} file
27975 * Fire when xhr upload the file
27976 * @param {Roo.bootstrap.UploadCropbox} this
27977 * @param {Object} data
27982 * Fire when arrange the file data
27983 * @param {Roo.bootstrap.UploadCropbox} this
27984 * @param {Object} formData
27989 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27992 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27994 emptyText : 'Click to upload image',
27995 rotateNotify : 'Image is too small to rotate',
27996 errorTimeout : 3000,
28010 cropType : 'image/jpeg',
28012 canvasLoaded : false,
28013 isDocument : false,
28015 paramName : 'imageUpload',
28017 loadingText : 'Loading...',
28020 getAutoCreate : function()
28024 cls : 'roo-upload-cropbox',
28028 cls : 'roo-upload-cropbox-selector',
28033 cls : 'roo-upload-cropbox-body',
28034 style : 'cursor:pointer',
28038 cls : 'roo-upload-cropbox-preview'
28042 cls : 'roo-upload-cropbox-thumb'
28046 cls : 'roo-upload-cropbox-empty-notify',
28047 html : this.emptyText
28051 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
28052 html : this.rotateNotify
28058 cls : 'roo-upload-cropbox-footer',
28061 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
28071 onRender : function(ct, position)
28073 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
28075 if (this.buttons.length) {
28077 Roo.each(this.buttons, function(bb) {
28079 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
28081 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
28087 this.maskEl = this.el;
28091 initEvents : function()
28093 this.urlAPI = (window.createObjectURL && window) ||
28094 (window.URL && URL.revokeObjectURL && URL) ||
28095 (window.webkitURL && webkitURL);
28097 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
28098 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28100 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
28101 this.selectorEl.hide();
28103 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
28104 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28106 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
28107 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28108 this.thumbEl.hide();
28110 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
28111 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28113 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
28114 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28115 this.errorEl.hide();
28117 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
28118 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28119 this.footerEl.hide();
28121 this.setThumbBoxSize();
28127 this.fireEvent('initial', this);
28134 window.addEventListener("resize", function() { _this.resize(); } );
28136 this.bodyEl.on('click', this.beforeSelectFile, this);
28139 this.bodyEl.on('touchstart', this.onTouchStart, this);
28140 this.bodyEl.on('touchmove', this.onTouchMove, this);
28141 this.bodyEl.on('touchend', this.onTouchEnd, this);
28145 this.bodyEl.on('mousedown', this.onMouseDown, this);
28146 this.bodyEl.on('mousemove', this.onMouseMove, this);
28147 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
28148 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
28149 Roo.get(document).on('mouseup', this.onMouseUp, this);
28152 this.selectorEl.on('change', this.onFileSelected, this);
28158 this.baseScale = 1;
28160 this.baseRotate = 1;
28161 this.dragable = false;
28162 this.pinching = false;
28165 this.cropData = false;
28166 this.notifyEl.dom.innerHTML = this.emptyText;
28168 this.selectorEl.dom.value = '';
28172 resize : function()
28174 if(this.fireEvent('resize', this) != false){
28175 this.setThumbBoxPosition();
28176 this.setCanvasPosition();
28180 onFooterButtonClick : function(e, el, o, type)
28183 case 'rotate-left' :
28184 this.onRotateLeft(e);
28186 case 'rotate-right' :
28187 this.onRotateRight(e);
28190 this.beforeSelectFile(e);
28205 this.fireEvent('footerbuttonclick', this, type);
28208 beforeSelectFile : function(e)
28210 e.preventDefault();
28212 if(this.fireEvent('beforeselectfile', this) != false){
28213 this.selectorEl.dom.click();
28217 onFileSelected : function(e)
28219 e.preventDefault();
28221 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28225 var file = this.selectorEl.dom.files[0];
28227 if(this.fireEvent('inspect', this, file) != false){
28228 this.prepare(file);
28233 trash : function(e)
28235 this.fireEvent('trash', this);
28238 download : function(e)
28240 this.fireEvent('download', this);
28243 loadCanvas : function(src)
28245 if(this.fireEvent('beforeloadcanvas', this, src) != false){
28249 this.imageEl = document.createElement('img');
28253 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
28255 this.imageEl.src = src;
28259 onLoadCanvas : function()
28261 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
28262 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
28264 this.bodyEl.un('click', this.beforeSelectFile, this);
28266 this.notifyEl.hide();
28267 this.thumbEl.show();
28268 this.footerEl.show();
28270 this.baseRotateLevel();
28272 if(this.isDocument){
28273 this.setThumbBoxSize();
28276 this.setThumbBoxPosition();
28278 this.baseScaleLevel();
28284 this.canvasLoaded = true;
28287 this.maskEl.unmask();
28292 setCanvasPosition : function()
28294 if(!this.canvasEl){
28298 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
28299 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
28301 this.previewEl.setLeft(pw);
28302 this.previewEl.setTop(ph);
28306 onMouseDown : function(e)
28310 this.dragable = true;
28311 this.pinching = false;
28313 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
28314 this.dragable = false;
28318 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28319 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28323 onMouseMove : function(e)
28327 if(!this.canvasLoaded){
28331 if (!this.dragable){
28335 var minX = Math.ceil(this.thumbEl.getLeft(true));
28336 var minY = Math.ceil(this.thumbEl.getTop(true));
28338 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
28339 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
28341 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28342 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28344 x = x - this.mouseX;
28345 y = y - this.mouseY;
28347 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
28348 var bgY = Math.ceil(y + this.previewEl.getTop(true));
28350 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
28351 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
28353 this.previewEl.setLeft(bgX);
28354 this.previewEl.setTop(bgY);
28356 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28357 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28360 onMouseUp : function(e)
28364 this.dragable = false;
28367 onMouseWheel : function(e)
28371 this.startScale = this.scale;
28373 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
28375 if(!this.zoomable()){
28376 this.scale = this.startScale;
28385 zoomable : function()
28387 var minScale = this.thumbEl.getWidth() / this.minWidth;
28389 if(this.minWidth < this.minHeight){
28390 minScale = this.thumbEl.getHeight() / this.minHeight;
28393 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
28394 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
28398 (this.rotate == 0 || this.rotate == 180) &&
28400 width > this.imageEl.OriginWidth ||
28401 height > this.imageEl.OriginHeight ||
28402 (width < this.minWidth && height < this.minHeight)
28410 (this.rotate == 90 || this.rotate == 270) &&
28412 width > this.imageEl.OriginWidth ||
28413 height > this.imageEl.OriginHeight ||
28414 (width < this.minHeight && height < this.minWidth)
28421 !this.isDocument &&
28422 (this.rotate == 0 || this.rotate == 180) &&
28424 width < this.minWidth ||
28425 width > this.imageEl.OriginWidth ||
28426 height < this.minHeight ||
28427 height > this.imageEl.OriginHeight
28434 !this.isDocument &&
28435 (this.rotate == 90 || this.rotate == 270) &&
28437 width < this.minHeight ||
28438 width > this.imageEl.OriginWidth ||
28439 height < this.minWidth ||
28440 height > this.imageEl.OriginHeight
28450 onRotateLeft : function(e)
28452 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28454 var minScale = this.thumbEl.getWidth() / this.minWidth;
28456 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28457 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28459 this.startScale = this.scale;
28461 while (this.getScaleLevel() < minScale){
28463 this.scale = this.scale + 1;
28465 if(!this.zoomable()){
28470 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28471 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28476 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28483 this.scale = this.startScale;
28485 this.onRotateFail();
28490 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28492 if(this.isDocument){
28493 this.setThumbBoxSize();
28494 this.setThumbBoxPosition();
28495 this.setCanvasPosition();
28500 this.fireEvent('rotate', this, 'left');
28504 onRotateRight : function(e)
28506 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28508 var minScale = this.thumbEl.getWidth() / this.minWidth;
28510 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28511 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28513 this.startScale = this.scale;
28515 while (this.getScaleLevel() < minScale){
28517 this.scale = this.scale + 1;
28519 if(!this.zoomable()){
28524 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28525 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28530 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28537 this.scale = this.startScale;
28539 this.onRotateFail();
28544 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28546 if(this.isDocument){
28547 this.setThumbBoxSize();
28548 this.setThumbBoxPosition();
28549 this.setCanvasPosition();
28554 this.fireEvent('rotate', this, 'right');
28557 onRotateFail : function()
28559 this.errorEl.show(true);
28563 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28568 this.previewEl.dom.innerHTML = '';
28570 var canvasEl = document.createElement("canvas");
28572 var contextEl = canvasEl.getContext("2d");
28574 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28575 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28576 var center = this.imageEl.OriginWidth / 2;
28578 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28579 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28580 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28581 center = this.imageEl.OriginHeight / 2;
28584 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28586 contextEl.translate(center, center);
28587 contextEl.rotate(this.rotate * Math.PI / 180);
28589 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28591 this.canvasEl = document.createElement("canvas");
28593 this.contextEl = this.canvasEl.getContext("2d");
28595 switch (this.rotate) {
28598 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28599 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28601 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28606 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28607 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28609 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28610 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);
28614 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28619 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28620 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28622 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28623 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);
28627 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);
28632 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28633 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28635 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28636 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28640 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);
28647 this.previewEl.appendChild(this.canvasEl);
28649 this.setCanvasPosition();
28654 if(!this.canvasLoaded){
28658 var imageCanvas = document.createElement("canvas");
28660 var imageContext = imageCanvas.getContext("2d");
28662 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28663 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28665 var center = imageCanvas.width / 2;
28667 imageContext.translate(center, center);
28669 imageContext.rotate(this.rotate * Math.PI / 180);
28671 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28673 var canvas = document.createElement("canvas");
28675 var context = canvas.getContext("2d");
28677 canvas.width = this.minWidth;
28678 canvas.height = this.minHeight;
28680 switch (this.rotate) {
28683 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28684 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28686 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28687 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28689 var targetWidth = this.minWidth - 2 * x;
28690 var targetHeight = this.minHeight - 2 * y;
28694 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28695 scale = targetWidth / width;
28698 if(x > 0 && y == 0){
28699 scale = targetHeight / height;
28702 if(x > 0 && y > 0){
28703 scale = targetWidth / width;
28705 if(width < height){
28706 scale = targetHeight / height;
28710 context.scale(scale, scale);
28712 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28713 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28715 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28716 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28718 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28723 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28724 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28726 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28727 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28729 var targetWidth = this.minWidth - 2 * x;
28730 var targetHeight = this.minHeight - 2 * y;
28734 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28735 scale = targetWidth / width;
28738 if(x > 0 && y == 0){
28739 scale = targetHeight / height;
28742 if(x > 0 && y > 0){
28743 scale = targetWidth / width;
28745 if(width < height){
28746 scale = targetHeight / height;
28750 context.scale(scale, scale);
28752 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28753 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28755 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28756 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28758 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28760 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28765 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28766 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28768 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28769 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28771 var targetWidth = this.minWidth - 2 * x;
28772 var targetHeight = this.minHeight - 2 * y;
28776 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28777 scale = targetWidth / width;
28780 if(x > 0 && y == 0){
28781 scale = targetHeight / height;
28784 if(x > 0 && y > 0){
28785 scale = targetWidth / width;
28787 if(width < height){
28788 scale = targetHeight / height;
28792 context.scale(scale, scale);
28794 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28795 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28797 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28798 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28800 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28801 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28803 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28808 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28809 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28811 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28812 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28814 var targetWidth = this.minWidth - 2 * x;
28815 var targetHeight = this.minHeight - 2 * y;
28819 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28820 scale = targetWidth / width;
28823 if(x > 0 && y == 0){
28824 scale = targetHeight / height;
28827 if(x > 0 && y > 0){
28828 scale = targetWidth / width;
28830 if(width < height){
28831 scale = targetHeight / height;
28835 context.scale(scale, scale);
28837 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28838 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28840 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28841 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28843 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28845 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28852 this.cropData = canvas.toDataURL(this.cropType);
28854 if(this.fireEvent('crop', this, this.cropData) !== false){
28855 this.process(this.file, this.cropData);
28862 setThumbBoxSize : function()
28866 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28867 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28868 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28870 this.minWidth = width;
28871 this.minHeight = height;
28873 if(this.rotate == 90 || this.rotate == 270){
28874 this.minWidth = height;
28875 this.minHeight = width;
28880 width = Math.ceil(this.minWidth * height / this.minHeight);
28882 if(this.minWidth > this.minHeight){
28884 height = Math.ceil(this.minHeight * width / this.minWidth);
28887 this.thumbEl.setStyle({
28888 width : width + 'px',
28889 height : height + 'px'
28896 setThumbBoxPosition : function()
28898 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28899 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28901 this.thumbEl.setLeft(x);
28902 this.thumbEl.setTop(y);
28906 baseRotateLevel : function()
28908 this.baseRotate = 1;
28911 typeof(this.exif) != 'undefined' &&
28912 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28913 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28915 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28918 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28922 baseScaleLevel : function()
28926 if(this.isDocument){
28928 if(this.baseRotate == 6 || this.baseRotate == 8){
28930 height = this.thumbEl.getHeight();
28931 this.baseScale = height / this.imageEl.OriginWidth;
28933 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28934 width = this.thumbEl.getWidth();
28935 this.baseScale = width / this.imageEl.OriginHeight;
28941 height = this.thumbEl.getHeight();
28942 this.baseScale = height / this.imageEl.OriginHeight;
28944 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28945 width = this.thumbEl.getWidth();
28946 this.baseScale = width / this.imageEl.OriginWidth;
28952 if(this.baseRotate == 6 || this.baseRotate == 8){
28954 width = this.thumbEl.getHeight();
28955 this.baseScale = width / this.imageEl.OriginHeight;
28957 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28958 height = this.thumbEl.getWidth();
28959 this.baseScale = height / this.imageEl.OriginHeight;
28962 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28963 height = this.thumbEl.getWidth();
28964 this.baseScale = height / this.imageEl.OriginHeight;
28966 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28967 width = this.thumbEl.getHeight();
28968 this.baseScale = width / this.imageEl.OriginWidth;
28975 width = this.thumbEl.getWidth();
28976 this.baseScale = width / this.imageEl.OriginWidth;
28978 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28979 height = this.thumbEl.getHeight();
28980 this.baseScale = height / this.imageEl.OriginHeight;
28983 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28985 height = this.thumbEl.getHeight();
28986 this.baseScale = height / this.imageEl.OriginHeight;
28988 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28989 width = this.thumbEl.getWidth();
28990 this.baseScale = width / this.imageEl.OriginWidth;
28998 getScaleLevel : function()
29000 return this.baseScale * Math.pow(1.1, this.scale);
29003 onTouchStart : function(e)
29005 if(!this.canvasLoaded){
29006 this.beforeSelectFile(e);
29010 var touches = e.browserEvent.touches;
29016 if(touches.length == 1){
29017 this.onMouseDown(e);
29021 if(touches.length != 2){
29027 for(var i = 0, finger; finger = touches[i]; i++){
29028 coords.push(finger.pageX, finger.pageY);
29031 var x = Math.pow(coords[0] - coords[2], 2);
29032 var y = Math.pow(coords[1] - coords[3], 2);
29034 this.startDistance = Math.sqrt(x + y);
29036 this.startScale = this.scale;
29038 this.pinching = true;
29039 this.dragable = false;
29043 onTouchMove : function(e)
29045 if(!this.pinching && !this.dragable){
29049 var touches = e.browserEvent.touches;
29056 this.onMouseMove(e);
29062 for(var i = 0, finger; finger = touches[i]; i++){
29063 coords.push(finger.pageX, finger.pageY);
29066 var x = Math.pow(coords[0] - coords[2], 2);
29067 var y = Math.pow(coords[1] - coords[3], 2);
29069 this.endDistance = Math.sqrt(x + y);
29071 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
29073 if(!this.zoomable()){
29074 this.scale = this.startScale;
29082 onTouchEnd : function(e)
29084 this.pinching = false;
29085 this.dragable = false;
29089 process : function(file, crop)
29092 this.maskEl.mask(this.loadingText);
29095 this.xhr = new XMLHttpRequest();
29097 file.xhr = this.xhr;
29099 this.xhr.open(this.method, this.url, true);
29102 "Accept": "application/json",
29103 "Cache-Control": "no-cache",
29104 "X-Requested-With": "XMLHttpRequest"
29107 for (var headerName in headers) {
29108 var headerValue = headers[headerName];
29110 this.xhr.setRequestHeader(headerName, headerValue);
29116 this.xhr.onload = function()
29118 _this.xhrOnLoad(_this.xhr);
29121 this.xhr.onerror = function()
29123 _this.xhrOnError(_this.xhr);
29126 var formData = new FormData();
29128 formData.append('returnHTML', 'NO');
29131 formData.append('crop', crop);
29134 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
29135 formData.append(this.paramName, file, file.name);
29138 if(typeof(file.filename) != 'undefined'){
29139 formData.append('filename', file.filename);
29142 if(typeof(file.mimetype) != 'undefined'){
29143 formData.append('mimetype', file.mimetype);
29146 if(this.fireEvent('arrange', this, formData) != false){
29147 this.xhr.send(formData);
29151 xhrOnLoad : function(xhr)
29154 this.maskEl.unmask();
29157 if (xhr.readyState !== 4) {
29158 this.fireEvent('exception', this, xhr);
29162 var response = Roo.decode(xhr.responseText);
29164 if(!response.success){
29165 this.fireEvent('exception', this, xhr);
29169 var response = Roo.decode(xhr.responseText);
29171 this.fireEvent('upload', this, response);
29175 xhrOnError : function()
29178 this.maskEl.unmask();
29181 Roo.log('xhr on error');
29183 var response = Roo.decode(xhr.responseText);
29189 prepare : function(file)
29192 this.maskEl.mask(this.loadingText);
29198 if(typeof(file) === 'string'){
29199 this.loadCanvas(file);
29203 if(!file || !this.urlAPI){
29208 this.cropType = file.type;
29212 if(this.fireEvent('prepare', this, this.file) != false){
29214 var reader = new FileReader();
29216 reader.onload = function (e) {
29217 if (e.target.error) {
29218 Roo.log(e.target.error);
29222 var buffer = e.target.result,
29223 dataView = new DataView(buffer),
29225 maxOffset = dataView.byteLength - 4,
29229 if (dataView.getUint16(0) === 0xffd8) {
29230 while (offset < maxOffset) {
29231 markerBytes = dataView.getUint16(offset);
29233 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
29234 markerLength = dataView.getUint16(offset + 2) + 2;
29235 if (offset + markerLength > dataView.byteLength) {
29236 Roo.log('Invalid meta data: Invalid segment size.');
29240 if(markerBytes == 0xffe1){
29241 _this.parseExifData(
29248 offset += markerLength;
29258 var url = _this.urlAPI.createObjectURL(_this.file);
29260 _this.loadCanvas(url);
29265 reader.readAsArrayBuffer(this.file);
29271 parseExifData : function(dataView, offset, length)
29273 var tiffOffset = offset + 10,
29277 if (dataView.getUint32(offset + 4) !== 0x45786966) {
29278 // No Exif data, might be XMP data instead
29282 // Check for the ASCII code for "Exif" (0x45786966):
29283 if (dataView.getUint32(offset + 4) !== 0x45786966) {
29284 // No Exif data, might be XMP data instead
29287 if (tiffOffset + 8 > dataView.byteLength) {
29288 Roo.log('Invalid Exif data: Invalid segment size.');
29291 // Check for the two null bytes:
29292 if (dataView.getUint16(offset + 8) !== 0x0000) {
29293 Roo.log('Invalid Exif data: Missing byte alignment offset.');
29296 // Check the byte alignment:
29297 switch (dataView.getUint16(tiffOffset)) {
29299 littleEndian = true;
29302 littleEndian = false;
29305 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
29308 // Check for the TIFF tag marker (0x002A):
29309 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
29310 Roo.log('Invalid Exif data: Missing TIFF marker.');
29313 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
29314 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
29316 this.parseExifTags(
29319 tiffOffset + dirOffset,
29324 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
29329 if (dirOffset + 6 > dataView.byteLength) {
29330 Roo.log('Invalid Exif data: Invalid directory offset.');
29333 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
29334 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
29335 if (dirEndOffset + 4 > dataView.byteLength) {
29336 Roo.log('Invalid Exif data: Invalid directory size.');
29339 for (i = 0; i < tagsNumber; i += 1) {
29343 dirOffset + 2 + 12 * i, // tag offset
29347 // Return the offset to the next directory:
29348 return dataView.getUint32(dirEndOffset, littleEndian);
29351 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
29353 var tag = dataView.getUint16(offset, littleEndian);
29355 this.exif[tag] = this.getExifValue(
29359 dataView.getUint16(offset + 2, littleEndian), // tag type
29360 dataView.getUint32(offset + 4, littleEndian), // tag length
29365 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
29367 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
29376 Roo.log('Invalid Exif data: Invalid tag type.');
29380 tagSize = tagType.size * length;
29381 // Determine if the value is contained in the dataOffset bytes,
29382 // or if the value at the dataOffset is a pointer to the actual data:
29383 dataOffset = tagSize > 4 ?
29384 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
29385 if (dataOffset + tagSize > dataView.byteLength) {
29386 Roo.log('Invalid Exif data: Invalid data offset.');
29389 if (length === 1) {
29390 return tagType.getValue(dataView, dataOffset, littleEndian);
29393 for (i = 0; i < length; i += 1) {
29394 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
29397 if (tagType.ascii) {
29399 // Concatenate the chars:
29400 for (i = 0; i < values.length; i += 1) {
29402 // Ignore the terminating NULL byte(s):
29403 if (c === '\u0000') {
29415 Roo.apply(Roo.bootstrap.UploadCropbox, {
29417 'Orientation': 0x0112
29421 1: 0, //'top-left',
29423 3: 180, //'bottom-right',
29424 // 4: 'bottom-left',
29426 6: 90, //'right-top',
29427 // 7: 'right-bottom',
29428 8: 270 //'left-bottom'
29432 // byte, 8-bit unsigned int:
29434 getValue: function (dataView, dataOffset) {
29435 return dataView.getUint8(dataOffset);
29439 // ascii, 8-bit byte:
29441 getValue: function (dataView, dataOffset) {
29442 return String.fromCharCode(dataView.getUint8(dataOffset));
29447 // short, 16 bit int:
29449 getValue: function (dataView, dataOffset, littleEndian) {
29450 return dataView.getUint16(dataOffset, littleEndian);
29454 // long, 32 bit int:
29456 getValue: function (dataView, dataOffset, littleEndian) {
29457 return dataView.getUint32(dataOffset, littleEndian);
29461 // rational = two long values, first is numerator, second is denominator:
29463 getValue: function (dataView, dataOffset, littleEndian) {
29464 return dataView.getUint32(dataOffset, littleEndian) /
29465 dataView.getUint32(dataOffset + 4, littleEndian);
29469 // slong, 32 bit signed int:
29471 getValue: function (dataView, dataOffset, littleEndian) {
29472 return dataView.getInt32(dataOffset, littleEndian);
29476 // srational, two slongs, first is numerator, second is denominator:
29478 getValue: function (dataView, dataOffset, littleEndian) {
29479 return dataView.getInt32(dataOffset, littleEndian) /
29480 dataView.getInt32(dataOffset + 4, littleEndian);
29490 cls : 'btn-group roo-upload-cropbox-rotate-left',
29491 action : 'rotate-left',
29495 cls : 'btn btn-default',
29496 html : '<i class="fa fa-undo"></i>'
29502 cls : 'btn-group roo-upload-cropbox-picture',
29503 action : 'picture',
29507 cls : 'btn btn-default',
29508 html : '<i class="fa fa-picture-o"></i>'
29514 cls : 'btn-group roo-upload-cropbox-rotate-right',
29515 action : 'rotate-right',
29519 cls : 'btn btn-default',
29520 html : '<i class="fa fa-repeat"></i>'
29528 cls : 'btn-group roo-upload-cropbox-rotate-left',
29529 action : 'rotate-left',
29533 cls : 'btn btn-default',
29534 html : '<i class="fa fa-undo"></i>'
29540 cls : 'btn-group roo-upload-cropbox-download',
29541 action : 'download',
29545 cls : 'btn btn-default',
29546 html : '<i class="fa fa-download"></i>'
29552 cls : 'btn-group roo-upload-cropbox-crop',
29557 cls : 'btn btn-default',
29558 html : '<i class="fa fa-crop"></i>'
29564 cls : 'btn-group roo-upload-cropbox-trash',
29569 cls : 'btn btn-default',
29570 html : '<i class="fa fa-trash"></i>'
29576 cls : 'btn-group roo-upload-cropbox-rotate-right',
29577 action : 'rotate-right',
29581 cls : 'btn btn-default',
29582 html : '<i class="fa fa-repeat"></i>'
29590 cls : 'btn-group roo-upload-cropbox-rotate-left',
29591 action : 'rotate-left',
29595 cls : 'btn btn-default',
29596 html : '<i class="fa fa-undo"></i>'
29602 cls : 'btn-group roo-upload-cropbox-rotate-right',
29603 action : 'rotate-right',
29607 cls : 'btn btn-default',
29608 html : '<i class="fa fa-repeat"></i>'
29621 * @class Roo.bootstrap.DocumentManager
29622 * @extends Roo.bootstrap.Component
29623 * Bootstrap DocumentManager class
29624 * @cfg {String} paramName default 'imageUpload'
29625 * @cfg {String} toolTipName default 'filename'
29626 * @cfg {String} method default POST
29627 * @cfg {String} url action url
29628 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29629 * @cfg {Boolean} multiple multiple upload default true
29630 * @cfg {Number} thumbSize default 300
29631 * @cfg {String} fieldLabel
29632 * @cfg {Number} labelWidth default 4
29633 * @cfg {String} labelAlign (left|top) default left
29634 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29635 * @cfg {Number} labellg set the width of label (1-12)
29636 * @cfg {Number} labelmd set the width of label (1-12)
29637 * @cfg {Number} labelsm set the width of label (1-12)
29638 * @cfg {Number} labelxs set the width of label (1-12)
29641 * Create a new DocumentManager
29642 * @param {Object} config The config object
29645 Roo.bootstrap.DocumentManager = function(config){
29646 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29649 this.delegates = [];
29654 * Fire when initial the DocumentManager
29655 * @param {Roo.bootstrap.DocumentManager} this
29660 * inspect selected file
29661 * @param {Roo.bootstrap.DocumentManager} this
29662 * @param {File} file
29667 * Fire when xhr load exception
29668 * @param {Roo.bootstrap.DocumentManager} this
29669 * @param {XMLHttpRequest} xhr
29671 "exception" : true,
29673 * @event afterupload
29674 * Fire when xhr load exception
29675 * @param {Roo.bootstrap.DocumentManager} this
29676 * @param {XMLHttpRequest} xhr
29678 "afterupload" : true,
29681 * prepare the form data
29682 * @param {Roo.bootstrap.DocumentManager} this
29683 * @param {Object} formData
29688 * Fire when remove the file
29689 * @param {Roo.bootstrap.DocumentManager} this
29690 * @param {Object} file
29695 * Fire after refresh the file
29696 * @param {Roo.bootstrap.DocumentManager} this
29701 * Fire after click the image
29702 * @param {Roo.bootstrap.DocumentManager} this
29703 * @param {Object} file
29708 * Fire when upload a image and editable set to true
29709 * @param {Roo.bootstrap.DocumentManager} this
29710 * @param {Object} file
29714 * @event beforeselectfile
29715 * Fire before select file
29716 * @param {Roo.bootstrap.DocumentManager} this
29718 "beforeselectfile" : true,
29721 * Fire before process file
29722 * @param {Roo.bootstrap.DocumentManager} this
29723 * @param {Object} file
29727 * @event previewrendered
29728 * Fire when preview rendered
29729 * @param {Roo.bootstrap.DocumentManager} this
29730 * @param {Object} file
29732 "previewrendered" : true,
29735 "previewResize" : true
29740 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
29749 paramName : 'imageUpload',
29750 toolTipName : 'filename',
29753 labelAlign : 'left',
29763 getAutoCreate : function()
29765 var managerWidget = {
29767 cls : 'roo-document-manager',
29771 cls : 'roo-document-manager-selector',
29776 cls : 'roo-document-manager-uploader',
29780 cls : 'roo-document-manager-upload-btn',
29781 html : '<i class="fa fa-plus"></i>'
29792 cls : 'column col-md-12',
29797 if(this.fieldLabel.length){
29802 cls : 'column col-md-12',
29803 html : this.fieldLabel
29807 cls : 'column col-md-12',
29812 if(this.labelAlign == 'left'){
29817 html : this.fieldLabel
29826 if(this.labelWidth > 12){
29827 content[0].style = "width: " + this.labelWidth + 'px';
29830 if(this.labelWidth < 13 && this.labelmd == 0){
29831 this.labelmd = this.labelWidth;
29834 if(this.labellg > 0){
29835 content[0].cls += ' col-lg-' + this.labellg;
29836 content[1].cls += ' col-lg-' + (12 - this.labellg);
29839 if(this.labelmd > 0){
29840 content[0].cls += ' col-md-' + this.labelmd;
29841 content[1].cls += ' col-md-' + (12 - this.labelmd);
29844 if(this.labelsm > 0){
29845 content[0].cls += ' col-sm-' + this.labelsm;
29846 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29849 if(this.labelxs > 0){
29850 content[0].cls += ' col-xs-' + this.labelxs;
29851 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29859 cls : 'row clearfix',
29867 initEvents : function()
29869 this.managerEl = this.el.select('.roo-document-manager', true).first();
29870 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29872 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29873 this.selectorEl.hide();
29876 this.selectorEl.attr('multiple', 'multiple');
29879 this.selectorEl.on('change', this.onFileSelected, this);
29881 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29882 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29884 this.uploader.on('click', this.onUploaderClick, this);
29886 this.renderProgressDialog();
29890 window.addEventListener("resize", function() { _this.refresh(); } );
29892 this.fireEvent('initial', this);
29895 renderProgressDialog : function()
29899 this.progressDialog = new Roo.bootstrap.Modal({
29900 cls : 'roo-document-manager-progress-dialog',
29901 allow_close : false,
29912 btnclick : function() {
29913 _this.uploadCancel();
29919 this.progressDialog.render(Roo.get(document.body));
29921 this.progress = new Roo.bootstrap.Progress({
29922 cls : 'roo-document-manager-progress',
29927 this.progress.render(this.progressDialog.getChildContainer());
29929 this.progressBar = new Roo.bootstrap.ProgressBar({
29930 cls : 'roo-document-manager-progress-bar',
29933 aria_valuemax : 12,
29937 this.progressBar.render(this.progress.getChildContainer());
29940 onUploaderClick : function(e)
29942 e.preventDefault();
29944 if(this.fireEvent('beforeselectfile', this) != false){
29945 this.selectorEl.dom.click();
29950 onFileSelected : function(e)
29952 e.preventDefault();
29954 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29958 Roo.each(this.selectorEl.dom.files, function(file){
29959 if(this.fireEvent('inspect', this, file) != false){
29960 this.files.push(file);
29970 this.selectorEl.dom.value = '';
29972 if(!this.files || !this.files.length){
29976 if(this.boxes > 0 && this.files.length > this.boxes){
29977 this.files = this.files.slice(0, this.boxes);
29980 this.uploader.show();
29982 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29983 this.uploader.hide();
29992 Roo.each(this.files, function(file){
29994 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29995 var f = this.renderPreview(file);
30000 if(file.type.indexOf('image') != -1){
30001 this.delegates.push(
30003 _this.process(file);
30004 }).createDelegate(this)
30012 _this.process(file);
30013 }).createDelegate(this)
30018 this.files = files;
30020 this.delegates = this.delegates.concat(docs);
30022 if(!this.delegates.length){
30027 this.progressBar.aria_valuemax = this.delegates.length;
30034 arrange : function()
30036 if(!this.delegates.length){
30037 this.progressDialog.hide();
30042 var delegate = this.delegates.shift();
30044 this.progressDialog.show();
30046 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
30048 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
30053 refresh : function()
30055 this.uploader.show();
30057 if(this.boxes > 0 && this.files.length > this.boxes - 1){
30058 this.uploader.hide();
30061 Roo.isTouch ? this.closable(false) : this.closable(true);
30063 this.fireEvent('refresh', this);
30066 onRemove : function(e, el, o)
30068 e.preventDefault();
30070 this.fireEvent('remove', this, o);
30074 remove : function(o)
30078 Roo.each(this.files, function(file){
30079 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
30088 this.files = files;
30095 Roo.each(this.files, function(file){
30100 file.target.remove();
30109 onClick : function(e, el, o)
30111 e.preventDefault();
30113 this.fireEvent('click', this, o);
30117 closable : function(closable)
30119 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
30121 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30133 xhrOnLoad : function(xhr)
30135 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30139 if (xhr.readyState !== 4) {
30141 this.fireEvent('exception', this, xhr);
30145 var response = Roo.decode(xhr.responseText);
30147 if(!response.success){
30149 this.fireEvent('exception', this, xhr);
30153 var file = this.renderPreview(response.data);
30155 this.files.push(file);
30159 this.fireEvent('afterupload', this, xhr);
30163 xhrOnError : function(xhr)
30165 Roo.log('xhr on error');
30167 var response = Roo.decode(xhr.responseText);
30174 process : function(file)
30176 if(this.fireEvent('process', this, file) !== false){
30177 if(this.editable && file.type.indexOf('image') != -1){
30178 this.fireEvent('edit', this, file);
30182 this.uploadStart(file, false);
30189 uploadStart : function(file, crop)
30191 this.xhr = new XMLHttpRequest();
30193 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30198 file.xhr = this.xhr;
30200 this.managerEl.createChild({
30202 cls : 'roo-document-manager-loading',
30206 tooltip : file.name,
30207 cls : 'roo-document-manager-thumb',
30208 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30214 this.xhr.open(this.method, this.url, true);
30217 "Accept": "application/json",
30218 "Cache-Control": "no-cache",
30219 "X-Requested-With": "XMLHttpRequest"
30222 for (var headerName in headers) {
30223 var headerValue = headers[headerName];
30225 this.xhr.setRequestHeader(headerName, headerValue);
30231 this.xhr.onload = function()
30233 _this.xhrOnLoad(_this.xhr);
30236 this.xhr.onerror = function()
30238 _this.xhrOnError(_this.xhr);
30241 var formData = new FormData();
30243 formData.append('returnHTML', 'NO');
30246 formData.append('crop', crop);
30249 formData.append(this.paramName, file, file.name);
30256 if(this.fireEvent('prepare', this, formData, options) != false){
30258 if(options.manually){
30262 this.xhr.send(formData);
30266 this.uploadCancel();
30269 uploadCancel : function()
30275 this.delegates = [];
30277 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30284 renderPreview : function(file)
30286 if(typeof(file.target) != 'undefined' && file.target){
30290 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
30292 var previewEl = this.managerEl.createChild({
30294 cls : 'roo-document-manager-preview',
30298 tooltip : file[this.toolTipName],
30299 cls : 'roo-document-manager-thumb',
30300 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
30305 html : '<i class="fa fa-times-circle"></i>'
30310 var close = previewEl.select('button.close', true).first();
30312 close.on('click', this.onRemove, this, file);
30314 file.target = previewEl;
30316 var image = previewEl.select('img', true).first();
30320 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
30322 image.on('click', this.onClick, this, file);
30324 this.fireEvent('previewrendered', this, file);
30330 onPreviewLoad : function(file, image)
30332 if(typeof(file.target) == 'undefined' || !file.target){
30336 var width = image.dom.naturalWidth || image.dom.width;
30337 var height = image.dom.naturalHeight || image.dom.height;
30339 if(!this.previewResize) {
30343 if(width > height){
30344 file.target.addClass('wide');
30348 file.target.addClass('tall');
30353 uploadFromSource : function(file, crop)
30355 this.xhr = new XMLHttpRequest();
30357 this.managerEl.createChild({
30359 cls : 'roo-document-manager-loading',
30363 tooltip : file.name,
30364 cls : 'roo-document-manager-thumb',
30365 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30371 this.xhr.open(this.method, this.url, true);
30374 "Accept": "application/json",
30375 "Cache-Control": "no-cache",
30376 "X-Requested-With": "XMLHttpRequest"
30379 for (var headerName in headers) {
30380 var headerValue = headers[headerName];
30382 this.xhr.setRequestHeader(headerName, headerValue);
30388 this.xhr.onload = function()
30390 _this.xhrOnLoad(_this.xhr);
30393 this.xhr.onerror = function()
30395 _this.xhrOnError(_this.xhr);
30398 var formData = new FormData();
30400 formData.append('returnHTML', 'NO');
30402 formData.append('crop', crop);
30404 if(typeof(file.filename) != 'undefined'){
30405 formData.append('filename', file.filename);
30408 if(typeof(file.mimetype) != 'undefined'){
30409 formData.append('mimetype', file.mimetype);
30414 if(this.fireEvent('prepare', this, formData) != false){
30415 this.xhr.send(formData);
30425 * @class Roo.bootstrap.DocumentViewer
30426 * @extends Roo.bootstrap.Component
30427 * Bootstrap DocumentViewer class
30428 * @cfg {Boolean} showDownload (true|false) show download button (default true)
30429 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30432 * Create a new DocumentViewer
30433 * @param {Object} config The config object
30436 Roo.bootstrap.DocumentViewer = function(config){
30437 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30442 * Fire after initEvent
30443 * @param {Roo.bootstrap.DocumentViewer} this
30449 * @param {Roo.bootstrap.DocumentViewer} this
30454 * Fire after download button
30455 * @param {Roo.bootstrap.DocumentViewer} this
30460 * Fire after trash button
30461 * @param {Roo.bootstrap.DocumentViewer} this
30468 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
30470 showDownload : true,
30474 getAutoCreate : function()
30478 cls : 'roo-document-viewer',
30482 cls : 'roo-document-viewer-body',
30486 cls : 'roo-document-viewer-thumb',
30490 cls : 'roo-document-viewer-image'
30498 cls : 'roo-document-viewer-footer',
30501 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30505 cls : 'btn-group roo-document-viewer-download',
30509 cls : 'btn btn-default',
30510 html : '<i class="fa fa-download"></i>'
30516 cls : 'btn-group roo-document-viewer-trash',
30520 cls : 'btn btn-default',
30521 html : '<i class="fa fa-trash"></i>'
30534 initEvents : function()
30536 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30537 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30539 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30540 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30542 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30543 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30545 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30546 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30548 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30549 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30551 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30552 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30554 this.bodyEl.on('click', this.onClick, this);
30555 this.downloadBtn.on('click', this.onDownload, this);
30556 this.trashBtn.on('click', this.onTrash, this);
30558 this.downloadBtn.hide();
30559 this.trashBtn.hide();
30561 if(this.showDownload){
30562 this.downloadBtn.show();
30565 if(this.showTrash){
30566 this.trashBtn.show();
30569 if(!this.showDownload && !this.showTrash) {
30570 this.footerEl.hide();
30575 initial : function()
30577 this.fireEvent('initial', this);
30581 onClick : function(e)
30583 e.preventDefault();
30585 this.fireEvent('click', this);
30588 onDownload : function(e)
30590 e.preventDefault();
30592 this.fireEvent('download', this);
30595 onTrash : function(e)
30597 e.preventDefault();
30599 this.fireEvent('trash', this);
30611 * @class Roo.bootstrap.NavProgressBar
30612 * @extends Roo.bootstrap.Component
30613 * Bootstrap NavProgressBar class
30616 * Create a new nav progress bar
30617 * @param {Object} config The config object
30620 Roo.bootstrap.NavProgressBar = function(config){
30621 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30623 this.bullets = this.bullets || [];
30625 // Roo.bootstrap.NavProgressBar.register(this);
30629 * Fires when the active item changes
30630 * @param {Roo.bootstrap.NavProgressBar} this
30631 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30632 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
30639 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
30644 getAutoCreate : function()
30646 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30650 cls : 'roo-navigation-bar-group',
30654 cls : 'roo-navigation-top-bar'
30658 cls : 'roo-navigation-bullets-bar',
30662 cls : 'roo-navigation-bar'
30669 cls : 'roo-navigation-bottom-bar'
30679 initEvents: function()
30684 onRender : function(ct, position)
30686 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30688 if(this.bullets.length){
30689 Roo.each(this.bullets, function(b){
30698 addItem : function(cfg)
30700 var item = new Roo.bootstrap.NavProgressItem(cfg);
30702 item.parentId = this.id;
30703 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30706 var top = new Roo.bootstrap.Element({
30708 cls : 'roo-navigation-bar-text'
30711 var bottom = new Roo.bootstrap.Element({
30713 cls : 'roo-navigation-bar-text'
30716 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30717 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30719 var topText = new Roo.bootstrap.Element({
30721 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30724 var bottomText = new Roo.bootstrap.Element({
30726 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30729 topText.onRender(top.el, null);
30730 bottomText.onRender(bottom.el, null);
30733 item.bottomEl = bottom;
30736 this.barItems.push(item);
30741 getActive : function()
30743 var active = false;
30745 Roo.each(this.barItems, function(v){
30747 if (!v.isActive()) {
30759 setActiveItem : function(item)
30763 Roo.each(this.barItems, function(v){
30764 if (v.rid == item.rid) {
30768 if (v.isActive()) {
30769 v.setActive(false);
30774 item.setActive(true);
30776 this.fireEvent('changed', this, item, prev);
30779 getBarItem: function(rid)
30783 Roo.each(this.barItems, function(e) {
30784 if (e.rid != rid) {
30795 indexOfItem : function(item)
30799 Roo.each(this.barItems, function(v, i){
30801 if (v.rid != item.rid) {
30812 setActiveNext : function()
30814 var i = this.indexOfItem(this.getActive());
30816 if (i > this.barItems.length) {
30820 this.setActiveItem(this.barItems[i+1]);
30823 setActivePrev : function()
30825 var i = this.indexOfItem(this.getActive());
30831 this.setActiveItem(this.barItems[i-1]);
30834 format : function()
30836 if(!this.barItems.length){
30840 var width = 100 / this.barItems.length;
30842 Roo.each(this.barItems, function(i){
30843 i.el.setStyle('width', width + '%');
30844 i.topEl.el.setStyle('width', width + '%');
30845 i.bottomEl.el.setStyle('width', width + '%');
30854 * Nav Progress Item
30859 * @class Roo.bootstrap.NavProgressItem
30860 * @extends Roo.bootstrap.Component
30861 * Bootstrap NavProgressItem class
30862 * @cfg {String} rid the reference id
30863 * @cfg {Boolean} active (true|false) Is item active default false
30864 * @cfg {Boolean} disabled (true|false) Is item active default false
30865 * @cfg {String} html
30866 * @cfg {String} position (top|bottom) text position default bottom
30867 * @cfg {String} icon show icon instead of number
30870 * Create a new NavProgressItem
30871 * @param {Object} config The config object
30873 Roo.bootstrap.NavProgressItem = function(config){
30874 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30879 * The raw click event for the entire grid.
30880 * @param {Roo.bootstrap.NavProgressItem} this
30881 * @param {Roo.EventObject} e
30888 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30894 position : 'bottom',
30897 getAutoCreate : function()
30899 var iconCls = 'roo-navigation-bar-item-icon';
30901 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30905 cls: 'roo-navigation-bar-item',
30915 cfg.cls += ' active';
30918 cfg.cls += ' disabled';
30924 disable : function()
30926 this.setDisabled(true);
30929 enable : function()
30931 this.setDisabled(false);
30934 initEvents: function()
30936 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30938 this.iconEl.on('click', this.onClick, this);
30941 onClick : function(e)
30943 e.preventDefault();
30949 if(this.fireEvent('click', this, e) === false){
30953 this.parent().setActiveItem(this);
30956 isActive: function ()
30958 return this.active;
30961 setActive : function(state)
30963 if(this.active == state){
30967 this.active = state;
30970 this.el.addClass('active');
30974 this.el.removeClass('active');
30979 setDisabled : function(state)
30981 if(this.disabled == state){
30985 this.disabled = state;
30988 this.el.addClass('disabled');
30992 this.el.removeClass('disabled');
30995 tooltipEl : function()
30997 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
31010 * @class Roo.bootstrap.FieldLabel
31011 * @extends Roo.bootstrap.Component
31012 * Bootstrap FieldLabel class
31013 * @cfg {String} html contents of the element
31014 * @cfg {String} tag tag of the element default label
31015 * @cfg {String} cls class of the element
31016 * @cfg {String} target label target
31017 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
31018 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
31019 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
31020 * @cfg {String} iconTooltip default "This field is required"
31021 * @cfg {String} indicatorpos (left|right) default left
31024 * Create a new FieldLabel
31025 * @param {Object} config The config object
31028 Roo.bootstrap.FieldLabel = function(config){
31029 Roo.bootstrap.Element.superclass.constructor.call(this, config);
31034 * Fires after the field has been marked as invalid.
31035 * @param {Roo.form.FieldLabel} this
31036 * @param {String} msg The validation message
31041 * Fires after the field has been validated with no errors.
31042 * @param {Roo.form.FieldLabel} this
31048 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
31055 invalidClass : 'has-warning',
31056 validClass : 'has-success',
31057 iconTooltip : 'This field is required',
31058 indicatorpos : 'left',
31060 getAutoCreate : function(){
31063 if (!this.allowBlank) {
31069 cls : 'roo-bootstrap-field-label ' + this.cls,
31074 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
31075 tooltip : this.iconTooltip
31084 if(this.indicatorpos == 'right'){
31087 cls : 'roo-bootstrap-field-label ' + this.cls,
31096 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
31097 tooltip : this.iconTooltip
31106 initEvents: function()
31108 Roo.bootstrap.Element.superclass.initEvents.call(this);
31110 this.indicator = this.indicatorEl();
31112 if(this.indicator){
31113 this.indicator.removeClass('visible');
31114 this.indicator.addClass('invisible');
31117 Roo.bootstrap.FieldLabel.register(this);
31120 indicatorEl : function()
31122 var indicator = this.el.select('i.roo-required-indicator',true).first();
31133 * Mark this field as valid
31135 markValid : function()
31137 if(this.indicator){
31138 this.indicator.removeClass('visible');
31139 this.indicator.addClass('invisible');
31141 if (Roo.bootstrap.version == 3) {
31142 this.el.removeClass(this.invalidClass);
31143 this.el.addClass(this.validClass);
31145 this.el.removeClass('is-invalid');
31146 this.el.addClass('is-valid');
31150 this.fireEvent('valid', this);
31154 * Mark this field as invalid
31155 * @param {String} msg The validation message
31157 markInvalid : function(msg)
31159 if(this.indicator){
31160 this.indicator.removeClass('invisible');
31161 this.indicator.addClass('visible');
31163 if (Roo.bootstrap.version == 3) {
31164 this.el.removeClass(this.validClass);
31165 this.el.addClass(this.invalidClass);
31167 this.el.removeClass('is-valid');
31168 this.el.addClass('is-invalid');
31172 this.fireEvent('invalid', this, msg);
31178 Roo.apply(Roo.bootstrap.FieldLabel, {
31183 * register a FieldLabel Group
31184 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
31186 register : function(label)
31188 if(this.groups.hasOwnProperty(label.target)){
31192 this.groups[label.target] = label;
31196 * fetch a FieldLabel Group based on the target
31197 * @param {string} target
31198 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
31200 get: function(target) {
31201 if (typeof(this.groups[target]) == 'undefined') {
31205 return this.groups[target] ;
31214 * page DateSplitField.
31220 * @class Roo.bootstrap.DateSplitField
31221 * @extends Roo.bootstrap.Component
31222 * Bootstrap DateSplitField class
31223 * @cfg {string} fieldLabel - the label associated
31224 * @cfg {Number} labelWidth set the width of label (0-12)
31225 * @cfg {String} labelAlign (top|left)
31226 * @cfg {Boolean} dayAllowBlank (true|false) default false
31227 * @cfg {Boolean} monthAllowBlank (true|false) default false
31228 * @cfg {Boolean} yearAllowBlank (true|false) default false
31229 * @cfg {string} dayPlaceholder
31230 * @cfg {string} monthPlaceholder
31231 * @cfg {string} yearPlaceholder
31232 * @cfg {string} dayFormat default 'd'
31233 * @cfg {string} monthFormat default 'm'
31234 * @cfg {string} yearFormat default 'Y'
31235 * @cfg {Number} labellg set the width of label (1-12)
31236 * @cfg {Number} labelmd set the width of label (1-12)
31237 * @cfg {Number} labelsm set the width of label (1-12)
31238 * @cfg {Number} labelxs set the width of label (1-12)
31242 * Create a new DateSplitField
31243 * @param {Object} config The config object
31246 Roo.bootstrap.DateSplitField = function(config){
31247 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
31253 * getting the data of years
31254 * @param {Roo.bootstrap.DateSplitField} this
31255 * @param {Object} years
31260 * getting the data of days
31261 * @param {Roo.bootstrap.DateSplitField} this
31262 * @param {Object} days
31267 * Fires after the field has been marked as invalid.
31268 * @param {Roo.form.Field} this
31269 * @param {String} msg The validation message
31274 * Fires after the field has been validated with no errors.
31275 * @param {Roo.form.Field} this
31281 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
31284 labelAlign : 'top',
31286 dayAllowBlank : false,
31287 monthAllowBlank : false,
31288 yearAllowBlank : false,
31289 dayPlaceholder : '',
31290 monthPlaceholder : '',
31291 yearPlaceholder : '',
31295 isFormField : true,
31301 getAutoCreate : function()
31305 cls : 'row roo-date-split-field-group',
31310 cls : 'form-hidden-field roo-date-split-field-group-value',
31316 var labelCls = 'col-md-12';
31317 var contentCls = 'col-md-4';
31319 if(this.fieldLabel){
31323 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
31327 html : this.fieldLabel
31332 if(this.labelAlign == 'left'){
31334 if(this.labelWidth > 12){
31335 label.style = "width: " + this.labelWidth + 'px';
31338 if(this.labelWidth < 13 && this.labelmd == 0){
31339 this.labelmd = this.labelWidth;
31342 if(this.labellg > 0){
31343 labelCls = ' col-lg-' + this.labellg;
31344 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
31347 if(this.labelmd > 0){
31348 labelCls = ' col-md-' + this.labelmd;
31349 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
31352 if(this.labelsm > 0){
31353 labelCls = ' col-sm-' + this.labelsm;
31354 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
31357 if(this.labelxs > 0){
31358 labelCls = ' col-xs-' + this.labelxs;
31359 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
31363 label.cls += ' ' + labelCls;
31365 cfg.cn.push(label);
31368 Roo.each(['day', 'month', 'year'], function(t){
31371 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
31378 inputEl: function ()
31380 return this.el.select('.roo-date-split-field-group-value', true).first();
31383 onRender : function(ct, position)
31387 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31389 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
31391 this.dayField = new Roo.bootstrap.ComboBox({
31392 allowBlank : this.dayAllowBlank,
31393 alwaysQuery : true,
31394 displayField : 'value',
31397 forceSelection : true,
31399 placeholder : this.dayPlaceholder,
31400 selectOnFocus : true,
31401 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31402 triggerAction : 'all',
31404 valueField : 'value',
31405 store : new Roo.data.SimpleStore({
31406 data : (function() {
31408 _this.fireEvent('days', _this, days);
31411 fields : [ 'value' ]
31414 select : function (_self, record, index)
31416 _this.setValue(_this.getValue());
31421 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31423 this.monthField = new Roo.bootstrap.MonthField({
31424 after : '<i class=\"fa fa-calendar\"></i>',
31425 allowBlank : this.monthAllowBlank,
31426 placeholder : this.monthPlaceholder,
31429 render : function (_self)
31431 this.el.select('span.input-group-addon', true).first().on('click', function(e){
31432 e.preventDefault();
31436 select : function (_self, oldvalue, newvalue)
31438 _this.setValue(_this.getValue());
31443 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31445 this.yearField = new Roo.bootstrap.ComboBox({
31446 allowBlank : this.yearAllowBlank,
31447 alwaysQuery : true,
31448 displayField : 'value',
31451 forceSelection : true,
31453 placeholder : this.yearPlaceholder,
31454 selectOnFocus : true,
31455 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31456 triggerAction : 'all',
31458 valueField : 'value',
31459 store : new Roo.data.SimpleStore({
31460 data : (function() {
31462 _this.fireEvent('years', _this, years);
31465 fields : [ 'value' ]
31468 select : function (_self, record, index)
31470 _this.setValue(_this.getValue());
31475 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31478 setValue : function(v, format)
31480 this.inputEl.dom.value = v;
31482 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31484 var d = Date.parseDate(v, f);
31491 this.setDay(d.format(this.dayFormat));
31492 this.setMonth(d.format(this.monthFormat));
31493 this.setYear(d.format(this.yearFormat));
31500 setDay : function(v)
31502 this.dayField.setValue(v);
31503 this.inputEl.dom.value = this.getValue();
31508 setMonth : function(v)
31510 this.monthField.setValue(v, true);
31511 this.inputEl.dom.value = this.getValue();
31516 setYear : function(v)
31518 this.yearField.setValue(v);
31519 this.inputEl.dom.value = this.getValue();
31524 getDay : function()
31526 return this.dayField.getValue();
31529 getMonth : function()
31531 return this.monthField.getValue();
31534 getYear : function()
31536 return this.yearField.getValue();
31539 getValue : function()
31541 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31543 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31553 this.inputEl.dom.value = '';
31558 validate : function()
31560 var d = this.dayField.validate();
31561 var m = this.monthField.validate();
31562 var y = this.yearField.validate();
31567 (!this.dayAllowBlank && !d) ||
31568 (!this.monthAllowBlank && !m) ||
31569 (!this.yearAllowBlank && !y)
31574 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31583 this.markInvalid();
31588 markValid : function()
31591 var label = this.el.select('label', true).first();
31592 var icon = this.el.select('i.fa-star', true).first();
31598 this.fireEvent('valid', this);
31602 * Mark this field as invalid
31603 * @param {String} msg The validation message
31605 markInvalid : function(msg)
31608 var label = this.el.select('label', true).first();
31609 var icon = this.el.select('i.fa-star', true).first();
31611 if(label && !icon){
31612 this.el.select('.roo-date-split-field-label', true).createChild({
31614 cls : 'text-danger fa fa-lg fa-star',
31615 tooltip : 'This field is required',
31616 style : 'margin-right:5px;'
31620 this.fireEvent('invalid', this, msg);
31623 clearInvalid : function()
31625 var label = this.el.select('label', true).first();
31626 var icon = this.el.select('i.fa-star', true).first();
31632 this.fireEvent('valid', this);
31635 getName: function()
31645 * http://masonry.desandro.com
31647 * The idea is to render all the bricks based on vertical width...
31649 * The original code extends 'outlayer' - we might need to use that....
31655 * @class Roo.bootstrap.LayoutMasonry
31656 * @extends Roo.bootstrap.Component
31657 * Bootstrap Layout Masonry class
31660 * Create a new Element
31661 * @param {Object} config The config object
31664 Roo.bootstrap.LayoutMasonry = function(config){
31666 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31670 Roo.bootstrap.LayoutMasonry.register(this);
31676 * Fire after layout the items
31677 * @param {Roo.bootstrap.LayoutMasonry} this
31678 * @param {Roo.EventObject} e
31685 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
31688 * @cfg {Boolean} isLayoutInstant = no animation?
31690 isLayoutInstant : false, // needed?
31693 * @cfg {Number} boxWidth width of the columns
31698 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
31703 * @cfg {Number} padWidth padding below box..
31708 * @cfg {Number} gutter gutter width..
31713 * @cfg {Number} maxCols maximum number of columns
31719 * @cfg {Boolean} isAutoInitial defalut true
31721 isAutoInitial : true,
31726 * @cfg {Boolean} isHorizontal defalut false
31728 isHorizontal : false,
31730 currentSize : null,
31736 bricks: null, //CompositeElement
31740 _isLayoutInited : false,
31742 // isAlternative : false, // only use for vertical layout...
31745 * @cfg {Number} alternativePadWidth padding below box..
31747 alternativePadWidth : 50,
31749 selectedBrick : [],
31751 getAutoCreate : function(){
31753 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31757 cls: 'blog-masonary-wrapper ' + this.cls,
31759 cls : 'mas-boxes masonary'
31766 getChildContainer: function( )
31768 if (this.boxesEl) {
31769 return this.boxesEl;
31772 this.boxesEl = this.el.select('.mas-boxes').first();
31774 return this.boxesEl;
31778 initEvents : function()
31782 if(this.isAutoInitial){
31783 Roo.log('hook children rendered');
31784 this.on('childrenrendered', function() {
31785 Roo.log('children rendered');
31791 initial : function()
31793 this.selectedBrick = [];
31795 this.currentSize = this.el.getBox(true);
31797 Roo.EventManager.onWindowResize(this.resize, this);
31799 if(!this.isAutoInitial){
31807 //this.layout.defer(500,this);
31811 resize : function()
31813 var cs = this.el.getBox(true);
31816 this.currentSize.width == cs.width &&
31817 this.currentSize.x == cs.x &&
31818 this.currentSize.height == cs.height &&
31819 this.currentSize.y == cs.y
31821 Roo.log("no change in with or X or Y");
31825 this.currentSize = cs;
31831 layout : function()
31833 this._resetLayout();
31835 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31837 this.layoutItems( isInstant );
31839 this._isLayoutInited = true;
31841 this.fireEvent('layout', this);
31845 _resetLayout : function()
31847 if(this.isHorizontal){
31848 this.horizontalMeasureColumns();
31852 this.verticalMeasureColumns();
31856 verticalMeasureColumns : function()
31858 this.getContainerWidth();
31860 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31861 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31865 var boxWidth = this.boxWidth + this.padWidth;
31867 if(this.containerWidth < this.boxWidth){
31868 boxWidth = this.containerWidth
31871 var containerWidth = this.containerWidth;
31873 var cols = Math.floor(containerWidth / boxWidth);
31875 this.cols = Math.max( cols, 1 );
31877 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31879 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31881 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31883 this.colWidth = boxWidth + avail - this.padWidth;
31885 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31886 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31889 horizontalMeasureColumns : function()
31891 this.getContainerWidth();
31893 var boxWidth = this.boxWidth;
31895 if(this.containerWidth < boxWidth){
31896 boxWidth = this.containerWidth;
31899 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31901 this.el.setHeight(boxWidth);
31905 getContainerWidth : function()
31907 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31910 layoutItems : function( isInstant )
31912 Roo.log(this.bricks);
31914 var items = Roo.apply([], this.bricks);
31916 if(this.isHorizontal){
31917 this._horizontalLayoutItems( items , isInstant );
31921 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31922 // this._verticalAlternativeLayoutItems( items , isInstant );
31926 this._verticalLayoutItems( items , isInstant );
31930 _verticalLayoutItems : function ( items , isInstant)
31932 if ( !items || !items.length ) {
31937 ['xs', 'xs', 'xs', 'tall'],
31938 ['xs', 'xs', 'tall'],
31939 ['xs', 'xs', 'sm'],
31940 ['xs', 'xs', 'xs'],
31946 ['sm', 'xs', 'xs'],
31950 ['tall', 'xs', 'xs', 'xs'],
31951 ['tall', 'xs', 'xs'],
31963 Roo.each(items, function(item, k){
31965 switch (item.size) {
31966 // these layouts take up a full box,
31977 boxes.push([item]);
32000 var filterPattern = function(box, length)
32008 var pattern = box.slice(0, length);
32012 Roo.each(pattern, function(i){
32013 format.push(i.size);
32016 Roo.each(standard, function(s){
32018 if(String(s) != String(format)){
32027 if(!match && length == 1){
32032 filterPattern(box, length - 1);
32036 queue.push(pattern);
32038 box = box.slice(length, box.length);
32040 filterPattern(box, 4);
32046 Roo.each(boxes, function(box, k){
32052 if(box.length == 1){
32057 filterPattern(box, 4);
32061 this._processVerticalLayoutQueue( queue, isInstant );
32065 // _verticalAlternativeLayoutItems : function( items , isInstant )
32067 // if ( !items || !items.length ) {
32071 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
32075 _horizontalLayoutItems : function ( items , isInstant)
32077 if ( !items || !items.length || items.length < 3) {
32083 var eItems = items.slice(0, 3);
32085 items = items.slice(3, items.length);
32088 ['xs', 'xs', 'xs', 'wide'],
32089 ['xs', 'xs', 'wide'],
32090 ['xs', 'xs', 'sm'],
32091 ['xs', 'xs', 'xs'],
32097 ['sm', 'xs', 'xs'],
32101 ['wide', 'xs', 'xs', 'xs'],
32102 ['wide', 'xs', 'xs'],
32115 Roo.each(items, function(item, k){
32117 switch (item.size) {
32128 boxes.push([item]);
32152 var filterPattern = function(box, length)
32160 var pattern = box.slice(0, length);
32164 Roo.each(pattern, function(i){
32165 format.push(i.size);
32168 Roo.each(standard, function(s){
32170 if(String(s) != String(format)){
32179 if(!match && length == 1){
32184 filterPattern(box, length - 1);
32188 queue.push(pattern);
32190 box = box.slice(length, box.length);
32192 filterPattern(box, 4);
32198 Roo.each(boxes, function(box, k){
32204 if(box.length == 1){
32209 filterPattern(box, 4);
32216 var pos = this.el.getBox(true);
32220 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32222 var hit_end = false;
32224 Roo.each(queue, function(box){
32228 Roo.each(box, function(b){
32230 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32240 Roo.each(box, function(b){
32242 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32245 mx = Math.max(mx, b.x);
32249 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
32253 Roo.each(box, function(b){
32255 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32269 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
32272 /** Sets position of item in DOM
32273 * @param {Element} item
32274 * @param {Number} x - horizontal position
32275 * @param {Number} y - vertical position
32276 * @param {Boolean} isInstant - disables transitions
32278 _processVerticalLayoutQueue : function( queue, isInstant )
32280 var pos = this.el.getBox(true);
32285 for (var i = 0; i < this.cols; i++){
32289 Roo.each(queue, function(box, k){
32291 var col = k % this.cols;
32293 Roo.each(box, function(b,kk){
32295 b.el.position('absolute');
32297 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32298 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32300 if(b.size == 'md-left' || b.size == 'md-right'){
32301 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32302 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32305 b.el.setWidth(width);
32306 b.el.setHeight(height);
32308 b.el.select('iframe',true).setSize(width,height);
32312 for (var i = 0; i < this.cols; i++){
32314 if(maxY[i] < maxY[col]){
32319 col = Math.min(col, i);
32323 x = pos.x + col * (this.colWidth + this.padWidth);
32327 var positions = [];
32329 switch (box.length){
32331 positions = this.getVerticalOneBoxColPositions(x, y, box);
32334 positions = this.getVerticalTwoBoxColPositions(x, y, box);
32337 positions = this.getVerticalThreeBoxColPositions(x, y, box);
32340 positions = this.getVerticalFourBoxColPositions(x, y, box);
32346 Roo.each(box, function(b,kk){
32348 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32350 var sz = b.el.getSize();
32352 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
32360 for (var i = 0; i < this.cols; i++){
32361 mY = Math.max(mY, maxY[i]);
32364 this.el.setHeight(mY - pos.y);
32368 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
32370 // var pos = this.el.getBox(true);
32373 // var maxX = pos.right;
32375 // var maxHeight = 0;
32377 // Roo.each(items, function(item, k){
32381 // item.el.position('absolute');
32383 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
32385 // item.el.setWidth(width);
32387 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
32389 // item.el.setHeight(height);
32392 // item.el.setXY([x, y], isInstant ? false : true);
32394 // item.el.setXY([maxX - width, y], isInstant ? false : true);
32397 // y = y + height + this.alternativePadWidth;
32399 // maxHeight = maxHeight + height + this.alternativePadWidth;
32403 // this.el.setHeight(maxHeight);
32407 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32409 var pos = this.el.getBox(true);
32414 var maxX = pos.right;
32416 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32418 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32420 Roo.each(queue, function(box, k){
32422 Roo.each(box, function(b, kk){
32424 b.el.position('absolute');
32426 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32427 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32429 if(b.size == 'md-left' || b.size == 'md-right'){
32430 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32431 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32434 b.el.setWidth(width);
32435 b.el.setHeight(height);
32443 var positions = [];
32445 switch (box.length){
32447 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32450 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32453 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32456 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32462 Roo.each(box, function(b,kk){
32464 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32466 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32474 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32476 Roo.each(eItems, function(b,k){
32478 b.size = (k == 0) ? 'sm' : 'xs';
32479 b.x = (k == 0) ? 2 : 1;
32480 b.y = (k == 0) ? 2 : 1;
32482 b.el.position('absolute');
32484 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32486 b.el.setWidth(width);
32488 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32490 b.el.setHeight(height);
32494 var positions = [];
32497 x : maxX - this.unitWidth * 2 - this.gutter,
32502 x : maxX - this.unitWidth,
32503 y : minY + (this.unitWidth + this.gutter) * 2
32507 x : maxX - this.unitWidth * 3 - this.gutter * 2,
32511 Roo.each(eItems, function(b,k){
32513 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32519 getVerticalOneBoxColPositions : function(x, y, box)
32523 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32525 if(box[0].size == 'md-left'){
32529 if(box[0].size == 'md-right'){
32534 x : x + (this.unitWidth + this.gutter) * rand,
32541 getVerticalTwoBoxColPositions : function(x, y, box)
32545 if(box[0].size == 'xs'){
32549 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32553 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32567 x : x + (this.unitWidth + this.gutter) * 2,
32568 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32575 getVerticalThreeBoxColPositions : function(x, y, box)
32579 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32587 x : x + (this.unitWidth + this.gutter) * 1,
32592 x : x + (this.unitWidth + this.gutter) * 2,
32600 if(box[0].size == 'xs' && box[1].size == 'xs'){
32609 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32613 x : x + (this.unitWidth + this.gutter) * 1,
32627 x : x + (this.unitWidth + this.gutter) * 2,
32632 x : x + (this.unitWidth + this.gutter) * 2,
32633 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32640 getVerticalFourBoxColPositions : function(x, y, box)
32644 if(box[0].size == 'xs'){
32653 y : y + (this.unitHeight + this.gutter) * 1
32658 y : y + (this.unitHeight + this.gutter) * 2
32662 x : x + (this.unitWidth + this.gutter) * 1,
32676 x : x + (this.unitWidth + this.gutter) * 2,
32681 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32682 y : y + (this.unitHeight + this.gutter) * 1
32686 x : x + (this.unitWidth + this.gutter) * 2,
32687 y : y + (this.unitWidth + this.gutter) * 2
32694 getHorizontalOneBoxColPositions : function(maxX, minY, box)
32698 if(box[0].size == 'md-left'){
32700 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32707 if(box[0].size == 'md-right'){
32709 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32710 y : minY + (this.unitWidth + this.gutter) * 1
32716 var rand = Math.floor(Math.random() * (4 - box[0].y));
32719 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32720 y : minY + (this.unitWidth + this.gutter) * rand
32727 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32731 if(box[0].size == 'xs'){
32734 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32739 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32740 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32748 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32753 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32754 y : minY + (this.unitWidth + this.gutter) * 2
32761 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32765 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32768 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32773 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32774 y : minY + (this.unitWidth + this.gutter) * 1
32778 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32779 y : minY + (this.unitWidth + this.gutter) * 2
32786 if(box[0].size == 'xs' && box[1].size == 'xs'){
32789 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32794 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32799 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32800 y : minY + (this.unitWidth + this.gutter) * 1
32808 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32813 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32814 y : minY + (this.unitWidth + this.gutter) * 2
32818 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32819 y : minY + (this.unitWidth + this.gutter) * 2
32826 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32830 if(box[0].size == 'xs'){
32833 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32838 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32843 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),
32848 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32849 y : minY + (this.unitWidth + this.gutter) * 1
32857 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32862 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32863 y : minY + (this.unitWidth + this.gutter) * 2
32867 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32868 y : minY + (this.unitWidth + this.gutter) * 2
32872 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),
32873 y : minY + (this.unitWidth + this.gutter) * 2
32881 * remove a Masonry Brick
32882 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32884 removeBrick : function(brick_id)
32890 for (var i = 0; i<this.bricks.length; i++) {
32891 if (this.bricks[i].id == brick_id) {
32892 this.bricks.splice(i,1);
32893 this.el.dom.removeChild(Roo.get(brick_id).dom);
32900 * adds a Masonry Brick
32901 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32903 addBrick : function(cfg)
32905 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32906 //this.register(cn);
32907 cn.parentId = this.id;
32908 cn.render(this.el);
32913 * register a Masonry Brick
32914 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32917 register : function(brick)
32919 this.bricks.push(brick);
32920 brick.masonryId = this.id;
32924 * clear all the Masonry Brick
32926 clearAll : function()
32929 //this.getChildContainer().dom.innerHTML = "";
32930 this.el.dom.innerHTML = '';
32933 getSelected : function()
32935 if (!this.selectedBrick) {
32939 return this.selectedBrick;
32943 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32947 * register a Masonry Layout
32948 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32951 register : function(layout)
32953 this.groups[layout.id] = layout;
32956 * fetch a Masonry Layout based on the masonry layout ID
32957 * @param {string} the masonry layout to add
32958 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32961 get: function(layout_id) {
32962 if (typeof(this.groups[layout_id]) == 'undefined') {
32965 return this.groups[layout_id] ;
32977 * http://masonry.desandro.com
32979 * The idea is to render all the bricks based on vertical width...
32981 * The original code extends 'outlayer' - we might need to use that....
32987 * @class Roo.bootstrap.LayoutMasonryAuto
32988 * @extends Roo.bootstrap.Component
32989 * Bootstrap Layout Masonry class
32992 * Create a new Element
32993 * @param {Object} config The config object
32996 Roo.bootstrap.LayoutMasonryAuto = function(config){
32997 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
33000 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
33003 * @cfg {Boolean} isFitWidth - resize the width..
33005 isFitWidth : false, // options..
33007 * @cfg {Boolean} isOriginLeft = left align?
33009 isOriginLeft : true,
33011 * @cfg {Boolean} isOriginTop = top align?
33013 isOriginTop : false,
33015 * @cfg {Boolean} isLayoutInstant = no animation?
33017 isLayoutInstant : false, // needed?
33019 * @cfg {Boolean} isResizingContainer = not sure if this is used..
33021 isResizingContainer : true,
33023 * @cfg {Number} columnWidth width of the columns
33029 * @cfg {Number} maxCols maximum number of columns
33034 * @cfg {Number} padHeight padding below box..
33040 * @cfg {Boolean} isAutoInitial defalut true
33043 isAutoInitial : true,
33049 initialColumnWidth : 0,
33050 currentSize : null,
33052 colYs : null, // array.
33059 bricks: null, //CompositeElement
33060 cols : 0, // array?
33061 // element : null, // wrapped now this.el
33062 _isLayoutInited : null,
33065 getAutoCreate : function(){
33069 cls: 'blog-masonary-wrapper ' + this.cls,
33071 cls : 'mas-boxes masonary'
33078 getChildContainer: function( )
33080 if (this.boxesEl) {
33081 return this.boxesEl;
33084 this.boxesEl = this.el.select('.mas-boxes').first();
33086 return this.boxesEl;
33090 initEvents : function()
33094 if(this.isAutoInitial){
33095 Roo.log('hook children rendered');
33096 this.on('childrenrendered', function() {
33097 Roo.log('children rendered');
33104 initial : function()
33106 this.reloadItems();
33108 this.currentSize = this.el.getBox(true);
33110 /// was window resize... - let's see if this works..
33111 Roo.EventManager.onWindowResize(this.resize, this);
33113 if(!this.isAutoInitial){
33118 this.layout.defer(500,this);
33121 reloadItems: function()
33123 this.bricks = this.el.select('.masonry-brick', true);
33125 this.bricks.each(function(b) {
33126 //Roo.log(b.getSize());
33127 if (!b.attr('originalwidth')) {
33128 b.attr('originalwidth', b.getSize().width);
33133 Roo.log(this.bricks.elements.length);
33136 resize : function()
33139 var cs = this.el.getBox(true);
33141 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
33142 Roo.log("no change in with or X");
33145 this.currentSize = cs;
33149 layout : function()
33152 this._resetLayout();
33153 //this._manageStamps();
33155 // don't animate first layout
33156 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33157 this.layoutItems( isInstant );
33159 // flag for initalized
33160 this._isLayoutInited = true;
33163 layoutItems : function( isInstant )
33165 //var items = this._getItemsForLayout( this.items );
33166 // original code supports filtering layout items.. we just ignore it..
33168 this._layoutItems( this.bricks , isInstant );
33170 this._postLayout();
33172 _layoutItems : function ( items , isInstant)
33174 //this.fireEvent( 'layout', this, items );
33177 if ( !items || !items.elements.length ) {
33178 // no items, emit event with empty array
33183 items.each(function(item) {
33184 Roo.log("layout item");
33186 // get x/y object from method
33187 var position = this._getItemLayoutPosition( item );
33189 position.item = item;
33190 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
33191 queue.push( position );
33194 this._processLayoutQueue( queue );
33196 /** Sets position of item in DOM
33197 * @param {Element} item
33198 * @param {Number} x - horizontal position
33199 * @param {Number} y - vertical position
33200 * @param {Boolean} isInstant - disables transitions
33202 _processLayoutQueue : function( queue )
33204 for ( var i=0, len = queue.length; i < len; i++ ) {
33205 var obj = queue[i];
33206 obj.item.position('absolute');
33207 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
33213 * Any logic you want to do after each layout,
33214 * i.e. size the container
33216 _postLayout : function()
33218 this.resizeContainer();
33221 resizeContainer : function()
33223 if ( !this.isResizingContainer ) {
33226 var size = this._getContainerSize();
33228 this.el.setSize(size.width,size.height);
33229 this.boxesEl.setSize(size.width,size.height);
33235 _resetLayout : function()
33237 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
33238 this.colWidth = this.el.getWidth();
33239 //this.gutter = this.el.getWidth();
33241 this.measureColumns();
33247 this.colYs.push( 0 );
33253 measureColumns : function()
33255 this.getContainerWidth();
33256 // if columnWidth is 0, default to outerWidth of first item
33257 if ( !this.columnWidth ) {
33258 var firstItem = this.bricks.first();
33259 Roo.log(firstItem);
33260 this.columnWidth = this.containerWidth;
33261 if (firstItem && firstItem.attr('originalwidth') ) {
33262 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
33264 // columnWidth fall back to item of first element
33265 Roo.log("set column width?");
33266 this.initialColumnWidth = this.columnWidth ;
33268 // if first elem has no width, default to size of container
33273 if (this.initialColumnWidth) {
33274 this.columnWidth = this.initialColumnWidth;
33279 // column width is fixed at the top - however if container width get's smaller we should
33282 // this bit calcs how man columns..
33284 var columnWidth = this.columnWidth += this.gutter;
33286 // calculate columns
33287 var containerWidth = this.containerWidth + this.gutter;
33289 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
33290 // fix rounding errors, typically with gutters
33291 var excess = columnWidth - containerWidth % columnWidth;
33294 // if overshoot is less than a pixel, round up, otherwise floor it
33295 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
33296 cols = Math[ mathMethod ]( cols );
33297 this.cols = Math.max( cols, 1 );
33298 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33300 // padding positioning..
33301 var totalColWidth = this.cols * this.columnWidth;
33302 var padavail = this.containerWidth - totalColWidth;
33303 // so for 2 columns - we need 3 'pads'
33305 var padNeeded = (1+this.cols) * this.padWidth;
33307 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
33309 this.columnWidth += padExtra
33310 //this.padWidth = Math.floor(padavail / ( this.cols));
33312 // adjust colum width so that padding is fixed??
33314 // we have 3 columns ... total = width * 3
33315 // we have X left over... that should be used by
33317 //if (this.expandC) {
33325 getContainerWidth : function()
33327 /* // container is parent if fit width
33328 var container = this.isFitWidth ? this.element.parentNode : this.element;
33329 // check that this.size and size are there
33330 // IE8 triggers resize on body size change, so they might not be
33332 var size = getSize( container ); //FIXME
33333 this.containerWidth = size && size.innerWidth; //FIXME
33336 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33340 _getItemLayoutPosition : function( item ) // what is item?
33342 // we resize the item to our columnWidth..
33344 item.setWidth(this.columnWidth);
33345 item.autoBoxAdjust = false;
33347 var sz = item.getSize();
33349 // how many columns does this brick span
33350 var remainder = this.containerWidth % this.columnWidth;
33352 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
33353 // round if off by 1 pixel, otherwise use ceil
33354 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
33355 colSpan = Math.min( colSpan, this.cols );
33357 // normally this should be '1' as we dont' currently allow multi width columns..
33359 var colGroup = this._getColGroup( colSpan );
33360 // get the minimum Y value from the columns
33361 var minimumY = Math.min.apply( Math, colGroup );
33362 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
33364 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
33366 // position the brick
33368 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
33369 y: this.currentSize.y + minimumY + this.padHeight
33373 // apply setHeight to necessary columns
33374 var setHeight = minimumY + sz.height + this.padHeight;
33375 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
33377 var setSpan = this.cols + 1 - colGroup.length;
33378 for ( var i = 0; i < setSpan; i++ ) {
33379 this.colYs[ shortColIndex + i ] = setHeight ;
33386 * @param {Number} colSpan - number of columns the element spans
33387 * @returns {Array} colGroup
33389 _getColGroup : function( colSpan )
33391 if ( colSpan < 2 ) {
33392 // if brick spans only one column, use all the column Ys
33397 // how many different places could this brick fit horizontally
33398 var groupCount = this.cols + 1 - colSpan;
33399 // for each group potential horizontal position
33400 for ( var i = 0; i < groupCount; i++ ) {
33401 // make an array of colY values for that one group
33402 var groupColYs = this.colYs.slice( i, i + colSpan );
33403 // and get the max value of the array
33404 colGroup[i] = Math.max.apply( Math, groupColYs );
33409 _manageStamp : function( stamp )
33411 var stampSize = stamp.getSize();
33412 var offset = stamp.getBox();
33413 // get the columns that this stamp affects
33414 var firstX = this.isOriginLeft ? offset.x : offset.right;
33415 var lastX = firstX + stampSize.width;
33416 var firstCol = Math.floor( firstX / this.columnWidth );
33417 firstCol = Math.max( 0, firstCol );
33419 var lastCol = Math.floor( lastX / this.columnWidth );
33420 // lastCol should not go over if multiple of columnWidth #425
33421 lastCol -= lastX % this.columnWidth ? 0 : 1;
33422 lastCol = Math.min( this.cols - 1, lastCol );
33424 // set colYs to bottom of the stamp
33425 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33428 for ( var i = firstCol; i <= lastCol; i++ ) {
33429 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33434 _getContainerSize : function()
33436 this.maxY = Math.max.apply( Math, this.colYs );
33441 if ( this.isFitWidth ) {
33442 size.width = this._getContainerFitWidth();
33448 _getContainerFitWidth : function()
33450 var unusedCols = 0;
33451 // count unused columns
33454 if ( this.colYs[i] !== 0 ) {
33459 // fit container to columns that have been used
33460 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33463 needsResizeLayout : function()
33465 var previousWidth = this.containerWidth;
33466 this.getContainerWidth();
33467 return previousWidth !== this.containerWidth;
33482 * @class Roo.bootstrap.MasonryBrick
33483 * @extends Roo.bootstrap.Component
33484 * Bootstrap MasonryBrick class
33487 * Create a new MasonryBrick
33488 * @param {Object} config The config object
33491 Roo.bootstrap.MasonryBrick = function(config){
33493 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33495 Roo.bootstrap.MasonryBrick.register(this);
33501 * When a MasonryBrick is clcik
33502 * @param {Roo.bootstrap.MasonryBrick} this
33503 * @param {Roo.EventObject} e
33509 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
33512 * @cfg {String} title
33516 * @cfg {String} html
33520 * @cfg {String} bgimage
33524 * @cfg {String} videourl
33528 * @cfg {String} cls
33532 * @cfg {String} href
33536 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33541 * @cfg {String} placetitle (center|bottom)
33546 * @cfg {Boolean} isFitContainer defalut true
33548 isFitContainer : true,
33551 * @cfg {Boolean} preventDefault defalut false
33553 preventDefault : false,
33556 * @cfg {Boolean} inverse defalut false
33558 maskInverse : false,
33560 getAutoCreate : function()
33562 if(!this.isFitContainer){
33563 return this.getSplitAutoCreate();
33566 var cls = 'masonry-brick masonry-brick-full';
33568 if(this.href.length){
33569 cls += ' masonry-brick-link';
33572 if(this.bgimage.length){
33573 cls += ' masonry-brick-image';
33576 if(this.maskInverse){
33577 cls += ' mask-inverse';
33580 if(!this.html.length && !this.maskInverse && !this.videourl.length){
33581 cls += ' enable-mask';
33585 cls += ' masonry-' + this.size + '-brick';
33588 if(this.placetitle.length){
33590 switch (this.placetitle) {
33592 cls += ' masonry-center-title';
33595 cls += ' masonry-bottom-title';
33602 if(!this.html.length && !this.bgimage.length){
33603 cls += ' masonry-center-title';
33606 if(!this.html.length && this.bgimage.length){
33607 cls += ' masonry-bottom-title';
33612 cls += ' ' + this.cls;
33616 tag: (this.href.length) ? 'a' : 'div',
33621 cls: 'masonry-brick-mask'
33625 cls: 'masonry-brick-paragraph',
33631 if(this.href.length){
33632 cfg.href = this.href;
33635 var cn = cfg.cn[1].cn;
33637 if(this.title.length){
33640 cls: 'masonry-brick-title',
33645 if(this.html.length){
33648 cls: 'masonry-brick-text',
33653 if (!this.title.length && !this.html.length) {
33654 cfg.cn[1].cls += ' hide';
33657 if(this.bgimage.length){
33660 cls: 'masonry-brick-image-view',
33665 if(this.videourl.length){
33666 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33667 // youtube support only?
33670 cls: 'masonry-brick-image-view',
33673 allowfullscreen : true
33681 getSplitAutoCreate : function()
33683 var cls = 'masonry-brick masonry-brick-split';
33685 if(this.href.length){
33686 cls += ' masonry-brick-link';
33689 if(this.bgimage.length){
33690 cls += ' masonry-brick-image';
33694 cls += ' masonry-' + this.size + '-brick';
33697 switch (this.placetitle) {
33699 cls += ' masonry-center-title';
33702 cls += ' masonry-bottom-title';
33705 if(!this.bgimage.length){
33706 cls += ' masonry-center-title';
33709 if(this.bgimage.length){
33710 cls += ' masonry-bottom-title';
33716 cls += ' ' + this.cls;
33720 tag: (this.href.length) ? 'a' : 'div',
33725 cls: 'masonry-brick-split-head',
33729 cls: 'masonry-brick-paragraph',
33736 cls: 'masonry-brick-split-body',
33742 if(this.href.length){
33743 cfg.href = this.href;
33746 if(this.title.length){
33747 cfg.cn[0].cn[0].cn.push({
33749 cls: 'masonry-brick-title',
33754 if(this.html.length){
33755 cfg.cn[1].cn.push({
33757 cls: 'masonry-brick-text',
33762 if(this.bgimage.length){
33763 cfg.cn[0].cn.push({
33765 cls: 'masonry-brick-image-view',
33770 if(this.videourl.length){
33771 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33772 // youtube support only?
33773 cfg.cn[0].cn.cn.push({
33775 cls: 'masonry-brick-image-view',
33778 allowfullscreen : true
33785 initEvents: function()
33787 switch (this.size) {
33820 this.el.on('touchstart', this.onTouchStart, this);
33821 this.el.on('touchmove', this.onTouchMove, this);
33822 this.el.on('touchend', this.onTouchEnd, this);
33823 this.el.on('contextmenu', this.onContextMenu, this);
33825 this.el.on('mouseenter' ,this.enter, this);
33826 this.el.on('mouseleave', this.leave, this);
33827 this.el.on('click', this.onClick, this);
33830 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33831 this.parent().bricks.push(this);
33836 onClick: function(e, el)
33838 var time = this.endTimer - this.startTimer;
33839 // Roo.log(e.preventDefault());
33842 e.preventDefault();
33847 if(!this.preventDefault){
33851 e.preventDefault();
33853 if (this.activeClass != '') {
33854 this.selectBrick();
33857 this.fireEvent('click', this, e);
33860 enter: function(e, el)
33862 e.preventDefault();
33864 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33868 if(this.bgimage.length && this.html.length){
33869 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33873 leave: function(e, el)
33875 e.preventDefault();
33877 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33881 if(this.bgimage.length && this.html.length){
33882 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33886 onTouchStart: function(e, el)
33888 // e.preventDefault();
33890 this.touchmoved = false;
33892 if(!this.isFitContainer){
33896 if(!this.bgimage.length || !this.html.length){
33900 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33902 this.timer = new Date().getTime();
33906 onTouchMove: function(e, el)
33908 this.touchmoved = true;
33911 onContextMenu : function(e,el)
33913 e.preventDefault();
33914 e.stopPropagation();
33918 onTouchEnd: function(e, el)
33920 // e.preventDefault();
33922 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33929 if(!this.bgimage.length || !this.html.length){
33931 if(this.href.length){
33932 window.location.href = this.href;
33938 if(!this.isFitContainer){
33942 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33944 window.location.href = this.href;
33947 //selection on single brick only
33948 selectBrick : function() {
33950 if (!this.parentId) {
33954 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33955 var index = m.selectedBrick.indexOf(this.id);
33958 m.selectedBrick.splice(index,1);
33959 this.el.removeClass(this.activeClass);
33963 for(var i = 0; i < m.selectedBrick.length; i++) {
33964 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33965 b.el.removeClass(b.activeClass);
33968 m.selectedBrick = [];
33970 m.selectedBrick.push(this.id);
33971 this.el.addClass(this.activeClass);
33975 isSelected : function(){
33976 return this.el.hasClass(this.activeClass);
33981 Roo.apply(Roo.bootstrap.MasonryBrick, {
33984 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33986 * register a Masonry Brick
33987 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33990 register : function(brick)
33992 //this.groups[brick.id] = brick;
33993 this.groups.add(brick.id, brick);
33996 * fetch a masonry brick based on the masonry brick ID
33997 * @param {string} the masonry brick to add
33998 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
34001 get: function(brick_id)
34003 // if (typeof(this.groups[brick_id]) == 'undefined') {
34006 // return this.groups[brick_id] ;
34008 if(this.groups.key(brick_id)) {
34009 return this.groups.key(brick_id);
34027 * @class Roo.bootstrap.Brick
34028 * @extends Roo.bootstrap.Component
34029 * Bootstrap Brick class
34032 * Create a new Brick
34033 * @param {Object} config The config object
34036 Roo.bootstrap.Brick = function(config){
34037 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
34043 * When a Brick is click
34044 * @param {Roo.bootstrap.Brick} this
34045 * @param {Roo.EventObject} e
34051 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
34054 * @cfg {String} title
34058 * @cfg {String} html
34062 * @cfg {String} bgimage
34066 * @cfg {String} cls
34070 * @cfg {String} href
34074 * @cfg {String} video
34078 * @cfg {Boolean} square
34082 getAutoCreate : function()
34084 var cls = 'roo-brick';
34086 if(this.href.length){
34087 cls += ' roo-brick-link';
34090 if(this.bgimage.length){
34091 cls += ' roo-brick-image';
34094 if(!this.html.length && !this.bgimage.length){
34095 cls += ' roo-brick-center-title';
34098 if(!this.html.length && this.bgimage.length){
34099 cls += ' roo-brick-bottom-title';
34103 cls += ' ' + this.cls;
34107 tag: (this.href.length) ? 'a' : 'div',
34112 cls: 'roo-brick-paragraph',
34118 if(this.href.length){
34119 cfg.href = this.href;
34122 var cn = cfg.cn[0].cn;
34124 if(this.title.length){
34127 cls: 'roo-brick-title',
34132 if(this.html.length){
34135 cls: 'roo-brick-text',
34142 if(this.bgimage.length){
34145 cls: 'roo-brick-image-view',
34153 initEvents: function()
34155 if(this.title.length || this.html.length){
34156 this.el.on('mouseenter' ,this.enter, this);
34157 this.el.on('mouseleave', this.leave, this);
34160 Roo.EventManager.onWindowResize(this.resize, this);
34162 if(this.bgimage.length){
34163 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
34164 this.imageEl.on('load', this.onImageLoad, this);
34171 onImageLoad : function()
34176 resize : function()
34178 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
34180 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
34182 if(this.bgimage.length){
34183 var image = this.el.select('.roo-brick-image-view', true).first();
34185 image.setWidth(paragraph.getWidth());
34188 image.setHeight(paragraph.getWidth());
34191 this.el.setHeight(image.getHeight());
34192 paragraph.setHeight(image.getHeight());
34198 enter: function(e, el)
34200 e.preventDefault();
34202 if(this.bgimage.length){
34203 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
34204 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
34208 leave: function(e, el)
34210 e.preventDefault();
34212 if(this.bgimage.length){
34213 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
34214 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
34229 * @class Roo.bootstrap.NumberField
34230 * @extends Roo.bootstrap.Input
34231 * Bootstrap NumberField class
34237 * Create a new NumberField
34238 * @param {Object} config The config object
34241 Roo.bootstrap.NumberField = function(config){
34242 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
34245 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
34248 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
34250 allowDecimals : true,
34252 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
34254 decimalSeparator : ".",
34256 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
34258 decimalPrecision : 2,
34260 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
34262 allowNegative : true,
34265 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
34269 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
34271 minValue : Number.NEGATIVE_INFINITY,
34273 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
34275 maxValue : Number.MAX_VALUE,
34277 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
34279 minText : "The minimum value for this field is {0}",
34281 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
34283 maxText : "The maximum value for this field is {0}",
34285 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
34286 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
34288 nanText : "{0} is not a valid number",
34290 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
34292 thousandsDelimiter : false,
34294 * @cfg {String} valueAlign alignment of value
34296 valueAlign : "left",
34298 getAutoCreate : function()
34300 var hiddenInput = {
34304 cls: 'hidden-number-input'
34308 hiddenInput.name = this.name;
34313 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
34315 this.name = hiddenInput.name;
34317 if(cfg.cn.length > 0) {
34318 cfg.cn.push(hiddenInput);
34325 initEvents : function()
34327 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
34329 var allowed = "0123456789";
34331 if(this.allowDecimals){
34332 allowed += this.decimalSeparator;
34335 if(this.allowNegative){
34339 if(this.thousandsDelimiter) {
34343 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
34345 var keyPress = function(e){
34347 var k = e.getKey();
34349 var c = e.getCharCode();
34352 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
34353 allowed.indexOf(String.fromCharCode(c)) === -1
34359 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
34363 if(allowed.indexOf(String.fromCharCode(c)) === -1){
34368 this.el.on("keypress", keyPress, this);
34371 validateValue : function(value)
34374 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
34378 var num = this.parseValue(value);
34381 this.markInvalid(String.format(this.nanText, value));
34385 if(num < this.minValue){
34386 this.markInvalid(String.format(this.minText, this.minValue));
34390 if(num > this.maxValue){
34391 this.markInvalid(String.format(this.maxText, this.maxValue));
34398 getValue : function()
34400 var v = this.hiddenEl().getValue();
34402 return this.fixPrecision(this.parseValue(v));
34405 parseValue : function(value)
34407 if(this.thousandsDelimiter) {
34409 r = new RegExp(",", "g");
34410 value = value.replace(r, "");
34413 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34414 return isNaN(value) ? '' : value;
34417 fixPrecision : function(value)
34419 if(this.thousandsDelimiter) {
34421 r = new RegExp(",", "g");
34422 value = value.replace(r, "");
34425 var nan = isNaN(value);
34427 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34428 return nan ? '' : value;
34430 return parseFloat(value).toFixed(this.decimalPrecision);
34433 setValue : function(v)
34435 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34441 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34443 this.inputEl().dom.value = (v == '') ? '' :
34444 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34446 if(!this.allowZero && v === '0') {
34447 this.hiddenEl().dom.value = '';
34448 this.inputEl().dom.value = '';
34455 decimalPrecisionFcn : function(v)
34457 return Math.floor(v);
34460 beforeBlur : function()
34462 var v = this.parseValue(this.getRawValue());
34464 if(v || v === 0 || v === ''){
34469 hiddenEl : function()
34471 return this.el.select('input.hidden-number-input',true).first();
34483 * @class Roo.bootstrap.DocumentSlider
34484 * @extends Roo.bootstrap.Component
34485 * Bootstrap DocumentSlider class
34488 * Create a new DocumentViewer
34489 * @param {Object} config The config object
34492 Roo.bootstrap.DocumentSlider = function(config){
34493 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34500 * Fire after initEvent
34501 * @param {Roo.bootstrap.DocumentSlider} this
34506 * Fire after update
34507 * @param {Roo.bootstrap.DocumentSlider} this
34513 * @param {Roo.bootstrap.DocumentSlider} this
34519 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
34525 getAutoCreate : function()
34529 cls : 'roo-document-slider',
34533 cls : 'roo-document-slider-header',
34537 cls : 'roo-document-slider-header-title'
34543 cls : 'roo-document-slider-body',
34547 cls : 'roo-document-slider-prev',
34551 cls : 'fa fa-chevron-left'
34557 cls : 'roo-document-slider-thumb',
34561 cls : 'roo-document-slider-image'
34567 cls : 'roo-document-slider-next',
34571 cls : 'fa fa-chevron-right'
34583 initEvents : function()
34585 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34586 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34588 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34589 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34591 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34592 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34594 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34595 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34597 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34598 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34600 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34601 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34603 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34604 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34606 this.thumbEl.on('click', this.onClick, this);
34608 this.prevIndicator.on('click', this.prev, this);
34610 this.nextIndicator.on('click', this.next, this);
34614 initial : function()
34616 if(this.files.length){
34617 this.indicator = 1;
34621 this.fireEvent('initial', this);
34624 update : function()
34626 this.imageEl.attr('src', this.files[this.indicator - 1]);
34628 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34630 this.prevIndicator.show();
34632 if(this.indicator == 1){
34633 this.prevIndicator.hide();
34636 this.nextIndicator.show();
34638 if(this.indicator == this.files.length){
34639 this.nextIndicator.hide();
34642 this.thumbEl.scrollTo('top');
34644 this.fireEvent('update', this);
34647 onClick : function(e)
34649 e.preventDefault();
34651 this.fireEvent('click', this);
34656 e.preventDefault();
34658 this.indicator = Math.max(1, this.indicator - 1);
34665 e.preventDefault();
34667 this.indicator = Math.min(this.files.length, this.indicator + 1);
34681 * @class Roo.bootstrap.RadioSet
34682 * @extends Roo.bootstrap.Input
34683 * Bootstrap RadioSet class
34684 * @cfg {String} indicatorpos (left|right) default left
34685 * @cfg {Boolean} inline (true|false) inline the element (default true)
34686 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34688 * Create a new RadioSet
34689 * @param {Object} config The config object
34692 Roo.bootstrap.RadioSet = function(config){
34694 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34698 Roo.bootstrap.RadioSet.register(this);
34703 * Fires when the element is checked or unchecked.
34704 * @param {Roo.bootstrap.RadioSet} this This radio
34705 * @param {Roo.bootstrap.Radio} item The checked item
34710 * Fires when the element is click.
34711 * @param {Roo.bootstrap.RadioSet} this This radio set
34712 * @param {Roo.bootstrap.Radio} item The checked item
34713 * @param {Roo.EventObject} e The event object
34720 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
34728 indicatorpos : 'left',
34730 getAutoCreate : function()
34734 cls : 'roo-radio-set-label',
34738 html : this.fieldLabel
34742 if (Roo.bootstrap.version == 3) {
34745 if(this.indicatorpos == 'left'){
34748 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34749 tooltip : 'This field is required'
34754 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34755 tooltip : 'This field is required'
34761 cls : 'roo-radio-set-items'
34764 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34766 if (align === 'left' && this.fieldLabel.length) {
34769 cls : "roo-radio-set-right",
34775 if(this.labelWidth > 12){
34776 label.style = "width: " + this.labelWidth + 'px';
34779 if(this.labelWidth < 13 && this.labelmd == 0){
34780 this.labelmd = this.labelWidth;
34783 if(this.labellg > 0){
34784 label.cls += ' col-lg-' + this.labellg;
34785 items.cls += ' col-lg-' + (12 - this.labellg);
34788 if(this.labelmd > 0){
34789 label.cls += ' col-md-' + this.labelmd;
34790 items.cls += ' col-md-' + (12 - this.labelmd);
34793 if(this.labelsm > 0){
34794 label.cls += ' col-sm-' + this.labelsm;
34795 items.cls += ' col-sm-' + (12 - this.labelsm);
34798 if(this.labelxs > 0){
34799 label.cls += ' col-xs-' + this.labelxs;
34800 items.cls += ' col-xs-' + (12 - this.labelxs);
34806 cls : 'roo-radio-set',
34810 cls : 'roo-radio-set-input',
34813 value : this.value ? this.value : ''
34820 if(this.weight.length){
34821 cfg.cls += ' roo-radio-' + this.weight;
34825 cfg.cls += ' roo-radio-set-inline';
34829 ['xs','sm','md','lg'].map(function(size){
34830 if (settings[size]) {
34831 cfg.cls += ' col-' + size + '-' + settings[size];
34839 initEvents : function()
34841 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34842 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34844 if(!this.fieldLabel.length){
34845 this.labelEl.hide();
34848 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34849 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34851 this.indicator = this.indicatorEl();
34853 if(this.indicator){
34854 this.indicator.addClass('invisible');
34857 this.originalValue = this.getValue();
34861 inputEl: function ()
34863 return this.el.select('.roo-radio-set-input', true).first();
34866 getChildContainer : function()
34868 return this.itemsEl;
34871 register : function(item)
34873 this.radioes.push(item);
34877 validate : function()
34879 if(this.getVisibilityEl().hasClass('hidden')){
34885 Roo.each(this.radioes, function(i){
34894 if(this.allowBlank) {
34898 if(this.disabled || valid){
34903 this.markInvalid();
34908 markValid : function()
34910 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34911 this.indicatorEl().removeClass('visible');
34912 this.indicatorEl().addClass('invisible');
34916 if (Roo.bootstrap.version == 3) {
34917 this.el.removeClass([this.invalidClass, this.validClass]);
34918 this.el.addClass(this.validClass);
34920 this.el.removeClass(['is-invalid','is-valid']);
34921 this.el.addClass(['is-valid']);
34923 this.fireEvent('valid', this);
34926 markInvalid : function(msg)
34928 if(this.allowBlank || this.disabled){
34932 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34933 this.indicatorEl().removeClass('invisible');
34934 this.indicatorEl().addClass('visible');
34936 if (Roo.bootstrap.version == 3) {
34937 this.el.removeClass([this.invalidClass, this.validClass]);
34938 this.el.addClass(this.invalidClass);
34940 this.el.removeClass(['is-invalid','is-valid']);
34941 this.el.addClass(['is-invalid']);
34944 this.fireEvent('invalid', this, msg);
34948 setValue : function(v, suppressEvent)
34950 if(this.value === v){
34957 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34960 Roo.each(this.radioes, function(i){
34962 i.el.removeClass('checked');
34965 Roo.each(this.radioes, function(i){
34967 if(i.value === v || i.value.toString() === v.toString()){
34969 i.el.addClass('checked');
34971 if(suppressEvent !== true){
34972 this.fireEvent('check', this, i);
34983 clearInvalid : function(){
34985 if(!this.el || this.preventMark){
34989 this.el.removeClass([this.invalidClass]);
34991 this.fireEvent('valid', this);
34996 Roo.apply(Roo.bootstrap.RadioSet, {
35000 register : function(set)
35002 this.groups[set.name] = set;
35005 get: function(name)
35007 if (typeof(this.groups[name]) == 'undefined') {
35011 return this.groups[name] ;
35017 * Ext JS Library 1.1.1
35018 * Copyright(c) 2006-2007, Ext JS, LLC.
35020 * Originally Released Under LGPL - original licence link has changed is not relivant.
35023 * <script type="text/javascript">
35028 * @class Roo.bootstrap.SplitBar
35029 * @extends Roo.util.Observable
35030 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
35034 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
35035 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
35036 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
35037 split.minSize = 100;
35038 split.maxSize = 600;
35039 split.animate = true;
35040 split.on('moved', splitterMoved);
35043 * Create a new SplitBar
35044 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
35045 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
35046 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
35047 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
35048 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
35049 position of the SplitBar).
35051 Roo.bootstrap.SplitBar = function(cfg){
35056 // dragElement : elm
35057 // resizingElement: el,
35059 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
35060 // placement : Roo.bootstrap.SplitBar.LEFT ,
35061 // existingProxy ???
35064 this.el = Roo.get(cfg.dragElement, true);
35065 this.el.dom.unselectable = "on";
35067 this.resizingEl = Roo.get(cfg.resizingElement, true);
35071 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
35072 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
35075 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
35078 * The minimum size of the resizing element. (Defaults to 0)
35084 * The maximum size of the resizing element. (Defaults to 2000)
35087 this.maxSize = 2000;
35090 * Whether to animate the transition to the new size
35093 this.animate = false;
35096 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
35099 this.useShim = false;
35104 if(!cfg.existingProxy){
35106 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
35108 this.proxy = Roo.get(cfg.existingProxy).dom;
35111 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
35114 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
35117 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
35120 this.dragSpecs = {};
35123 * @private The adapter to use to positon and resize elements
35125 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35126 this.adapter.init(this);
35128 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35130 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
35131 this.el.addClass("roo-splitbar-h");
35134 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
35135 this.el.addClass("roo-splitbar-v");
35141 * Fires when the splitter is moved (alias for {@link #event-moved})
35142 * @param {Roo.bootstrap.SplitBar} this
35143 * @param {Number} newSize the new width or height
35148 * Fires when the splitter is moved
35149 * @param {Roo.bootstrap.SplitBar} this
35150 * @param {Number} newSize the new width or height
35154 * @event beforeresize
35155 * Fires before the splitter is dragged
35156 * @param {Roo.bootstrap.SplitBar} this
35158 "beforeresize" : true,
35160 "beforeapply" : true
35163 Roo.util.Observable.call(this);
35166 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
35167 onStartProxyDrag : function(x, y){
35168 this.fireEvent("beforeresize", this);
35170 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
35172 o.enableDisplayMode("block");
35173 // all splitbars share the same overlay
35174 Roo.bootstrap.SplitBar.prototype.overlay = o;
35176 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
35177 this.overlay.show();
35178 Roo.get(this.proxy).setDisplayed("block");
35179 var size = this.adapter.getElementSize(this);
35180 this.activeMinSize = this.getMinimumSize();;
35181 this.activeMaxSize = this.getMaximumSize();;
35182 var c1 = size - this.activeMinSize;
35183 var c2 = Math.max(this.activeMaxSize - size, 0);
35184 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35185 this.dd.resetConstraints();
35186 this.dd.setXConstraint(
35187 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
35188 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
35190 this.dd.setYConstraint(0, 0);
35192 this.dd.resetConstraints();
35193 this.dd.setXConstraint(0, 0);
35194 this.dd.setYConstraint(
35195 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
35196 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
35199 this.dragSpecs.startSize = size;
35200 this.dragSpecs.startPoint = [x, y];
35201 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
35205 * @private Called after the drag operation by the DDProxy
35207 onEndProxyDrag : function(e){
35208 Roo.get(this.proxy).setDisplayed(false);
35209 var endPoint = Roo.lib.Event.getXY(e);
35211 this.overlay.hide();
35214 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35215 newSize = this.dragSpecs.startSize +
35216 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
35217 endPoint[0] - this.dragSpecs.startPoint[0] :
35218 this.dragSpecs.startPoint[0] - endPoint[0]
35221 newSize = this.dragSpecs.startSize +
35222 (this.placement == Roo.bootstrap.SplitBar.TOP ?
35223 endPoint[1] - this.dragSpecs.startPoint[1] :
35224 this.dragSpecs.startPoint[1] - endPoint[1]
35227 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
35228 if(newSize != this.dragSpecs.startSize){
35229 if(this.fireEvent('beforeapply', this, newSize) !== false){
35230 this.adapter.setElementSize(this, newSize);
35231 this.fireEvent("moved", this, newSize);
35232 this.fireEvent("resize", this, newSize);
35238 * Get the adapter this SplitBar uses
35239 * @return The adapter object
35241 getAdapter : function(){
35242 return this.adapter;
35246 * Set the adapter this SplitBar uses
35247 * @param {Object} adapter A SplitBar adapter object
35249 setAdapter : function(adapter){
35250 this.adapter = adapter;
35251 this.adapter.init(this);
35255 * Gets the minimum size for the resizing element
35256 * @return {Number} The minimum size
35258 getMinimumSize : function(){
35259 return this.minSize;
35263 * Sets the minimum size for the resizing element
35264 * @param {Number} minSize The minimum size
35266 setMinimumSize : function(minSize){
35267 this.minSize = minSize;
35271 * Gets the maximum size for the resizing element
35272 * @return {Number} The maximum size
35274 getMaximumSize : function(){
35275 return this.maxSize;
35279 * Sets the maximum size for the resizing element
35280 * @param {Number} maxSize The maximum size
35282 setMaximumSize : function(maxSize){
35283 this.maxSize = maxSize;
35287 * Sets the initialize size for the resizing element
35288 * @param {Number} size The initial size
35290 setCurrentSize : function(size){
35291 var oldAnimate = this.animate;
35292 this.animate = false;
35293 this.adapter.setElementSize(this, size);
35294 this.animate = oldAnimate;
35298 * Destroy this splitbar.
35299 * @param {Boolean} removeEl True to remove the element
35301 destroy : function(removeEl){
35303 this.shim.remove();
35306 this.proxy.parentNode.removeChild(this.proxy);
35314 * @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.
35316 Roo.bootstrap.SplitBar.createProxy = function(dir){
35317 var proxy = new Roo.Element(document.createElement("div"));
35318 proxy.unselectable();
35319 var cls = 'roo-splitbar-proxy';
35320 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
35321 document.body.appendChild(proxy.dom);
35326 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
35327 * Default Adapter. It assumes the splitter and resizing element are not positioned
35328 * elements and only gets/sets the width of the element. Generally used for table based layouts.
35330 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
35333 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
35334 // do nothing for now
35335 init : function(s){
35339 * Called before drag operations to get the current size of the resizing element.
35340 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35342 getElementSize : function(s){
35343 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35344 return s.resizingEl.getWidth();
35346 return s.resizingEl.getHeight();
35351 * Called after drag operations to set the size of the resizing element.
35352 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35353 * @param {Number} newSize The new size to set
35354 * @param {Function} onComplete A function to be invoked when resizing is complete
35356 setElementSize : function(s, newSize, onComplete){
35357 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35359 s.resizingEl.setWidth(newSize);
35361 onComplete(s, newSize);
35364 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
35369 s.resizingEl.setHeight(newSize);
35371 onComplete(s, newSize);
35374 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
35381 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
35382 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
35383 * Adapter that moves the splitter element to align with the resized sizing element.
35384 * Used with an absolute positioned SplitBar.
35385 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
35386 * document.body, make sure you assign an id to the body element.
35388 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
35389 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35390 this.container = Roo.get(container);
35393 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
35394 init : function(s){
35395 this.basic.init(s);
35398 getElementSize : function(s){
35399 return this.basic.getElementSize(s);
35402 setElementSize : function(s, newSize, onComplete){
35403 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
35406 moveSplitter : function(s){
35407 var yes = Roo.bootstrap.SplitBar;
35408 switch(s.placement){
35410 s.el.setX(s.resizingEl.getRight());
35413 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35416 s.el.setY(s.resizingEl.getBottom());
35419 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35426 * Orientation constant - Create a vertical SplitBar
35430 Roo.bootstrap.SplitBar.VERTICAL = 1;
35433 * Orientation constant - Create a horizontal SplitBar
35437 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35440 * Placement constant - The resizing element is to the left of the splitter element
35444 Roo.bootstrap.SplitBar.LEFT = 1;
35447 * Placement constant - The resizing element is to the right of the splitter element
35451 Roo.bootstrap.SplitBar.RIGHT = 2;
35454 * Placement constant - The resizing element is positioned above the splitter element
35458 Roo.bootstrap.SplitBar.TOP = 3;
35461 * Placement constant - The resizing element is positioned under splitter element
35465 Roo.bootstrap.SplitBar.BOTTOM = 4;
35466 Roo.namespace("Roo.bootstrap.layout");/*
35468 * Ext JS Library 1.1.1
35469 * Copyright(c) 2006-2007, Ext JS, LLC.
35471 * Originally Released Under LGPL - original licence link has changed is not relivant.
35474 * <script type="text/javascript">
35478 * @class Roo.bootstrap.layout.Manager
35479 * @extends Roo.bootstrap.Component
35480 * Base class for layout managers.
35482 Roo.bootstrap.layout.Manager = function(config)
35484 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35490 /** false to disable window resize monitoring @type Boolean */
35491 this.monitorWindowResize = true;
35496 * Fires when a layout is performed.
35497 * @param {Roo.LayoutManager} this
35501 * @event regionresized
35502 * Fires when the user resizes a region.
35503 * @param {Roo.LayoutRegion} region The resized region
35504 * @param {Number} newSize The new size (width for east/west, height for north/south)
35506 "regionresized" : true,
35508 * @event regioncollapsed
35509 * Fires when a region is collapsed.
35510 * @param {Roo.LayoutRegion} region The collapsed region
35512 "regioncollapsed" : true,
35514 * @event regionexpanded
35515 * Fires when a region is expanded.
35516 * @param {Roo.LayoutRegion} region The expanded region
35518 "regionexpanded" : true
35520 this.updating = false;
35523 this.el = Roo.get(config.el);
35529 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35534 monitorWindowResize : true,
35540 onRender : function(ct, position)
35543 this.el = Roo.get(ct);
35546 //this.fireEvent('render',this);
35550 initEvents: function()
35554 // ie scrollbar fix
35555 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35556 document.body.scroll = "no";
35557 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35558 this.el.position('relative');
35560 this.id = this.el.id;
35561 this.el.addClass("roo-layout-container");
35562 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35563 if(this.el.dom != document.body ) {
35564 this.el.on('resize', this.layout,this);
35565 this.el.on('show', this.layout,this);
35571 * Returns true if this layout is currently being updated
35572 * @return {Boolean}
35574 isUpdating : function(){
35575 return this.updating;
35579 * Suspend the LayoutManager from doing auto-layouts while
35580 * making multiple add or remove calls
35582 beginUpdate : function(){
35583 this.updating = true;
35587 * Restore auto-layouts and optionally disable the manager from performing a layout
35588 * @param {Boolean} noLayout true to disable a layout update
35590 endUpdate : function(noLayout){
35591 this.updating = false;
35597 layout: function(){
35601 onRegionResized : function(region, newSize){
35602 this.fireEvent("regionresized", region, newSize);
35606 onRegionCollapsed : function(region){
35607 this.fireEvent("regioncollapsed", region);
35610 onRegionExpanded : function(region){
35611 this.fireEvent("regionexpanded", region);
35615 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35616 * performs box-model adjustments.
35617 * @return {Object} The size as an object {width: (the width), height: (the height)}
35619 getViewSize : function()
35622 if(this.el.dom != document.body){
35623 size = this.el.getSize();
35625 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35627 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35628 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35633 * Returns the Element this layout is bound to.
35634 * @return {Roo.Element}
35636 getEl : function(){
35641 * Returns the specified region.
35642 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35643 * @return {Roo.LayoutRegion}
35645 getRegion : function(target){
35646 return this.regions[target.toLowerCase()];
35649 onWindowResize : function(){
35650 if(this.monitorWindowResize){
35657 * Ext JS Library 1.1.1
35658 * Copyright(c) 2006-2007, Ext JS, LLC.
35660 * Originally Released Under LGPL - original licence link has changed is not relivant.
35663 * <script type="text/javascript">
35666 * @class Roo.bootstrap.layout.Border
35667 * @extends Roo.bootstrap.layout.Manager
35668 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35669 * please see: examples/bootstrap/nested.html<br><br>
35671 <b>The container the layout is rendered into can be either the body element or any other element.
35672 If it is not the body element, the container needs to either be an absolute positioned element,
35673 or you will need to add "position:relative" to the css of the container. You will also need to specify
35674 the container size if it is not the body element.</b>
35677 * Create a new Border
35678 * @param {Object} config Configuration options
35680 Roo.bootstrap.layout.Border = function(config){
35681 config = config || {};
35682 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35686 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35687 if(config[region]){
35688 config[region].region = region;
35689 this.addRegion(config[region]);
35695 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
35697 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35699 parent : false, // this might point to a 'nest' or a ???
35702 * Creates and adds a new region if it doesn't already exist.
35703 * @param {String} target The target region key (north, south, east, west or center).
35704 * @param {Object} config The regions config object
35705 * @return {BorderLayoutRegion} The new region
35707 addRegion : function(config)
35709 if(!this.regions[config.region]){
35710 var r = this.factory(config);
35711 this.bindRegion(r);
35713 return this.regions[config.region];
35717 bindRegion : function(r){
35718 this.regions[r.config.region] = r;
35720 r.on("visibilitychange", this.layout, this);
35721 r.on("paneladded", this.layout, this);
35722 r.on("panelremoved", this.layout, this);
35723 r.on("invalidated", this.layout, this);
35724 r.on("resized", this.onRegionResized, this);
35725 r.on("collapsed", this.onRegionCollapsed, this);
35726 r.on("expanded", this.onRegionExpanded, this);
35730 * Performs a layout update.
35732 layout : function()
35734 if(this.updating) {
35738 // render all the rebions if they have not been done alreayd?
35739 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35740 if(this.regions[region] && !this.regions[region].bodyEl){
35741 this.regions[region].onRender(this.el)
35745 var size = this.getViewSize();
35746 var w = size.width;
35747 var h = size.height;
35752 //var x = 0, y = 0;
35754 var rs = this.regions;
35755 var north = rs["north"];
35756 var south = rs["south"];
35757 var west = rs["west"];
35758 var east = rs["east"];
35759 var center = rs["center"];
35760 //if(this.hideOnLayout){ // not supported anymore
35761 //c.el.setStyle("display", "none");
35763 if(north && north.isVisible()){
35764 var b = north.getBox();
35765 var m = north.getMargins();
35766 b.width = w - (m.left+m.right);
35769 centerY = b.height + b.y + m.bottom;
35770 centerH -= centerY;
35771 north.updateBox(this.safeBox(b));
35773 if(south && south.isVisible()){
35774 var b = south.getBox();
35775 var m = south.getMargins();
35776 b.width = w - (m.left+m.right);
35778 var totalHeight = (b.height + m.top + m.bottom);
35779 b.y = h - totalHeight + m.top;
35780 centerH -= totalHeight;
35781 south.updateBox(this.safeBox(b));
35783 if(west && west.isVisible()){
35784 var b = west.getBox();
35785 var m = west.getMargins();
35786 b.height = centerH - (m.top+m.bottom);
35788 b.y = centerY + m.top;
35789 var totalWidth = (b.width + m.left + m.right);
35790 centerX += totalWidth;
35791 centerW -= totalWidth;
35792 west.updateBox(this.safeBox(b));
35794 if(east && east.isVisible()){
35795 var b = east.getBox();
35796 var m = east.getMargins();
35797 b.height = centerH - (m.top+m.bottom);
35798 var totalWidth = (b.width + m.left + m.right);
35799 b.x = w - totalWidth + m.left;
35800 b.y = centerY + m.top;
35801 centerW -= totalWidth;
35802 east.updateBox(this.safeBox(b));
35805 var m = center.getMargins();
35807 x: centerX + m.left,
35808 y: centerY + m.top,
35809 width: centerW - (m.left+m.right),
35810 height: centerH - (m.top+m.bottom)
35812 //if(this.hideOnLayout){
35813 //center.el.setStyle("display", "block");
35815 center.updateBox(this.safeBox(centerBox));
35818 this.fireEvent("layout", this);
35822 safeBox : function(box){
35823 box.width = Math.max(0, box.width);
35824 box.height = Math.max(0, box.height);
35829 * Adds a ContentPanel (or subclass) to this layout.
35830 * @param {String} target The target region key (north, south, east, west or center).
35831 * @param {Roo.ContentPanel} panel The panel to add
35832 * @return {Roo.ContentPanel} The added panel
35834 add : function(target, panel){
35836 target = target.toLowerCase();
35837 return this.regions[target].add(panel);
35841 * Remove a ContentPanel (or subclass) to this layout.
35842 * @param {String} target The target region key (north, south, east, west or center).
35843 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35844 * @return {Roo.ContentPanel} The removed panel
35846 remove : function(target, panel){
35847 target = target.toLowerCase();
35848 return this.regions[target].remove(panel);
35852 * Searches all regions for a panel with the specified id
35853 * @param {String} panelId
35854 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35856 findPanel : function(panelId){
35857 var rs = this.regions;
35858 for(var target in rs){
35859 if(typeof rs[target] != "function"){
35860 var p = rs[target].getPanel(panelId);
35870 * Searches all regions for a panel with the specified id and activates (shows) it.
35871 * @param {String/ContentPanel} panelId The panels id or the panel itself
35872 * @return {Roo.ContentPanel} The shown panel or null
35874 showPanel : function(panelId) {
35875 var rs = this.regions;
35876 for(var target in rs){
35877 var r = rs[target];
35878 if(typeof r != "function"){
35879 if(r.hasPanel(panelId)){
35880 return r.showPanel(panelId);
35888 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35889 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35892 restoreState : function(provider){
35894 provider = Roo.state.Manager;
35896 var sm = new Roo.LayoutStateManager();
35897 sm.init(this, provider);
35903 * Adds a xtype elements to the layout.
35907 xtype : 'ContentPanel',
35914 xtype : 'NestedLayoutPanel',
35920 items : [ ... list of content panels or nested layout panels.. ]
35924 * @param {Object} cfg Xtype definition of item to add.
35926 addxtype : function(cfg)
35928 // basically accepts a pannel...
35929 // can accept a layout region..!?!?
35930 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35933 // theory? children can only be panels??
35935 //if (!cfg.xtype.match(/Panel$/)) {
35940 if (typeof(cfg.region) == 'undefined') {
35941 Roo.log("Failed to add Panel, region was not set");
35945 var region = cfg.region;
35951 xitems = cfg.items;
35956 if ( region == 'center') {
35957 Roo.log("Center: " + cfg.title);
35963 case 'Content': // ContentPanel (el, cfg)
35964 case 'Scroll': // ContentPanel (el, cfg)
35966 cfg.autoCreate = cfg.autoCreate || true;
35967 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35969 // var el = this.el.createChild();
35970 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35973 this.add(region, ret);
35977 case 'TreePanel': // our new panel!
35978 cfg.el = this.el.createChild();
35979 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35980 this.add(region, ret);
35985 // create a new Layout (which is a Border Layout...
35987 var clayout = cfg.layout;
35988 clayout.el = this.el.createChild();
35989 clayout.items = clayout.items || [];
35993 // replace this exitems with the clayout ones..
35994 xitems = clayout.items;
35996 // force background off if it's in center...
35997 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35998 cfg.background = false;
36000 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
36003 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36004 //console.log('adding nested layout panel ' + cfg.toSource());
36005 this.add(region, ret);
36006 nb = {}; /// find first...
36011 // needs grid and region
36013 //var el = this.getRegion(region).el.createChild();
36015 *var el = this.el.createChild();
36016 // create the grid first...
36017 cfg.grid.container = el;
36018 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
36021 if (region == 'center' && this.active ) {
36022 cfg.background = false;
36025 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36027 this.add(region, ret);
36029 if (cfg.background) {
36030 // render grid on panel activation (if panel background)
36031 ret.on('activate', function(gp) {
36032 if (!gp.grid.rendered) {
36033 // gp.grid.render(el);
36037 // cfg.grid.render(el);
36043 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
36044 // it was the old xcomponent building that caused this before.
36045 // espeically if border is the top element in the tree.
36055 if (typeof(Roo[cfg.xtype]) != 'undefined') {
36057 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
36058 this.add(region, ret);
36062 throw "Can not add '" + cfg.xtype + "' to Border";
36068 this.beginUpdate();
36072 Roo.each(xitems, function(i) {
36073 region = nb && i.region ? i.region : false;
36075 var add = ret.addxtype(i);
36078 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
36079 if (!i.background) {
36080 abn[region] = nb[region] ;
36087 // make the last non-background panel active..
36088 //if (nb) { Roo.log(abn); }
36091 for(var r in abn) {
36092 region = this.getRegion(r);
36094 // tried using nb[r], but it does not work..
36096 region.showPanel(abn[r]);
36107 factory : function(cfg)
36110 var validRegions = Roo.bootstrap.layout.Border.regions;
36112 var target = cfg.region;
36115 var r = Roo.bootstrap.layout;
36119 return new r.North(cfg);
36121 return new r.South(cfg);
36123 return new r.East(cfg);
36125 return new r.West(cfg);
36127 return new r.Center(cfg);
36129 throw 'Layout region "'+target+'" not supported.';
36136 * Ext JS Library 1.1.1
36137 * Copyright(c) 2006-2007, Ext JS, LLC.
36139 * Originally Released Under LGPL - original licence link has changed is not relivant.
36142 * <script type="text/javascript">
36146 * @class Roo.bootstrap.layout.Basic
36147 * @extends Roo.util.Observable
36148 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
36149 * and does not have a titlebar, tabs or any other features. All it does is size and position
36150 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
36151 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
36152 * @cfg {string} region the region that it inhabits..
36153 * @cfg {bool} skipConfig skip config?
36157 Roo.bootstrap.layout.Basic = function(config){
36159 this.mgr = config.mgr;
36161 this.position = config.region;
36163 var skipConfig = config.skipConfig;
36167 * @scope Roo.BasicLayoutRegion
36171 * @event beforeremove
36172 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
36173 * @param {Roo.LayoutRegion} this
36174 * @param {Roo.ContentPanel} panel The panel
36175 * @param {Object} e The cancel event object
36177 "beforeremove" : true,
36179 * @event invalidated
36180 * Fires when the layout for this region is changed.
36181 * @param {Roo.LayoutRegion} this
36183 "invalidated" : true,
36185 * @event visibilitychange
36186 * Fires when this region is shown or hidden
36187 * @param {Roo.LayoutRegion} this
36188 * @param {Boolean} visibility true or false
36190 "visibilitychange" : true,
36192 * @event paneladded
36193 * Fires when a panel is added.
36194 * @param {Roo.LayoutRegion} this
36195 * @param {Roo.ContentPanel} panel The panel
36197 "paneladded" : true,
36199 * @event panelremoved
36200 * Fires when a panel is removed.
36201 * @param {Roo.LayoutRegion} this
36202 * @param {Roo.ContentPanel} panel The panel
36204 "panelremoved" : true,
36206 * @event beforecollapse
36207 * Fires when this region before collapse.
36208 * @param {Roo.LayoutRegion} this
36210 "beforecollapse" : true,
36213 * Fires when this region is collapsed.
36214 * @param {Roo.LayoutRegion} this
36216 "collapsed" : true,
36219 * Fires when this region is expanded.
36220 * @param {Roo.LayoutRegion} this
36225 * Fires when this region is slid into view.
36226 * @param {Roo.LayoutRegion} this
36228 "slideshow" : true,
36231 * Fires when this region slides out of view.
36232 * @param {Roo.LayoutRegion} this
36234 "slidehide" : true,
36236 * @event panelactivated
36237 * Fires when a panel is activated.
36238 * @param {Roo.LayoutRegion} this
36239 * @param {Roo.ContentPanel} panel The activated panel
36241 "panelactivated" : true,
36244 * Fires when the user resizes this region.
36245 * @param {Roo.LayoutRegion} this
36246 * @param {Number} newSize The new size (width for east/west, height for north/south)
36250 /** A collection of panels in this region. @type Roo.util.MixedCollection */
36251 this.panels = new Roo.util.MixedCollection();
36252 this.panels.getKey = this.getPanelId.createDelegate(this);
36254 this.activePanel = null;
36255 // ensure listeners are added...
36257 if (config.listeners || config.events) {
36258 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
36259 listeners : config.listeners || {},
36260 events : config.events || {}
36264 if(skipConfig !== true){
36265 this.applyConfig(config);
36269 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
36271 getPanelId : function(p){
36275 applyConfig : function(config){
36276 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36277 this.config = config;
36282 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
36283 * the width, for horizontal (north, south) the height.
36284 * @param {Number} newSize The new width or height
36286 resizeTo : function(newSize){
36287 var el = this.el ? this.el :
36288 (this.activePanel ? this.activePanel.getEl() : null);
36290 switch(this.position){
36293 el.setWidth(newSize);
36294 this.fireEvent("resized", this, newSize);
36298 el.setHeight(newSize);
36299 this.fireEvent("resized", this, newSize);
36305 getBox : function(){
36306 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
36309 getMargins : function(){
36310 return this.margins;
36313 updateBox : function(box){
36315 var el = this.activePanel.getEl();
36316 el.dom.style.left = box.x + "px";
36317 el.dom.style.top = box.y + "px";
36318 this.activePanel.setSize(box.width, box.height);
36322 * Returns the container element for this region.
36323 * @return {Roo.Element}
36325 getEl : function(){
36326 return this.activePanel;
36330 * Returns true if this region is currently visible.
36331 * @return {Boolean}
36333 isVisible : function(){
36334 return this.activePanel ? true : false;
36337 setActivePanel : function(panel){
36338 panel = this.getPanel(panel);
36339 if(this.activePanel && this.activePanel != panel){
36340 this.activePanel.setActiveState(false);
36341 this.activePanel.getEl().setLeftTop(-10000,-10000);
36343 this.activePanel = panel;
36344 panel.setActiveState(true);
36346 panel.setSize(this.box.width, this.box.height);
36348 this.fireEvent("panelactivated", this, panel);
36349 this.fireEvent("invalidated");
36353 * Show the specified panel.
36354 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
36355 * @return {Roo.ContentPanel} The shown panel or null
36357 showPanel : function(panel){
36358 panel = this.getPanel(panel);
36360 this.setActivePanel(panel);
36366 * Get the active panel for this region.
36367 * @return {Roo.ContentPanel} The active panel or null
36369 getActivePanel : function(){
36370 return this.activePanel;
36374 * Add the passed ContentPanel(s)
36375 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36376 * @return {Roo.ContentPanel} The panel added (if only one was added)
36378 add : function(panel){
36379 if(arguments.length > 1){
36380 for(var i = 0, len = arguments.length; i < len; i++) {
36381 this.add(arguments[i]);
36385 if(this.hasPanel(panel)){
36386 this.showPanel(panel);
36389 var el = panel.getEl();
36390 if(el.dom.parentNode != this.mgr.el.dom){
36391 this.mgr.el.dom.appendChild(el.dom);
36393 if(panel.setRegion){
36394 panel.setRegion(this);
36396 this.panels.add(panel);
36397 el.setStyle("position", "absolute");
36398 if(!panel.background){
36399 this.setActivePanel(panel);
36400 if(this.config.initialSize && this.panels.getCount()==1){
36401 this.resizeTo(this.config.initialSize);
36404 this.fireEvent("paneladded", this, panel);
36409 * Returns true if the panel is in this region.
36410 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36411 * @return {Boolean}
36413 hasPanel : function(panel){
36414 if(typeof panel == "object"){ // must be panel obj
36415 panel = panel.getId();
36417 return this.getPanel(panel) ? true : false;
36421 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36422 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36423 * @param {Boolean} preservePanel Overrides the config preservePanel option
36424 * @return {Roo.ContentPanel} The panel that was removed
36426 remove : function(panel, preservePanel){
36427 panel = this.getPanel(panel);
36432 this.fireEvent("beforeremove", this, panel, e);
36433 if(e.cancel === true){
36436 var panelId = panel.getId();
36437 this.panels.removeKey(panelId);
36442 * Returns the panel specified or null if it's not in this region.
36443 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36444 * @return {Roo.ContentPanel}
36446 getPanel : function(id){
36447 if(typeof id == "object"){ // must be panel obj
36450 return this.panels.get(id);
36454 * Returns this regions position (north/south/east/west/center).
36457 getPosition: function(){
36458 return this.position;
36462 * Ext JS Library 1.1.1
36463 * Copyright(c) 2006-2007, Ext JS, LLC.
36465 * Originally Released Under LGPL - original licence link has changed is not relivant.
36468 * <script type="text/javascript">
36472 * @class Roo.bootstrap.layout.Region
36473 * @extends Roo.bootstrap.layout.Basic
36474 * This class represents a region in a layout manager.
36476 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36477 * @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})
36478 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
36479 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
36480 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
36481 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
36482 * @cfg {String} title The title for the region (overrides panel titles)
36483 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
36484 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36485 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
36486 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36487 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
36488 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36489 * the space available, similar to FireFox 1.5 tabs (defaults to false)
36490 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
36491 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
36492 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
36494 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
36495 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
36496 * @cfg {Boolean} disableTabTips True to disable tab tooltips
36497 * @cfg {Number} width For East/West panels
36498 * @cfg {Number} height For North/South panels
36499 * @cfg {Boolean} split To show the splitter
36500 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
36502 * @cfg {string} cls Extra CSS classes to add to region
36504 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
36505 * @cfg {string} region the region that it inhabits..
36508 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
36509 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
36511 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
36512 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
36513 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
36515 Roo.bootstrap.layout.Region = function(config)
36517 this.applyConfig(config);
36519 var mgr = config.mgr;
36520 var pos = config.region;
36521 config.skipConfig = true;
36522 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36525 this.onRender(mgr.el);
36528 this.visible = true;
36529 this.collapsed = false;
36530 this.unrendered_panels = [];
36533 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36535 position: '', // set by wrapper (eg. north/south etc..)
36536 unrendered_panels : null, // unrendered panels.
36538 tabPosition : false,
36540 mgr: false, // points to 'Border'
36543 createBody : function(){
36544 /** This region's body element
36545 * @type Roo.Element */
36546 this.bodyEl = this.el.createChild({
36548 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36552 onRender: function(ctr, pos)
36554 var dh = Roo.DomHelper;
36555 /** This region's container element
36556 * @type Roo.Element */
36557 this.el = dh.append(ctr.dom, {
36559 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36561 /** This region's title element
36562 * @type Roo.Element */
36564 this.titleEl = dh.append(this.el.dom, {
36566 unselectable: "on",
36567 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36569 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
36570 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36574 this.titleEl.enableDisplayMode();
36575 /** This region's title text element
36576 * @type HTMLElement */
36577 this.titleTextEl = this.titleEl.dom.firstChild;
36578 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36580 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36581 this.closeBtn.enableDisplayMode();
36582 this.closeBtn.on("click", this.closeClicked, this);
36583 this.closeBtn.hide();
36585 this.createBody(this.config);
36586 if(this.config.hideWhenEmpty){
36588 this.on("paneladded", this.validateVisibility, this);
36589 this.on("panelremoved", this.validateVisibility, this);
36591 if(this.autoScroll){
36592 this.bodyEl.setStyle("overflow", "auto");
36594 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36596 //if(c.titlebar !== false){
36597 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36598 this.titleEl.hide();
36600 this.titleEl.show();
36601 if(this.config.title){
36602 this.titleTextEl.innerHTML = this.config.title;
36606 if(this.config.collapsed){
36607 this.collapse(true);
36609 if(this.config.hidden){
36613 if (this.unrendered_panels && this.unrendered_panels.length) {
36614 for (var i =0;i< this.unrendered_panels.length; i++) {
36615 this.add(this.unrendered_panels[i]);
36617 this.unrendered_panels = null;
36623 applyConfig : function(c)
36626 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36627 var dh = Roo.DomHelper;
36628 if(c.titlebar !== false){
36629 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36630 this.collapseBtn.on("click", this.collapse, this);
36631 this.collapseBtn.enableDisplayMode();
36633 if(c.showPin === true || this.showPin){
36634 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36635 this.stickBtn.enableDisplayMode();
36636 this.stickBtn.on("click", this.expand, this);
36637 this.stickBtn.hide();
36642 /** This region's collapsed element
36643 * @type Roo.Element */
36646 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36647 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36650 if(c.floatable !== false){
36651 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36652 this.collapsedEl.on("click", this.collapseClick, this);
36655 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36656 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36657 id: "message", unselectable: "on", style:{"float":"left"}});
36658 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36660 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36661 this.expandBtn.on("click", this.expand, this);
36665 if(this.collapseBtn){
36666 this.collapseBtn.setVisible(c.collapsible == true);
36669 this.cmargins = c.cmargins || this.cmargins ||
36670 (this.position == "west" || this.position == "east" ?
36671 {top: 0, left: 2, right:2, bottom: 0} :
36672 {top: 2, left: 0, right:0, bottom: 2});
36674 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36677 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36679 this.autoScroll = c.autoScroll || false;
36684 this.duration = c.duration || .30;
36685 this.slideDuration = c.slideDuration || .45;
36690 * Returns true if this region is currently visible.
36691 * @return {Boolean}
36693 isVisible : function(){
36694 return this.visible;
36698 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36699 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
36701 //setCollapsedTitle : function(title){
36702 // title = title || " ";
36703 // if(this.collapsedTitleTextEl){
36704 // this.collapsedTitleTextEl.innerHTML = title;
36708 getBox : function(){
36710 // if(!this.collapsed){
36711 b = this.el.getBox(false, true);
36713 // b = this.collapsedEl.getBox(false, true);
36718 getMargins : function(){
36719 return this.margins;
36720 //return this.collapsed ? this.cmargins : this.margins;
36723 highlight : function(){
36724 this.el.addClass("x-layout-panel-dragover");
36727 unhighlight : function(){
36728 this.el.removeClass("x-layout-panel-dragover");
36731 updateBox : function(box)
36733 if (!this.bodyEl) {
36734 return; // not rendered yet..
36738 if(!this.collapsed){
36739 this.el.dom.style.left = box.x + "px";
36740 this.el.dom.style.top = box.y + "px";
36741 this.updateBody(box.width, box.height);
36743 this.collapsedEl.dom.style.left = box.x + "px";
36744 this.collapsedEl.dom.style.top = box.y + "px";
36745 this.collapsedEl.setSize(box.width, box.height);
36748 this.tabs.autoSizeTabs();
36752 updateBody : function(w, h)
36755 this.el.setWidth(w);
36756 w -= this.el.getBorderWidth("rl");
36757 if(this.config.adjustments){
36758 w += this.config.adjustments[0];
36761 if(h !== null && h > 0){
36762 this.el.setHeight(h);
36763 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36764 h -= this.el.getBorderWidth("tb");
36765 if(this.config.adjustments){
36766 h += this.config.adjustments[1];
36768 this.bodyEl.setHeight(h);
36770 h = this.tabs.syncHeight(h);
36773 if(this.panelSize){
36774 w = w !== null ? w : this.panelSize.width;
36775 h = h !== null ? h : this.panelSize.height;
36777 if(this.activePanel){
36778 var el = this.activePanel.getEl();
36779 w = w !== null ? w : el.getWidth();
36780 h = h !== null ? h : el.getHeight();
36781 this.panelSize = {width: w, height: h};
36782 this.activePanel.setSize(w, h);
36784 if(Roo.isIE && this.tabs){
36785 this.tabs.el.repaint();
36790 * Returns the container element for this region.
36791 * @return {Roo.Element}
36793 getEl : function(){
36798 * Hides this region.
36801 //if(!this.collapsed){
36802 this.el.dom.style.left = "-2000px";
36805 // this.collapsedEl.dom.style.left = "-2000px";
36806 // this.collapsedEl.hide();
36808 this.visible = false;
36809 this.fireEvent("visibilitychange", this, false);
36813 * Shows this region if it was previously hidden.
36816 //if(!this.collapsed){
36819 // this.collapsedEl.show();
36821 this.visible = true;
36822 this.fireEvent("visibilitychange", this, true);
36825 closeClicked : function(){
36826 if(this.activePanel){
36827 this.remove(this.activePanel);
36831 collapseClick : function(e){
36833 e.stopPropagation();
36836 e.stopPropagation();
36842 * Collapses this region.
36843 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36846 collapse : function(skipAnim, skipCheck = false){
36847 if(this.collapsed) {
36851 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36853 this.collapsed = true;
36855 this.split.el.hide();
36857 if(this.config.animate && skipAnim !== true){
36858 this.fireEvent("invalidated", this);
36859 this.animateCollapse();
36861 this.el.setLocation(-20000,-20000);
36863 this.collapsedEl.show();
36864 this.fireEvent("collapsed", this);
36865 this.fireEvent("invalidated", this);
36871 animateCollapse : function(){
36876 * Expands this region if it was previously collapsed.
36877 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36878 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36881 expand : function(e, skipAnim){
36883 e.stopPropagation();
36885 if(!this.collapsed || this.el.hasActiveFx()) {
36889 this.afterSlideIn();
36892 this.collapsed = false;
36893 if(this.config.animate && skipAnim !== true){
36894 this.animateExpand();
36898 this.split.el.show();
36900 this.collapsedEl.setLocation(-2000,-2000);
36901 this.collapsedEl.hide();
36902 this.fireEvent("invalidated", this);
36903 this.fireEvent("expanded", this);
36907 animateExpand : function(){
36911 initTabs : function()
36913 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36915 var ts = new Roo.bootstrap.panel.Tabs({
36916 el: this.bodyEl.dom,
36918 tabPosition: this.tabPosition ? this.tabPosition : 'top',
36919 disableTooltips: this.config.disableTabTips,
36920 toolbar : this.config.toolbar
36923 if(this.config.hideTabs){
36924 ts.stripWrap.setDisplayed(false);
36927 ts.resizeTabs = this.config.resizeTabs === true;
36928 ts.minTabWidth = this.config.minTabWidth || 40;
36929 ts.maxTabWidth = this.config.maxTabWidth || 250;
36930 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36931 ts.monitorResize = false;
36932 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36933 ts.bodyEl.addClass('roo-layout-tabs-body');
36934 this.panels.each(this.initPanelAsTab, this);
36937 initPanelAsTab : function(panel){
36938 var ti = this.tabs.addTab(
36942 this.config.closeOnTab && panel.isClosable(),
36945 if(panel.tabTip !== undefined){
36946 ti.setTooltip(panel.tabTip);
36948 ti.on("activate", function(){
36949 this.setActivePanel(panel);
36952 if(this.config.closeOnTab){
36953 ti.on("beforeclose", function(t, e){
36955 this.remove(panel);
36959 panel.tabItem = ti;
36964 updatePanelTitle : function(panel, title)
36966 if(this.activePanel == panel){
36967 this.updateTitle(title);
36970 var ti = this.tabs.getTab(panel.getEl().id);
36972 if(panel.tabTip !== undefined){
36973 ti.setTooltip(panel.tabTip);
36978 updateTitle : function(title){
36979 if(this.titleTextEl && !this.config.title){
36980 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36984 setActivePanel : function(panel)
36986 panel = this.getPanel(panel);
36987 if(this.activePanel && this.activePanel != panel){
36988 if(this.activePanel.setActiveState(false) === false){
36992 this.activePanel = panel;
36993 panel.setActiveState(true);
36994 if(this.panelSize){
36995 panel.setSize(this.panelSize.width, this.panelSize.height);
36998 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
37000 this.updateTitle(panel.getTitle());
37002 this.fireEvent("invalidated", this);
37004 this.fireEvent("panelactivated", this, panel);
37008 * Shows the specified panel.
37009 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
37010 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
37012 showPanel : function(panel)
37014 panel = this.getPanel(panel);
37017 var tab = this.tabs.getTab(panel.getEl().id);
37018 if(tab.isHidden()){
37019 this.tabs.unhideTab(tab.id);
37023 this.setActivePanel(panel);
37030 * Get the active panel for this region.
37031 * @return {Roo.ContentPanel} The active panel or null
37033 getActivePanel : function(){
37034 return this.activePanel;
37037 validateVisibility : function(){
37038 if(this.panels.getCount() < 1){
37039 this.updateTitle(" ");
37040 this.closeBtn.hide();
37043 if(!this.isVisible()){
37050 * Adds the passed ContentPanel(s) to this region.
37051 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37052 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
37054 add : function(panel)
37056 if(arguments.length > 1){
37057 for(var i = 0, len = arguments.length; i < len; i++) {
37058 this.add(arguments[i]);
37063 // if we have not been rendered yet, then we can not really do much of this..
37064 if (!this.bodyEl) {
37065 this.unrendered_panels.push(panel);
37072 if(this.hasPanel(panel)){
37073 this.showPanel(panel);
37076 panel.setRegion(this);
37077 this.panels.add(panel);
37078 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
37079 // sinle panel - no tab...?? would it not be better to render it with the tabs,
37080 // and hide them... ???
37081 this.bodyEl.dom.appendChild(panel.getEl().dom);
37082 if(panel.background !== true){
37083 this.setActivePanel(panel);
37085 this.fireEvent("paneladded", this, panel);
37092 this.initPanelAsTab(panel);
37096 if(panel.background !== true){
37097 this.tabs.activate(panel.getEl().id);
37099 this.fireEvent("paneladded", this, panel);
37104 * Hides the tab for the specified panel.
37105 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37107 hidePanel : function(panel){
37108 if(this.tabs && (panel = this.getPanel(panel))){
37109 this.tabs.hideTab(panel.getEl().id);
37114 * Unhides the tab for a previously hidden panel.
37115 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37117 unhidePanel : function(panel){
37118 if(this.tabs && (panel = this.getPanel(panel))){
37119 this.tabs.unhideTab(panel.getEl().id);
37123 clearPanels : function(){
37124 while(this.panels.getCount() > 0){
37125 this.remove(this.panels.first());
37130 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37131 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37132 * @param {Boolean} preservePanel Overrides the config preservePanel option
37133 * @return {Roo.ContentPanel} The panel that was removed
37135 remove : function(panel, preservePanel)
37137 panel = this.getPanel(panel);
37142 this.fireEvent("beforeremove", this, panel, e);
37143 if(e.cancel === true){
37146 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
37147 var panelId = panel.getId();
37148 this.panels.removeKey(panelId);
37150 document.body.appendChild(panel.getEl().dom);
37153 this.tabs.removeTab(panel.getEl().id);
37154 }else if (!preservePanel){
37155 this.bodyEl.dom.removeChild(panel.getEl().dom);
37157 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
37158 var p = this.panels.first();
37159 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
37160 tempEl.appendChild(p.getEl().dom);
37161 this.bodyEl.update("");
37162 this.bodyEl.dom.appendChild(p.getEl().dom);
37164 this.updateTitle(p.getTitle());
37166 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
37167 this.setActivePanel(p);
37169 panel.setRegion(null);
37170 if(this.activePanel == panel){
37171 this.activePanel = null;
37173 if(this.config.autoDestroy !== false && preservePanel !== true){
37174 try{panel.destroy();}catch(e){}
37176 this.fireEvent("panelremoved", this, panel);
37181 * Returns the TabPanel component used by this region
37182 * @return {Roo.TabPanel}
37184 getTabs : function(){
37188 createTool : function(parentEl, className){
37189 var btn = Roo.DomHelper.append(parentEl, {
37191 cls: "x-layout-tools-button",
37194 cls: "roo-layout-tools-button-inner " + className,
37198 btn.addClassOnOver("roo-layout-tools-button-over");
37203 * Ext JS Library 1.1.1
37204 * Copyright(c) 2006-2007, Ext JS, LLC.
37206 * Originally Released Under LGPL - original licence link has changed is not relivant.
37209 * <script type="text/javascript">
37215 * @class Roo.SplitLayoutRegion
37216 * @extends Roo.LayoutRegion
37217 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
37219 Roo.bootstrap.layout.Split = function(config){
37220 this.cursor = config.cursor;
37221 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
37224 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
37226 splitTip : "Drag to resize.",
37227 collapsibleSplitTip : "Drag to resize. Double click to hide.",
37228 useSplitTips : false,
37230 applyConfig : function(config){
37231 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
37234 onRender : function(ctr,pos) {
37236 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
37237 if(!this.config.split){
37242 var splitEl = Roo.DomHelper.append(ctr.dom, {
37244 id: this.el.id + "-split",
37245 cls: "roo-layout-split roo-layout-split-"+this.position,
37248 /** The SplitBar for this region
37249 * @type Roo.SplitBar */
37250 // does not exist yet...
37251 Roo.log([this.position, this.orientation]);
37253 this.split = new Roo.bootstrap.SplitBar({
37254 dragElement : splitEl,
37255 resizingElement: this.el,
37256 orientation : this.orientation
37259 this.split.on("moved", this.onSplitMove, this);
37260 this.split.useShim = this.config.useShim === true;
37261 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
37262 if(this.useSplitTips){
37263 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
37265 //if(config.collapsible){
37266 // this.split.el.on("dblclick", this.collapse, this);
37269 if(typeof this.config.minSize != "undefined"){
37270 this.split.minSize = this.config.minSize;
37272 if(typeof this.config.maxSize != "undefined"){
37273 this.split.maxSize = this.config.maxSize;
37275 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
37276 this.hideSplitter();
37281 getHMaxSize : function(){
37282 var cmax = this.config.maxSize || 10000;
37283 var center = this.mgr.getRegion("center");
37284 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
37287 getVMaxSize : function(){
37288 var cmax = this.config.maxSize || 10000;
37289 var center = this.mgr.getRegion("center");
37290 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
37293 onSplitMove : function(split, newSize){
37294 this.fireEvent("resized", this, newSize);
37298 * Returns the {@link Roo.SplitBar} for this region.
37299 * @return {Roo.SplitBar}
37301 getSplitBar : function(){
37306 this.hideSplitter();
37307 Roo.bootstrap.layout.Split.superclass.hide.call(this);
37310 hideSplitter : function(){
37312 this.split.el.setLocation(-2000,-2000);
37313 this.split.el.hide();
37319 this.split.el.show();
37321 Roo.bootstrap.layout.Split.superclass.show.call(this);
37324 beforeSlide: function(){
37325 if(Roo.isGecko){// firefox overflow auto bug workaround
37326 this.bodyEl.clip();
37328 this.tabs.bodyEl.clip();
37330 if(this.activePanel){
37331 this.activePanel.getEl().clip();
37333 if(this.activePanel.beforeSlide){
37334 this.activePanel.beforeSlide();
37340 afterSlide : function(){
37341 if(Roo.isGecko){// firefox overflow auto bug workaround
37342 this.bodyEl.unclip();
37344 this.tabs.bodyEl.unclip();
37346 if(this.activePanel){
37347 this.activePanel.getEl().unclip();
37348 if(this.activePanel.afterSlide){
37349 this.activePanel.afterSlide();
37355 initAutoHide : function(){
37356 if(this.autoHide !== false){
37357 if(!this.autoHideHd){
37358 var st = new Roo.util.DelayedTask(this.slideIn, this);
37359 this.autoHideHd = {
37360 "mouseout": function(e){
37361 if(!e.within(this.el, true)){
37365 "mouseover" : function(e){
37371 this.el.on(this.autoHideHd);
37375 clearAutoHide : function(){
37376 if(this.autoHide !== false){
37377 this.el.un("mouseout", this.autoHideHd.mouseout);
37378 this.el.un("mouseover", this.autoHideHd.mouseover);
37382 clearMonitor : function(){
37383 Roo.get(document).un("click", this.slideInIf, this);
37386 // these names are backwards but not changed for compat
37387 slideOut : function(){
37388 if(this.isSlid || this.el.hasActiveFx()){
37391 this.isSlid = true;
37392 if(this.collapseBtn){
37393 this.collapseBtn.hide();
37395 this.closeBtnState = this.closeBtn.getStyle('display');
37396 this.closeBtn.hide();
37398 this.stickBtn.show();
37401 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37402 this.beforeSlide();
37403 this.el.setStyle("z-index", 10001);
37404 this.el.slideIn(this.getSlideAnchor(), {
37405 callback: function(){
37407 this.initAutoHide();
37408 Roo.get(document).on("click", this.slideInIf, this);
37409 this.fireEvent("slideshow", this);
37416 afterSlideIn : function(){
37417 this.clearAutoHide();
37418 this.isSlid = false;
37419 this.clearMonitor();
37420 this.el.setStyle("z-index", "");
37421 if(this.collapseBtn){
37422 this.collapseBtn.show();
37424 this.closeBtn.setStyle('display', this.closeBtnState);
37426 this.stickBtn.hide();
37428 this.fireEvent("slidehide", this);
37431 slideIn : function(cb){
37432 if(!this.isSlid || this.el.hasActiveFx()){
37436 this.isSlid = false;
37437 this.beforeSlide();
37438 this.el.slideOut(this.getSlideAnchor(), {
37439 callback: function(){
37440 this.el.setLeftTop(-10000, -10000);
37442 this.afterSlideIn();
37450 slideInIf : function(e){
37451 if(!e.within(this.el)){
37456 animateCollapse : function(){
37457 this.beforeSlide();
37458 this.el.setStyle("z-index", 20000);
37459 var anchor = this.getSlideAnchor();
37460 this.el.slideOut(anchor, {
37461 callback : function(){
37462 this.el.setStyle("z-index", "");
37463 this.collapsedEl.slideIn(anchor, {duration:.3});
37465 this.el.setLocation(-10000,-10000);
37467 this.fireEvent("collapsed", this);
37474 animateExpand : function(){
37475 this.beforeSlide();
37476 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37477 this.el.setStyle("z-index", 20000);
37478 this.collapsedEl.hide({
37481 this.el.slideIn(this.getSlideAnchor(), {
37482 callback : function(){
37483 this.el.setStyle("z-index", "");
37486 this.split.el.show();
37488 this.fireEvent("invalidated", this);
37489 this.fireEvent("expanded", this);
37517 getAnchor : function(){
37518 return this.anchors[this.position];
37521 getCollapseAnchor : function(){
37522 return this.canchors[this.position];
37525 getSlideAnchor : function(){
37526 return this.sanchors[this.position];
37529 getAlignAdj : function(){
37530 var cm = this.cmargins;
37531 switch(this.position){
37547 getExpandAdj : function(){
37548 var c = this.collapsedEl, cm = this.cmargins;
37549 switch(this.position){
37551 return [-(cm.right+c.getWidth()+cm.left), 0];
37554 return [cm.right+c.getWidth()+cm.left, 0];
37557 return [0, -(cm.top+cm.bottom+c.getHeight())];
37560 return [0, cm.top+cm.bottom+c.getHeight()];
37566 * Ext JS Library 1.1.1
37567 * Copyright(c) 2006-2007, Ext JS, LLC.
37569 * Originally Released Under LGPL - original licence link has changed is not relivant.
37572 * <script type="text/javascript">
37575 * These classes are private internal classes
37577 Roo.bootstrap.layout.Center = function(config){
37578 config.region = "center";
37579 Roo.bootstrap.layout.Region.call(this, config);
37580 this.visible = true;
37581 this.minWidth = config.minWidth || 20;
37582 this.minHeight = config.minHeight || 20;
37585 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37587 // center panel can't be hidden
37591 // center panel can't be hidden
37594 getMinWidth: function(){
37595 return this.minWidth;
37598 getMinHeight: function(){
37599 return this.minHeight;
37613 Roo.bootstrap.layout.North = function(config)
37615 config.region = 'north';
37616 config.cursor = 'n-resize';
37618 Roo.bootstrap.layout.Split.call(this, config);
37622 this.split.placement = Roo.bootstrap.SplitBar.TOP;
37623 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37624 this.split.el.addClass("roo-layout-split-v");
37626 var size = config.initialSize || config.height;
37627 if(typeof size != "undefined"){
37628 this.el.setHeight(size);
37631 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37633 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37637 getBox : function(){
37638 if(this.collapsed){
37639 return this.collapsedEl.getBox();
37641 var box = this.el.getBox();
37643 box.height += this.split.el.getHeight();
37648 updateBox : function(box){
37649 if(this.split && !this.collapsed){
37650 box.height -= this.split.el.getHeight();
37651 this.split.el.setLeft(box.x);
37652 this.split.el.setTop(box.y+box.height);
37653 this.split.el.setWidth(box.width);
37655 if(this.collapsed){
37656 this.updateBody(box.width, null);
37658 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37666 Roo.bootstrap.layout.South = function(config){
37667 config.region = 'south';
37668 config.cursor = 's-resize';
37669 Roo.bootstrap.layout.Split.call(this, config);
37671 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37672 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37673 this.split.el.addClass("roo-layout-split-v");
37675 var size = config.initialSize || config.height;
37676 if(typeof size != "undefined"){
37677 this.el.setHeight(size);
37681 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37682 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37683 getBox : function(){
37684 if(this.collapsed){
37685 return this.collapsedEl.getBox();
37687 var box = this.el.getBox();
37689 var sh = this.split.el.getHeight();
37696 updateBox : function(box){
37697 if(this.split && !this.collapsed){
37698 var sh = this.split.el.getHeight();
37701 this.split.el.setLeft(box.x);
37702 this.split.el.setTop(box.y-sh);
37703 this.split.el.setWidth(box.width);
37705 if(this.collapsed){
37706 this.updateBody(box.width, null);
37708 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37712 Roo.bootstrap.layout.East = function(config){
37713 config.region = "east";
37714 config.cursor = "e-resize";
37715 Roo.bootstrap.layout.Split.call(this, config);
37717 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37718 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37719 this.split.el.addClass("roo-layout-split-h");
37721 var size = config.initialSize || config.width;
37722 if(typeof size != "undefined"){
37723 this.el.setWidth(size);
37726 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37727 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37728 getBox : function(){
37729 if(this.collapsed){
37730 return this.collapsedEl.getBox();
37732 var box = this.el.getBox();
37734 var sw = this.split.el.getWidth();
37741 updateBox : function(box){
37742 if(this.split && !this.collapsed){
37743 var sw = this.split.el.getWidth();
37745 this.split.el.setLeft(box.x);
37746 this.split.el.setTop(box.y);
37747 this.split.el.setHeight(box.height);
37750 if(this.collapsed){
37751 this.updateBody(null, box.height);
37753 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37757 Roo.bootstrap.layout.West = function(config){
37758 config.region = "west";
37759 config.cursor = "w-resize";
37761 Roo.bootstrap.layout.Split.call(this, config);
37763 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37764 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37765 this.split.el.addClass("roo-layout-split-h");
37769 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37770 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37772 onRender: function(ctr, pos)
37774 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37775 var size = this.config.initialSize || this.config.width;
37776 if(typeof size != "undefined"){
37777 this.el.setWidth(size);
37781 getBox : function(){
37782 if(this.collapsed){
37783 return this.collapsedEl.getBox();
37785 var box = this.el.getBox();
37787 box.width += this.split.el.getWidth();
37792 updateBox : function(box){
37793 if(this.split && !this.collapsed){
37794 var sw = this.split.el.getWidth();
37796 this.split.el.setLeft(box.x+box.width);
37797 this.split.el.setTop(box.y);
37798 this.split.el.setHeight(box.height);
37800 if(this.collapsed){
37801 this.updateBody(null, box.height);
37803 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37805 });Roo.namespace("Roo.bootstrap.panel");/*
37807 * Ext JS Library 1.1.1
37808 * Copyright(c) 2006-2007, Ext JS, LLC.
37810 * Originally Released Under LGPL - original licence link has changed is not relivant.
37813 * <script type="text/javascript">
37816 * @class Roo.ContentPanel
37817 * @extends Roo.util.Observable
37818 * A basic ContentPanel element.
37819 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37820 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37821 * @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
37822 * @cfg {Boolean} closable True if the panel can be closed/removed
37823 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37824 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37825 * @cfg {Toolbar} toolbar A toolbar for this panel
37826 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37827 * @cfg {String} title The title for this panel
37828 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37829 * @cfg {String} url Calls {@link #setUrl} with this value
37830 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37831 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37832 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37833 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37834 * @cfg {Boolean} badges render the badges
37837 * Create a new ContentPanel.
37838 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37839 * @param {String/Object} config A string to set only the title or a config object
37840 * @param {String} content (optional) Set the HTML content for this panel
37841 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37843 Roo.bootstrap.panel.Content = function( config){
37845 this.tpl = config.tpl || false;
37847 var el = config.el;
37848 var content = config.content;
37850 if(config.autoCreate){ // xtype is available if this is called from factory
37853 this.el = Roo.get(el);
37854 if(!this.el && config && config.autoCreate){
37855 if(typeof config.autoCreate == "object"){
37856 if(!config.autoCreate.id){
37857 config.autoCreate.id = config.id||el;
37859 this.el = Roo.DomHelper.append(document.body,
37860 config.autoCreate, true);
37862 var elcfg = { tag: "div",
37863 cls: "roo-layout-inactive-content",
37867 elcfg.html = config.html;
37871 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37874 this.closable = false;
37875 this.loaded = false;
37876 this.active = false;
37879 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37881 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37883 this.wrapEl = this.el; //this.el.wrap();
37885 if (config.toolbar.items) {
37886 ti = config.toolbar.items ;
37887 delete config.toolbar.items ;
37891 this.toolbar.render(this.wrapEl, 'before');
37892 for(var i =0;i < ti.length;i++) {
37893 // Roo.log(['add child', items[i]]);
37894 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37896 this.toolbar.items = nitems;
37897 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37898 delete config.toolbar;
37902 // xtype created footer. - not sure if will work as we normally have to render first..
37903 if (this.footer && !this.footer.el && this.footer.xtype) {
37904 if (!this.wrapEl) {
37905 this.wrapEl = this.el.wrap();
37908 this.footer.container = this.wrapEl.createChild();
37910 this.footer = Roo.factory(this.footer, Roo);
37915 if(typeof config == "string"){
37916 this.title = config;
37918 Roo.apply(this, config);
37922 this.resizeEl = Roo.get(this.resizeEl, true);
37924 this.resizeEl = this.el;
37926 // handle view.xtype
37934 * Fires when this panel is activated.
37935 * @param {Roo.ContentPanel} this
37939 * @event deactivate
37940 * Fires when this panel is activated.
37941 * @param {Roo.ContentPanel} this
37943 "deactivate" : true,
37947 * Fires when this panel is resized if fitToFrame is true.
37948 * @param {Roo.ContentPanel} this
37949 * @param {Number} width The width after any component adjustments
37950 * @param {Number} height The height after any component adjustments
37956 * Fires when this tab is created
37957 * @param {Roo.ContentPanel} this
37968 if(this.autoScroll){
37969 this.resizeEl.setStyle("overflow", "auto");
37971 // fix randome scrolling
37972 //this.el.on('scroll', function() {
37973 // Roo.log('fix random scolling');
37974 // this.scrollTo('top',0);
37977 content = content || this.content;
37979 this.setContent(content);
37981 if(config && config.url){
37982 this.setUrl(this.url, this.params, this.loadOnce);
37987 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37989 if (this.view && typeof(this.view.xtype) != 'undefined') {
37990 this.view.el = this.el.appendChild(document.createElement("div"));
37991 this.view = Roo.factory(this.view);
37992 this.view.render && this.view.render(false, '');
37996 this.fireEvent('render', this);
37999 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
38003 setRegion : function(region){
38004 this.region = region;
38005 this.setActiveClass(region && !this.background);
38009 setActiveClass: function(state)
38012 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
38013 this.el.setStyle('position','relative');
38015 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
38016 this.el.setStyle('position', 'absolute');
38021 * Returns the toolbar for this Panel if one was configured.
38022 * @return {Roo.Toolbar}
38024 getToolbar : function(){
38025 return this.toolbar;
38028 setActiveState : function(active)
38030 this.active = active;
38031 this.setActiveClass(active);
38033 if(this.fireEvent("deactivate", this) === false){
38038 this.fireEvent("activate", this);
38042 * Updates this panel's element
38043 * @param {String} content The new content
38044 * @param {Boolean} loadScripts (optional) true to look for and process scripts
38046 setContent : function(content, loadScripts){
38047 this.el.update(content, loadScripts);
38050 ignoreResize : function(w, h){
38051 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
38054 this.lastSize = {width: w, height: h};
38059 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
38060 * @return {Roo.UpdateManager} The UpdateManager
38062 getUpdateManager : function(){
38063 return this.el.getUpdateManager();
38066 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
38067 * @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:
38070 url: "your-url.php",
38071 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
38072 callback: yourFunction,
38073 scope: yourObject, //(optional scope)
38076 text: "Loading...",
38081 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
38082 * 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.
38083 * @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}
38084 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
38085 * @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.
38086 * @return {Roo.ContentPanel} this
38089 var um = this.el.getUpdateManager();
38090 um.update.apply(um, arguments);
38096 * 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.
38097 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
38098 * @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)
38099 * @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)
38100 * @return {Roo.UpdateManager} The UpdateManager
38102 setUrl : function(url, params, loadOnce){
38103 if(this.refreshDelegate){
38104 this.removeListener("activate", this.refreshDelegate);
38106 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38107 this.on("activate", this.refreshDelegate);
38108 return this.el.getUpdateManager();
38111 _handleRefresh : function(url, params, loadOnce){
38112 if(!loadOnce || !this.loaded){
38113 var updater = this.el.getUpdateManager();
38114 updater.update(url, params, this._setLoaded.createDelegate(this));
38118 _setLoaded : function(){
38119 this.loaded = true;
38123 * Returns this panel's id
38126 getId : function(){
38131 * Returns this panel's element - used by regiosn to add.
38132 * @return {Roo.Element}
38134 getEl : function(){
38135 return this.wrapEl || this.el;
38140 adjustForComponents : function(width, height)
38142 //Roo.log('adjustForComponents ');
38143 if(this.resizeEl != this.el){
38144 width -= this.el.getFrameWidth('lr');
38145 height -= this.el.getFrameWidth('tb');
38148 var te = this.toolbar.getEl();
38149 te.setWidth(width);
38150 height -= te.getHeight();
38153 var te = this.footer.getEl();
38154 te.setWidth(width);
38155 height -= te.getHeight();
38159 if(this.adjustments){
38160 width += this.adjustments[0];
38161 height += this.adjustments[1];
38163 return {"width": width, "height": height};
38166 setSize : function(width, height){
38167 if(this.fitToFrame && !this.ignoreResize(width, height)){
38168 if(this.fitContainer && this.resizeEl != this.el){
38169 this.el.setSize(width, height);
38171 var size = this.adjustForComponents(width, height);
38172 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
38173 this.fireEvent('resize', this, size.width, size.height);
38178 * Returns this panel's title
38181 getTitle : function(){
38183 if (typeof(this.title) != 'object') {
38188 for (var k in this.title) {
38189 if (!this.title.hasOwnProperty(k)) {
38193 if (k.indexOf('-') >= 0) {
38194 var s = k.split('-');
38195 for (var i = 0; i<s.length; i++) {
38196 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
38199 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
38206 * Set this panel's title
38207 * @param {String} title
38209 setTitle : function(title){
38210 this.title = title;
38212 this.region.updatePanelTitle(this, title);
38217 * Returns true is this panel was configured to be closable
38218 * @return {Boolean}
38220 isClosable : function(){
38221 return this.closable;
38224 beforeSlide : function(){
38226 this.resizeEl.clip();
38229 afterSlide : function(){
38231 this.resizeEl.unclip();
38235 * Force a content refresh from the URL specified in the {@link #setUrl} method.
38236 * Will fail silently if the {@link #setUrl} method has not been called.
38237 * This does not activate the panel, just updates its content.
38239 refresh : function(){
38240 if(this.refreshDelegate){
38241 this.loaded = false;
38242 this.refreshDelegate();
38247 * Destroys this panel
38249 destroy : function(){
38250 this.el.removeAllListeners();
38251 var tempEl = document.createElement("span");
38252 tempEl.appendChild(this.el.dom);
38253 tempEl.innerHTML = "";
38259 * form - if the content panel contains a form - this is a reference to it.
38260 * @type {Roo.form.Form}
38264 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
38265 * This contains a reference to it.
38271 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
38281 * @param {Object} cfg Xtype definition of item to add.
38285 getChildContainer: function () {
38286 return this.getEl();
38291 var ret = new Roo.factory(cfg);
38296 if (cfg.xtype.match(/^Form$/)) {
38299 //if (this.footer) {
38300 // el = this.footer.container.insertSibling(false, 'before');
38302 el = this.el.createChild();
38305 this.form = new Roo.form.Form(cfg);
38308 if ( this.form.allItems.length) {
38309 this.form.render(el.dom);
38313 // should only have one of theses..
38314 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
38315 // views.. should not be just added - used named prop 'view''
38317 cfg.el = this.el.appendChild(document.createElement("div"));
38320 var ret = new Roo.factory(cfg);
38322 ret.render && ret.render(false, ''); // render blank..
38332 * @class Roo.bootstrap.panel.Grid
38333 * @extends Roo.bootstrap.panel.Content
38335 * Create a new GridPanel.
38336 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
38337 * @param {Object} config A the config object
38343 Roo.bootstrap.panel.Grid = function(config)
38347 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
38348 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
38350 config.el = this.wrapper;
38351 //this.el = this.wrapper;
38353 if (config.container) {
38354 // ctor'ed from a Border/panel.grid
38357 this.wrapper.setStyle("overflow", "hidden");
38358 this.wrapper.addClass('roo-grid-container');
38363 if(config.toolbar){
38364 var tool_el = this.wrapper.createChild();
38365 this.toolbar = Roo.factory(config.toolbar);
38367 if (config.toolbar.items) {
38368 ti = config.toolbar.items ;
38369 delete config.toolbar.items ;
38373 this.toolbar.render(tool_el);
38374 for(var i =0;i < ti.length;i++) {
38375 // Roo.log(['add child', items[i]]);
38376 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38378 this.toolbar.items = nitems;
38380 delete config.toolbar;
38383 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
38384 config.grid.scrollBody = true;;
38385 config.grid.monitorWindowResize = false; // turn off autosizing
38386 config.grid.autoHeight = false;
38387 config.grid.autoWidth = false;
38389 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
38391 if (config.background) {
38392 // render grid on panel activation (if panel background)
38393 this.on('activate', function(gp) {
38394 if (!gp.grid.rendered) {
38395 gp.grid.render(this.wrapper);
38396 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
38401 this.grid.render(this.wrapper);
38402 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
38405 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
38406 // ??? needed ??? config.el = this.wrapper;
38411 // xtype created footer. - not sure if will work as we normally have to render first..
38412 if (this.footer && !this.footer.el && this.footer.xtype) {
38414 var ctr = this.grid.getView().getFooterPanel(true);
38415 this.footer.dataSource = this.grid.dataSource;
38416 this.footer = Roo.factory(this.footer, Roo);
38417 this.footer.render(ctr);
38427 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38428 getId : function(){
38429 return this.grid.id;
38433 * Returns the grid for this panel
38434 * @return {Roo.bootstrap.Table}
38436 getGrid : function(){
38440 setSize : function(width, height){
38441 if(!this.ignoreResize(width, height)){
38442 var grid = this.grid;
38443 var size = this.adjustForComponents(width, height);
38444 var gridel = grid.getGridEl();
38445 gridel.setSize(size.width, size.height);
38447 var thd = grid.getGridEl().select('thead',true).first();
38448 var tbd = grid.getGridEl().select('tbody', true).first();
38450 tbd.setSize(width, height - thd.getHeight());
38459 beforeSlide : function(){
38460 this.grid.getView().scroller.clip();
38463 afterSlide : function(){
38464 this.grid.getView().scroller.unclip();
38467 destroy : function(){
38468 this.grid.destroy();
38470 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
38475 * @class Roo.bootstrap.panel.Nest
38476 * @extends Roo.bootstrap.panel.Content
38478 * Create a new Panel, that can contain a layout.Border.
38481 * @param {Roo.BorderLayout} layout The layout for this panel
38482 * @param {String/Object} config A string to set only the title or a config object
38484 Roo.bootstrap.panel.Nest = function(config)
38486 // construct with only one argument..
38487 /* FIXME - implement nicer consturctors
38488 if (layout.layout) {
38490 layout = config.layout;
38491 delete config.layout;
38493 if (layout.xtype && !layout.getEl) {
38494 // then layout needs constructing..
38495 layout = Roo.factory(layout, Roo);
38499 config.el = config.layout.getEl();
38501 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38503 config.layout.monitorWindowResize = false; // turn off autosizing
38504 this.layout = config.layout;
38505 this.layout.getEl().addClass("roo-layout-nested-layout");
38506 this.layout.parent = this;
38513 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38515 setSize : function(width, height){
38516 if(!this.ignoreResize(width, height)){
38517 var size = this.adjustForComponents(width, height);
38518 var el = this.layout.getEl();
38519 if (size.height < 1) {
38520 el.setWidth(size.width);
38522 el.setSize(size.width, size.height);
38524 var touch = el.dom.offsetWidth;
38525 this.layout.layout();
38526 // ie requires a double layout on the first pass
38527 if(Roo.isIE && !this.initialized){
38528 this.initialized = true;
38529 this.layout.layout();
38534 // activate all subpanels if not currently active..
38536 setActiveState : function(active){
38537 this.active = active;
38538 this.setActiveClass(active);
38541 this.fireEvent("deactivate", this);
38545 this.fireEvent("activate", this);
38546 // not sure if this should happen before or after..
38547 if (!this.layout) {
38548 return; // should not happen..
38551 for (var r in this.layout.regions) {
38552 reg = this.layout.getRegion(r);
38553 if (reg.getActivePanel()) {
38554 //reg.showPanel(reg.getActivePanel()); // force it to activate..
38555 reg.setActivePanel(reg.getActivePanel());
38558 if (!reg.panels.length) {
38561 reg.showPanel(reg.getPanel(0));
38570 * Returns the nested BorderLayout for this panel
38571 * @return {Roo.BorderLayout}
38573 getLayout : function(){
38574 return this.layout;
38578 * Adds a xtype elements to the layout of the nested panel
38582 xtype : 'ContentPanel',
38589 xtype : 'NestedLayoutPanel',
38595 items : [ ... list of content panels or nested layout panels.. ]
38599 * @param {Object} cfg Xtype definition of item to add.
38601 addxtype : function(cfg) {
38602 return this.layout.addxtype(cfg);
38607 * Ext JS Library 1.1.1
38608 * Copyright(c) 2006-2007, Ext JS, LLC.
38610 * Originally Released Under LGPL - original licence link has changed is not relivant.
38613 * <script type="text/javascript">
38616 * @class Roo.TabPanel
38617 * @extends Roo.util.Observable
38618 * A lightweight tab container.
38622 // basic tabs 1, built from existing content
38623 var tabs = new Roo.TabPanel("tabs1");
38624 tabs.addTab("script", "View Script");
38625 tabs.addTab("markup", "View Markup");
38626 tabs.activate("script");
38628 // more advanced tabs, built from javascript
38629 var jtabs = new Roo.TabPanel("jtabs");
38630 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38632 // set up the UpdateManager
38633 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38634 var updater = tab2.getUpdateManager();
38635 updater.setDefaultUrl("ajax1.htm");
38636 tab2.on('activate', updater.refresh, updater, true);
38638 // Use setUrl for Ajax loading
38639 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38640 tab3.setUrl("ajax2.htm", null, true);
38643 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38646 jtabs.activate("jtabs-1");
38649 * Create a new TabPanel.
38650 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38651 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38653 Roo.bootstrap.panel.Tabs = function(config){
38655 * The container element for this TabPanel.
38656 * @type Roo.Element
38658 this.el = Roo.get(config.el);
38661 if(typeof config == "boolean"){
38662 this.tabPosition = config ? "bottom" : "top";
38664 Roo.apply(this, config);
38668 if(this.tabPosition == "bottom"){
38669 // if tabs are at the bottom = create the body first.
38670 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38671 this.el.addClass("roo-tabs-bottom");
38673 // next create the tabs holders
38675 if (this.tabPosition == "west"){
38677 var reg = this.region; // fake it..
38679 if (!reg.mgr.parent) {
38682 reg = reg.mgr.parent.region;
38684 Roo.log("got nest?");
38686 if (reg.mgr.getRegion('west')) {
38687 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38688 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38689 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38690 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38691 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38699 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38700 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38701 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38702 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38707 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38710 // finally - if tabs are at the top, then create the body last..
38711 if(this.tabPosition != "bottom"){
38712 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38713 * @type Roo.Element
38715 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38716 this.el.addClass("roo-tabs-top");
38720 this.bodyEl.setStyle("position", "relative");
38722 this.active = null;
38723 this.activateDelegate = this.activate.createDelegate(this);
38728 * Fires when the active tab changes
38729 * @param {Roo.TabPanel} this
38730 * @param {Roo.TabPanelItem} activePanel The new active tab
38734 * @event beforetabchange
38735 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38736 * @param {Roo.TabPanel} this
38737 * @param {Object} e Set cancel to true on this object to cancel the tab change
38738 * @param {Roo.TabPanelItem} tab The tab being changed to
38740 "beforetabchange" : true
38743 Roo.EventManager.onWindowResize(this.onResize, this);
38744 this.cpad = this.el.getPadding("lr");
38745 this.hiddenCount = 0;
38748 // toolbar on the tabbar support...
38749 if (this.toolbar) {
38750 alert("no toolbar support yet");
38751 this.toolbar = false;
38753 var tcfg = this.toolbar;
38754 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
38755 this.toolbar = new Roo.Toolbar(tcfg);
38756 if (Roo.isSafari) {
38757 var tbl = tcfg.container.child('table', true);
38758 tbl.setAttribute('width', '100%');
38766 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38769 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38771 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38773 tabPosition : "top",
38775 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38777 currentTabWidth : 0,
38779 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38783 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38787 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38789 preferredTabWidth : 175,
38791 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38793 resizeTabs : false,
38795 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38797 monitorResize : true,
38799 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
38801 toolbar : false, // set by caller..
38803 region : false, /// set by caller
38805 disableTooltips : true, // not used yet...
38808 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38809 * @param {String} id The id of the div to use <b>or create</b>
38810 * @param {String} text The text for the tab
38811 * @param {String} content (optional) Content to put in the TabPanelItem body
38812 * @param {Boolean} closable (optional) True to create a close icon on the tab
38813 * @return {Roo.TabPanelItem} The created TabPanelItem
38815 addTab : function(id, text, content, closable, tpl)
38817 var item = new Roo.bootstrap.panel.TabItem({
38821 closable : closable,
38824 this.addTabItem(item);
38826 item.setContent(content);
38832 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38833 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38834 * @return {Roo.TabPanelItem}
38836 getTab : function(id){
38837 return this.items[id];
38841 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38842 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38844 hideTab : function(id){
38845 var t = this.items[id];
38848 this.hiddenCount++;
38849 this.autoSizeTabs();
38854 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38855 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38857 unhideTab : function(id){
38858 var t = this.items[id];
38860 t.setHidden(false);
38861 this.hiddenCount--;
38862 this.autoSizeTabs();
38867 * Adds an existing {@link Roo.TabPanelItem}.
38868 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38870 addTabItem : function(item)
38872 this.items[item.id] = item;
38873 this.items.push(item);
38874 this.autoSizeTabs();
38875 // if(this.resizeTabs){
38876 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38877 // this.autoSizeTabs();
38879 // item.autoSize();
38884 * Removes a {@link Roo.TabPanelItem}.
38885 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38887 removeTab : function(id){
38888 var items = this.items;
38889 var tab = items[id];
38890 if(!tab) { return; }
38891 var index = items.indexOf(tab);
38892 if(this.active == tab && items.length > 1){
38893 var newTab = this.getNextAvailable(index);
38898 this.stripEl.dom.removeChild(tab.pnode.dom);
38899 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38900 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38902 items.splice(index, 1);
38903 delete this.items[tab.id];
38904 tab.fireEvent("close", tab);
38905 tab.purgeListeners();
38906 this.autoSizeTabs();
38909 getNextAvailable : function(start){
38910 var items = this.items;
38912 // look for a next tab that will slide over to
38913 // replace the one being removed
38914 while(index < items.length){
38915 var item = items[++index];
38916 if(item && !item.isHidden()){
38920 // if one isn't found select the previous tab (on the left)
38923 var item = items[--index];
38924 if(item && !item.isHidden()){
38932 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38933 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38935 disableTab : function(id){
38936 var tab = this.items[id];
38937 if(tab && this.active != tab){
38943 * Enables a {@link Roo.TabPanelItem} that is disabled.
38944 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38946 enableTab : function(id){
38947 var tab = this.items[id];
38952 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38953 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38954 * @return {Roo.TabPanelItem} The TabPanelItem.
38956 activate : function(id)
38958 //Roo.log('activite:' + id);
38960 var tab = this.items[id];
38964 if(tab == this.active || tab.disabled){
38968 this.fireEvent("beforetabchange", this, e, tab);
38969 if(e.cancel !== true && !tab.disabled){
38971 this.active.hide();
38973 this.active = this.items[id];
38974 this.active.show();
38975 this.fireEvent("tabchange", this, this.active);
38981 * Gets the active {@link Roo.TabPanelItem}.
38982 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38984 getActiveTab : function(){
38985 return this.active;
38989 * Updates the tab body element to fit the height of the container element
38990 * for overflow scrolling
38991 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38993 syncHeight : function(targetHeight){
38994 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38995 var bm = this.bodyEl.getMargins();
38996 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38997 this.bodyEl.setHeight(newHeight);
39001 onResize : function(){
39002 if(this.monitorResize){
39003 this.autoSizeTabs();
39008 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
39010 beginUpdate : function(){
39011 this.updating = true;
39015 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
39017 endUpdate : function(){
39018 this.updating = false;
39019 this.autoSizeTabs();
39023 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
39025 autoSizeTabs : function()
39027 var count = this.items.length;
39028 var vcount = count - this.hiddenCount;
39031 this.stripEl.hide();
39033 this.stripEl.show();
39036 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
39041 var w = Math.max(this.el.getWidth() - this.cpad, 10);
39042 var availWidth = Math.floor(w / vcount);
39043 var b = this.stripBody;
39044 if(b.getWidth() > w){
39045 var tabs = this.items;
39046 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
39047 if(availWidth < this.minTabWidth){
39048 /*if(!this.sleft){ // incomplete scrolling code
39049 this.createScrollButtons();
39052 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
39055 if(this.currentTabWidth < this.preferredTabWidth){
39056 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
39062 * Returns the number of tabs in this TabPanel.
39065 getCount : function(){
39066 return this.items.length;
39070 * Resizes all the tabs to the passed width
39071 * @param {Number} The new width
39073 setTabWidth : function(width){
39074 this.currentTabWidth = width;
39075 for(var i = 0, len = this.items.length; i < len; i++) {
39076 if(!this.items[i].isHidden()) {
39077 this.items[i].setWidth(width);
39083 * Destroys this TabPanel
39084 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
39086 destroy : function(removeEl){
39087 Roo.EventManager.removeResizeListener(this.onResize, this);
39088 for(var i = 0, len = this.items.length; i < len; i++){
39089 this.items[i].purgeListeners();
39091 if(removeEl === true){
39092 this.el.update("");
39097 createStrip : function(container)
39099 var strip = document.createElement("nav");
39100 strip.className = Roo.bootstrap.version == 4 ?
39101 "navbar-light bg-light" :
39102 "navbar navbar-default"; //"x-tabs-wrap";
39103 container.appendChild(strip);
39107 createStripList : function(strip)
39109 // div wrapper for retard IE
39110 // returns the "tr" element.
39111 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
39112 //'<div class="x-tabs-strip-wrap">'+
39113 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
39114 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
39115 return strip.firstChild; //.firstChild.firstChild.firstChild;
39117 createBody : function(container)
39119 var body = document.createElement("div");
39120 Roo.id(body, "tab-body");
39121 //Roo.fly(body).addClass("x-tabs-body");
39122 Roo.fly(body).addClass("tab-content");
39123 container.appendChild(body);
39126 createItemBody :function(bodyEl, id){
39127 var body = Roo.getDom(id);
39129 body = document.createElement("div");
39132 //Roo.fly(body).addClass("x-tabs-item-body");
39133 Roo.fly(body).addClass("tab-pane");
39134 bodyEl.insertBefore(body, bodyEl.firstChild);
39138 createStripElements : function(stripEl, text, closable, tpl)
39140 var td = document.createElement("li"); // was td..
39141 td.className = 'nav-item';
39143 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
39146 stripEl.appendChild(td);
39148 td.className = "x-tabs-closable";
39149 if(!this.closeTpl){
39150 this.closeTpl = new Roo.Template(
39151 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39152 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
39153 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
39156 var el = this.closeTpl.overwrite(td, {"text": text});
39157 var close = el.getElementsByTagName("div")[0];
39158 var inner = el.getElementsByTagName("em")[0];
39159 return {"el": el, "close": close, "inner": inner};
39162 // not sure what this is..
39163 // if(!this.tabTpl){
39164 //this.tabTpl = new Roo.Template(
39165 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39166 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
39168 // this.tabTpl = new Roo.Template(
39169 // '<a href="#">' +
39170 // '<span unselectable="on"' +
39171 // (this.disableTooltips ? '' : ' title="{text}"') +
39172 // ' >{text}</span></a>'
39178 var template = tpl || this.tabTpl || false;
39181 template = new Roo.Template(
39182 Roo.bootstrap.version == 4 ?
39184 '<a class="nav-link" href="#" unselectable="on"' +
39185 (this.disableTooltips ? '' : ' title="{text}"') +
39188 '<a class="nav-link" href="#">' +
39189 '<span unselectable="on"' +
39190 (this.disableTooltips ? '' : ' title="{text}"') +
39191 ' >{text}</span></a>'
39196 switch (typeof(template)) {
39200 template = new Roo.Template(template);
39206 var el = template.overwrite(td, {"text": text});
39208 var inner = el.getElementsByTagName("span")[0];
39210 return {"el": el, "inner": inner};
39218 * @class Roo.TabPanelItem
39219 * @extends Roo.util.Observable
39220 * Represents an individual item (tab plus body) in a TabPanel.
39221 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
39222 * @param {String} id The id of this TabPanelItem
39223 * @param {String} text The text for the tab of this TabPanelItem
39224 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
39226 Roo.bootstrap.panel.TabItem = function(config){
39228 * The {@link Roo.TabPanel} this TabPanelItem belongs to
39229 * @type Roo.TabPanel
39231 this.tabPanel = config.panel;
39233 * The id for this TabPanelItem
39236 this.id = config.id;
39238 this.disabled = false;
39240 this.text = config.text;
39242 this.loaded = false;
39243 this.closable = config.closable;
39246 * The body element for this TabPanelItem.
39247 * @type Roo.Element
39249 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
39250 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
39251 this.bodyEl.setStyle("display", "block");
39252 this.bodyEl.setStyle("zoom", "1");
39253 //this.hideAction();
39255 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
39257 this.el = Roo.get(els.el);
39258 this.inner = Roo.get(els.inner, true);
39259 this.textEl = Roo.bootstrap.version == 4 ?
39260 this.el : Roo.get(this.el.dom.firstChild, true);
39262 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
39263 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
39266 // this.el.on("mousedown", this.onTabMouseDown, this);
39267 this.el.on("click", this.onTabClick, this);
39269 if(config.closable){
39270 var c = Roo.get(els.close, true);
39271 c.dom.title = this.closeText;
39272 c.addClassOnOver("close-over");
39273 c.on("click", this.closeClick, this);
39279 * Fires when this tab becomes the active tab.
39280 * @param {Roo.TabPanel} tabPanel The parent TabPanel
39281 * @param {Roo.TabPanelItem} this
39285 * @event beforeclose
39286 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
39287 * @param {Roo.TabPanelItem} this
39288 * @param {Object} e Set cancel to true on this object to cancel the close.
39290 "beforeclose": true,
39293 * Fires when this tab is closed.
39294 * @param {Roo.TabPanelItem} this
39298 * @event deactivate
39299 * Fires when this tab is no longer the active tab.
39300 * @param {Roo.TabPanel} tabPanel The parent TabPanel
39301 * @param {Roo.TabPanelItem} this
39303 "deactivate" : true
39305 this.hidden = false;
39307 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
39310 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
39312 purgeListeners : function(){
39313 Roo.util.Observable.prototype.purgeListeners.call(this);
39314 this.el.removeAllListeners();
39317 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
39320 this.status_node.addClass("active");
39323 this.tabPanel.stripWrap.repaint();
39325 this.fireEvent("activate", this.tabPanel, this);
39329 * Returns true if this tab is the active tab.
39330 * @return {Boolean}
39332 isActive : function(){
39333 return this.tabPanel.getActiveTab() == this;
39337 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
39340 this.status_node.removeClass("active");
39342 this.fireEvent("deactivate", this.tabPanel, this);
39345 hideAction : function(){
39346 this.bodyEl.hide();
39347 this.bodyEl.setStyle("position", "absolute");
39348 this.bodyEl.setLeft("-20000px");
39349 this.bodyEl.setTop("-20000px");
39352 showAction : function(){
39353 this.bodyEl.setStyle("position", "relative");
39354 this.bodyEl.setTop("");
39355 this.bodyEl.setLeft("");
39356 this.bodyEl.show();
39360 * Set the tooltip for the tab.
39361 * @param {String} tooltip The tab's tooltip
39363 setTooltip : function(text){
39364 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
39365 this.textEl.dom.qtip = text;
39366 this.textEl.dom.removeAttribute('title');
39368 this.textEl.dom.title = text;
39372 onTabClick : function(e){
39373 e.preventDefault();
39374 this.tabPanel.activate(this.id);
39377 onTabMouseDown : function(e){
39378 e.preventDefault();
39379 this.tabPanel.activate(this.id);
39382 getWidth : function(){
39383 return this.inner.getWidth();
39386 setWidth : function(width){
39387 var iwidth = width - this.linode.getPadding("lr");
39388 this.inner.setWidth(iwidth);
39389 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
39390 this.linode.setWidth(width);
39394 * Show or hide the tab
39395 * @param {Boolean} hidden True to hide or false to show.
39397 setHidden : function(hidden){
39398 this.hidden = hidden;
39399 this.linode.setStyle("display", hidden ? "none" : "");
39403 * Returns true if this tab is "hidden"
39404 * @return {Boolean}
39406 isHidden : function(){
39407 return this.hidden;
39411 * Returns the text for this tab
39414 getText : function(){
39418 autoSize : function(){
39419 //this.el.beginMeasure();
39420 this.textEl.setWidth(1);
39422 * #2804 [new] Tabs in Roojs
39423 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39425 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39426 //this.el.endMeasure();
39430 * Sets the text for the tab (Note: this also sets the tooltip text)
39431 * @param {String} text The tab's text and tooltip
39433 setText : function(text){
39435 this.textEl.update(text);
39436 this.setTooltip(text);
39437 //if(!this.tabPanel.resizeTabs){
39438 // this.autoSize();
39442 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39444 activate : function(){
39445 this.tabPanel.activate(this.id);
39449 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39451 disable : function(){
39452 if(this.tabPanel.active != this){
39453 this.disabled = true;
39454 this.status_node.addClass("disabled");
39459 * Enables this TabPanelItem if it was previously disabled.
39461 enable : function(){
39462 this.disabled = false;
39463 this.status_node.removeClass("disabled");
39467 * Sets the content for this TabPanelItem.
39468 * @param {String} content The content
39469 * @param {Boolean} loadScripts true to look for and load scripts
39471 setContent : function(content, loadScripts){
39472 this.bodyEl.update(content, loadScripts);
39476 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39477 * @return {Roo.UpdateManager} The UpdateManager
39479 getUpdateManager : function(){
39480 return this.bodyEl.getUpdateManager();
39484 * Set a URL to be used to load the content for this TabPanelItem.
39485 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39486 * @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)
39487 * @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)
39488 * @return {Roo.UpdateManager} The UpdateManager
39490 setUrl : function(url, params, loadOnce){
39491 if(this.refreshDelegate){
39492 this.un('activate', this.refreshDelegate);
39494 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39495 this.on("activate", this.refreshDelegate);
39496 return this.bodyEl.getUpdateManager();
39500 _handleRefresh : function(url, params, loadOnce){
39501 if(!loadOnce || !this.loaded){
39502 var updater = this.bodyEl.getUpdateManager();
39503 updater.update(url, params, this._setLoaded.createDelegate(this));
39508 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
39509 * Will fail silently if the setUrl method has not been called.
39510 * This does not activate the panel, just updates its content.
39512 refresh : function(){
39513 if(this.refreshDelegate){
39514 this.loaded = false;
39515 this.refreshDelegate();
39520 _setLoaded : function(){
39521 this.loaded = true;
39525 closeClick : function(e){
39528 this.fireEvent("beforeclose", this, o);
39529 if(o.cancel !== true){
39530 this.tabPanel.removeTab(this.id);
39534 * The text displayed in the tooltip for the close icon.
39537 closeText : "Close this tab"
39540 * This script refer to:
39541 * Title: International Telephone Input
39542 * Author: Jack O'Connor
39543 * Code version: v12.1.12
39544 * Availability: https://github.com/jackocnr/intl-tel-input.git
39547 Roo.bootstrap.PhoneInputData = function() {
39550 "Afghanistan (افغانستان)",
39555 "Albania (Shqipëri)",
39560 "Algeria (الجزائر)",
39585 "Antigua and Barbuda",
39595 "Armenia (Հայաստան)",
39611 "Austria (Österreich)",
39616 "Azerbaijan (Azərbaycan)",
39626 "Bahrain (البحرين)",
39631 "Bangladesh (বাংলাদেশ)",
39641 "Belarus (Беларусь)",
39646 "Belgium (België)",
39676 "Bosnia and Herzegovina (Босна и Херцеговина)",
39691 "British Indian Ocean Territory",
39696 "British Virgin Islands",
39706 "Bulgaria (България)",
39716 "Burundi (Uburundi)",
39721 "Cambodia (កម្ពុជា)",
39726 "Cameroon (Cameroun)",
39735 ["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"]
39738 "Cape Verde (Kabu Verdi)",
39743 "Caribbean Netherlands",
39754 "Central African Republic (République centrafricaine)",
39774 "Christmas Island",
39780 "Cocos (Keeling) Islands",
39791 "Comoros (جزر القمر)",
39796 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39801 "Congo (Republic) (Congo-Brazzaville)",
39821 "Croatia (Hrvatska)",
39842 "Czech Republic (Česká republika)",
39847 "Denmark (Danmark)",
39862 "Dominican Republic (República Dominicana)",
39866 ["809", "829", "849"]
39884 "Equatorial Guinea (Guinea Ecuatorial)",
39904 "Falkland Islands (Islas Malvinas)",
39909 "Faroe Islands (Føroyar)",
39930 "French Guiana (Guyane française)",
39935 "French Polynesia (Polynésie française)",
39950 "Georgia (საქართველო)",
39955 "Germany (Deutschland)",
39975 "Greenland (Kalaallit Nunaat)",
40012 "Guinea-Bissau (Guiné Bissau)",
40037 "Hungary (Magyarország)",
40042 "Iceland (Ísland)",
40062 "Iraq (العراق)",
40078 "Israel (ישראל)",
40105 "Jordan (الأردن)",
40110 "Kazakhstan (Казахстан)",
40131 "Kuwait (الكويت)",
40136 "Kyrgyzstan (Кыргызстан)",
40146 "Latvia (Latvija)",
40151 "Lebanon (لبنان)",
40166 "Libya (ليبيا)",
40176 "Lithuania (Lietuva)",
40191 "Macedonia (FYROM) (Македонија)",
40196 "Madagascar (Madagasikara)",
40226 "Marshall Islands",
40236 "Mauritania (موريتانيا)",
40241 "Mauritius (Moris)",
40262 "Moldova (Republica Moldova)",
40272 "Mongolia (Монгол)",
40277 "Montenegro (Crna Gora)",
40287 "Morocco (المغرب)",
40293 "Mozambique (Moçambique)",
40298 "Myanmar (Burma) (မြန်မာ)",
40303 "Namibia (Namibië)",
40318 "Netherlands (Nederland)",
40323 "New Caledonia (Nouvelle-Calédonie)",
40358 "North Korea (조선 민주주의 인민 공화국)",
40363 "Northern Mariana Islands",
40379 "Pakistan (پاکستان)",
40389 "Palestine (فلسطين)",
40399 "Papua New Guinea",
40441 "Réunion (La Réunion)",
40447 "Romania (România)",
40463 "Saint Barthélemy",
40474 "Saint Kitts and Nevis",
40484 "Saint Martin (Saint-Martin (partie française))",
40490 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40495 "Saint Vincent and the Grenadines",
40510 "São Tomé and Príncipe (São Tomé e Príncipe)",
40515 "Saudi Arabia (المملكة العربية السعودية)",
40520 "Senegal (Sénégal)",
40550 "Slovakia (Slovensko)",
40555 "Slovenia (Slovenija)",
40565 "Somalia (Soomaaliya)",
40575 "South Korea (대한민국)",
40580 "South Sudan (جنوب السودان)",
40590 "Sri Lanka (ශ්රී ලංකාව)",
40595 "Sudan (السودان)",
40605 "Svalbard and Jan Mayen",
40616 "Sweden (Sverige)",
40621 "Switzerland (Schweiz)",
40626 "Syria (سوريا)",
40671 "Trinidad and Tobago",
40676 "Tunisia (تونس)",
40681 "Turkey (Türkiye)",
40691 "Turks and Caicos Islands",
40701 "U.S. Virgin Islands",
40711 "Ukraine (Україна)",
40716 "United Arab Emirates (الإمارات العربية المتحدة)",
40738 "Uzbekistan (Oʻzbekiston)",
40748 "Vatican City (Città del Vaticano)",
40759 "Vietnam (Việt Nam)",
40764 "Wallis and Futuna (Wallis-et-Futuna)",
40769 "Western Sahara (الصحراء الغربية)",
40775 "Yemen (اليمن)",
40799 * This script refer to:
40800 * Title: International Telephone Input
40801 * Author: Jack O'Connor
40802 * Code version: v12.1.12
40803 * Availability: https://github.com/jackocnr/intl-tel-input.git
40807 * @class Roo.bootstrap.PhoneInput
40808 * @extends Roo.bootstrap.TriggerField
40809 * An input with International dial-code selection
40811 * @cfg {String} defaultDialCode default '+852'
40812 * @cfg {Array} preferedCountries default []
40815 * Create a new PhoneInput.
40816 * @param {Object} config Configuration options
40819 Roo.bootstrap.PhoneInput = function(config) {
40820 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40823 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40825 listWidth: undefined,
40827 selectedClass: 'active',
40829 invalidClass : "has-warning",
40831 validClass: 'has-success',
40833 allowed: '0123456789',
40838 * @cfg {String} defaultDialCode The default dial code when initializing the input
40840 defaultDialCode: '+852',
40843 * @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
40845 preferedCountries: false,
40847 getAutoCreate : function()
40849 var data = Roo.bootstrap.PhoneInputData();
40850 var align = this.labelAlign || this.parentLabelAlign();
40853 this.allCountries = [];
40854 this.dialCodeMapping = [];
40856 for (var i = 0; i < data.length; i++) {
40858 this.allCountries[i] = {
40862 priority: c[3] || 0,
40863 areaCodes: c[4] || null
40865 this.dialCodeMapping[c[2]] = {
40868 priority: c[3] || 0,
40869 areaCodes: c[4] || null
40881 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40882 maxlength: this.max_length,
40883 cls : 'form-control tel-input',
40884 autocomplete: 'new-password'
40887 var hiddenInput = {
40890 cls: 'hidden-tel-input'
40894 hiddenInput.name = this.name;
40897 if (this.disabled) {
40898 input.disabled = true;
40901 var flag_container = {
40918 cls: this.hasFeedback ? 'has-feedback' : '',
40924 cls: 'dial-code-holder',
40931 cls: 'roo-select2-container input-group',
40938 if (this.fieldLabel.length) {
40941 tooltip: 'This field is required'
40947 cls: 'control-label',
40953 html: this.fieldLabel
40956 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40962 if(this.indicatorpos == 'right') {
40963 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40970 if(align == 'left') {
40978 if(this.labelWidth > 12){
40979 label.style = "width: " + this.labelWidth + 'px';
40981 if(this.labelWidth < 13 && this.labelmd == 0){
40982 this.labelmd = this.labelWidth;
40984 if(this.labellg > 0){
40985 label.cls += ' col-lg-' + this.labellg;
40986 input.cls += ' col-lg-' + (12 - this.labellg);
40988 if(this.labelmd > 0){
40989 label.cls += ' col-md-' + this.labelmd;
40990 container.cls += ' col-md-' + (12 - this.labelmd);
40992 if(this.labelsm > 0){
40993 label.cls += ' col-sm-' + this.labelsm;
40994 container.cls += ' col-sm-' + (12 - this.labelsm);
40996 if(this.labelxs > 0){
40997 label.cls += ' col-xs-' + this.labelxs;
40998 container.cls += ' col-xs-' + (12 - this.labelxs);
41008 var settings = this;
41010 ['xs','sm','md','lg'].map(function(size){
41011 if (settings[size]) {
41012 cfg.cls += ' col-' + size + '-' + settings[size];
41016 this.store = new Roo.data.Store({
41017 proxy : new Roo.data.MemoryProxy({}),
41018 reader : new Roo.data.JsonReader({
41029 'name' : 'dialCode',
41033 'name' : 'priority',
41037 'name' : 'areaCodes',
41044 if(!this.preferedCountries) {
41045 this.preferedCountries = [
41052 var p = this.preferedCountries.reverse();
41055 for (var i = 0; i < p.length; i++) {
41056 for (var j = 0; j < this.allCountries.length; j++) {
41057 if(this.allCountries[j].iso2 == p[i]) {
41058 var t = this.allCountries[j];
41059 this.allCountries.splice(j,1);
41060 this.allCountries.unshift(t);
41066 this.store.proxy.data = {
41068 data: this.allCountries
41074 initEvents : function()
41077 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
41079 this.indicator = this.indicatorEl();
41080 this.flag = this.flagEl();
41081 this.dialCodeHolder = this.dialCodeHolderEl();
41083 this.trigger = this.el.select('div.flag-box',true).first();
41084 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41089 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41090 _this.list.setWidth(lw);
41093 this.list.on('mouseover', this.onViewOver, this);
41094 this.list.on('mousemove', this.onViewMove, this);
41095 this.inputEl().on("keyup", this.onKeyUp, this);
41096 this.inputEl().on("keypress", this.onKeyPress, this);
41098 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
41100 this.view = new Roo.View(this.list, this.tpl, {
41101 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41104 this.view.on('click', this.onViewClick, this);
41105 this.setValue(this.defaultDialCode);
41108 onTriggerClick : function(e)
41110 Roo.log('trigger click');
41115 if(this.isExpanded()){
41117 this.hasFocus = false;
41119 this.store.load({});
41120 this.hasFocus = true;
41125 isExpanded : function()
41127 return this.list.isVisible();
41130 collapse : function()
41132 if(!this.isExpanded()){
41136 Roo.get(document).un('mousedown', this.collapseIf, this);
41137 Roo.get(document).un('mousewheel', this.collapseIf, this);
41138 this.fireEvent('collapse', this);
41142 expand : function()
41146 if(this.isExpanded() || !this.hasFocus){
41150 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
41151 this.list.setWidth(lw);
41154 this.restrictHeight();
41156 Roo.get(document).on('mousedown', this.collapseIf, this);
41157 Roo.get(document).on('mousewheel', this.collapseIf, this);
41159 this.fireEvent('expand', this);
41162 restrictHeight : function()
41164 this.list.alignTo(this.inputEl(), this.listAlign);
41165 this.list.alignTo(this.inputEl(), this.listAlign);
41168 onViewOver : function(e, t)
41170 if(this.inKeyMode){
41173 var item = this.view.findItemFromChild(t);
41176 var index = this.view.indexOf(item);
41177 this.select(index, false);
41182 onViewClick : function(view, doFocus, el, e)
41184 var index = this.view.getSelectedIndexes()[0];
41186 var r = this.store.getAt(index);
41189 this.onSelect(r, index);
41191 if(doFocus !== false && !this.blockFocus){
41192 this.inputEl().focus();
41196 onViewMove : function(e, t)
41198 this.inKeyMode = false;
41201 select : function(index, scrollIntoView)
41203 this.selectedIndex = index;
41204 this.view.select(index);
41205 if(scrollIntoView !== false){
41206 var el = this.view.getNode(index);
41208 this.list.scrollChildIntoView(el, false);
41213 createList : function()
41215 this.list = Roo.get(document.body).createChild({
41217 cls: 'typeahead typeahead-long dropdown-menu tel-list',
41218 style: 'display:none'
41221 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
41224 collapseIf : function(e)
41226 var in_combo = e.within(this.el);
41227 var in_list = e.within(this.list);
41228 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
41230 if (in_combo || in_list || is_list) {
41236 onSelect : function(record, index)
41238 if(this.fireEvent('beforeselect', this, record, index) !== false){
41240 this.setFlagClass(record.data.iso2);
41241 this.setDialCode(record.data.dialCode);
41242 this.hasFocus = false;
41244 this.fireEvent('select', this, record, index);
41248 flagEl : function()
41250 var flag = this.el.select('div.flag',true).first();
41257 dialCodeHolderEl : function()
41259 var d = this.el.select('input.dial-code-holder',true).first();
41266 setDialCode : function(v)
41268 this.dialCodeHolder.dom.value = '+'+v;
41271 setFlagClass : function(n)
41273 this.flag.dom.className = 'flag '+n;
41276 getValue : function()
41278 var v = this.inputEl().getValue();
41279 if(this.dialCodeHolder) {
41280 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
41285 setValue : function(v)
41287 var d = this.getDialCode(v);
41289 //invalid dial code
41290 if(v.length == 0 || !d || d.length == 0) {
41292 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41293 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41299 this.setFlagClass(this.dialCodeMapping[d].iso2);
41300 this.setDialCode(d);
41301 this.inputEl().dom.value = v.replace('+'+d,'');
41302 this.hiddenEl().dom.value = this.getValue();
41307 getDialCode : function(v)
41311 if (v.length == 0) {
41312 return this.dialCodeHolder.dom.value;
41316 if (v.charAt(0) != "+") {
41319 var numericChars = "";
41320 for (var i = 1; i < v.length; i++) {
41321 var c = v.charAt(i);
41324 if (this.dialCodeMapping[numericChars]) {
41325 dialCode = v.substr(1, i);
41327 if (numericChars.length == 4) {
41337 this.setValue(this.defaultDialCode);
41341 hiddenEl : function()
41343 return this.el.select('input.hidden-tel-input',true).first();
41346 // after setting val
41347 onKeyUp : function(e){
41348 this.setValue(this.getValue());
41351 onKeyPress : function(e){
41352 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
41359 * @class Roo.bootstrap.MoneyField
41360 * @extends Roo.bootstrap.ComboBox
41361 * Bootstrap MoneyField class
41364 * Create a new MoneyField.
41365 * @param {Object} config Configuration options
41368 Roo.bootstrap.MoneyField = function(config) {
41370 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
41374 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
41377 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41379 allowDecimals : true,
41381 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41383 decimalSeparator : ".",
41385 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41387 decimalPrecision : 0,
41389 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41391 allowNegative : true,
41393 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41397 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41399 minValue : Number.NEGATIVE_INFINITY,
41401 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41403 maxValue : Number.MAX_VALUE,
41405 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41407 minText : "The minimum value for this field is {0}",
41409 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41411 maxText : "The maximum value for this field is {0}",
41413 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
41414 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41416 nanText : "{0} is not a valid number",
41418 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41422 * @cfg {String} defaults currency of the MoneyField
41423 * value should be in lkey
41425 defaultCurrency : false,
41427 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41429 thousandsDelimiter : false,
41431 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41442 getAutoCreate : function()
41444 var align = this.labelAlign || this.parentLabelAlign();
41456 cls : 'form-control roo-money-amount-input',
41457 autocomplete: 'new-password'
41460 var hiddenInput = {
41464 cls: 'hidden-number-input'
41467 if(this.max_length) {
41468 input.maxlength = this.max_length;
41472 hiddenInput.name = this.name;
41475 if (this.disabled) {
41476 input.disabled = true;
41479 var clg = 12 - this.inputlg;
41480 var cmd = 12 - this.inputmd;
41481 var csm = 12 - this.inputsm;
41482 var cxs = 12 - this.inputxs;
41486 cls : 'row roo-money-field',
41490 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41494 cls: 'roo-select2-container input-group',
41498 cls : 'form-control roo-money-currency-input',
41499 autocomplete: 'new-password',
41501 name : this.currencyName
41505 cls : 'input-group-addon',
41519 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41523 cls: this.hasFeedback ? 'has-feedback' : '',
41534 if (this.fieldLabel.length) {
41537 tooltip: 'This field is required'
41543 cls: 'control-label',
41549 html: this.fieldLabel
41552 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41558 if(this.indicatorpos == 'right') {
41559 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41566 if(align == 'left') {
41574 if(this.labelWidth > 12){
41575 label.style = "width: " + this.labelWidth + 'px';
41577 if(this.labelWidth < 13 && this.labelmd == 0){
41578 this.labelmd = this.labelWidth;
41580 if(this.labellg > 0){
41581 label.cls += ' col-lg-' + this.labellg;
41582 input.cls += ' col-lg-' + (12 - this.labellg);
41584 if(this.labelmd > 0){
41585 label.cls += ' col-md-' + this.labelmd;
41586 container.cls += ' col-md-' + (12 - this.labelmd);
41588 if(this.labelsm > 0){
41589 label.cls += ' col-sm-' + this.labelsm;
41590 container.cls += ' col-sm-' + (12 - this.labelsm);
41592 if(this.labelxs > 0){
41593 label.cls += ' col-xs-' + this.labelxs;
41594 container.cls += ' col-xs-' + (12 - this.labelxs);
41605 var settings = this;
41607 ['xs','sm','md','lg'].map(function(size){
41608 if (settings[size]) {
41609 cfg.cls += ' col-' + size + '-' + settings[size];
41616 initEvents : function()
41618 this.indicator = this.indicatorEl();
41620 this.initCurrencyEvent();
41622 this.initNumberEvent();
41625 initCurrencyEvent : function()
41628 throw "can not find store for combo";
41631 this.store = Roo.factory(this.store, Roo.data);
41632 this.store.parent = this;
41636 this.triggerEl = this.el.select('.input-group-addon', true).first();
41638 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41643 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41644 _this.list.setWidth(lw);
41647 this.list.on('mouseover', this.onViewOver, this);
41648 this.list.on('mousemove', this.onViewMove, this);
41649 this.list.on('scroll', this.onViewScroll, this);
41652 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41655 this.view = new Roo.View(this.list, this.tpl, {
41656 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41659 this.view.on('click', this.onViewClick, this);
41661 this.store.on('beforeload', this.onBeforeLoad, this);
41662 this.store.on('load', this.onLoad, this);
41663 this.store.on('loadexception', this.onLoadException, this);
41665 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41666 "up" : function(e){
41667 this.inKeyMode = true;
41671 "down" : function(e){
41672 if(!this.isExpanded()){
41673 this.onTriggerClick();
41675 this.inKeyMode = true;
41680 "enter" : function(e){
41683 if(this.fireEvent("specialkey", this, e)){
41684 this.onViewClick(false);
41690 "esc" : function(e){
41694 "tab" : function(e){
41697 if(this.fireEvent("specialkey", this, e)){
41698 this.onViewClick(false);
41706 doRelay : function(foo, bar, hname){
41707 if(hname == 'down' || this.scope.isExpanded()){
41708 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41716 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41720 initNumberEvent : function(e)
41722 this.inputEl().on("keydown" , this.fireKey, this);
41723 this.inputEl().on("focus", this.onFocus, this);
41724 this.inputEl().on("blur", this.onBlur, this);
41726 this.inputEl().relayEvent('keyup', this);
41728 if(this.indicator){
41729 this.indicator.addClass('invisible');
41732 this.originalValue = this.getValue();
41734 if(this.validationEvent == 'keyup'){
41735 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41736 this.inputEl().on('keyup', this.filterValidation, this);
41738 else if(this.validationEvent !== false){
41739 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41742 if(this.selectOnFocus){
41743 this.on("focus", this.preFocus, this);
41746 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41747 this.inputEl().on("keypress", this.filterKeys, this);
41749 this.inputEl().relayEvent('keypress', this);
41752 var allowed = "0123456789";
41754 if(this.allowDecimals){
41755 allowed += this.decimalSeparator;
41758 if(this.allowNegative){
41762 if(this.thousandsDelimiter) {
41766 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41768 var keyPress = function(e){
41770 var k = e.getKey();
41772 var c = e.getCharCode();
41775 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41776 allowed.indexOf(String.fromCharCode(c)) === -1
41782 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41786 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41791 this.inputEl().on("keypress", keyPress, this);
41795 onTriggerClick : function(e)
41802 this.loadNext = false;
41804 if(this.isExpanded()){
41809 this.hasFocus = true;
41811 if(this.triggerAction == 'all') {
41812 this.doQuery(this.allQuery, true);
41816 this.doQuery(this.getRawValue());
41819 getCurrency : function()
41821 var v = this.currencyEl().getValue();
41826 restrictHeight : function()
41828 this.list.alignTo(this.currencyEl(), this.listAlign);
41829 this.list.alignTo(this.currencyEl(), this.listAlign);
41832 onViewClick : function(view, doFocus, el, e)
41834 var index = this.view.getSelectedIndexes()[0];
41836 var r = this.store.getAt(index);
41839 this.onSelect(r, index);
41843 onSelect : function(record, index){
41845 if(this.fireEvent('beforeselect', this, record, index) !== false){
41847 this.setFromCurrencyData(index > -1 ? record.data : false);
41851 this.fireEvent('select', this, record, index);
41855 setFromCurrencyData : function(o)
41859 this.lastCurrency = o;
41861 if (this.currencyField) {
41862 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41864 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41867 this.lastSelectionText = currency;
41869 //setting default currency
41870 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41871 this.setCurrency(this.defaultCurrency);
41875 this.setCurrency(currency);
41878 setFromData : function(o)
41882 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41884 this.setFromCurrencyData(c);
41889 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41891 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41894 this.setValue(value);
41898 setCurrency : function(v)
41900 this.currencyValue = v;
41903 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41908 setValue : function(v)
41910 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41916 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41918 this.inputEl().dom.value = (v == '') ? '' :
41919 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41921 if(!this.allowZero && v === '0') {
41922 this.hiddenEl().dom.value = '';
41923 this.inputEl().dom.value = '';
41930 getRawValue : function()
41932 var v = this.inputEl().getValue();
41937 getValue : function()
41939 return this.fixPrecision(this.parseValue(this.getRawValue()));
41942 parseValue : function(value)
41944 if(this.thousandsDelimiter) {
41946 r = new RegExp(",", "g");
41947 value = value.replace(r, "");
41950 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41951 return isNaN(value) ? '' : value;
41955 fixPrecision : function(value)
41957 if(this.thousandsDelimiter) {
41959 r = new RegExp(",", "g");
41960 value = value.replace(r, "");
41963 var nan = isNaN(value);
41965 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41966 return nan ? '' : value;
41968 return parseFloat(value).toFixed(this.decimalPrecision);
41971 decimalPrecisionFcn : function(v)
41973 return Math.floor(v);
41976 validateValue : function(value)
41978 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41982 var num = this.parseValue(value);
41985 this.markInvalid(String.format(this.nanText, value));
41989 if(num < this.minValue){
41990 this.markInvalid(String.format(this.minText, this.minValue));
41994 if(num > this.maxValue){
41995 this.markInvalid(String.format(this.maxText, this.maxValue));
42002 validate : function()
42004 if(this.disabled || this.allowBlank){
42009 var currency = this.getCurrency();
42011 if(this.validateValue(this.getRawValue()) && currency.length){
42016 this.markInvalid();
42020 getName: function()
42025 beforeBlur : function()
42031 var v = this.parseValue(this.getRawValue());
42038 onBlur : function()
42042 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
42043 //this.el.removeClass(this.focusClass);
42046 this.hasFocus = false;
42048 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
42052 var v = this.getValue();
42054 if(String(v) !== String(this.startValue)){
42055 this.fireEvent('change', this, v, this.startValue);
42058 this.fireEvent("blur", this);
42061 inputEl : function()
42063 return this.el.select('.roo-money-amount-input', true).first();
42066 currencyEl : function()
42068 return this.el.select('.roo-money-currency-input', true).first();
42071 hiddenEl : function()
42073 return this.el.select('input.hidden-number-input',true).first();
42077 * @class Roo.bootstrap.BezierSignature
42078 * @extends Roo.bootstrap.Component
42079 * Bootstrap BezierSignature class
42080 * This script refer to:
42081 * Title: Signature Pad
42083 * Availability: https://github.com/szimek/signature_pad
42086 * Create a new BezierSignature
42087 * @param {Object} config The config object
42090 Roo.bootstrap.BezierSignature = function(config){
42091 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
42097 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
42104 mouse_btn_down: true,
42107 * @cfg {int} canvas height
42109 canvas_height: '200px',
42112 * @cfg {float|function} Radius of a single dot.
42117 * @cfg {float} Minimum width of a line. Defaults to 0.5.
42122 * @cfg {float} Maximum width of a line. Defaults to 2.5.
42127 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
42132 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
42137 * @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.
42139 bg_color: 'rgba(0, 0, 0, 0)',
42142 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
42144 dot_color: 'black',
42147 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
42149 velocity_filter_weight: 0.7,
42152 * @cfg {function} Callback when stroke begin.
42157 * @cfg {function} Callback when stroke end.
42161 getAutoCreate : function()
42163 var cls = 'roo-signature column';
42166 cls += ' ' + this.cls;
42176 for(var i = 0; i < col_sizes.length; i++) {
42177 if(this[col_sizes[i]]) {
42178 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
42188 cls: 'roo-signature-body',
42192 cls: 'roo-signature-body-canvas',
42193 height: this.canvas_height,
42194 width: this.canvas_width
42201 style: 'display: none'
42209 initEvents: function()
42211 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
42213 var canvas = this.canvasEl();
42215 // mouse && touch event swapping...
42216 canvas.dom.style.touchAction = 'none';
42217 canvas.dom.style.msTouchAction = 'none';
42219 this.mouse_btn_down = false;
42220 canvas.on('mousedown', this._handleMouseDown, this);
42221 canvas.on('mousemove', this._handleMouseMove, this);
42222 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
42224 if (window.PointerEvent) {
42225 canvas.on('pointerdown', this._handleMouseDown, this);
42226 canvas.on('pointermove', this._handleMouseMove, this);
42227 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
42230 if ('ontouchstart' in window) {
42231 canvas.on('touchstart', this._handleTouchStart, this);
42232 canvas.on('touchmove', this._handleTouchMove, this);
42233 canvas.on('touchend', this._handleTouchEnd, this);
42236 Roo.EventManager.onWindowResize(this.resize, this, true);
42238 // file input event
42239 this.fileEl().on('change', this.uploadImage, this);
42246 resize: function(){
42248 var canvas = this.canvasEl().dom;
42249 var ctx = this.canvasElCtx();
42250 var img_data = false;
42252 if(canvas.width > 0) {
42253 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
42255 // setting canvas width will clean img data
42258 var style = window.getComputedStyle ?
42259 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
42261 var padding_left = parseInt(style.paddingLeft) || 0;
42262 var padding_right = parseInt(style.paddingRight) || 0;
42264 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
42267 ctx.putImageData(img_data, 0, 0);
42271 _handleMouseDown: function(e)
42273 if (e.browserEvent.which === 1) {
42274 this.mouse_btn_down = true;
42275 this.strokeBegin(e);
42279 _handleMouseMove: function (e)
42281 if (this.mouse_btn_down) {
42282 this.strokeMoveUpdate(e);
42286 _handleMouseUp: function (e)
42288 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
42289 this.mouse_btn_down = false;
42294 _handleTouchStart: function (e) {
42296 e.preventDefault();
42297 if (e.browserEvent.targetTouches.length === 1) {
42298 // var touch = e.browserEvent.changedTouches[0];
42299 // this.strokeBegin(touch);
42301 this.strokeBegin(e); // assume e catching the correct xy...
42305 _handleTouchMove: function (e) {
42306 e.preventDefault();
42307 // var touch = event.targetTouches[0];
42308 // _this._strokeMoveUpdate(touch);
42309 this.strokeMoveUpdate(e);
42312 _handleTouchEnd: function (e) {
42313 var wasCanvasTouched = e.target === this.canvasEl().dom;
42314 if (wasCanvasTouched) {
42315 e.preventDefault();
42316 // var touch = event.changedTouches[0];
42317 // _this._strokeEnd(touch);
42322 reset: function () {
42323 this._lastPoints = [];
42324 this._lastVelocity = 0;
42325 this._lastWidth = (this.min_width + this.max_width) / 2;
42326 this.canvasElCtx().fillStyle = this.dot_color;
42329 strokeMoveUpdate: function(e)
42331 this.strokeUpdate(e);
42333 if (this.throttle) {
42334 this.throttleStroke(this.strokeUpdate, this.throttle);
42337 this.strokeUpdate(e);
42341 strokeBegin: function(e)
42343 var newPointGroup = {
42344 color: this.dot_color,
42348 if (typeof this.onBegin === 'function') {
42352 this.curve_data.push(newPointGroup);
42354 this.strokeUpdate(e);
42357 strokeUpdate: function(e)
42359 var rect = this.canvasEl().dom.getBoundingClientRect();
42360 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
42361 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
42362 var lastPoints = lastPointGroup.points;
42363 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
42364 var isLastPointTooClose = lastPoint
42365 ? point.distanceTo(lastPoint) <= this.min_distance
42367 var color = lastPointGroup.color;
42368 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
42369 var curve = this.addPoint(point);
42371 this.drawDot({color: color, point: point});
42374 this.drawCurve({color: color, curve: curve});
42384 strokeEnd: function(e)
42386 this.strokeUpdate(e);
42387 if (typeof this.onEnd === 'function') {
42392 addPoint: function (point) {
42393 var _lastPoints = this._lastPoints;
42394 _lastPoints.push(point);
42395 if (_lastPoints.length > 2) {
42396 if (_lastPoints.length === 3) {
42397 _lastPoints.unshift(_lastPoints[0]);
42399 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
42400 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
42401 _lastPoints.shift();
42407 calculateCurveWidths: function (startPoint, endPoint) {
42408 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42409 (1 - this.velocity_filter_weight) * this._lastVelocity;
42411 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42414 start: this._lastWidth
42417 this._lastVelocity = velocity;
42418 this._lastWidth = newWidth;
42422 drawDot: function (_a) {
42423 var color = _a.color, point = _a.point;
42424 var ctx = this.canvasElCtx();
42425 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42427 this.drawCurveSegment(point.x, point.y, width);
42429 ctx.fillStyle = color;
42433 drawCurve: function (_a) {
42434 var color = _a.color, curve = _a.curve;
42435 var ctx = this.canvasElCtx();
42436 var widthDelta = curve.endWidth - curve.startWidth;
42437 var drawSteps = Math.floor(curve.length()) * 2;
42439 ctx.fillStyle = color;
42440 for (var i = 0; i < drawSteps; i += 1) {
42441 var t = i / drawSteps;
42447 var x = uuu * curve.startPoint.x;
42448 x += 3 * uu * t * curve.control1.x;
42449 x += 3 * u * tt * curve.control2.x;
42450 x += ttt * curve.endPoint.x;
42451 var y = uuu * curve.startPoint.y;
42452 y += 3 * uu * t * curve.control1.y;
42453 y += 3 * u * tt * curve.control2.y;
42454 y += ttt * curve.endPoint.y;
42455 var width = curve.startWidth + ttt * widthDelta;
42456 this.drawCurveSegment(x, y, width);
42462 drawCurveSegment: function (x, y, width) {
42463 var ctx = this.canvasElCtx();
42465 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42466 this.is_empty = false;
42471 var ctx = this.canvasElCtx();
42472 var canvas = this.canvasEl().dom;
42473 ctx.fillStyle = this.bg_color;
42474 ctx.clearRect(0, 0, canvas.width, canvas.height);
42475 ctx.fillRect(0, 0, canvas.width, canvas.height);
42476 this.curve_data = [];
42478 this.is_empty = true;
42483 return this.el.select('input',true).first();
42486 canvasEl: function()
42488 return this.el.select('canvas',true).first();
42491 canvasElCtx: function()
42493 return this.el.select('canvas',true).first().dom.getContext('2d');
42496 getImage: function(type)
42498 if(this.is_empty) {
42503 return this.canvasEl().dom.toDataURL('image/'+type, 1);
42506 drawFromImage: function(img_src)
42508 var img = new Image();
42510 img.onload = function(){
42511 this.canvasElCtx().drawImage(img, 0, 0);
42516 this.is_empty = false;
42519 selectImage: function()
42521 this.fileEl().dom.click();
42524 uploadImage: function(e)
42526 var reader = new FileReader();
42528 reader.onload = function(e){
42529 var img = new Image();
42530 img.onload = function(){
42532 this.canvasElCtx().drawImage(img, 0, 0);
42534 img.src = e.target.result;
42537 reader.readAsDataURL(e.target.files[0]);
42540 // Bezier Point Constructor
42541 Point: (function () {
42542 function Point(x, y, time) {
42545 this.time = time || Date.now();
42547 Point.prototype.distanceTo = function (start) {
42548 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42550 Point.prototype.equals = function (other) {
42551 return this.x === other.x && this.y === other.y && this.time === other.time;
42553 Point.prototype.velocityFrom = function (start) {
42554 return this.time !== start.time
42555 ? this.distanceTo(start) / (this.time - start.time)
42562 // Bezier Constructor
42563 Bezier: (function () {
42564 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42565 this.startPoint = startPoint;
42566 this.control2 = control2;
42567 this.control1 = control1;
42568 this.endPoint = endPoint;
42569 this.startWidth = startWidth;
42570 this.endWidth = endWidth;
42572 Bezier.fromPoints = function (points, widths, scope) {
42573 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42574 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42575 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42577 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42578 var dx1 = s1.x - s2.x;
42579 var dy1 = s1.y - s2.y;
42580 var dx2 = s2.x - s3.x;
42581 var dy2 = s2.y - s3.y;
42582 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42583 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42584 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42585 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42586 var dxm = m1.x - m2.x;
42587 var dym = m1.y - m2.y;
42588 var k = l2 / (l1 + l2);
42589 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42590 var tx = s2.x - cm.x;
42591 var ty = s2.y - cm.y;
42593 c1: new scope.Point(m1.x + tx, m1.y + ty),
42594 c2: new scope.Point(m2.x + tx, m2.y + ty)
42597 Bezier.prototype.length = function () {
42602 for (var i = 0; i <= steps; i += 1) {
42604 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42605 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42607 var xdiff = cx - px;
42608 var ydiff = cy - py;
42609 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42616 Bezier.prototype.point = function (t, start, c1, c2, end) {
42617 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42618 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42619 + (3.0 * c2 * (1.0 - t) * t * t)
42620 + (end * t * t * t);
42625 throttleStroke: function(fn, wait) {
42626 if (wait === void 0) { wait = 250; }
42628 var timeout = null;
42632 var later = function () {
42633 previous = Date.now();
42635 result = fn.apply(storedContext, storedArgs);
42637 storedContext = null;
42641 return function wrapper() {
42643 for (var _i = 0; _i < arguments.length; _i++) {
42644 args[_i] = arguments[_i];
42646 var now = Date.now();
42647 var remaining = wait - (now - previous);
42648 storedContext = this;
42650 if (remaining <= 0 || remaining > wait) {
42652 clearTimeout(timeout);
42656 result = fn.apply(storedContext, storedArgs);
42658 storedContext = null;
42662 else if (!timeout) {
42663 timeout = window.setTimeout(later, remaining);