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?
1790 if (this.title.length) {
1794 src: this.title // escape?
1798 if (this.subtitle.length) {
1802 src: this.subtitle // escape?
1808 cls : 'roo-card-body-ctr'
1811 if (this.html.length) {
1817 // fixme ? handle objects?
1818 if (this.footer.length) {
1821 cls : 'card-footer',
1822 html: this.footer // escape?
1831 getCardHeader : function()
1833 var ret = this.el.select('.card-header',true).first();
1834 if (ret.hasClass('d-none')) {
1835 ret.removeClass('d-none');
1841 getChildContainer : function()
1847 return this.el.select('.roo-card-body-ctr',true).first();
1850 initEvents: function()
1853 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
1854 containerScroll: true,
1855 ddGroup: this.drag_group || 'default_card_drag_group'
1857 this.dragZone.getDragData = this.getDragData.createDelegate(this);
1863 getDragData : function(e) {
1864 var target = this.getEl();
1866 //this.handleSelection(e);
1871 nodes: this.getEl(),
1876 dragData.ddel = target.dom ; // the div element
1877 Roo.log(target.getWidth( ));
1878 dragData.ddel.style.width = target.getWidth() + 'px';
1890 * Card header - holder for the card header elements.
1895 * @class Roo.bootstrap.CardHeader
1896 * @extends Roo.bootstrap.Element
1897 * Bootstrap Element class
1899 * Create a new Element
1900 * @param {Object} config The config object
1903 Roo.bootstrap.CardHeader = function(config){
1904 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
1907 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
1910 container_method : 'getCardHeader',
1912 getAutoCreate : function() {
1936 * @class Roo.bootstrap.Img
1937 * @extends Roo.bootstrap.Component
1938 * Bootstrap Img class
1939 * @cfg {Boolean} imgResponsive false | true
1940 * @cfg {String} border rounded | circle | thumbnail
1941 * @cfg {String} src image source
1942 * @cfg {String} alt image alternative text
1943 * @cfg {String} href a tag href
1944 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1945 * @cfg {String} xsUrl xs image source
1946 * @cfg {String} smUrl sm image source
1947 * @cfg {String} mdUrl md image source
1948 * @cfg {String} lgUrl lg image source
1951 * Create a new Input
1952 * @param {Object} config The config object
1955 Roo.bootstrap.Img = function(config){
1956 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1962 * The img click event for the img.
1963 * @param {Roo.EventObject} e
1969 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1971 imgResponsive: true,
1981 getAutoCreate : function()
1983 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1984 return this.createSingleImg();
1989 cls: 'roo-image-responsive-group',
1994 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1996 if(!_this[size + 'Url']){
2002 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2003 html: _this.html || cfg.html,
2004 src: _this[size + 'Url']
2007 img.cls += ' roo-image-responsive-' + size;
2009 var s = ['xs', 'sm', 'md', 'lg'];
2011 s.splice(s.indexOf(size), 1);
2013 Roo.each(s, function(ss){
2014 img.cls += ' hidden-' + ss;
2017 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2018 cfg.cls += ' img-' + _this.border;
2022 cfg.alt = _this.alt;
2035 a.target = _this.target;
2039 cfg.cn.push((_this.href) ? a : img);
2046 createSingleImg : function()
2050 cls: (this.imgResponsive) ? 'img-responsive' : '',
2052 src : 'about:blank' // just incase src get's set to undefined?!?
2055 cfg.html = this.html || cfg.html;
2057 cfg.src = this.src || cfg.src;
2059 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2060 cfg.cls += ' img-' + this.border;
2077 a.target = this.target;
2082 return (this.href) ? a : cfg;
2085 initEvents: function()
2088 this.el.on('click', this.onClick, this);
2093 onClick : function(e)
2095 Roo.log('img onclick');
2096 this.fireEvent('click', this, e);
2099 * Sets the url of the image - used to update it
2100 * @param {String} url the url of the image
2103 setSrc : function(url)
2107 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2108 this.el.dom.src = url;
2112 this.el.select('img', true).first().dom.src = url;
2128 * @class Roo.bootstrap.Link
2129 * @extends Roo.bootstrap.Component
2130 * Bootstrap Link Class
2131 * @cfg {String} alt image alternative text
2132 * @cfg {String} href a tag href
2133 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2134 * @cfg {String} html the content of the link.
2135 * @cfg {String} anchor name for the anchor link
2136 * @cfg {String} fa - favicon
2138 * @cfg {Boolean} preventDefault (true | false) default false
2142 * Create a new Input
2143 * @param {Object} config The config object
2146 Roo.bootstrap.Link = function(config){
2147 Roo.bootstrap.Link.superclass.constructor.call(this, config);
2153 * The img click event for the img.
2154 * @param {Roo.EventObject} e
2160 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
2164 preventDefault: false,
2170 getAutoCreate : function()
2172 var html = this.html || '';
2174 if (this.fa !== false) {
2175 html = '<i class="fa fa-' + this.fa + '"></i>';
2180 // anchor's do not require html/href...
2181 if (this.anchor === false) {
2183 cfg.href = this.href || '#';
2185 cfg.name = this.anchor;
2186 if (this.html !== false || this.fa !== false) {
2189 if (this.href !== false) {
2190 cfg.href = this.href;
2194 if(this.alt !== false){
2199 if(this.target !== false) {
2200 cfg.target = this.target;
2206 initEvents: function() {
2208 if(!this.href || this.preventDefault){
2209 this.el.on('click', this.onClick, this);
2213 onClick : function(e)
2215 if(this.preventDefault){
2218 //Roo.log('img onclick');
2219 this.fireEvent('click', this, e);
2232 * @class Roo.bootstrap.Header
2233 * @extends Roo.bootstrap.Component
2234 * Bootstrap Header class
2235 * @cfg {String} html content of header
2236 * @cfg {Number} level (1|2|3|4|5|6) default 1
2239 * Create a new Header
2240 * @param {Object} config The config object
2244 Roo.bootstrap.Header = function(config){
2245 Roo.bootstrap.Header.superclass.constructor.call(this, config);
2248 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
2256 getAutoCreate : function(){
2261 tag: 'h' + (1 *this.level),
2262 html: this.html || ''
2274 * Ext JS Library 1.1.1
2275 * Copyright(c) 2006-2007, Ext JS, LLC.
2277 * Originally Released Under LGPL - original licence link has changed is not relivant.
2280 * <script type="text/javascript">
2284 * @class Roo.bootstrap.MenuMgr
2285 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
2288 Roo.bootstrap.MenuMgr = function(){
2289 var menus, active, groups = {}, attached = false, lastShow = new Date();
2291 // private - called when first menu is created
2294 active = new Roo.util.MixedCollection();
2295 Roo.get(document).addKeyListener(27, function(){
2296 if(active.length > 0){
2304 if(active && active.length > 0){
2305 var c = active.clone();
2315 if(active.length < 1){
2316 Roo.get(document).un("mouseup", onMouseDown);
2324 var last = active.last();
2325 lastShow = new Date();
2328 Roo.get(document).on("mouseup", onMouseDown);
2333 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
2334 m.parentMenu.activeChild = m;
2335 }else if(last && last.isVisible()){
2336 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
2341 function onBeforeHide(m){
2343 m.activeChild.hide();
2345 if(m.autoHideTimer){
2346 clearTimeout(m.autoHideTimer);
2347 delete m.autoHideTimer;
2352 function onBeforeShow(m){
2353 var pm = m.parentMenu;
2354 if(!pm && !m.allowOtherMenus){
2356 }else if(pm && pm.activeChild && active != m){
2357 pm.activeChild.hide();
2361 // private this should really trigger on mouseup..
2362 function onMouseDown(e){
2363 Roo.log("on Mouse Up");
2365 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
2366 Roo.log("MenuManager hideAll");
2375 function onBeforeCheck(mi, state){
2377 var g = groups[mi.group];
2378 for(var i = 0, l = g.length; i < l; i++){
2380 g[i].setChecked(false);
2389 * Hides all menus that are currently visible
2391 hideAll : function(){
2396 register : function(menu){
2400 menus[menu.id] = menu;
2401 menu.on("beforehide", onBeforeHide);
2402 menu.on("hide", onHide);
2403 menu.on("beforeshow", onBeforeShow);
2404 menu.on("show", onShow);
2406 if(g && menu.events["checkchange"]){
2410 groups[g].push(menu);
2411 menu.on("checkchange", onCheck);
2416 * Returns a {@link Roo.menu.Menu} object
2417 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
2418 * be used to generate and return a new Menu instance.
2420 get : function(menu){
2421 if(typeof menu == "string"){ // menu id
2423 }else if(menu.events){ // menu instance
2426 /*else if(typeof menu.length == 'number'){ // array of menu items?
2427 return new Roo.bootstrap.Menu({items:menu});
2428 }else{ // otherwise, must be a config
2429 return new Roo.bootstrap.Menu(menu);
2436 unregister : function(menu){
2437 delete menus[menu.id];
2438 menu.un("beforehide", onBeforeHide);
2439 menu.un("hide", onHide);
2440 menu.un("beforeshow", onBeforeShow);
2441 menu.un("show", onShow);
2443 if(g && menu.events["checkchange"]){
2444 groups[g].remove(menu);
2445 menu.un("checkchange", onCheck);
2450 registerCheckable : function(menuItem){
2451 var g = menuItem.group;
2456 groups[g].push(menuItem);
2457 menuItem.on("beforecheckchange", onBeforeCheck);
2462 unregisterCheckable : function(menuItem){
2463 var g = menuItem.group;
2465 groups[g].remove(menuItem);
2466 menuItem.un("beforecheckchange", onBeforeCheck);
2478 * @class Roo.bootstrap.Menu
2479 * @extends Roo.bootstrap.Component
2480 * Bootstrap Menu class - container for MenuItems
2481 * @cfg {String} type (dropdown|treeview|submenu) type of menu
2482 * @cfg {bool} hidden if the menu should be hidden when rendered.
2483 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
2484 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
2488 * @param {Object} config The config object
2492 Roo.bootstrap.Menu = function(config){
2493 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2494 if (this.registerMenu && this.type != 'treeview') {
2495 Roo.bootstrap.MenuMgr.register(this);
2502 * Fires before this menu is displayed (return false to block)
2503 * @param {Roo.menu.Menu} this
2508 * Fires before this menu is hidden (return false to block)
2509 * @param {Roo.menu.Menu} this
2514 * Fires after this menu is displayed
2515 * @param {Roo.menu.Menu} this
2520 * Fires after this menu is hidden
2521 * @param {Roo.menu.Menu} this
2526 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2527 * @param {Roo.menu.Menu} this
2528 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2529 * @param {Roo.EventObject} e
2534 * Fires when the mouse is hovering over this menu
2535 * @param {Roo.menu.Menu} this
2536 * @param {Roo.EventObject} e
2537 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2542 * Fires when the mouse exits this menu
2543 * @param {Roo.menu.Menu} this
2544 * @param {Roo.EventObject} e
2545 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2550 * Fires when a menu item contained in this menu is clicked
2551 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2552 * @param {Roo.EventObject} e
2556 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2559 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2563 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2566 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2568 registerMenu : true,
2570 menuItems :false, // stores the menu items..
2580 getChildContainer : function() {
2584 getAutoCreate : function(){
2586 //if (['right'].indexOf(this.align)!==-1) {
2587 // cfg.cn[1].cls += ' pull-right'
2593 cls : 'dropdown-menu' ,
2594 style : 'z-index:1000'
2598 if (this.type === 'submenu') {
2599 cfg.cls = 'submenu active';
2601 if (this.type === 'treeview') {
2602 cfg.cls = 'treeview-menu';
2607 initEvents : function() {
2609 // Roo.log("ADD event");
2610 // Roo.log(this.triggerEl.dom);
2612 this.triggerEl.on('click', this.onTriggerClick, this);
2614 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2617 if (this.triggerEl.hasClass('nav-item')) {
2618 // dropdown toggle on the 'a' in BS4?
2619 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2621 this.triggerEl.addClass('dropdown-toggle');
2624 this.el.on('touchstart' , this.onTouch, this);
2626 this.el.on('click' , this.onClick, this);
2628 this.el.on("mouseover", this.onMouseOver, this);
2629 this.el.on("mouseout", this.onMouseOut, this);
2633 findTargetItem : function(e)
2635 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2639 //Roo.log(t); Roo.log(t.id);
2641 //Roo.log(this.menuitems);
2642 return this.menuitems.get(t.id);
2644 //return this.items.get(t.menuItemId);
2650 onTouch : function(e)
2652 Roo.log("menu.onTouch");
2653 //e.stopEvent(); this make the user popdown broken
2657 onClick : function(e)
2659 Roo.log("menu.onClick");
2661 var t = this.findTargetItem(e);
2662 if(!t || t.isContainer){
2667 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2668 if(t == this.activeItem && t.shouldDeactivate(e)){
2669 this.activeItem.deactivate();
2670 delete this.activeItem;
2674 this.setActiveItem(t, true);
2682 Roo.log('pass click event');
2686 this.fireEvent("click", this, t, e);
2690 if(!t.href.length || t.href == '#'){
2691 (function() { _this.hide(); }).defer(100);
2696 onMouseOver : function(e){
2697 var t = this.findTargetItem(e);
2700 // if(t.canActivate && !t.disabled){
2701 // this.setActiveItem(t, true);
2705 this.fireEvent("mouseover", this, e, t);
2707 isVisible : function(){
2708 return !this.hidden;
2710 onMouseOut : function(e){
2711 var t = this.findTargetItem(e);
2714 // if(t == this.activeItem && t.shouldDeactivate(e)){
2715 // this.activeItem.deactivate();
2716 // delete this.activeItem;
2719 this.fireEvent("mouseout", this, e, t);
2724 * Displays this menu relative to another element
2725 * @param {String/HTMLElement/Roo.Element} element The element to align to
2726 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2727 * the element (defaults to this.defaultAlign)
2728 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2730 show : function(el, pos, parentMenu)
2732 if (false === this.fireEvent("beforeshow", this)) {
2733 Roo.log("show canceled");
2736 this.parentMenu = parentMenu;
2741 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2744 * Displays this menu at a specific xy position
2745 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2746 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2748 showAt : function(xy, parentMenu, /* private: */_e){
2749 this.parentMenu = parentMenu;
2754 this.fireEvent("beforeshow", this);
2755 //xy = this.el.adjustForConstraints(xy);
2759 this.hideMenuItems();
2760 this.hidden = false;
2761 this.triggerEl.addClass('open');
2762 this.el.addClass('show');
2764 // reassign x when hitting right
2765 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2766 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2769 // reassign y when hitting bottom
2770 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2771 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2774 // but the list may align on trigger left or trigger top... should it be a properity?
2776 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2781 this.fireEvent("show", this);
2787 this.doFocus.defer(50, this);
2791 doFocus : function(){
2793 this.focusEl.focus();
2798 * Hides this menu and optionally all parent menus
2799 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2801 hide : function(deep)
2803 if (false === this.fireEvent("beforehide", this)) {
2804 Roo.log("hide canceled");
2807 this.hideMenuItems();
2808 if(this.el && this.isVisible()){
2810 if(this.activeItem){
2811 this.activeItem.deactivate();
2812 this.activeItem = null;
2814 this.triggerEl.removeClass('open');;
2815 this.el.removeClass('show');
2817 this.fireEvent("hide", this);
2819 if(deep === true && this.parentMenu){
2820 this.parentMenu.hide(true);
2824 onTriggerClick : function(e)
2826 Roo.log('trigger click');
2828 var target = e.getTarget();
2830 Roo.log(target.nodeName.toLowerCase());
2832 if(target.nodeName.toLowerCase() === 'i'){
2838 onTriggerPress : function(e)
2840 Roo.log('trigger press');
2841 //Roo.log(e.getTarget());
2842 // Roo.log(this.triggerEl.dom);
2844 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2845 var pel = Roo.get(e.getTarget());
2846 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2847 Roo.log('is treeview or dropdown?');
2851 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2855 if (this.isVisible()) {
2860 this.show(this.triggerEl, '?', false);
2863 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2870 hideMenuItems : function()
2872 Roo.log("hide Menu Items");
2877 this.el.select('.open',true).each(function(aa) {
2879 aa.removeClass('open');
2883 addxtypeChild : function (tree, cntr) {
2884 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2886 this.menuitems.add(comp);
2898 this.getEl().dom.innerHTML = '';
2899 this.menuitems.clear();
2913 * @class Roo.bootstrap.MenuItem
2914 * @extends Roo.bootstrap.Component
2915 * Bootstrap MenuItem class
2916 * @cfg {String} html the menu label
2917 * @cfg {String} href the link
2918 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2919 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2920 * @cfg {Boolean} active used on sidebars to highlight active itesm
2921 * @cfg {String} fa favicon to show on left of menu item.
2922 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2926 * Create a new MenuItem
2927 * @param {Object} config The config object
2931 Roo.bootstrap.MenuItem = function(config){
2932 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2937 * The raw click event for the entire grid.
2938 * @param {Roo.bootstrap.MenuItem} this
2939 * @param {Roo.EventObject} e
2945 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2949 preventDefault: false,
2950 isContainer : false,
2954 getAutoCreate : function(){
2956 if(this.isContainer){
2959 cls: 'dropdown-menu-item '
2969 cls : 'dropdown-item',
2974 if (this.fa !== false) {
2977 cls : 'fa fa-' + this.fa
2986 cls: 'dropdown-menu-item',
2989 if (this.parent().type == 'treeview') {
2990 cfg.cls = 'treeview-menu';
2993 cfg.cls += ' active';
2998 anc.href = this.href || cfg.cn[0].href ;
2999 ctag.html = this.html || cfg.cn[0].html ;
3003 initEvents: function()
3005 if (this.parent().type == 'treeview') {
3006 this.el.select('a').on('click', this.onClick, this);
3010 this.menu.parentType = this.xtype;
3011 this.menu.triggerEl = this.el;
3012 this.menu = this.addxtype(Roo.apply({}, this.menu));
3016 onClick : function(e)
3018 Roo.log('item on click ');
3020 if(this.preventDefault){
3023 //this.parent().hideMenuItems();
3025 this.fireEvent('click', this, e);
3044 * @class Roo.bootstrap.MenuSeparator
3045 * @extends Roo.bootstrap.Component
3046 * Bootstrap MenuSeparator class
3049 * Create a new MenuItem
3050 * @param {Object} config The config object
3054 Roo.bootstrap.MenuSeparator = function(config){
3055 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3058 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3060 getAutoCreate : function(){
3079 * @class Roo.bootstrap.Modal
3080 * @extends Roo.bootstrap.Component
3081 * Bootstrap Modal class
3082 * @cfg {String} title Title of dialog
3083 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3084 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3085 * @cfg {Boolean} specificTitle default false
3086 * @cfg {Array} buttons Array of buttons or standard button set..
3087 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3088 * @cfg {Boolean} animate default true
3089 * @cfg {Boolean} allow_close default true
3090 * @cfg {Boolean} fitwindow default false
3091 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3092 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3093 * @cfg {String} size (sm|lg) default empty
3094 * @cfg {Number} max_width set the max width of modal
3098 * Create a new Modal Dialog
3099 * @param {Object} config The config object
3102 Roo.bootstrap.Modal = function(config){
3103 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3108 * The raw btnclick event for the button
3109 * @param {Roo.EventObject} e
3114 * Fire when dialog resize
3115 * @param {Roo.bootstrap.Modal} this
3116 * @param {Roo.EventObject} e
3120 this.buttons = this.buttons || [];
3123 this.tmpl = Roo.factory(this.tmpl);
3128 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
3130 title : 'test dialog',
3140 specificTitle: false,
3142 buttonPosition: 'right',
3165 onRender : function(ct, position)
3167 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3170 var cfg = Roo.apply({}, this.getAutoCreate());
3173 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3175 //if (!cfg.name.length) {
3179 cfg.cls += ' ' + this.cls;
3182 cfg.style = this.style;
3184 this.el = Roo.get(document.body).createChild(cfg, position);
3186 //var type = this.el.dom.type;
3189 if(this.tabIndex !== undefined){
3190 this.el.dom.setAttribute('tabIndex', this.tabIndex);
3193 this.dialogEl = this.el.select('.modal-dialog',true).first();
3194 this.bodyEl = this.el.select('.modal-body',true).first();
3195 this.closeEl = this.el.select('.modal-header .close', true).first();
3196 this.headerEl = this.el.select('.modal-header',true).first();
3197 this.titleEl = this.el.select('.modal-title',true).first();
3198 this.footerEl = this.el.select('.modal-footer',true).first();
3200 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3202 //this.el.addClass("x-dlg-modal");
3204 if (this.buttons.length) {
3205 Roo.each(this.buttons, function(bb) {
3206 var b = Roo.apply({}, bb);
3207 b.xns = b.xns || Roo.bootstrap;
3208 b.xtype = b.xtype || 'Button';
3209 if (typeof(b.listeners) == 'undefined') {
3210 b.listeners = { click : this.onButtonClick.createDelegate(this) };
3213 var btn = Roo.factory(b);
3215 btn.render(this.getButtonContainer());
3219 // render the children.
3222 if(typeof(this.items) != 'undefined'){
3223 var items = this.items;
3226 for(var i =0;i < items.length;i++) {
3227 nitems.push(this.addxtype(Roo.apply({}, items[i])));
3231 this.items = nitems;
3233 // where are these used - they used to be body/close/footer
3237 //this.el.addClass([this.fieldClass, this.cls]);
3241 getAutoCreate : function()
3243 // we will default to modal-body-overflow - might need to remove or make optional later.
3245 cls : 'modal-body enable-modal-body-overflow ',
3246 html : this.html || ''
3251 cls : 'modal-title',
3255 if(this.specificTitle){
3261 if (this.allow_close && Roo.bootstrap.version == 3) {
3271 if (this.allow_close && Roo.bootstrap.version == 4) {
3281 if(this.size.length){
3282 size = 'modal-' + this.size;
3285 var footer = Roo.bootstrap.version == 3 ?
3287 cls : 'modal-footer',
3291 cls: 'btn-' + this.buttonPosition
3296 { // BS4 uses mr-auto on left buttons....
3297 cls : 'modal-footer'
3308 cls: "modal-dialog " + size,
3311 cls : "modal-content",
3314 cls : 'modal-header',
3329 modal.cls += ' fade';
3335 getChildContainer : function() {
3340 getButtonContainer : function() {
3342 return Roo.bootstrap.version == 4 ?
3343 this.el.select('.modal-footer',true).first()
3344 : this.el.select('.modal-footer div',true).first();
3347 initEvents : function()
3349 if (this.allow_close) {
3350 this.closeEl.on('click', this.hide, this);
3352 Roo.EventManager.onWindowResize(this.resize, this, true);
3360 this.maskEl.setSize(
3361 Roo.lib.Dom.getViewWidth(true),
3362 Roo.lib.Dom.getViewHeight(true)
3365 if (this.fitwindow) {
3369 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
3370 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
3375 if(this.max_width !== 0) {
3377 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
3380 this.setSize(w, this.height);
3384 if(this.max_height) {
3385 this.setSize(w,Math.min(
3387 Roo.lib.Dom.getViewportHeight(true) - 60
3393 if(!this.fit_content) {
3394 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
3398 this.setSize(w, Math.min(
3400 this.headerEl.getHeight() +
3401 this.footerEl.getHeight() +
3402 this.getChildHeight(this.bodyEl.dom.childNodes),
3403 Roo.lib.Dom.getViewportHeight(true) - 60)
3409 setSize : function(w,h)
3420 if (!this.rendered) {
3424 //this.el.setStyle('display', 'block');
3425 this.el.removeClass('hideing');
3426 this.el.dom.style.display='block';
3428 Roo.get(document.body).addClass('modal-open');
3430 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
3433 this.el.addClass('show');
3434 this.el.addClass('in');
3437 this.el.addClass('show');
3438 this.el.addClass('in');
3441 // not sure how we can show data in here..
3443 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3446 Roo.get(document.body).addClass("x-body-masked");
3448 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3449 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3450 this.maskEl.dom.style.display = 'block';
3451 this.maskEl.addClass('show');
3456 this.fireEvent('show', this);
3458 // set zindex here - otherwise it appears to be ignored...
3459 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3462 this.items.forEach( function(e) {
3463 e.layout ? e.layout() : false;
3471 if(this.fireEvent("beforehide", this) !== false){
3473 this.maskEl.removeClass('show');
3475 this.maskEl.dom.style.display = '';
3476 Roo.get(document.body).removeClass("x-body-masked");
3477 this.el.removeClass('in');
3478 this.el.select('.modal-dialog', true).first().setStyle('transform','');
3480 if(this.animate){ // why
3481 this.el.addClass('hideing');
3482 this.el.removeClass('show');
3484 if (!this.el.hasClass('hideing')) {
3485 return; // it's been shown again...
3488 this.el.dom.style.display='';
3490 Roo.get(document.body).removeClass('modal-open');
3491 this.el.removeClass('hideing');
3495 this.el.removeClass('show');
3496 this.el.dom.style.display='';
3497 Roo.get(document.body).removeClass('modal-open');
3500 this.fireEvent('hide', this);
3503 isVisible : function()
3506 return this.el.hasClass('show') && !this.el.hasClass('hideing');
3510 addButton : function(str, cb)
3514 var b = Roo.apply({}, { html : str } );
3515 b.xns = b.xns || Roo.bootstrap;
3516 b.xtype = b.xtype || 'Button';
3517 if (typeof(b.listeners) == 'undefined') {
3518 b.listeners = { click : cb.createDelegate(this) };
3521 var btn = Roo.factory(b);
3523 btn.render(this.getButtonContainer());
3529 setDefaultButton : function(btn)
3531 //this.el.select('.modal-footer').()
3534 resizeTo: function(w,h)
3536 this.dialogEl.setWidth(w);
3538 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
3540 this.bodyEl.setHeight(h - diff);
3542 this.fireEvent('resize', this);
3545 setContentSize : function(w, h)
3549 onButtonClick: function(btn,e)
3552 this.fireEvent('btnclick', btn.name, e);
3555 * Set the title of the Dialog
3556 * @param {String} str new Title
3558 setTitle: function(str) {
3559 this.titleEl.dom.innerHTML = str;
3562 * Set the body of the Dialog
3563 * @param {String} str new Title
3565 setBody: function(str) {
3566 this.bodyEl.dom.innerHTML = str;
3569 * Set the body of the Dialog using the template
3570 * @param {Obj} data - apply this data to the template and replace the body contents.
3572 applyBody: function(obj)
3575 Roo.log("Error - using apply Body without a template");
3578 this.tmpl.overwrite(this.bodyEl, obj);
3581 getChildHeight : function(child_nodes)
3585 child_nodes.length == 0
3590 var child_height = 0;
3592 for(var i = 0; i < child_nodes.length; i++) {
3595 * for modal with tabs...
3596 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3598 var layout_childs = child_nodes[i].childNodes;
3600 for(var j = 0; j < layout_childs.length; j++) {
3602 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3604 var layout_body_childs = layout_childs[j].childNodes;
3606 for(var k = 0; k < layout_body_childs.length; k++) {
3608 if(layout_body_childs[k].classList.contains('navbar')) {
3609 child_height += layout_body_childs[k].offsetHeight;
3613 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3615 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3617 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3619 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3620 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3635 child_height += child_nodes[i].offsetHeight;
3636 // Roo.log(child_nodes[i].offsetHeight);
3639 return child_height;
3645 Roo.apply(Roo.bootstrap.Modal, {
3647 * Button config that displays a single OK button
3656 * Button config that displays Yes and No buttons
3672 * Button config that displays OK and Cancel buttons
3687 * Button config that displays Yes, No and Cancel buttons
3711 * messagebox - can be used as a replace
3715 * @class Roo.MessageBox
3716 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3720 Roo.Msg.alert('Status', 'Changes saved successfully.');
3722 // Prompt for user data:
3723 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3725 // process text value...
3729 // Show a dialog using config options:
3731 title:'Save Changes?',
3732 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3733 buttons: Roo.Msg.YESNOCANCEL,
3740 Roo.bootstrap.MessageBox = function(){
3741 var dlg, opt, mask, waitTimer;
3742 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3743 var buttons, activeTextEl, bwidth;
3747 var handleButton = function(button){
3749 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3753 var handleHide = function(){
3755 dlg.el.removeClass(opt.cls);
3758 // Roo.TaskMgr.stop(waitTimer);
3759 // waitTimer = null;
3764 var updateButtons = function(b){
3767 buttons["ok"].hide();
3768 buttons["cancel"].hide();
3769 buttons["yes"].hide();
3770 buttons["no"].hide();
3771 dlg.footerEl.hide();
3775 dlg.footerEl.show();
3776 for(var k in buttons){
3777 if(typeof buttons[k] != "function"){
3780 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3781 width += buttons[k].el.getWidth()+15;
3791 var handleEsc = function(d, k, e){
3792 if(opt && opt.closable !== false){
3802 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3803 * @return {Roo.BasicDialog} The BasicDialog element
3805 getDialog : function(){
3807 dlg = new Roo.bootstrap.Modal( {
3810 //constraintoviewport:false,
3812 //collapsible : false,
3817 //buttonAlign:"center",
3818 closeClick : function(){
3819 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3822 handleButton("cancel");
3827 dlg.on("hide", handleHide);
3829 //dlg.addKeyListener(27, handleEsc);
3831 this.buttons = buttons;
3832 var bt = this.buttonText;
3833 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3834 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3835 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3836 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3838 bodyEl = dlg.bodyEl.createChild({
3840 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3841 '<textarea class="roo-mb-textarea"></textarea>' +
3842 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3844 msgEl = bodyEl.dom.firstChild;
3845 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3846 textboxEl.enableDisplayMode();
3847 textboxEl.addKeyListener([10,13], function(){
3848 if(dlg.isVisible() && opt && opt.buttons){
3851 }else if(opt.buttons.yes){
3852 handleButton("yes");
3856 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3857 textareaEl.enableDisplayMode();
3858 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3859 progressEl.enableDisplayMode();
3861 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3862 var pf = progressEl.dom.firstChild;
3864 pp = Roo.get(pf.firstChild);
3865 pp.setHeight(pf.offsetHeight);
3873 * Updates the message box body text
3874 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3875 * the XHTML-compliant non-breaking space character '&#160;')
3876 * @return {Roo.MessageBox} This message box
3878 updateText : function(text)
3880 if(!dlg.isVisible() && !opt.width){
3881 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3882 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3884 msgEl.innerHTML = text || ' ';
3886 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3887 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3889 Math.min(opt.width || cw , this.maxWidth),
3890 Math.max(opt.minWidth || this.minWidth, bwidth)
3893 activeTextEl.setWidth(w);
3895 if(dlg.isVisible()){
3896 dlg.fixedcenter = false;
3898 // to big, make it scroll. = But as usual stupid IE does not support
3901 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3902 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3903 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3905 bodyEl.dom.style.height = '';
3906 bodyEl.dom.style.overflowY = '';
3909 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3911 bodyEl.dom.style.overflowX = '';
3914 dlg.setContentSize(w, bodyEl.getHeight());
3915 if(dlg.isVisible()){
3916 dlg.fixedcenter = true;
3922 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3923 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3924 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3925 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3926 * @return {Roo.MessageBox} This message box
3928 updateProgress : function(value, text){
3930 this.updateText(text);
3933 if (pp) { // weird bug on my firefox - for some reason this is not defined
3934 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3935 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3941 * Returns true if the message box is currently displayed
3942 * @return {Boolean} True if the message box is visible, else false
3944 isVisible : function(){
3945 return dlg && dlg.isVisible();
3949 * Hides the message box if it is displayed
3952 if(this.isVisible()){
3958 * Displays a new message box, or reinitializes an existing message box, based on the config options
3959 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3960 * The following config object properties are supported:
3962 Property Type Description
3963 ---------- --------------- ------------------------------------------------------------------------------------
3964 animEl String/Element An id or Element from which the message box should animate as it opens and
3965 closes (defaults to undefined)
3966 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3967 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3968 closable Boolean False to hide the top-right close button (defaults to true). Note that
3969 progress and wait dialogs will ignore this property and always hide the
3970 close button as they can only be closed programmatically.
3971 cls String A custom CSS class to apply to the message box element
3972 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3973 displayed (defaults to 75)
3974 fn Function A callback function to execute after closing the dialog. The arguments to the
3975 function will be btn (the name of the button that was clicked, if applicable,
3976 e.g. "ok"), and text (the value of the active text field, if applicable).
3977 Progress and wait dialogs will ignore this option since they do not respond to
3978 user actions and can only be closed programmatically, so any required function
3979 should be called by the same code after it closes the dialog.
3980 icon String A CSS class that provides a background image to be used as an icon for
3981 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3982 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3983 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3984 modal Boolean False to allow user interaction with the page while the message box is
3985 displayed (defaults to true)
3986 msg String A string that will replace the existing message box body text (defaults
3987 to the XHTML-compliant non-breaking space character ' ')
3988 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3989 progress Boolean True to display a progress bar (defaults to false)
3990 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3991 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3992 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3993 title String The title text
3994 value String The string value to set into the active textbox element if displayed
3995 wait Boolean True to display a progress bar (defaults to false)
3996 width Number The width of the dialog in pixels
4003 msg: 'Please enter your address:',
4005 buttons: Roo.MessageBox.OKCANCEL,
4008 animEl: 'addAddressBtn'
4011 * @param {Object} config Configuration options
4012 * @return {Roo.MessageBox} This message box
4014 show : function(options)
4017 // this causes nightmares if you show one dialog after another
4018 // especially on callbacks..
4020 if(this.isVisible()){
4023 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4024 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4025 Roo.log("New Dialog Message:" + options.msg )
4026 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4027 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4030 var d = this.getDialog();
4032 d.setTitle(opt.title || " ");
4033 d.closeEl.setDisplayed(opt.closable !== false);
4034 activeTextEl = textboxEl;
4035 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4040 textareaEl.setHeight(typeof opt.multiline == "number" ?
4041 opt.multiline : this.defaultTextHeight);
4042 activeTextEl = textareaEl;
4051 progressEl.setDisplayed(opt.progress === true);
4053 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4055 this.updateProgress(0);
4056 activeTextEl.dom.value = opt.value || "";
4058 dlg.setDefaultButton(activeTextEl);
4060 var bs = opt.buttons;
4064 }else if(bs && bs.yes){
4065 db = buttons["yes"];
4067 dlg.setDefaultButton(db);
4069 bwidth = updateButtons(opt.buttons);
4070 this.updateText(opt.msg);
4072 d.el.addClass(opt.cls);
4074 d.proxyDrag = opt.proxyDrag === true;
4075 d.modal = opt.modal !== false;
4076 d.mask = opt.modal !== false ? mask : false;
4078 // force it to the end of the z-index stack so it gets a cursor in FF
4079 document.body.appendChild(dlg.el.dom);
4080 d.animateTarget = null;
4081 d.show(options.animEl);
4087 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
4088 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
4089 * and closing the message box when the process is complete.
4090 * @param {String} title The title bar text
4091 * @param {String} msg The message box body text
4092 * @return {Roo.MessageBox} This message box
4094 progress : function(title, msg){
4101 minWidth: this.minProgressWidth,
4108 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
4109 * If a callback function is passed it will be called after the user clicks the button, and the
4110 * id of the button that was clicked will be passed as the only parameter to the callback
4111 * (could also be the top-right close button).
4112 * @param {String} title The title bar text
4113 * @param {String} msg The message box body text
4114 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4115 * @param {Object} scope (optional) The scope of the callback function
4116 * @return {Roo.MessageBox} This message box
4118 alert : function(title, msg, fn, scope)
4133 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
4134 * interaction while waiting for a long-running process to complete that does not have defined intervals.
4135 * You are responsible for closing the message box when the process is complete.
4136 * @param {String} msg The message box body text
4137 * @param {String} title (optional) The title bar text
4138 * @return {Roo.MessageBox} This message box
4140 wait : function(msg, title){
4151 waitTimer = Roo.TaskMgr.start({
4153 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
4161 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
4162 * If a callback function is passed it will be called after the user clicks either button, and the id of the
4163 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
4164 * @param {String} title The title bar text
4165 * @param {String} msg The message box body text
4166 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4167 * @param {Object} scope (optional) The scope of the callback function
4168 * @return {Roo.MessageBox} This message box
4170 confirm : function(title, msg, fn, scope){
4174 buttons: this.YESNO,
4183 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
4184 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
4185 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
4186 * (could also be the top-right close button) and the text that was entered will be passed as the two
4187 * parameters to the callback.
4188 * @param {String} title The title bar text
4189 * @param {String} msg The message box body text
4190 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4191 * @param {Object} scope (optional) The scope of the callback function
4192 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
4193 * property, or the height in pixels to create the textbox (defaults to false / single-line)
4194 * @return {Roo.MessageBox} This message box
4196 prompt : function(title, msg, fn, scope, multiline){
4200 buttons: this.OKCANCEL,
4205 multiline: multiline,
4212 * Button config that displays a single OK button
4217 * Button config that displays Yes and No buttons
4220 YESNO : {yes:true, no:true},
4222 * Button config that displays OK and Cancel buttons
4225 OKCANCEL : {ok:true, cancel:true},
4227 * Button config that displays Yes, No and Cancel buttons
4230 YESNOCANCEL : {yes:true, no:true, cancel:true},
4233 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
4236 defaultTextHeight : 75,
4238 * The maximum width in pixels of the message box (defaults to 600)
4243 * The minimum width in pixels of the message box (defaults to 100)
4248 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
4249 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
4252 minProgressWidth : 250,
4254 * An object containing the default button text strings that can be overriden for localized language support.
4255 * Supported properties are: ok, cancel, yes and no.
4256 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
4269 * Shorthand for {@link Roo.MessageBox}
4271 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
4272 Roo.Msg = Roo.Msg || Roo.MessageBox;
4281 * @class Roo.bootstrap.Navbar
4282 * @extends Roo.bootstrap.Component
4283 * Bootstrap Navbar class
4286 * Create a new Navbar
4287 * @param {Object} config The config object
4291 Roo.bootstrap.Navbar = function(config){
4292 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
4296 * @event beforetoggle
4297 * Fire before toggle the menu
4298 * @param {Roo.EventObject} e
4300 "beforetoggle" : true
4304 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
4313 getAutoCreate : function(){
4316 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
4320 initEvents :function ()
4322 //Roo.log(this.el.select('.navbar-toggle',true));
4323 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
4330 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
4332 var size = this.el.getSize();
4333 this.maskEl.setSize(size.width, size.height);
4334 this.maskEl.enableDisplayMode("block");
4343 getChildContainer : function()
4345 if (this.el && this.el.select('.collapse').getCount()) {
4346 return this.el.select('.collapse',true).first();
4361 onToggle : function()
4364 if(this.fireEvent('beforetoggle', this) === false){
4367 var ce = this.el.select('.navbar-collapse',true).first();
4369 if (!ce.hasClass('show')) {
4379 * Expand the navbar pulldown
4381 expand : function ()
4384 var ce = this.el.select('.navbar-collapse',true).first();
4385 if (ce.hasClass('collapsing')) {
4388 ce.dom.style.height = '';
4390 ce.addClass('in'); // old...
4391 ce.removeClass('collapse');
4392 ce.addClass('show');
4393 var h = ce.getHeight();
4395 ce.removeClass('show');
4396 // at this point we should be able to see it..
4397 ce.addClass('collapsing');
4399 ce.setHeight(0); // resize it ...
4400 ce.on('transitionend', function() {
4401 //Roo.log('done transition');
4402 ce.removeClass('collapsing');
4403 ce.addClass('show');
4404 ce.removeClass('collapse');
4406 ce.dom.style.height = '';
4407 }, this, { single: true} );
4409 ce.dom.scrollTop = 0;
4412 * Collapse the navbar pulldown
4414 collapse : function()
4416 var ce = this.el.select('.navbar-collapse',true).first();
4418 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
4419 // it's collapsed or collapsing..
4422 ce.removeClass('in'); // old...
4423 ce.setHeight(ce.getHeight());
4424 ce.removeClass('show');
4425 ce.addClass('collapsing');
4427 ce.on('transitionend', function() {
4428 ce.dom.style.height = '';
4429 ce.removeClass('collapsing');
4430 ce.addClass('collapse');
4431 }, this, { single: true} );
4451 * @class Roo.bootstrap.NavSimplebar
4452 * @extends Roo.bootstrap.Navbar
4453 * Bootstrap Sidebar class
4455 * @cfg {Boolean} inverse is inverted color
4457 * @cfg {String} type (nav | pills | tabs)
4458 * @cfg {Boolean} arrangement stacked | justified
4459 * @cfg {String} align (left | right) alignment
4461 * @cfg {Boolean} main (true|false) main nav bar? default false
4462 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4464 * @cfg {String} tag (header|footer|nav|div) default is nav
4466 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4470 * Create a new Sidebar
4471 * @param {Object} config The config object
4475 Roo.bootstrap.NavSimplebar = function(config){
4476 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4479 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
4495 getAutoCreate : function(){
4499 tag : this.tag || 'div',
4500 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
4502 if (['light','white'].indexOf(this.weight) > -1) {
4503 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4505 cfg.cls += ' bg-' + this.weight;
4508 cfg.cls += ' navbar-inverse';
4512 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4514 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4523 cls: 'nav nav-' + this.xtype,
4529 this.type = this.type || 'nav';
4530 if (['tabs','pills'].indexOf(this.type) != -1) {
4531 cfg.cn[0].cls += ' nav-' + this.type
4535 if (this.type!=='nav') {
4536 Roo.log('nav type must be nav/tabs/pills')
4538 cfg.cn[0].cls += ' navbar-nav'
4544 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4545 cfg.cn[0].cls += ' nav-' + this.arrangement;
4549 if (this.align === 'right') {
4550 cfg.cn[0].cls += ' navbar-right';
4575 * navbar-expand-md fixed-top
4579 * @class Roo.bootstrap.NavHeaderbar
4580 * @extends Roo.bootstrap.NavSimplebar
4581 * Bootstrap Sidebar class
4583 * @cfg {String} brand what is brand
4584 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4585 * @cfg {String} brand_href href of the brand
4586 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4587 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4588 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4589 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4592 * Create a new Sidebar
4593 * @param {Object} config The config object
4597 Roo.bootstrap.NavHeaderbar = function(config){
4598 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4602 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4609 desktopCenter : false,
4612 getAutoCreate : function(){
4615 tag: this.nav || 'nav',
4616 cls: 'navbar navbar-expand-md',
4622 if (this.desktopCenter) {
4623 cn.push({cls : 'container', cn : []});
4631 cls: 'navbar-toggle navbar-toggler',
4632 'data-toggle': 'collapse',
4637 html: 'Toggle navigation'
4641 cls: 'icon-bar navbar-toggler-icon'
4654 cn.push( Roo.bootstrap.version == 4 ? btn : {
4656 cls: 'navbar-header',
4665 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
4669 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4671 if (['light','white'].indexOf(this.weight) > -1) {
4672 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4674 cfg.cls += ' bg-' + this.weight;
4677 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4678 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4680 // tag can override this..
4682 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4685 if (this.brand !== '') {
4686 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4687 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4689 href: this.brand_href ? this.brand_href : '#',
4690 cls: 'navbar-brand',
4698 cfg.cls += ' main-nav';
4706 getHeaderChildContainer : function()
4708 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4709 return this.el.select('.navbar-header',true).first();
4712 return this.getChildContainer();
4715 getChildContainer : function()
4718 return this.el.select('.roo-navbar-collapse',true).first();
4723 initEvents : function()
4725 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4727 if (this.autohide) {
4732 Roo.get(document).on('scroll',function(e) {
4733 var ns = Roo.get(document).getScroll().top;
4734 var os = prevScroll;
4738 ft.removeClass('slideDown');
4739 ft.addClass('slideUp');
4742 ft.removeClass('slideUp');
4743 ft.addClass('slideDown');
4764 * @class Roo.bootstrap.NavSidebar
4765 * @extends Roo.bootstrap.Navbar
4766 * Bootstrap Sidebar class
4769 * Create a new Sidebar
4770 * @param {Object} config The config object
4774 Roo.bootstrap.NavSidebar = function(config){
4775 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4778 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4780 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4782 getAutoCreate : function(){
4787 cls: 'sidebar sidebar-nav'
4809 * @class Roo.bootstrap.NavGroup
4810 * @extends Roo.bootstrap.Component
4811 * Bootstrap NavGroup class
4812 * @cfg {String} align (left|right)
4813 * @cfg {Boolean} inverse
4814 * @cfg {String} type (nav|pills|tab) default nav
4815 * @cfg {String} navId - reference Id for navbar.
4819 * Create a new nav group
4820 * @param {Object} config The config object
4823 Roo.bootstrap.NavGroup = function(config){
4824 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4827 Roo.bootstrap.NavGroup.register(this);
4831 * Fires when the active item changes
4832 * @param {Roo.bootstrap.NavGroup} this
4833 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4834 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4841 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4852 getAutoCreate : function()
4854 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4860 if (Roo.bootstrap.version == 4) {
4861 if (['tabs','pills'].indexOf(this.type) != -1) {
4862 cfg.cls += ' nav-' + this.type;
4864 // trying to remove so header bar can right align top?
4865 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
4866 // do not use on header bar...
4867 cfg.cls += ' navbar-nav';
4872 if (['tabs','pills'].indexOf(this.type) != -1) {
4873 cfg.cls += ' nav-' + this.type
4875 if (this.type !== 'nav') {
4876 Roo.log('nav type must be nav/tabs/pills')
4878 cfg.cls += ' navbar-nav'
4882 if (this.parent() && this.parent().sidebar) {
4885 cls: 'dashboard-menu sidebar-menu'
4891 if (this.form === true) {
4894 cls: 'navbar-form form-inline'
4896 //nav navbar-right ml-md-auto
4897 if (this.align === 'right') {
4898 cfg.cls += ' navbar-right ml-md-auto';
4900 cfg.cls += ' navbar-left';
4904 if (this.align === 'right') {
4905 cfg.cls += ' navbar-right ml-md-auto';
4907 cfg.cls += ' mr-auto';
4911 cfg.cls += ' navbar-inverse';
4919 * sets the active Navigation item
4920 * @param {Roo.bootstrap.NavItem} the new current navitem
4922 setActiveItem : function(item)
4925 Roo.each(this.navItems, function(v){
4930 v.setActive(false, true);
4937 item.setActive(true, true);
4938 this.fireEvent('changed', this, item, prev);
4943 * gets the active Navigation item
4944 * @return {Roo.bootstrap.NavItem} the current navitem
4946 getActive : function()
4950 Roo.each(this.navItems, function(v){
4961 indexOfNav : function()
4965 Roo.each(this.navItems, function(v,i){
4976 * adds a Navigation item
4977 * @param {Roo.bootstrap.NavItem} the navitem to add
4979 addItem : function(cfg)
4981 if (this.form && Roo.bootstrap.version == 4) {
4984 var cn = new Roo.bootstrap.NavItem(cfg);
4986 cn.parentId = this.id;
4987 cn.onRender(this.el, null);
4991 * register a Navigation item
4992 * @param {Roo.bootstrap.NavItem} the navitem to add
4994 register : function(item)
4996 this.navItems.push( item);
4997 item.navId = this.navId;
5002 * clear all the Navigation item
5005 clearAll : function()
5008 this.el.dom.innerHTML = '';
5011 getNavItem: function(tabId)
5014 Roo.each(this.navItems, function(e) {
5015 if (e.tabId == tabId) {
5025 setActiveNext : function()
5027 var i = this.indexOfNav(this.getActive());
5028 if (i > this.navItems.length) {
5031 this.setActiveItem(this.navItems[i+1]);
5033 setActivePrev : function()
5035 var i = this.indexOfNav(this.getActive());
5039 this.setActiveItem(this.navItems[i-1]);
5041 clearWasActive : function(except) {
5042 Roo.each(this.navItems, function(e) {
5043 if (e.tabId != except.tabId && e.was_active) {
5044 e.was_active = false;
5051 getWasActive : function ()
5054 Roo.each(this.navItems, function(e) {
5069 Roo.apply(Roo.bootstrap.NavGroup, {
5073 * register a Navigation Group
5074 * @param {Roo.bootstrap.NavGroup} the navgroup to add
5076 register : function(navgrp)
5078 this.groups[navgrp.navId] = navgrp;
5082 * fetch a Navigation Group based on the navigation ID
5083 * @param {string} the navgroup to add
5084 * @returns {Roo.bootstrap.NavGroup} the navgroup
5086 get: function(navId) {
5087 if (typeof(this.groups[navId]) == 'undefined') {
5089 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
5091 return this.groups[navId] ;
5106 * @class Roo.bootstrap.NavItem
5107 * @extends Roo.bootstrap.Component
5108 * Bootstrap Navbar.NavItem class
5109 * @cfg {String} href link to
5110 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
5112 * @cfg {String} html content of button
5113 * @cfg {String} badge text inside badge
5114 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
5115 * @cfg {String} glyphicon DEPRICATED - use fa
5116 * @cfg {String} icon DEPRICATED - use fa
5117 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
5118 * @cfg {Boolean} active Is item active
5119 * @cfg {Boolean} disabled Is item disabled
5121 * @cfg {Boolean} preventDefault (true | false) default false
5122 * @cfg {String} tabId the tab that this item activates.
5123 * @cfg {String} tagtype (a|span) render as a href or span?
5124 * @cfg {Boolean} animateRef (true|false) link to element default false
5127 * Create a new Navbar Item
5128 * @param {Object} config The config object
5130 Roo.bootstrap.NavItem = function(config){
5131 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5136 * The raw click event for the entire grid.
5137 * @param {Roo.EventObject} e
5142 * Fires when the active item active state changes
5143 * @param {Roo.bootstrap.NavItem} this
5144 * @param {boolean} state the new state
5150 * Fires when scroll to element
5151 * @param {Roo.bootstrap.NavItem} this
5152 * @param {Object} options
5153 * @param {Roo.EventObject} e
5161 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
5170 preventDefault : false,
5178 button_outline : false,
5182 getAutoCreate : function(){
5190 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
5192 if (this.disabled) {
5193 cfg.cls += ' disabled';
5197 if (this.button_weight.length) {
5198 cfg.tag = this.href ? 'a' : 'button';
5199 cfg.html = this.html || '';
5200 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
5202 cfg.href = this.href;
5205 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
5208 // menu .. should add dropdown-menu class - so no need for carat..
5210 if (this.badge !== '') {
5212 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5217 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
5221 href : this.href || "#",
5222 html: this.html || ''
5225 if (this.tagtype == 'a') {
5226 cfg.cn[0].cls = 'nav-link';
5229 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
5232 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
5234 if(this.glyphicon) {
5235 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
5240 cfg.cn[0].html += " <span class='caret'></span>";
5244 if (this.badge !== '') {
5246 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5254 onRender : function(ct, position)
5256 // Roo.log("Call onRender: " + this.xtype);
5257 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
5261 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
5262 this.navLink = this.el.select('.nav-link',true).first();
5267 initEvents: function()
5269 if (typeof (this.menu) != 'undefined') {
5270 this.menu.parentType = this.xtype;
5271 this.menu.triggerEl = this.el;
5272 this.menu = this.addxtype(Roo.apply({}, this.menu));
5275 this.el.select('a',true).on('click', this.onClick, this);
5277 if(this.tagtype == 'span'){
5278 this.el.select('span',true).on('click', this.onClick, this);
5281 // at this point parent should be available..
5282 this.parent().register(this);
5285 onClick : function(e)
5287 if (e.getTarget('.dropdown-menu-item')) {
5288 // did you click on a menu itemm.... - then don't trigger onclick..
5293 this.preventDefault ||
5296 Roo.log("NavItem - prevent Default?");
5300 if (this.disabled) {
5304 var tg = Roo.bootstrap.TabGroup.get(this.navId);
5305 if (tg && tg.transition) {
5306 Roo.log("waiting for the transitionend");
5312 //Roo.log("fire event clicked");
5313 if(this.fireEvent('click', this, e) === false){
5317 if(this.tagtype == 'span'){
5321 //Roo.log(this.href);
5322 var ael = this.el.select('a',true).first();
5325 if(ael && this.animateRef && this.href.indexOf('#') > -1){
5326 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
5327 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
5328 return; // ignore... - it's a 'hash' to another page.
5330 Roo.log("NavItem - prevent Default?");
5332 this.scrollToElement(e);
5336 var p = this.parent();
5338 if (['tabs','pills'].indexOf(p.type)!==-1) {
5339 if (typeof(p.setActiveItem) !== 'undefined') {
5340 p.setActiveItem(this);
5344 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
5345 if (p.parentType == 'NavHeaderbar' && !this.menu) {
5346 // remove the collapsed menu expand...
5347 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
5351 isActive: function () {
5354 setActive : function(state, fire, is_was_active)
5356 if (this.active && !state && this.navId) {
5357 this.was_active = true;
5358 var nv = Roo.bootstrap.NavGroup.get(this.navId);
5360 nv.clearWasActive(this);
5364 this.active = state;
5367 this.el.removeClass('active');
5368 this.navLink ? this.navLink.removeClass('active') : false;
5369 } else if (!this.el.hasClass('active')) {
5371 this.el.addClass('active');
5372 if (Roo.bootstrap.version == 4 && this.navLink ) {
5373 this.navLink.addClass('active');
5378 this.fireEvent('changed', this, state);
5381 // show a panel if it's registered and related..
5383 if (!this.navId || !this.tabId || !state || is_was_active) {
5387 var tg = Roo.bootstrap.TabGroup.get(this.navId);
5391 var pan = tg.getPanelByName(this.tabId);
5395 // if we can not flip to new panel - go back to old nav highlight..
5396 if (false == tg.showPanel(pan)) {
5397 var nv = Roo.bootstrap.NavGroup.get(this.navId);
5399 var onav = nv.getWasActive();
5401 onav.setActive(true, false, true);
5410 // this should not be here...
5411 setDisabled : function(state)
5413 this.disabled = state;
5415 this.el.removeClass('disabled');
5416 } else if (!this.el.hasClass('disabled')) {
5417 this.el.addClass('disabled');
5423 * Fetch the element to display the tooltip on.
5424 * @return {Roo.Element} defaults to this.el
5426 tooltipEl : function()
5428 return this.el.select('' + this.tagtype + '', true).first();
5431 scrollToElement : function(e)
5433 var c = document.body;
5436 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
5438 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
5439 c = document.documentElement;
5442 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5448 var o = target.calcOffsetsTo(c);
5455 this.fireEvent('scrollto', this, options, e);
5457 Roo.get(c).scrollTo('top', options.value, true);
5470 * <span> icon </span>
5471 * <span> text </span>
5472 * <span>badge </span>
5476 * @class Roo.bootstrap.NavSidebarItem
5477 * @extends Roo.bootstrap.NavItem
5478 * Bootstrap Navbar.NavSidebarItem class
5479 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5480 * {Boolean} open is the menu open
5481 * {Boolean} buttonView use button as the tigger el rather that a (default false)
5482 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5483 * {String} buttonSize (sm|md|lg)the extra classes for the button
5484 * {Boolean} showArrow show arrow next to the text (default true)
5486 * Create a new Navbar Button
5487 * @param {Object} config The config object
5489 Roo.bootstrap.NavSidebarItem = function(config){
5490 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5495 * The raw click event for the entire grid.
5496 * @param {Roo.EventObject} e
5501 * Fires when the active item active state changes
5502 * @param {Roo.bootstrap.NavSidebarItem} this
5503 * @param {boolean} state the new state
5511 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
5513 badgeWeight : 'default',
5519 buttonWeight : 'default',
5525 getAutoCreate : function(){
5530 href : this.href || '#',
5536 if(this.buttonView){
5539 href : this.href || '#',
5540 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5553 cfg.cls += ' active';
5556 if (this.disabled) {
5557 cfg.cls += ' disabled';
5560 cfg.cls += ' open x-open';
5563 if (this.glyphicon || this.icon) {
5564 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
5565 a.cn.push({ tag : 'i', cls : c }) ;
5568 if(!this.buttonView){
5571 html : this.html || ''
5578 if (this.badge !== '') {
5579 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
5585 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5588 a.cls += ' dropdown-toggle treeview' ;
5594 initEvents : function()
5596 if (typeof (this.menu) != 'undefined') {
5597 this.menu.parentType = this.xtype;
5598 this.menu.triggerEl = this.el;
5599 this.menu = this.addxtype(Roo.apply({}, this.menu));
5602 this.el.on('click', this.onClick, this);
5604 if(this.badge !== ''){
5605 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5610 onClick : function(e)
5617 if(this.preventDefault){
5621 this.fireEvent('click', this, e);
5624 disable : function()
5626 this.setDisabled(true);
5631 this.setDisabled(false);
5634 setDisabled : function(state)
5636 if(this.disabled == state){
5640 this.disabled = state;
5643 this.el.addClass('disabled');
5647 this.el.removeClass('disabled');
5652 setActive : function(state)
5654 if(this.active == state){
5658 this.active = state;
5661 this.el.addClass('active');
5665 this.el.removeClass('active');
5670 isActive: function ()
5675 setBadge : function(str)
5681 this.badgeEl.dom.innerHTML = str;
5698 * @class Roo.bootstrap.Row
5699 * @extends Roo.bootstrap.Component
5700 * Bootstrap Row class (contains columns...)
5704 * @param {Object} config The config object
5707 Roo.bootstrap.Row = function(config){
5708 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5711 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5713 getAutoCreate : function(){
5732 * @class Roo.bootstrap.Pagination
5733 * @extends Roo.bootstrap.Component
5734 * Bootstrap Pagination class
5735 * @cfg {String} size xs | sm | md | lg
5736 * @cfg {Boolean} inverse false | true
5739 * Create a new Pagination
5740 * @param {Object} config The config object
5743 Roo.bootstrap.Pagination = function(config){
5744 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5747 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5753 getAutoCreate : function(){
5759 cfg.cls += ' inverse';
5765 cfg.cls += " " + this.cls;
5783 * @class Roo.bootstrap.PaginationItem
5784 * @extends Roo.bootstrap.Component
5785 * Bootstrap PaginationItem class
5786 * @cfg {String} html text
5787 * @cfg {String} href the link
5788 * @cfg {Boolean} preventDefault (true | false) default true
5789 * @cfg {Boolean} active (true | false) default false
5790 * @cfg {Boolean} disabled default false
5794 * Create a new PaginationItem
5795 * @param {Object} config The config object
5799 Roo.bootstrap.PaginationItem = function(config){
5800 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5805 * The raw click event for the entire grid.
5806 * @param {Roo.EventObject} e
5812 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5816 preventDefault: true,
5821 getAutoCreate : function(){
5827 href : this.href ? this.href : '#',
5828 html : this.html ? this.html : ''
5838 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5842 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5848 initEvents: function() {
5850 this.el.on('click', this.onClick, this);
5853 onClick : function(e)
5855 Roo.log('PaginationItem on click ');
5856 if(this.preventDefault){
5864 this.fireEvent('click', this, e);
5880 * @class Roo.bootstrap.Slider
5881 * @extends Roo.bootstrap.Component
5882 * Bootstrap Slider class
5885 * Create a new Slider
5886 * @param {Object} config The config object
5889 Roo.bootstrap.Slider = function(config){
5890 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5893 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5895 getAutoCreate : function(){
5899 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5903 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5915 * Ext JS Library 1.1.1
5916 * Copyright(c) 2006-2007, Ext JS, LLC.
5918 * Originally Released Under LGPL - original licence link has changed is not relivant.
5921 * <script type="text/javascript">
5926 * @class Roo.grid.ColumnModel
5927 * @extends Roo.util.Observable
5928 * This is the default implementation of a ColumnModel used by the Grid. It defines
5929 * the columns in the grid.
5932 var colModel = new Roo.grid.ColumnModel([
5933 {header: "Ticker", width: 60, sortable: true, locked: true},
5934 {header: "Company Name", width: 150, sortable: true},
5935 {header: "Market Cap.", width: 100, sortable: true},
5936 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5937 {header: "Employees", width: 100, sortable: true, resizable: false}
5942 * The config options listed for this class are options which may appear in each
5943 * individual column definition.
5944 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5946 * @param {Object} config An Array of column config objects. See this class's
5947 * config objects for details.
5949 Roo.grid.ColumnModel = function(config){
5951 * The config passed into the constructor
5953 this.config = config;
5956 // if no id, create one
5957 // if the column does not have a dataIndex mapping,
5958 // map it to the order it is in the config
5959 for(var i = 0, len = config.length; i < len; i++){
5961 if(typeof c.dataIndex == "undefined"){
5964 if(typeof c.renderer == "string"){
5965 c.renderer = Roo.util.Format[c.renderer];
5967 if(typeof c.id == "undefined"){
5970 if(c.editor && c.editor.xtype){
5971 c.editor = Roo.factory(c.editor, Roo.grid);
5973 if(c.editor && c.editor.isFormField){
5974 c.editor = new Roo.grid.GridEditor(c.editor);
5976 this.lookup[c.id] = c;
5980 * The width of columns which have no width specified (defaults to 100)
5983 this.defaultWidth = 100;
5986 * Default sortable of columns which have no sortable specified (defaults to false)
5989 this.defaultSortable = false;
5993 * @event widthchange
5994 * Fires when the width of a column changes.
5995 * @param {ColumnModel} this
5996 * @param {Number} columnIndex The column index
5997 * @param {Number} newWidth The new width
5999 "widthchange": true,
6001 * @event headerchange
6002 * Fires when the text of a header changes.
6003 * @param {ColumnModel} this
6004 * @param {Number} columnIndex The column index
6005 * @param {Number} newText The new header text
6007 "headerchange": true,
6009 * @event hiddenchange
6010 * Fires when a column is hidden or "unhidden".
6011 * @param {ColumnModel} this
6012 * @param {Number} columnIndex The column index
6013 * @param {Boolean} hidden true if hidden, false otherwise
6015 "hiddenchange": true,
6017 * @event columnmoved
6018 * Fires when a column is moved.
6019 * @param {ColumnModel} this
6020 * @param {Number} oldIndex
6021 * @param {Number} newIndex
6023 "columnmoved" : true,
6025 * @event columlockchange
6026 * Fires when a column's locked state is changed
6027 * @param {ColumnModel} this
6028 * @param {Number} colIndex
6029 * @param {Boolean} locked true if locked
6031 "columnlockchange" : true
6033 Roo.grid.ColumnModel.superclass.constructor.call(this);
6035 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6037 * @cfg {String} header The header text to display in the Grid view.
6040 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6041 * {@link Roo.data.Record} definition from which to draw the column's value. If not
6042 * specified, the column's index is used as an index into the Record's data Array.
6045 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6046 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6049 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6050 * Defaults to the value of the {@link #defaultSortable} property.
6051 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6054 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
6057 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
6060 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6063 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6066 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6067 * given the cell's data value. See {@link #setRenderer}. If not specified, the
6068 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6069 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6072 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
6075 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
6078 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
6081 * @cfg {String} cursor (Optional)
6084 * @cfg {String} tooltip (Optional)
6087 * @cfg {Number} xs (Optional)
6090 * @cfg {Number} sm (Optional)
6093 * @cfg {Number} md (Optional)
6096 * @cfg {Number} lg (Optional)
6099 * Returns the id of the column at the specified index.
6100 * @param {Number} index The column index
6101 * @return {String} the id
6103 getColumnId : function(index){
6104 return this.config[index].id;
6108 * Returns the column for a specified id.
6109 * @param {String} id The column id
6110 * @return {Object} the column
6112 getColumnById : function(id){
6113 return this.lookup[id];
6118 * Returns the column for a specified dataIndex.
6119 * @param {String} dataIndex The column dataIndex
6120 * @return {Object|Boolean} the column or false if not found
6122 getColumnByDataIndex: function(dataIndex){
6123 var index = this.findColumnIndex(dataIndex);
6124 return index > -1 ? this.config[index] : false;
6128 * Returns the index for a specified column id.
6129 * @param {String} id The column id
6130 * @return {Number} the index, or -1 if not found
6132 getIndexById : function(id){
6133 for(var i = 0, len = this.config.length; i < len; i++){
6134 if(this.config[i].id == id){
6142 * Returns the index for a specified column dataIndex.
6143 * @param {String} dataIndex The column dataIndex
6144 * @return {Number} the index, or -1 if not found
6147 findColumnIndex : function(dataIndex){
6148 for(var i = 0, len = this.config.length; i < len; i++){
6149 if(this.config[i].dataIndex == dataIndex){
6157 moveColumn : function(oldIndex, newIndex){
6158 var c = this.config[oldIndex];
6159 this.config.splice(oldIndex, 1);
6160 this.config.splice(newIndex, 0, c);
6161 this.dataMap = null;
6162 this.fireEvent("columnmoved", this, oldIndex, newIndex);
6165 isLocked : function(colIndex){
6166 return this.config[colIndex].locked === true;
6169 setLocked : function(colIndex, value, suppressEvent){
6170 if(this.isLocked(colIndex) == value){
6173 this.config[colIndex].locked = value;
6175 this.fireEvent("columnlockchange", this, colIndex, value);
6179 getTotalLockedWidth : function(){
6181 for(var i = 0; i < this.config.length; i++){
6182 if(this.isLocked(i) && !this.isHidden(i)){
6183 this.totalWidth += this.getColumnWidth(i);
6189 getLockedCount : function(){
6190 for(var i = 0, len = this.config.length; i < len; i++){
6191 if(!this.isLocked(i)){
6196 return this.config.length;
6200 * Returns the number of columns.
6203 getColumnCount : function(visibleOnly){
6204 if(visibleOnly === true){
6206 for(var i = 0, len = this.config.length; i < len; i++){
6207 if(!this.isHidden(i)){
6213 return this.config.length;
6217 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
6218 * @param {Function} fn
6219 * @param {Object} scope (optional)
6220 * @return {Array} result
6222 getColumnsBy : function(fn, scope){
6224 for(var i = 0, len = this.config.length; i < len; i++){
6225 var c = this.config[i];
6226 if(fn.call(scope||this, c, i) === true){
6234 * Returns true if the specified column is sortable.
6235 * @param {Number} col The column index
6238 isSortable : function(col){
6239 if(typeof this.config[col].sortable == "undefined"){
6240 return this.defaultSortable;
6242 return this.config[col].sortable;
6246 * Returns the rendering (formatting) function defined for the column.
6247 * @param {Number} col The column index.
6248 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
6250 getRenderer : function(col){
6251 if(!this.config[col].renderer){
6252 return Roo.grid.ColumnModel.defaultRenderer;
6254 return this.config[col].renderer;
6258 * Sets the rendering (formatting) function for a column.
6259 * @param {Number} col The column index
6260 * @param {Function} fn The function to use to process the cell's raw data
6261 * to return HTML markup for the grid view. The render function is called with
6262 * the following parameters:<ul>
6263 * <li>Data value.</li>
6264 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
6265 * <li>css A CSS style string to apply to the table cell.</li>
6266 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
6267 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
6268 * <li>Row index</li>
6269 * <li>Column index</li>
6270 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
6272 setRenderer : function(col, fn){
6273 this.config[col].renderer = fn;
6277 * Returns the width for the specified column.
6278 * @param {Number} col The column index
6281 getColumnWidth : function(col){
6282 return this.config[col].width * 1 || this.defaultWidth;
6286 * Sets the width for a column.
6287 * @param {Number} col The column index
6288 * @param {Number} width The new width
6290 setColumnWidth : function(col, width, suppressEvent){
6291 this.config[col].width = width;
6292 this.totalWidth = null;
6294 this.fireEvent("widthchange", this, col, width);
6299 * Returns the total width of all columns.
6300 * @param {Boolean} includeHidden True to include hidden column widths
6303 getTotalWidth : function(includeHidden){
6304 if(!this.totalWidth){
6305 this.totalWidth = 0;
6306 for(var i = 0, len = this.config.length; i < len; i++){
6307 if(includeHidden || !this.isHidden(i)){
6308 this.totalWidth += this.getColumnWidth(i);
6312 return this.totalWidth;
6316 * Returns the header for the specified column.
6317 * @param {Number} col The column index
6320 getColumnHeader : function(col){
6321 return this.config[col].header;
6325 * Sets the header for a column.
6326 * @param {Number} col The column index
6327 * @param {String} header The new header
6329 setColumnHeader : function(col, header){
6330 this.config[col].header = header;
6331 this.fireEvent("headerchange", this, col, header);
6335 * Returns the tooltip for the specified column.
6336 * @param {Number} col The column index
6339 getColumnTooltip : function(col){
6340 return this.config[col].tooltip;
6343 * Sets the tooltip for a column.
6344 * @param {Number} col The column index
6345 * @param {String} tooltip The new tooltip
6347 setColumnTooltip : function(col, tooltip){
6348 this.config[col].tooltip = tooltip;
6352 * Returns the dataIndex for the specified column.
6353 * @param {Number} col The column index
6356 getDataIndex : function(col){
6357 return this.config[col].dataIndex;
6361 * Sets the dataIndex for a column.
6362 * @param {Number} col The column index
6363 * @param {Number} dataIndex The new dataIndex
6365 setDataIndex : function(col, dataIndex){
6366 this.config[col].dataIndex = dataIndex;
6372 * Returns true if the cell is editable.
6373 * @param {Number} colIndex The column index
6374 * @param {Number} rowIndex The row index - this is nto actually used..?
6377 isCellEditable : function(colIndex, rowIndex){
6378 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6382 * Returns the editor defined for the cell/column.
6383 * return false or null to disable editing.
6384 * @param {Number} colIndex The column index
6385 * @param {Number} rowIndex The row index
6388 getCellEditor : function(colIndex, rowIndex){
6389 return this.config[colIndex].editor;
6393 * Sets if a column is editable.
6394 * @param {Number} col The column index
6395 * @param {Boolean} editable True if the column is editable
6397 setEditable : function(col, editable){
6398 this.config[col].editable = editable;
6403 * Returns true if the column is hidden.
6404 * @param {Number} colIndex The column index
6407 isHidden : function(colIndex){
6408 return this.config[colIndex].hidden;
6413 * Returns true if the column width cannot be changed
6415 isFixed : function(colIndex){
6416 return this.config[colIndex].fixed;
6420 * Returns true if the column can be resized
6423 isResizable : function(colIndex){
6424 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6427 * Sets if a column is hidden.
6428 * @param {Number} colIndex The column index
6429 * @param {Boolean} hidden True if the column is hidden
6431 setHidden : function(colIndex, hidden){
6432 this.config[colIndex].hidden = hidden;
6433 this.totalWidth = null;
6434 this.fireEvent("hiddenchange", this, colIndex, hidden);
6438 * Sets the editor for a column.
6439 * @param {Number} col The column index
6440 * @param {Object} editor The editor object
6442 setEditor : function(col, editor){
6443 this.config[col].editor = editor;
6447 Roo.grid.ColumnModel.defaultRenderer = function(value)
6449 if(typeof value == "object") {
6452 if(typeof value == "string" && value.length < 1){
6456 return String.format("{0}", value);
6459 // Alias for backwards compatibility
6460 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6463 * Ext JS Library 1.1.1
6464 * Copyright(c) 2006-2007, Ext JS, LLC.
6466 * Originally Released Under LGPL - original licence link has changed is not relivant.
6469 * <script type="text/javascript">
6473 * @class Roo.LoadMask
6474 * A simple utility class for generically masking elements while loading data. If the element being masked has
6475 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6476 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
6477 * element's UpdateManager load indicator and will be destroyed after the initial load.
6479 * Create a new LoadMask
6480 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6481 * @param {Object} config The config object
6483 Roo.LoadMask = function(el, config){
6484 this.el = Roo.get(el);
6485 Roo.apply(this, config);
6487 this.store.on('beforeload', this.onBeforeLoad, this);
6488 this.store.on('load', this.onLoad, this);
6489 this.store.on('loadexception', this.onLoadException, this);
6490 this.removeMask = false;
6492 var um = this.el.getUpdateManager();
6493 um.showLoadIndicator = false; // disable the default indicator
6494 um.on('beforeupdate', this.onBeforeLoad, this);
6495 um.on('update', this.onLoad, this);
6496 um.on('failure', this.onLoad, this);
6497 this.removeMask = true;
6501 Roo.LoadMask.prototype = {
6503 * @cfg {Boolean} removeMask
6504 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6505 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
6509 * The text to display in a centered loading message box (defaults to 'Loading...')
6513 * @cfg {String} msgCls
6514 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6516 msgCls : 'x-mask-loading',
6519 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6525 * Disables the mask to prevent it from being displayed
6527 disable : function(){
6528 this.disabled = true;
6532 * Enables the mask so that it can be displayed
6534 enable : function(){
6535 this.disabled = false;
6538 onLoadException : function()
6542 if (typeof(arguments[3]) != 'undefined') {
6543 Roo.MessageBox.alert("Error loading",arguments[3]);
6547 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6548 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6555 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6560 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6564 onBeforeLoad : function(){
6566 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6571 destroy : function(){
6573 this.store.un('beforeload', this.onBeforeLoad, this);
6574 this.store.un('load', this.onLoad, this);
6575 this.store.un('loadexception', this.onLoadException, this);
6577 var um = this.el.getUpdateManager();
6578 um.un('beforeupdate', this.onBeforeLoad, this);
6579 um.un('update', this.onLoad, this);
6580 um.un('failure', this.onLoad, this);
6591 * @class Roo.bootstrap.Table
6592 * @extends Roo.bootstrap.Component
6593 * Bootstrap Table class
6594 * @cfg {String} cls table class
6595 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6596 * @cfg {String} bgcolor Specifies the background color for a table
6597 * @cfg {Number} border Specifies whether the table cells should have borders or not
6598 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6599 * @cfg {Number} cellspacing Specifies the space between cells
6600 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6601 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6602 * @cfg {String} sortable Specifies that the table should be sortable
6603 * @cfg {String} summary Specifies a summary of the content of a table
6604 * @cfg {Number} width Specifies the width of a table
6605 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6607 * @cfg {boolean} striped Should the rows be alternative striped
6608 * @cfg {boolean} bordered Add borders to the table
6609 * @cfg {boolean} hover Add hover highlighting
6610 * @cfg {boolean} condensed Format condensed
6611 * @cfg {boolean} responsive Format condensed
6612 * @cfg {Boolean} loadMask (true|false) default false
6613 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6614 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6615 * @cfg {Boolean} rowSelection (true|false) default false
6616 * @cfg {Boolean} cellSelection (true|false) default false
6617 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6618 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6619 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6620 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6624 * Create a new Table
6625 * @param {Object} config The config object
6628 Roo.bootstrap.Table = function(config){
6629 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6634 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6635 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6636 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6637 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6639 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6641 this.sm.grid = this;
6642 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6643 this.sm = this.selModel;
6644 this.sm.xmodule = this.xmodule || false;
6647 if (this.cm && typeof(this.cm.config) == 'undefined') {
6648 this.colModel = new Roo.grid.ColumnModel(this.cm);
6649 this.cm = this.colModel;
6650 this.cm.xmodule = this.xmodule || false;
6653 this.store= Roo.factory(this.store, Roo.data);
6654 this.ds = this.store;
6655 this.ds.xmodule = this.xmodule || false;
6658 if (this.footer && this.store) {
6659 this.footer.dataSource = this.ds;
6660 this.footer = Roo.factory(this.footer);
6667 * Fires when a cell is clicked
6668 * @param {Roo.bootstrap.Table} this
6669 * @param {Roo.Element} el
6670 * @param {Number} rowIndex
6671 * @param {Number} columnIndex
6672 * @param {Roo.EventObject} e
6676 * @event celldblclick
6677 * Fires when a cell is double clicked
6678 * @param {Roo.bootstrap.Table} this
6679 * @param {Roo.Element} el
6680 * @param {Number} rowIndex
6681 * @param {Number} columnIndex
6682 * @param {Roo.EventObject} e
6684 "celldblclick" : true,
6687 * Fires when a row is clicked
6688 * @param {Roo.bootstrap.Table} this
6689 * @param {Roo.Element} el
6690 * @param {Number} rowIndex
6691 * @param {Roo.EventObject} e
6695 * @event rowdblclick
6696 * Fires when a row is double clicked
6697 * @param {Roo.bootstrap.Table} this
6698 * @param {Roo.Element} el
6699 * @param {Number} rowIndex
6700 * @param {Roo.EventObject} e
6702 "rowdblclick" : true,
6705 * Fires when a mouseover occur
6706 * @param {Roo.bootstrap.Table} this
6707 * @param {Roo.Element} el
6708 * @param {Number} rowIndex
6709 * @param {Number} columnIndex
6710 * @param {Roo.EventObject} e
6715 * Fires when a mouseout occur
6716 * @param {Roo.bootstrap.Table} this
6717 * @param {Roo.Element} el
6718 * @param {Number} rowIndex
6719 * @param {Number} columnIndex
6720 * @param {Roo.EventObject} e
6725 * Fires when a row is rendered, so you can change add a style to it.
6726 * @param {Roo.bootstrap.Table} this
6727 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6731 * @event rowsrendered
6732 * Fires when all the rows have been rendered
6733 * @param {Roo.bootstrap.Table} this
6735 'rowsrendered' : true,
6737 * @event contextmenu
6738 * The raw contextmenu event for the entire grid.
6739 * @param {Roo.EventObject} e
6741 "contextmenu" : true,
6743 * @event rowcontextmenu
6744 * Fires when a row is right clicked
6745 * @param {Roo.bootstrap.Table} this
6746 * @param {Number} rowIndex
6747 * @param {Roo.EventObject} e
6749 "rowcontextmenu" : true,
6751 * @event cellcontextmenu
6752 * Fires when a cell is right clicked
6753 * @param {Roo.bootstrap.Table} this
6754 * @param {Number} rowIndex
6755 * @param {Number} cellIndex
6756 * @param {Roo.EventObject} e
6758 "cellcontextmenu" : true,
6760 * @event headercontextmenu
6761 * Fires when a header is right clicked
6762 * @param {Roo.bootstrap.Table} this
6763 * @param {Number} columnIndex
6764 * @param {Roo.EventObject} e
6766 "headercontextmenu" : true
6770 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6796 rowSelection : false,
6797 cellSelection : false,
6800 // Roo.Element - the tbody
6802 // Roo.Element - thead element
6805 container: false, // used by gridpanel...
6811 auto_hide_footer : false,
6813 getAutoCreate : function()
6815 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6822 if (this.scrollBody) {
6823 cfg.cls += ' table-body-fixed';
6826 cfg.cls += ' table-striped';
6830 cfg.cls += ' table-hover';
6832 if (this.bordered) {
6833 cfg.cls += ' table-bordered';
6835 if (this.condensed) {
6836 cfg.cls += ' table-condensed';
6838 if (this.responsive) {
6839 cfg.cls += ' table-responsive';
6843 cfg.cls+= ' ' +this.cls;
6846 // this lot should be simplifed...
6859 ].forEach(function(k) {
6867 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6870 if(this.store || this.cm){
6871 if(this.headerShow){
6872 cfg.cn.push(this.renderHeader());
6875 cfg.cn.push(this.renderBody());
6877 if(this.footerShow){
6878 cfg.cn.push(this.renderFooter());
6880 // where does this come from?
6881 //cfg.cls+= ' TableGrid';
6884 return { cn : [ cfg ] };
6887 initEvents : function()
6889 if(!this.store || !this.cm){
6892 if (this.selModel) {
6893 this.selModel.initEvents();
6897 //Roo.log('initEvents with ds!!!!');
6899 this.mainBody = this.el.select('tbody', true).first();
6900 this.mainHead = this.el.select('thead', true).first();
6901 this.mainFoot = this.el.select('tfoot', true).first();
6907 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6908 e.on('click', _this.sort, _this);
6911 this.mainBody.on("click", this.onClick, this);
6912 this.mainBody.on("dblclick", this.onDblClick, this);
6914 // why is this done????? = it breaks dialogs??
6915 //this.parent().el.setStyle('position', 'relative');
6919 this.footer.parentId = this.id;
6920 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6923 this.el.select('tfoot tr td').first().addClass('hide');
6928 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6931 this.store.on('load', this.onLoad, this);
6932 this.store.on('beforeload', this.onBeforeLoad, this);
6933 this.store.on('update', this.onUpdate, this);
6934 this.store.on('add', this.onAdd, this);
6935 this.store.on("clear", this.clear, this);
6937 this.el.on("contextmenu", this.onContextMenu, this);
6939 this.mainBody.on('scroll', this.onBodyScroll, this);
6941 this.cm.on("headerchange", this.onHeaderChange, this);
6943 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6947 onContextMenu : function(e, t)
6949 this.processEvent("contextmenu", e);
6952 processEvent : function(name, e)
6954 if (name != 'touchstart' ) {
6955 this.fireEvent(name, e);
6958 var t = e.getTarget();
6960 var cell = Roo.get(t);
6966 if(cell.findParent('tfoot', false, true)){
6970 if(cell.findParent('thead', false, true)){
6972 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6973 cell = Roo.get(t).findParent('th', false, true);
6975 Roo.log("failed to find th in thead?");
6976 Roo.log(e.getTarget());
6981 var cellIndex = cell.dom.cellIndex;
6983 var ename = name == 'touchstart' ? 'click' : name;
6984 this.fireEvent("header" + ename, this, cellIndex, e);
6989 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6990 cell = Roo.get(t).findParent('td', false, true);
6992 Roo.log("failed to find th in tbody?");
6993 Roo.log(e.getTarget());
6998 var row = cell.findParent('tr', false, true);
6999 var cellIndex = cell.dom.cellIndex;
7000 var rowIndex = row.dom.rowIndex - 1;
7004 this.fireEvent("row" + name, this, rowIndex, e);
7008 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7014 onMouseover : function(e, el)
7016 var cell = Roo.get(el);
7022 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7023 cell = cell.findParent('td', false, true);
7026 var row = cell.findParent('tr', false, true);
7027 var cellIndex = cell.dom.cellIndex;
7028 var rowIndex = row.dom.rowIndex - 1; // start from 0
7030 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7034 onMouseout : function(e, el)
7036 var cell = Roo.get(el);
7042 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7043 cell = cell.findParent('td', false, true);
7046 var row = cell.findParent('tr', false, true);
7047 var cellIndex = cell.dom.cellIndex;
7048 var rowIndex = row.dom.rowIndex - 1; // start from 0
7050 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7054 onClick : function(e, el)
7056 var cell = Roo.get(el);
7058 if(!cell || (!this.cellSelection && !this.rowSelection)){
7062 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7063 cell = cell.findParent('td', false, true);
7066 if(!cell || typeof(cell) == 'undefined'){
7070 var row = cell.findParent('tr', false, true);
7072 if(!row || typeof(row) == 'undefined'){
7076 var cellIndex = cell.dom.cellIndex;
7077 var rowIndex = this.getRowIndex(row);
7079 // why??? - should these not be based on SelectionModel?
7080 if(this.cellSelection){
7081 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7084 if(this.rowSelection){
7085 this.fireEvent('rowclick', this, row, rowIndex, e);
7091 onDblClick : function(e,el)
7093 var cell = Roo.get(el);
7095 if(!cell || (!this.cellSelection && !this.rowSelection)){
7099 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7100 cell = cell.findParent('td', false, true);
7103 if(!cell || typeof(cell) == 'undefined'){
7107 var row = cell.findParent('tr', false, true);
7109 if(!row || typeof(row) == 'undefined'){
7113 var cellIndex = cell.dom.cellIndex;
7114 var rowIndex = this.getRowIndex(row);
7116 if(this.cellSelection){
7117 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7120 if(this.rowSelection){
7121 this.fireEvent('rowdblclick', this, row, rowIndex, e);
7125 sort : function(e,el)
7127 var col = Roo.get(el);
7129 if(!col.hasClass('sortable')){
7133 var sort = col.attr('sort');
7136 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7140 this.store.sortInfo = {field : sort, direction : dir};
7143 Roo.log("calling footer first");
7144 this.footer.onClick('first');
7147 this.store.load({ params : { start : 0 } });
7151 renderHeader : function()
7159 this.totalWidth = 0;
7161 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7163 var config = cm.config[i];
7167 cls : 'x-hcol-' + i,
7169 html: cm.getColumnHeader(i)
7174 if(typeof(config.sortable) != 'undefined' && config.sortable){
7176 c.html = '<i class="glyphicon"></i>' + c.html;
7179 // could use BS4 hidden-..-down
7181 if(typeof(config.lgHeader) != 'undefined'){
7182 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
7185 if(typeof(config.mdHeader) != 'undefined'){
7186 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
7189 if(typeof(config.smHeader) != 'undefined'){
7190 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
7193 if(typeof(config.xsHeader) != 'undefined'){
7194 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
7201 if(typeof(config.tooltip) != 'undefined'){
7202 c.tooltip = config.tooltip;
7205 if(typeof(config.colspan) != 'undefined'){
7206 c.colspan = config.colspan;
7209 if(typeof(config.hidden) != 'undefined' && config.hidden){
7210 c.style += ' display:none;';
7213 if(typeof(config.dataIndex) != 'undefined'){
7214 c.sort = config.dataIndex;
7219 if(typeof(config.align) != 'undefined' && config.align.length){
7220 c.style += ' text-align:' + config.align + ';';
7223 if(typeof(config.width) != 'undefined'){
7224 c.style += ' width:' + config.width + 'px;';
7225 this.totalWidth += config.width;
7227 this.totalWidth += 100; // assume minimum of 100 per column?
7230 if(typeof(config.cls) != 'undefined'){
7231 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
7234 ['xs','sm','md','lg'].map(function(size){
7236 if(typeof(config[size]) == 'undefined'){
7240 if (!config[size]) { // 0 = hidden
7241 // BS 4 '0' is treated as hide that column and below.
7242 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
7246 c.cls += ' col-' + size + '-' + config[size] + (
7247 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7259 renderBody : function()
7269 colspan : this.cm.getColumnCount()
7279 renderFooter : function()
7289 colspan : this.cm.getColumnCount()
7303 // Roo.log('ds onload');
7308 var ds = this.store;
7310 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7311 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
7312 if (_this.store.sortInfo) {
7314 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
7315 e.select('i', true).addClass(['glyphicon-arrow-up']);
7318 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
7319 e.select('i', true).addClass(['glyphicon-arrow-down']);
7324 var tbody = this.mainBody;
7326 if(ds.getCount() > 0){
7327 ds.data.each(function(d,rowIndex){
7328 var row = this.renderRow(cm, ds, rowIndex);
7330 tbody.createChild(row);
7334 if(row.cellObjects.length){
7335 Roo.each(row.cellObjects, function(r){
7336 _this.renderCellObject(r);
7343 var tfoot = this.el.select('tfoot', true).first();
7345 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7347 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7349 var total = this.ds.getTotalCount();
7351 if(this.footer.pageSize < total){
7352 this.mainFoot.show();
7356 Roo.each(this.el.select('tbody td', true).elements, function(e){
7357 e.on('mouseover', _this.onMouseover, _this);
7360 Roo.each(this.el.select('tbody td', true).elements, function(e){
7361 e.on('mouseout', _this.onMouseout, _this);
7363 this.fireEvent('rowsrendered', this);
7369 onUpdate : function(ds,record)
7371 this.refreshRow(record);
7375 onRemove : function(ds, record, index, isUpdate){
7376 if(isUpdate !== true){
7377 this.fireEvent("beforerowremoved", this, index, record);
7379 var bt = this.mainBody.dom;
7381 var rows = this.el.select('tbody > tr', true).elements;
7383 if(typeof(rows[index]) != 'undefined'){
7384 bt.removeChild(rows[index].dom);
7387 // if(bt.rows[index]){
7388 // bt.removeChild(bt.rows[index]);
7391 if(isUpdate !== true){
7392 //this.stripeRows(index);
7393 //this.syncRowHeights(index, index);
7395 this.fireEvent("rowremoved", this, index, record);
7399 onAdd : function(ds, records, rowIndex)
7401 //Roo.log('on Add called');
7402 // - note this does not handle multiple adding very well..
7403 var bt = this.mainBody.dom;
7404 for (var i =0 ; i < records.length;i++) {
7405 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7406 //Roo.log(records[i]);
7407 //Roo.log(this.store.getAt(rowIndex+i));
7408 this.insertRow(this.store, rowIndex + i, false);
7415 refreshRow : function(record){
7416 var ds = this.store, index;
7417 if(typeof record == 'number'){
7419 record = ds.getAt(index);
7421 index = ds.indexOf(record);
7423 this.insertRow(ds, index, true);
7425 this.onRemove(ds, record, index+1, true);
7427 //this.syncRowHeights(index, index);
7429 this.fireEvent("rowupdated", this, index, record);
7432 insertRow : function(dm, rowIndex, isUpdate){
7435 this.fireEvent("beforerowsinserted", this, rowIndex);
7437 //var s = this.getScrollState();
7438 var row = this.renderRow(this.cm, this.store, rowIndex);
7439 // insert before rowIndex..
7440 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7444 if(row.cellObjects.length){
7445 Roo.each(row.cellObjects, function(r){
7446 _this.renderCellObject(r);
7451 this.fireEvent("rowsinserted", this, rowIndex);
7452 //this.syncRowHeights(firstRow, lastRow);
7453 //this.stripeRows(firstRow);
7460 getRowDom : function(rowIndex)
7462 var rows = this.el.select('tbody > tr', true).elements;
7464 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7467 // returns the object tree for a tr..
7470 renderRow : function(cm, ds, rowIndex)
7472 var d = ds.getAt(rowIndex);
7476 cls : 'x-row-' + rowIndex,
7480 var cellObjects = [];
7482 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7483 var config = cm.config[i];
7485 var renderer = cm.getRenderer(i);
7489 if(typeof(renderer) !== 'undefined'){
7490 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7492 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7493 // and are rendered into the cells after the row is rendered - using the id for the element.
7495 if(typeof(value) === 'object'){
7505 rowIndex : rowIndex,
7510 this.fireEvent('rowclass', this, rowcfg);
7514 cls : rowcfg.rowClass + ' x-col-' + i,
7516 html: (typeof(value) === 'object') ? '' : value
7523 if(typeof(config.colspan) != 'undefined'){
7524 td.colspan = config.colspan;
7527 if(typeof(config.hidden) != 'undefined' && config.hidden){
7528 td.style += ' display:none;';
7531 if(typeof(config.align) != 'undefined' && config.align.length){
7532 td.style += ' text-align:' + config.align + ';';
7534 if(typeof(config.valign) != 'undefined' && config.valign.length){
7535 td.style += ' vertical-align:' + config.valign + ';';
7538 if(typeof(config.width) != 'undefined'){
7539 td.style += ' width:' + config.width + 'px;';
7542 if(typeof(config.cursor) != 'undefined'){
7543 td.style += ' cursor:' + config.cursor + ';';
7546 if(typeof(config.cls) != 'undefined'){
7547 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7550 ['xs','sm','md','lg'].map(function(size){
7552 if(typeof(config[size]) == 'undefined'){
7558 if (!config[size]) { // 0 = hidden
7559 // BS 4 '0' is treated as hide that column and below.
7560 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7564 td.cls += ' col-' + size + '-' + config[size] + (
7565 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7575 row.cellObjects = cellObjects;
7583 onBeforeLoad : function()
7592 this.el.select('tbody', true).first().dom.innerHTML = '';
7595 * Show or hide a row.
7596 * @param {Number} rowIndex to show or hide
7597 * @param {Boolean} state hide
7599 setRowVisibility : function(rowIndex, state)
7601 var bt = this.mainBody.dom;
7603 var rows = this.el.select('tbody > tr', true).elements;
7605 if(typeof(rows[rowIndex]) == 'undefined'){
7608 rows[rowIndex].dom.style.display = state ? '' : 'none';
7612 getSelectionModel : function(){
7614 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7616 return this.selModel;
7619 * Render the Roo.bootstrap object from renderder
7621 renderCellObject : function(r)
7625 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7627 var t = r.cfg.render(r.container);
7630 Roo.each(r.cfg.cn, function(c){
7632 container: t.getChildContainer(),
7635 _this.renderCellObject(child);
7640 getRowIndex : function(row)
7644 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7655 * Returns the grid's underlying element = used by panel.Grid
7656 * @return {Element} The element
7658 getGridEl : function(){
7662 * Forces a resize - used by panel.Grid
7663 * @return {Element} The element
7665 autoSize : function()
7667 //var ctr = Roo.get(this.container.dom.parentElement);
7668 var ctr = Roo.get(this.el.dom);
7670 var thd = this.getGridEl().select('thead',true).first();
7671 var tbd = this.getGridEl().select('tbody', true).first();
7672 var tfd = this.getGridEl().select('tfoot', true).first();
7674 var cw = ctr.getWidth();
7678 tbd.setWidth(ctr.getWidth());
7679 // if the body has a max height - and then scrolls - we should perhaps set up the height here
7680 // this needs fixing for various usage - currently only hydra job advers I think..
7682 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7684 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7687 cw = Math.max(cw, this.totalWidth);
7688 this.getGridEl().select('tr',true).setWidth(cw);
7689 // resize 'expandable coloumn?
7691 return; // we doe not have a view in this design..
7694 onBodyScroll: function()
7696 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7698 this.mainHead.setStyle({
7699 'position' : 'relative',
7700 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7706 var scrollHeight = this.mainBody.dom.scrollHeight;
7708 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7710 var height = this.mainBody.getHeight();
7712 if(scrollHeight - height == scrollTop) {
7714 var total = this.ds.getTotalCount();
7716 if(this.footer.cursor + this.footer.pageSize < total){
7718 this.footer.ds.load({
7720 start : this.footer.cursor + this.footer.pageSize,
7721 limit : this.footer.pageSize
7731 onHeaderChange : function()
7733 var header = this.renderHeader();
7734 var table = this.el.select('table', true).first();
7736 this.mainHead.remove();
7737 this.mainHead = table.createChild(header, this.mainBody, false);
7740 onHiddenChange : function(colModel, colIndex, hidden)
7742 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7743 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7745 this.CSS.updateRule(thSelector, "display", "");
7746 this.CSS.updateRule(tdSelector, "display", "");
7749 this.CSS.updateRule(thSelector, "display", "none");
7750 this.CSS.updateRule(tdSelector, "display", "none");
7753 this.onHeaderChange();
7757 setColumnWidth: function(col_index, width)
7759 // width = "md-2 xs-2..."
7760 if(!this.colModel.config[col_index]) {
7764 var w = width.split(" ");
7766 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7768 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7771 for(var j = 0; j < w.length; j++) {
7777 var size_cls = w[j].split("-");
7779 if(!Number.isInteger(size_cls[1] * 1)) {
7783 if(!this.colModel.config[col_index][size_cls[0]]) {
7787 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7791 h_row[0].classList.replace(
7792 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7793 "col-"+size_cls[0]+"-"+size_cls[1]
7796 for(var i = 0; i < rows.length; i++) {
7798 var size_cls = w[j].split("-");
7800 if(!Number.isInteger(size_cls[1] * 1)) {
7804 if(!this.colModel.config[col_index][size_cls[0]]) {
7808 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7812 rows[i].classList.replace(
7813 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7814 "col-"+size_cls[0]+"-"+size_cls[1]
7818 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7833 * @class Roo.bootstrap.TableCell
7834 * @extends Roo.bootstrap.Component
7835 * Bootstrap TableCell class
7836 * @cfg {String} html cell contain text
7837 * @cfg {String} cls cell class
7838 * @cfg {String} tag cell tag (td|th) default td
7839 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7840 * @cfg {String} align Aligns the content in a cell
7841 * @cfg {String} axis Categorizes cells
7842 * @cfg {String} bgcolor Specifies the background color of a cell
7843 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7844 * @cfg {Number} colspan Specifies the number of columns a cell should span
7845 * @cfg {String} headers Specifies one or more header cells a cell is related to
7846 * @cfg {Number} height Sets the height of a cell
7847 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7848 * @cfg {Number} rowspan Sets the number of rows a cell should span
7849 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7850 * @cfg {String} valign Vertical aligns the content in a cell
7851 * @cfg {Number} width Specifies the width of a cell
7854 * Create a new TableCell
7855 * @param {Object} config The config object
7858 Roo.bootstrap.TableCell = function(config){
7859 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7862 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7882 getAutoCreate : function(){
7883 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7903 cfg.align=this.align
7909 cfg.bgcolor=this.bgcolor
7912 cfg.charoff=this.charoff
7915 cfg.colspan=this.colspan
7918 cfg.headers=this.headers
7921 cfg.height=this.height
7924 cfg.nowrap=this.nowrap
7927 cfg.rowspan=this.rowspan
7930 cfg.scope=this.scope
7933 cfg.valign=this.valign
7936 cfg.width=this.width
7955 * @class Roo.bootstrap.TableRow
7956 * @extends Roo.bootstrap.Component
7957 * Bootstrap TableRow class
7958 * @cfg {String} cls row class
7959 * @cfg {String} align Aligns the content in a table row
7960 * @cfg {String} bgcolor Specifies a background color for a table row
7961 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7962 * @cfg {String} valign Vertical aligns the content in a table row
7965 * Create a new TableRow
7966 * @param {Object} config The config object
7969 Roo.bootstrap.TableRow = function(config){
7970 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7973 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7981 getAutoCreate : function(){
7982 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7992 cfg.align = this.align;
7995 cfg.bgcolor = this.bgcolor;
7998 cfg.charoff = this.charoff;
8001 cfg.valign = this.valign;
8019 * @class Roo.bootstrap.TableBody
8020 * @extends Roo.bootstrap.Component
8021 * Bootstrap TableBody class
8022 * @cfg {String} cls element class
8023 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8024 * @cfg {String} align Aligns the content inside the element
8025 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8026 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8029 * Create a new TableBody
8030 * @param {Object} config The config object
8033 Roo.bootstrap.TableBody = function(config){
8034 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8037 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
8045 getAutoCreate : function(){
8046 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8060 cfg.align = this.align;
8063 cfg.charoff = this.charoff;
8066 cfg.valign = this.valign;
8073 // initEvents : function()
8080 // this.store = Roo.factory(this.store, Roo.data);
8081 // this.store.on('load', this.onLoad, this);
8083 // this.store.load();
8087 // onLoad: function ()
8089 // this.fireEvent('load', this);
8099 * Ext JS Library 1.1.1
8100 * Copyright(c) 2006-2007, Ext JS, LLC.
8102 * Originally Released Under LGPL - original licence link has changed is not relivant.
8105 * <script type="text/javascript">
8108 // as we use this in bootstrap.
8109 Roo.namespace('Roo.form');
8111 * @class Roo.form.Action
8112 * Internal Class used to handle form actions
8114 * @param {Roo.form.BasicForm} el The form element or its id
8115 * @param {Object} config Configuration options
8120 // define the action interface
8121 Roo.form.Action = function(form, options){
8123 this.options = options || {};
8126 * Client Validation Failed
8129 Roo.form.Action.CLIENT_INVALID = 'client';
8131 * Server Validation Failed
8134 Roo.form.Action.SERVER_INVALID = 'server';
8136 * Connect to Server Failed
8139 Roo.form.Action.CONNECT_FAILURE = 'connect';
8141 * Reading Data from Server Failed
8144 Roo.form.Action.LOAD_FAILURE = 'load';
8146 Roo.form.Action.prototype = {
8148 failureType : undefined,
8149 response : undefined,
8153 run : function(options){
8158 success : function(response){
8163 handleResponse : function(response){
8167 // default connection failure
8168 failure : function(response){
8170 this.response = response;
8171 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8172 this.form.afterAction(this, false);
8175 processResponse : function(response){
8176 this.response = response;
8177 if(!response.responseText){
8180 this.result = this.handleResponse(response);
8184 // utility functions used internally
8185 getUrl : function(appendParams){
8186 var url = this.options.url || this.form.url || this.form.el.dom.action;
8188 var p = this.getParams();
8190 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
8196 getMethod : function(){
8197 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
8200 getParams : function(){
8201 var bp = this.form.baseParams;
8202 var p = this.options.params;
8204 if(typeof p == "object"){
8205 p = Roo.urlEncode(Roo.applyIf(p, bp));
8206 }else if(typeof p == 'string' && bp){
8207 p += '&' + Roo.urlEncode(bp);
8210 p = Roo.urlEncode(bp);
8215 createCallback : function(){
8217 success: this.success,
8218 failure: this.failure,
8220 timeout: (this.form.timeout*1000),
8221 upload: this.form.fileUpload ? this.success : undefined
8226 Roo.form.Action.Submit = function(form, options){
8227 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
8230 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
8233 haveProgress : false,
8234 uploadComplete : false,
8236 // uploadProgress indicator.
8237 uploadProgress : function()
8239 if (!this.form.progressUrl) {
8243 if (!this.haveProgress) {
8244 Roo.MessageBox.progress("Uploading", "Uploading");
8246 if (this.uploadComplete) {
8247 Roo.MessageBox.hide();
8251 this.haveProgress = true;
8253 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
8255 var c = new Roo.data.Connection();
8257 url : this.form.progressUrl,
8262 success : function(req){
8263 //console.log(data);
8267 rdata = Roo.decode(req.responseText)
8269 Roo.log("Invalid data from server..");
8273 if (!rdata || !rdata.success) {
8275 Roo.MessageBox.alert(Roo.encode(rdata));
8278 var data = rdata.data;
8280 if (this.uploadComplete) {
8281 Roo.MessageBox.hide();
8286 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
8287 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
8290 this.uploadProgress.defer(2000,this);
8293 failure: function(data) {
8294 Roo.log('progress url failed ');
8305 // run get Values on the form, so it syncs any secondary forms.
8306 this.form.getValues();
8308 var o = this.options;
8309 var method = this.getMethod();
8310 var isPost = method == 'POST';
8311 if(o.clientValidation === false || this.form.isValid()){
8313 if (this.form.progressUrl) {
8314 this.form.findField('UPLOAD_IDENTIFIER').setValue(
8315 (new Date() * 1) + '' + Math.random());
8320 Roo.Ajax.request(Roo.apply(this.createCallback(), {
8321 form:this.form.el.dom,
8322 url:this.getUrl(!isPost),
8324 params:isPost ? this.getParams() : null,
8325 isUpload: this.form.fileUpload,
8326 formData : this.form.formData
8329 this.uploadProgress();
8331 }else if (o.clientValidation !== false){ // client validation failed
8332 this.failureType = Roo.form.Action.CLIENT_INVALID;
8333 this.form.afterAction(this, false);
8337 success : function(response)
8339 this.uploadComplete= true;
8340 if (this.haveProgress) {
8341 Roo.MessageBox.hide();
8345 var result = this.processResponse(response);
8346 if(result === true || result.success){
8347 this.form.afterAction(this, true);
8351 this.form.markInvalid(result.errors);
8352 this.failureType = Roo.form.Action.SERVER_INVALID;
8354 this.form.afterAction(this, false);
8356 failure : function(response)
8358 this.uploadComplete= true;
8359 if (this.haveProgress) {
8360 Roo.MessageBox.hide();
8363 this.response = response;
8364 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8365 this.form.afterAction(this, false);
8368 handleResponse : function(response){
8369 if(this.form.errorReader){
8370 var rs = this.form.errorReader.read(response);
8373 for(var i = 0, len = rs.records.length; i < len; i++) {
8374 var r = rs.records[i];
8378 if(errors.length < 1){
8382 success : rs.success,
8388 ret = Roo.decode(response.responseText);
8392 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8402 Roo.form.Action.Load = function(form, options){
8403 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8404 this.reader = this.form.reader;
8407 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8412 Roo.Ajax.request(Roo.apply(
8413 this.createCallback(), {
8414 method:this.getMethod(),
8415 url:this.getUrl(false),
8416 params:this.getParams()
8420 success : function(response){
8422 var result = this.processResponse(response);
8423 if(result === true || !result.success || !result.data){
8424 this.failureType = Roo.form.Action.LOAD_FAILURE;
8425 this.form.afterAction(this, false);
8428 this.form.clearInvalid();
8429 this.form.setValues(result.data);
8430 this.form.afterAction(this, true);
8433 handleResponse : function(response){
8434 if(this.form.reader){
8435 var rs = this.form.reader.read(response);
8436 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8438 success : rs.success,
8442 return Roo.decode(response.responseText);
8446 Roo.form.Action.ACTION_TYPES = {
8447 'load' : Roo.form.Action.Load,
8448 'submit' : Roo.form.Action.Submit
8457 * @class Roo.bootstrap.Form
8458 * @extends Roo.bootstrap.Component
8459 * Bootstrap Form class
8460 * @cfg {String} method GET | POST (default POST)
8461 * @cfg {String} labelAlign top | left (default top)
8462 * @cfg {String} align left | right - for navbars
8463 * @cfg {Boolean} loadMask load mask when submit (default true)
8468 * @param {Object} config The config object
8472 Roo.bootstrap.Form = function(config){
8474 Roo.bootstrap.Form.superclass.constructor.call(this, config);
8476 Roo.bootstrap.Form.popover.apply();
8480 * @event clientvalidation
8481 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8482 * @param {Form} this
8483 * @param {Boolean} valid true if the form has passed client-side validation
8485 clientvalidation: true,
8487 * @event beforeaction
8488 * Fires before any action is performed. Return false to cancel the action.
8489 * @param {Form} this
8490 * @param {Action} action The action to be performed
8494 * @event actionfailed
8495 * Fires when an action fails.
8496 * @param {Form} this
8497 * @param {Action} action The action that failed
8499 actionfailed : true,
8501 * @event actioncomplete
8502 * Fires when an action is completed.
8503 * @param {Form} this
8504 * @param {Action} action The action that completed
8506 actioncomplete : true
8510 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
8513 * @cfg {String} method
8514 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8519 * The URL to use for form actions if one isn't supplied in the action options.
8522 * @cfg {Boolean} fileUpload
8523 * Set to true if this form is a file upload.
8527 * @cfg {Object} baseParams
8528 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8532 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8536 * @cfg {Sting} align (left|right) for navbar forms
8541 activeAction : null,
8544 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8545 * element by passing it or its id or mask the form itself by passing in true.
8548 waitMsgTarget : false,
8553 * @cfg {Boolean} errorMask (true|false) default false
8558 * @cfg {Number} maskOffset Default 100
8563 * @cfg {Boolean} maskBody
8567 getAutoCreate : function(){
8571 method : this.method || 'POST',
8572 id : this.id || Roo.id(),
8575 if (this.parent().xtype.match(/^Nav/)) {
8576 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8580 if (this.labelAlign == 'left' ) {
8581 cfg.cls += ' form-horizontal';
8587 initEvents : function()
8589 this.el.on('submit', this.onSubmit, this);
8590 // this was added as random key presses on the form where triggering form submit.
8591 this.el.on('keypress', function(e) {
8592 if (e.getCharCode() != 13) {
8595 // we might need to allow it for textareas.. and some other items.
8596 // check e.getTarget().
8598 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8602 Roo.log("keypress blocked");
8610 onSubmit : function(e){
8615 * Returns true if client-side validation on the form is successful.
8618 isValid : function(){
8619 var items = this.getItems();
8623 items.each(function(f){
8629 Roo.log('invalid field: ' + f.name);
8633 if(!target && f.el.isVisible(true)){
8639 if(this.errorMask && !valid){
8640 Roo.bootstrap.Form.popover.mask(this, target);
8647 * Returns true if any fields in this form have changed since their original load.
8650 isDirty : function(){
8652 var items = this.getItems();
8653 items.each(function(f){
8663 * Performs a predefined action (submit or load) or custom actions you define on this form.
8664 * @param {String} actionName The name of the action type
8665 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8666 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8667 * accept other config options):
8669 Property Type Description
8670 ---------------- --------------- ----------------------------------------------------------------------------------
8671 url String The url for the action (defaults to the form's url)
8672 method String The form method to use (defaults to the form's method, or POST if not defined)
8673 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8674 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8675 validate the form on the client (defaults to false)
8677 * @return {BasicForm} this
8679 doAction : function(action, options){
8680 if(typeof action == 'string'){
8681 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8683 if(this.fireEvent('beforeaction', this, action) !== false){
8684 this.beforeAction(action);
8685 action.run.defer(100, action);
8691 beforeAction : function(action){
8692 var o = action.options;
8697 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8699 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8702 // not really supported yet.. ??
8704 //if(this.waitMsgTarget === true){
8705 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8706 //}else if(this.waitMsgTarget){
8707 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8708 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8710 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8716 afterAction : function(action, success){
8717 this.activeAction = null;
8718 var o = action.options;
8723 Roo.get(document.body).unmask();
8729 //if(this.waitMsgTarget === true){
8730 // this.el.unmask();
8731 //}else if(this.waitMsgTarget){
8732 // this.waitMsgTarget.unmask();
8734 // Roo.MessageBox.updateProgress(1);
8735 // Roo.MessageBox.hide();
8742 Roo.callback(o.success, o.scope, [this, action]);
8743 this.fireEvent('actioncomplete', this, action);
8747 // failure condition..
8748 // we have a scenario where updates need confirming.
8749 // eg. if a locking scenario exists..
8750 // we look for { errors : { needs_confirm : true }} in the response.
8752 (typeof(action.result) != 'undefined') &&
8753 (typeof(action.result.errors) != 'undefined') &&
8754 (typeof(action.result.errors.needs_confirm) != 'undefined')
8757 Roo.log("not supported yet");
8760 Roo.MessageBox.confirm(
8761 "Change requires confirmation",
8762 action.result.errorMsg,
8767 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8777 Roo.callback(o.failure, o.scope, [this, action]);
8778 // show an error message if no failed handler is set..
8779 if (!this.hasListener('actionfailed')) {
8780 Roo.log("need to add dialog support");
8782 Roo.MessageBox.alert("Error",
8783 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8784 action.result.errorMsg :
8785 "Saving Failed, please check your entries or try again"
8790 this.fireEvent('actionfailed', this, action);
8795 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8796 * @param {String} id The value to search for
8799 findField : function(id){
8800 var items = this.getItems();
8801 var field = items.get(id);
8803 items.each(function(f){
8804 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8811 return field || null;
8814 * Mark fields in this form invalid in bulk.
8815 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8816 * @return {BasicForm} this
8818 markInvalid : function(errors){
8819 if(errors instanceof Array){
8820 for(var i = 0, len = errors.length; i < len; i++){
8821 var fieldError = errors[i];
8822 var f = this.findField(fieldError.id);
8824 f.markInvalid(fieldError.msg);
8830 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8831 field.markInvalid(errors[id]);
8835 //Roo.each(this.childForms || [], function (f) {
8836 // f.markInvalid(errors);
8843 * Set values for fields in this form in bulk.
8844 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8845 * @return {BasicForm} this
8847 setValues : function(values){
8848 if(values instanceof Array){ // array of objects
8849 for(var i = 0, len = values.length; i < len; i++){
8851 var f = this.findField(v.id);
8853 f.setValue(v.value);
8854 if(this.trackResetOnLoad){
8855 f.originalValue = f.getValue();
8859 }else{ // object hash
8862 if(typeof values[id] != 'function' && (field = this.findField(id))){
8864 if (field.setFromData &&
8866 field.displayField &&
8867 // combos' with local stores can
8868 // be queried via setValue()
8869 // to set their value..
8870 (field.store && !field.store.isLocal)
8874 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8875 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8876 field.setFromData(sd);
8878 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8880 field.setFromData(values);
8883 field.setValue(values[id]);
8887 if(this.trackResetOnLoad){
8888 field.originalValue = field.getValue();
8894 //Roo.each(this.childForms || [], function (f) {
8895 // f.setValues(values);
8902 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8903 * they are returned as an array.
8904 * @param {Boolean} asString
8907 getValues : function(asString){
8908 //if (this.childForms) {
8909 // copy values from the child forms
8910 // Roo.each(this.childForms, function (f) {
8911 // this.setValues(f.getValues());
8917 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8918 if(asString === true){
8921 return Roo.urlDecode(fs);
8925 * Returns the fields in this form as an object with key/value pairs.
8926 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8929 getFieldValues : function(with_hidden)
8931 var items = this.getItems();
8933 items.each(function(f){
8939 var v = f.getValue();
8941 if (f.inputType =='radio') {
8942 if (typeof(ret[f.getName()]) == 'undefined') {
8943 ret[f.getName()] = ''; // empty..
8946 if (!f.el.dom.checked) {
8954 if(f.xtype == 'MoneyField'){
8955 ret[f.currencyName] = f.getCurrency();
8958 // not sure if this supported any more..
8959 if ((typeof(v) == 'object') && f.getRawValue) {
8960 v = f.getRawValue() ; // dates..
8962 // combo boxes where name != hiddenName...
8963 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8964 ret[f.name] = f.getRawValue();
8966 ret[f.getName()] = v;
8973 * Clears all invalid messages in this form.
8974 * @return {BasicForm} this
8976 clearInvalid : function(){
8977 var items = this.getItems();
8979 items.each(function(f){
8988 * @return {BasicForm} this
8991 var items = this.getItems();
8992 items.each(function(f){
8996 Roo.each(this.childForms || [], function (f) {
9004 getItems : function()
9006 var r=new Roo.util.MixedCollection(false, function(o){
9007 return o.id || (o.id = Roo.id());
9009 var iter = function(el) {
9016 Roo.each(el.items,function(e) {
9025 hideFields : function(items)
9027 Roo.each(items, function(i){
9029 var f = this.findField(i);
9040 showFields : function(items)
9042 Roo.each(items, function(i){
9044 var f = this.findField(i);
9057 Roo.apply(Roo.bootstrap.Form, {
9084 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9085 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9086 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9087 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9090 this.maskEl.top.enableDisplayMode("block");
9091 this.maskEl.left.enableDisplayMode("block");
9092 this.maskEl.bottom.enableDisplayMode("block");
9093 this.maskEl.right.enableDisplayMode("block");
9095 this.toolTip = new Roo.bootstrap.Tooltip({
9096 cls : 'roo-form-error-popover',
9098 'left' : ['r-l', [-2,0], 'right'],
9099 'right' : ['l-r', [2,0], 'left'],
9100 'bottom' : ['tl-bl', [0,2], 'top'],
9101 'top' : [ 'bl-tl', [0,-2], 'bottom']
9105 this.toolTip.render(Roo.get(document.body));
9107 this.toolTip.el.enableDisplayMode("block");
9109 Roo.get(document.body).on('click', function(){
9113 Roo.get(document.body).on('touchstart', function(){
9117 this.isApplied = true
9120 mask : function(form, target)
9124 this.target = target;
9126 if(!this.form.errorMask || !target.el){
9130 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9132 Roo.log(scrollable);
9134 var ot = this.target.el.calcOffsetsTo(scrollable);
9136 var scrollTo = ot[1] - this.form.maskOffset;
9138 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9140 scrollable.scrollTo('top', scrollTo);
9142 var box = this.target.el.getBox();
9144 var zIndex = Roo.bootstrap.Modal.zIndex++;
9147 this.maskEl.top.setStyle('position', 'absolute');
9148 this.maskEl.top.setStyle('z-index', zIndex);
9149 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9150 this.maskEl.top.setLeft(0);
9151 this.maskEl.top.setTop(0);
9152 this.maskEl.top.show();
9154 this.maskEl.left.setStyle('position', 'absolute');
9155 this.maskEl.left.setStyle('z-index', zIndex);
9156 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9157 this.maskEl.left.setLeft(0);
9158 this.maskEl.left.setTop(box.y - this.padding);
9159 this.maskEl.left.show();
9161 this.maskEl.bottom.setStyle('position', 'absolute');
9162 this.maskEl.bottom.setStyle('z-index', zIndex);
9163 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9164 this.maskEl.bottom.setLeft(0);
9165 this.maskEl.bottom.setTop(box.bottom + this.padding);
9166 this.maskEl.bottom.show();
9168 this.maskEl.right.setStyle('position', 'absolute');
9169 this.maskEl.right.setStyle('z-index', zIndex);
9170 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9171 this.maskEl.right.setLeft(box.right + this.padding);
9172 this.maskEl.right.setTop(box.y - this.padding);
9173 this.maskEl.right.show();
9175 this.toolTip.bindEl = this.target.el;
9177 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9179 var tip = this.target.blankText;
9181 if(this.target.getValue() !== '' ) {
9183 if (this.target.invalidText.length) {
9184 tip = this.target.invalidText;
9185 } else if (this.target.regexText.length){
9186 tip = this.target.regexText;
9190 this.toolTip.show(tip);
9192 this.intervalID = window.setInterval(function() {
9193 Roo.bootstrap.Form.popover.unmask();
9196 window.onwheel = function(){ return false;};
9198 (function(){ this.isMasked = true; }).defer(500, this);
9204 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
9208 this.maskEl.top.setStyle('position', 'absolute');
9209 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
9210 this.maskEl.top.hide();
9212 this.maskEl.left.setStyle('position', 'absolute');
9213 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
9214 this.maskEl.left.hide();
9216 this.maskEl.bottom.setStyle('position', 'absolute');
9217 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
9218 this.maskEl.bottom.hide();
9220 this.maskEl.right.setStyle('position', 'absolute');
9221 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
9222 this.maskEl.right.hide();
9224 this.toolTip.hide();
9226 this.toolTip.el.hide();
9228 window.onwheel = function(){ return true;};
9230 if(this.intervalID){
9231 window.clearInterval(this.intervalID);
9232 this.intervalID = false;
9235 this.isMasked = false;
9245 * Ext JS Library 1.1.1
9246 * Copyright(c) 2006-2007, Ext JS, LLC.
9248 * Originally Released Under LGPL - original licence link has changed is not relivant.
9251 * <script type="text/javascript">
9254 * @class Roo.form.VTypes
9255 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
9258 Roo.form.VTypes = function(){
9259 // closure these in so they are only created once.
9260 var alpha = /^[a-zA-Z_]+$/;
9261 var alphanum = /^[a-zA-Z0-9_]+$/;
9262 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
9263 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
9265 // All these messages and functions are configurable
9268 * The function used to validate email addresses
9269 * @param {String} value The email address
9271 'email' : function(v){
9272 return email.test(v);
9275 * The error text to display when the email validation function returns false
9278 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
9280 * The keystroke filter mask to be applied on email input
9283 'emailMask' : /[a-z0-9_\.\-@]/i,
9286 * The function used to validate URLs
9287 * @param {String} value The URL
9289 'url' : function(v){
9293 * The error text to display when the url validation function returns false
9296 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
9299 * The function used to validate alpha values
9300 * @param {String} value The value
9302 'alpha' : function(v){
9303 return alpha.test(v);
9306 * The error text to display when the alpha validation function returns false
9309 'alphaText' : 'This field should only contain letters and _',
9311 * The keystroke filter mask to be applied on alpha input
9314 'alphaMask' : /[a-z_]/i,
9317 * The function used to validate alphanumeric values
9318 * @param {String} value The value
9320 'alphanum' : function(v){
9321 return alphanum.test(v);
9324 * The error text to display when the alphanumeric validation function returns false
9327 'alphanumText' : 'This field should only contain letters, numbers and _',
9329 * The keystroke filter mask to be applied on alphanumeric input
9332 'alphanumMask' : /[a-z0-9_]/i
9342 * @class Roo.bootstrap.Input
9343 * @extends Roo.bootstrap.Component
9344 * Bootstrap Input class
9345 * @cfg {Boolean} disabled is it disabled
9346 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9347 * @cfg {String} name name of the input
9348 * @cfg {string} fieldLabel - the label associated
9349 * @cfg {string} placeholder - placeholder to put in text.
9350 * @cfg {string} before - input group add on before
9351 * @cfg {string} after - input group add on after
9352 * @cfg {string} size - (lg|sm) or leave empty..
9353 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9354 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9355 * @cfg {Number} md colspan out of 12 for computer-sized screens
9356 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9357 * @cfg {string} value default value of the input
9358 * @cfg {Number} labelWidth set the width of label
9359 * @cfg {Number} labellg set the width of label (1-12)
9360 * @cfg {Number} labelmd set the width of label (1-12)
9361 * @cfg {Number} labelsm set the width of label (1-12)
9362 * @cfg {Number} labelxs set the width of label (1-12)
9363 * @cfg {String} labelAlign (top|left)
9364 * @cfg {Boolean} readOnly Specifies that the field should be read-only
9365 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9366 * @cfg {String} indicatorpos (left|right) default left
9367 * @cfg {String} capture (user|camera) use for file input only. (default empty)
9368 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9370 * @cfg {String} align (left|center|right) Default left
9371 * @cfg {Boolean} forceFeedback (true|false) Default false
9374 * Create a new Input
9375 * @param {Object} config The config object
9378 Roo.bootstrap.Input = function(config){
9380 Roo.bootstrap.Input.superclass.constructor.call(this, config);
9385 * Fires when this field receives input focus.
9386 * @param {Roo.form.Field} this
9391 * Fires when this field loses input focus.
9392 * @param {Roo.form.Field} this
9397 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
9398 * {@link Roo.EventObject#getKey} to determine which key was pressed.
9399 * @param {Roo.form.Field} this
9400 * @param {Roo.EventObject} e The event object
9405 * Fires just before the field blurs if the field value has changed.
9406 * @param {Roo.form.Field} this
9407 * @param {Mixed} newValue The new value
9408 * @param {Mixed} oldValue The original value
9413 * Fires after the field has been marked as invalid.
9414 * @param {Roo.form.Field} this
9415 * @param {String} msg The validation message
9420 * Fires after the field has been validated with no errors.
9421 * @param {Roo.form.Field} this
9426 * Fires after the key up
9427 * @param {Roo.form.Field} this
9428 * @param {Roo.EventObject} e The event Object
9434 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
9436 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9437 automatic validation (defaults to "keyup").
9439 validationEvent : "keyup",
9441 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9443 validateOnBlur : true,
9445 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9447 validationDelay : 250,
9449 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9451 focusClass : "x-form-focus", // not needed???
9455 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9457 invalidClass : "has-warning",
9460 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9462 validClass : "has-success",
9465 * @cfg {Boolean} hasFeedback (true|false) default true
9470 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9472 invalidFeedbackClass : "glyphicon-warning-sign",
9475 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9477 validFeedbackClass : "glyphicon-ok",
9480 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9482 selectOnFocus : false,
9485 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9489 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9494 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9496 disableKeyFilter : false,
9499 * @cfg {Boolean} disabled True to disable the field (defaults to false).
9503 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9507 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9509 blankText : "Please complete this mandatory field",
9512 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9516 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9518 maxLength : Number.MAX_VALUE,
9520 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9522 minLengthText : "The minimum length for this field is {0}",
9524 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9526 maxLengthText : "The maximum length for this field is {0}",
9530 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9531 * If available, this function will be called only after the basic validators all return true, and will be passed the
9532 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9536 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9537 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9538 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9542 * @cfg {String} regexText -- Depricated - use Invalid Text
9547 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9553 autocomplete: false,
9572 formatedValue : false,
9573 forceFeedback : false,
9575 indicatorpos : 'left',
9585 parentLabelAlign : function()
9588 while (parent.parent()) {
9589 parent = parent.parent();
9590 if (typeof(parent.labelAlign) !='undefined') {
9591 return parent.labelAlign;
9598 getAutoCreate : function()
9600 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9606 if(this.inputType != 'hidden'){
9607 cfg.cls = 'form-group' //input-group
9613 type : this.inputType,
9615 cls : 'form-control',
9616 placeholder : this.placeholder || '',
9617 autocomplete : this.autocomplete || 'new-password'
9620 if(this.capture.length){
9621 input.capture = this.capture;
9624 if(this.accept.length){
9625 input.accept = this.accept + "/*";
9629 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9632 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9633 input.maxLength = this.maxLength;
9636 if (this.disabled) {
9637 input.disabled=true;
9640 if (this.readOnly) {
9641 input.readonly=true;
9645 input.name = this.name;
9649 input.cls += ' input-' + this.size;
9653 ['xs','sm','md','lg'].map(function(size){
9654 if (settings[size]) {
9655 cfg.cls += ' col-' + size + '-' + settings[size];
9659 var inputblock = input;
9663 cls: 'glyphicon form-control-feedback'
9666 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9669 cls : 'has-feedback',
9677 if (this.before || this.after) {
9680 cls : 'input-group',
9684 if (this.before && typeof(this.before) == 'string') {
9686 inputblock.cn.push({
9688 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9692 if (this.before && typeof(this.before) == 'object') {
9693 this.before = Roo.factory(this.before);
9695 inputblock.cn.push({
9697 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9698 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9702 inputblock.cn.push(input);
9704 if (this.after && typeof(this.after) == 'string') {
9705 inputblock.cn.push({
9707 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9711 if (this.after && typeof(this.after) == 'object') {
9712 this.after = Roo.factory(this.after);
9714 inputblock.cn.push({
9716 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9717 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9721 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9722 inputblock.cls += ' has-feedback';
9723 inputblock.cn.push(feedback);
9728 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9729 tooltip : 'This field is required'
9731 if (Roo.bootstrap.version == 4) {
9734 style : 'display-none'
9737 if (align ==='left' && this.fieldLabel.length) {
9739 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
9746 cls : 'control-label col-form-label',
9747 html : this.fieldLabel
9758 var labelCfg = cfg.cn[1];
9759 var contentCfg = cfg.cn[2];
9761 if(this.indicatorpos == 'right'){
9766 cls : 'control-label col-form-label',
9770 html : this.fieldLabel
9784 labelCfg = cfg.cn[0];
9785 contentCfg = cfg.cn[1];
9789 if(this.labelWidth > 12){
9790 labelCfg.style = "width: " + this.labelWidth + 'px';
9793 if(this.labelWidth < 13 && this.labelmd == 0){
9794 this.labelmd = this.labelWidth;
9797 if(this.labellg > 0){
9798 labelCfg.cls += ' col-lg-' + this.labellg;
9799 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9802 if(this.labelmd > 0){
9803 labelCfg.cls += ' col-md-' + this.labelmd;
9804 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9807 if(this.labelsm > 0){
9808 labelCfg.cls += ' col-sm-' + this.labelsm;
9809 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9812 if(this.labelxs > 0){
9813 labelCfg.cls += ' col-xs-' + this.labelxs;
9814 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9818 } else if ( this.fieldLabel.length) {
9823 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9824 tooltip : 'This field is required'
9828 //cls : 'input-group-addon',
9829 html : this.fieldLabel
9837 if(this.indicatorpos == 'right'){
9842 //cls : 'input-group-addon',
9843 html : this.fieldLabel
9848 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9849 tooltip : 'This field is required'
9869 if (this.parentType === 'Navbar' && this.parent().bar) {
9870 cfg.cls += ' navbar-form';
9873 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9874 // on BS4 we do this only if not form
9875 cfg.cls += ' navbar-form';
9883 * return the real input element.
9885 inputEl: function ()
9887 return this.el.select('input.form-control',true).first();
9890 tooltipEl : function()
9892 return this.inputEl();
9895 indicatorEl : function()
9897 if (Roo.bootstrap.version == 4) {
9898 return false; // not enabled in v4 yet.
9901 var indicator = this.el.select('i.roo-required-indicator',true).first();
9911 setDisabled : function(v)
9913 var i = this.inputEl().dom;
9915 i.removeAttribute('disabled');
9919 i.setAttribute('disabled','true');
9921 initEvents : function()
9924 this.inputEl().on("keydown" , this.fireKey, this);
9925 this.inputEl().on("focus", this.onFocus, this);
9926 this.inputEl().on("blur", this.onBlur, this);
9928 this.inputEl().relayEvent('keyup', this);
9930 this.indicator = this.indicatorEl();
9933 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9936 // reference to original value for reset
9937 this.originalValue = this.getValue();
9938 //Roo.form.TextField.superclass.initEvents.call(this);
9939 if(this.validationEvent == 'keyup'){
9940 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9941 this.inputEl().on('keyup', this.filterValidation, this);
9943 else if(this.validationEvent !== false){
9944 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9947 if(this.selectOnFocus){
9948 this.on("focus", this.preFocus, this);
9951 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9952 this.inputEl().on("keypress", this.filterKeys, this);
9954 this.inputEl().relayEvent('keypress', this);
9957 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9958 this.el.on("click", this.autoSize, this);
9961 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9962 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9965 if (typeof(this.before) == 'object') {
9966 this.before.render(this.el.select('.roo-input-before',true).first());
9968 if (typeof(this.after) == 'object') {
9969 this.after.render(this.el.select('.roo-input-after',true).first());
9972 this.inputEl().on('change', this.onChange, this);
9975 filterValidation : function(e){
9976 if(!e.isNavKeyPress()){
9977 this.validationTask.delay(this.validationDelay);
9981 * Validates the field value
9982 * @return {Boolean} True if the value is valid, else false
9984 validate : function(){
9985 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9986 if(this.disabled || this.validateValue(this.getRawValue())){
9997 * Validates a value according to the field's validation rules and marks the field as invalid
9998 * if the validation fails
9999 * @param {Mixed} value The value to validate
10000 * @return {Boolean} True if the value is valid, else false
10002 validateValue : function(value)
10004 if(this.getVisibilityEl().hasClass('hidden')){
10008 if(value.length < 1) { // if it's blank
10009 if(this.allowBlank){
10015 if(value.length < this.minLength){
10018 if(value.length > this.maxLength){
10022 var vt = Roo.form.VTypes;
10023 if(!vt[this.vtype](value, this)){
10027 if(typeof this.validator == "function"){
10028 var msg = this.validator(value);
10032 if (typeof(msg) == 'string') {
10033 this.invalidText = msg;
10037 if(this.regex && !this.regex.test(value)){
10045 fireKey : function(e){
10046 //Roo.log('field ' + e.getKey());
10047 if(e.isNavKeyPress()){
10048 this.fireEvent("specialkey", this, e);
10051 focus : function (selectText){
10053 this.inputEl().focus();
10054 if(selectText === true){
10055 this.inputEl().dom.select();
10061 onFocus : function(){
10062 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10063 // this.el.addClass(this.focusClass);
10065 if(!this.hasFocus){
10066 this.hasFocus = true;
10067 this.startValue = this.getValue();
10068 this.fireEvent("focus", this);
10072 beforeBlur : Roo.emptyFn,
10076 onBlur : function(){
10078 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10079 //this.el.removeClass(this.focusClass);
10081 this.hasFocus = false;
10082 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10085 var v = this.getValue();
10086 if(String(v) !== String(this.startValue)){
10087 this.fireEvent('change', this, v, this.startValue);
10089 this.fireEvent("blur", this);
10092 onChange : function(e)
10094 var v = this.getValue();
10095 if(String(v) !== String(this.startValue)){
10096 this.fireEvent('change', this, v, this.startValue);
10102 * Resets the current field value to the originally loaded value and clears any validation messages
10104 reset : function(){
10105 this.setValue(this.originalValue);
10109 * Returns the name of the field
10110 * @return {Mixed} name The name field
10112 getName: function(){
10116 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
10117 * @return {Mixed} value The field value
10119 getValue : function(){
10121 var v = this.inputEl().getValue();
10126 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
10127 * @return {Mixed} value The field value
10129 getRawValue : function(){
10130 var v = this.inputEl().getValue();
10136 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
10137 * @param {Mixed} value The value to set
10139 setRawValue : function(v){
10140 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10143 selectText : function(start, end){
10144 var v = this.getRawValue();
10146 start = start === undefined ? 0 : start;
10147 end = end === undefined ? v.length : end;
10148 var d = this.inputEl().dom;
10149 if(d.setSelectionRange){
10150 d.setSelectionRange(start, end);
10151 }else if(d.createTextRange){
10152 var range = d.createTextRange();
10153 range.moveStart("character", start);
10154 range.moveEnd("character", v.length-end);
10161 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
10162 * @param {Mixed} value The value to set
10164 setValue : function(v){
10167 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10173 processValue : function(value){
10174 if(this.stripCharsRe){
10175 var newValue = value.replace(this.stripCharsRe, '');
10176 if(newValue !== value){
10177 this.setRawValue(newValue);
10184 preFocus : function(){
10186 if(this.selectOnFocus){
10187 this.inputEl().dom.select();
10190 filterKeys : function(e){
10191 var k = e.getKey();
10192 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
10195 var c = e.getCharCode(), cc = String.fromCharCode(c);
10196 if(Roo.isIE && (e.isSpecialKey() || !cc)){
10199 if(!this.maskRe.test(cc)){
10204 * Clear any invalid styles/messages for this field
10206 clearInvalid : function(){
10208 if(!this.el || this.preventMark){ // not rendered
10213 this.el.removeClass([this.invalidClass, 'is-invalid']);
10215 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10217 var feedback = this.el.select('.form-control-feedback', true).first();
10220 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10225 if(this.indicator){
10226 this.indicator.removeClass('visible');
10227 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10230 this.fireEvent('valid', this);
10234 * Mark this field as valid
10236 markValid : function()
10238 if(!this.el || this.preventMark){ // not rendered...
10242 this.el.removeClass([this.invalidClass, this.validClass]);
10243 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10245 var feedback = this.el.select('.form-control-feedback', true).first();
10248 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10251 if(this.indicator){
10252 this.indicator.removeClass('visible');
10253 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10260 if(this.allowBlank && !this.getRawValue().length){
10263 if (Roo.bootstrap.version == 3) {
10264 this.el.addClass(this.validClass);
10266 this.inputEl().addClass('is-valid');
10269 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10271 var feedback = this.el.select('.form-control-feedback', true).first();
10274 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10275 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10280 this.fireEvent('valid', this);
10284 * Mark this field as invalid
10285 * @param {String} msg The validation message
10287 markInvalid : function(msg)
10289 if(!this.el || this.preventMark){ // not rendered
10293 this.el.removeClass([this.invalidClass, this.validClass]);
10294 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10296 var feedback = this.el.select('.form-control-feedback', true).first();
10299 this.el.select('.form-control-feedback', true).first().removeClass(
10300 [this.invalidFeedbackClass, this.validFeedbackClass]);
10307 if(this.allowBlank && !this.getRawValue().length){
10311 if(this.indicator){
10312 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10313 this.indicator.addClass('visible');
10315 if (Roo.bootstrap.version == 3) {
10316 this.el.addClass(this.invalidClass);
10318 this.inputEl().addClass('is-invalid');
10323 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10325 var feedback = this.el.select('.form-control-feedback', true).first();
10328 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10330 if(this.getValue().length || this.forceFeedback){
10331 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10338 this.fireEvent('invalid', this, msg);
10341 SafariOnKeyDown : function(event)
10343 // this is a workaround for a password hang bug on chrome/ webkit.
10344 if (this.inputEl().dom.type != 'password') {
10348 var isSelectAll = false;
10350 if(this.inputEl().dom.selectionEnd > 0){
10351 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10353 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10354 event.preventDefault();
10359 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10361 event.preventDefault();
10362 // this is very hacky as keydown always get's upper case.
10364 var cc = String.fromCharCode(event.getCharCode());
10365 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
10369 adjustWidth : function(tag, w){
10370 tag = tag.toLowerCase();
10371 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10372 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10373 if(tag == 'input'){
10376 if(tag == 'textarea'){
10379 }else if(Roo.isOpera){
10380 if(tag == 'input'){
10383 if(tag == 'textarea'){
10391 setFieldLabel : function(v)
10393 if(!this.rendered){
10397 if(this.indicatorEl()){
10398 var ar = this.el.select('label > span',true);
10400 if (ar.elements.length) {
10401 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10402 this.fieldLabel = v;
10406 var br = this.el.select('label',true);
10408 if(br.elements.length) {
10409 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10410 this.fieldLabel = v;
10414 Roo.log('Cannot Found any of label > span || label in input');
10418 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10419 this.fieldLabel = v;
10434 * @class Roo.bootstrap.TextArea
10435 * @extends Roo.bootstrap.Input
10436 * Bootstrap TextArea class
10437 * @cfg {Number} cols Specifies the visible width of a text area
10438 * @cfg {Number} rows Specifies the visible number of lines in a text area
10439 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10440 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10441 * @cfg {string} html text
10444 * Create a new TextArea
10445 * @param {Object} config The config object
10448 Roo.bootstrap.TextArea = function(config){
10449 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10453 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
10463 getAutoCreate : function(){
10465 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10471 if(this.inputType != 'hidden'){
10472 cfg.cls = 'form-group' //input-group
10480 value : this.value || '',
10481 html: this.html || '',
10482 cls : 'form-control',
10483 placeholder : this.placeholder || ''
10487 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10488 input.maxLength = this.maxLength;
10492 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10496 input.cols = this.cols;
10499 if (this.readOnly) {
10500 input.readonly = true;
10504 input.name = this.name;
10508 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10512 ['xs','sm','md','lg'].map(function(size){
10513 if (settings[size]) {
10514 cfg.cls += ' col-' + size + '-' + settings[size];
10518 var inputblock = input;
10520 if(this.hasFeedback && !this.allowBlank){
10524 cls: 'glyphicon form-control-feedback'
10528 cls : 'has-feedback',
10537 if (this.before || this.after) {
10540 cls : 'input-group',
10544 inputblock.cn.push({
10546 cls : 'input-group-addon',
10551 inputblock.cn.push(input);
10553 if(this.hasFeedback && !this.allowBlank){
10554 inputblock.cls += ' has-feedback';
10555 inputblock.cn.push(feedback);
10559 inputblock.cn.push({
10561 cls : 'input-group-addon',
10568 if (align ==='left' && this.fieldLabel.length) {
10573 cls : 'control-label',
10574 html : this.fieldLabel
10585 if(this.labelWidth > 12){
10586 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10589 if(this.labelWidth < 13 && this.labelmd == 0){
10590 this.labelmd = this.labelWidth;
10593 if(this.labellg > 0){
10594 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10595 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10598 if(this.labelmd > 0){
10599 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10600 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10603 if(this.labelsm > 0){
10604 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10605 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10608 if(this.labelxs > 0){
10609 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10610 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10613 } else if ( this.fieldLabel.length) {
10618 //cls : 'input-group-addon',
10619 html : this.fieldLabel
10637 if (this.disabled) {
10638 input.disabled=true;
10645 * return the real textarea element.
10647 inputEl: function ()
10649 return this.el.select('textarea.form-control',true).first();
10653 * Clear any invalid styles/messages for this field
10655 clearInvalid : function()
10658 if(!this.el || this.preventMark){ // not rendered
10662 var label = this.el.select('label', true).first();
10663 var icon = this.el.select('i.fa-star', true).first();
10668 this.el.removeClass( this.validClass);
10669 this.inputEl().removeClass('is-invalid');
10671 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10673 var feedback = this.el.select('.form-control-feedback', true).first();
10676 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10681 this.fireEvent('valid', this);
10685 * Mark this field as valid
10687 markValid : function()
10689 if(!this.el || this.preventMark){ // not rendered
10693 this.el.removeClass([this.invalidClass, this.validClass]);
10694 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10696 var feedback = this.el.select('.form-control-feedback', true).first();
10699 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10702 if(this.disabled || this.allowBlank){
10706 var label = this.el.select('label', true).first();
10707 var icon = this.el.select('i.fa-star', true).first();
10712 if (Roo.bootstrap.version == 3) {
10713 this.el.addClass(this.validClass);
10715 this.inputEl().addClass('is-valid');
10719 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10721 var feedback = this.el.select('.form-control-feedback', true).first();
10724 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10725 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10730 this.fireEvent('valid', this);
10734 * Mark this field as invalid
10735 * @param {String} msg The validation message
10737 markInvalid : function(msg)
10739 if(!this.el || this.preventMark){ // not rendered
10743 this.el.removeClass([this.invalidClass, this.validClass]);
10744 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10746 var feedback = this.el.select('.form-control-feedback', true).first();
10749 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10752 if(this.disabled || this.allowBlank){
10756 var label = this.el.select('label', true).first();
10757 var icon = this.el.select('i.fa-star', true).first();
10759 if(!this.getValue().length && label && !icon){
10760 this.el.createChild({
10762 cls : 'text-danger fa fa-lg fa-star',
10763 tooltip : 'This field is required',
10764 style : 'margin-right:5px;'
10768 if (Roo.bootstrap.version == 3) {
10769 this.el.addClass(this.invalidClass);
10771 this.inputEl().addClass('is-invalid');
10774 // fixme ... this may be depricated need to test..
10775 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10777 var feedback = this.el.select('.form-control-feedback', true).first();
10780 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10782 if(this.getValue().length || this.forceFeedback){
10783 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10790 this.fireEvent('invalid', this, msg);
10798 * trigger field - base class for combo..
10803 * @class Roo.bootstrap.TriggerField
10804 * @extends Roo.bootstrap.Input
10805 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10806 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10807 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10808 * for which you can provide a custom implementation. For example:
10810 var trigger = new Roo.bootstrap.TriggerField();
10811 trigger.onTriggerClick = myTriggerFn;
10812 trigger.applyTo('my-field');
10815 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10816 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10817 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10818 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10819 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10822 * Create a new TriggerField.
10823 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10824 * to the base TextField)
10826 Roo.bootstrap.TriggerField = function(config){
10827 this.mimicing = false;
10828 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10831 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10833 * @cfg {String} triggerClass A CSS class to apply to the trigger
10836 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10841 * @cfg {Boolean} removable (true|false) special filter default false
10845 /** @cfg {Boolean} grow @hide */
10846 /** @cfg {Number} growMin @hide */
10847 /** @cfg {Number} growMax @hide */
10853 autoSize: Roo.emptyFn,
10857 deferHeight : true,
10860 actionMode : 'wrap',
10865 getAutoCreate : function(){
10867 var align = this.labelAlign || this.parentLabelAlign();
10872 cls: 'form-group' //input-group
10879 type : this.inputType,
10880 cls : 'form-control',
10881 autocomplete: 'new-password',
10882 placeholder : this.placeholder || ''
10886 input.name = this.name;
10889 input.cls += ' input-' + this.size;
10892 if (this.disabled) {
10893 input.disabled=true;
10896 var inputblock = input;
10898 if(this.hasFeedback && !this.allowBlank){
10902 cls: 'glyphicon form-control-feedback'
10905 if(this.removable && !this.editable && !this.tickable){
10907 cls : 'has-feedback',
10913 cls : 'roo-combo-removable-btn close'
10920 cls : 'has-feedback',
10929 if(this.removable && !this.editable && !this.tickable){
10931 cls : 'roo-removable',
10937 cls : 'roo-combo-removable-btn close'
10944 if (this.before || this.after) {
10947 cls : 'input-group',
10951 inputblock.cn.push({
10953 cls : 'input-group-addon input-group-prepend input-group-text',
10958 inputblock.cn.push(input);
10960 if(this.hasFeedback && !this.allowBlank){
10961 inputblock.cls += ' has-feedback';
10962 inputblock.cn.push(feedback);
10966 inputblock.cn.push({
10968 cls : 'input-group-addon input-group-append input-group-text',
10977 var ibwrap = inputblock;
10982 cls: 'roo-select2-choices',
10986 cls: 'roo-select2-search-field',
10998 cls: 'roo-select2-container input-group',
11003 cls: 'form-hidden-field'
11009 if(!this.multiple && this.showToggleBtn){
11015 if (this.caret != false) {
11018 cls: 'fa fa-' + this.caret
11025 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11027 Roo.bootstrap.version == 3 ? caret : '',
11030 cls: 'combobox-clear',
11044 combobox.cls += ' roo-select2-container-multi';
11048 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11049 tooltip : 'This field is required'
11051 if (Roo.bootstrap.version == 4) {
11054 style : 'display:none'
11059 if (align ==='left' && this.fieldLabel.length) {
11061 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11068 cls : 'control-label',
11069 html : this.fieldLabel
11081 var labelCfg = cfg.cn[1];
11082 var contentCfg = cfg.cn[2];
11084 if(this.indicatorpos == 'right'){
11089 cls : 'control-label',
11093 html : this.fieldLabel
11107 labelCfg = cfg.cn[0];
11108 contentCfg = cfg.cn[1];
11111 if(this.labelWidth > 12){
11112 labelCfg.style = "width: " + this.labelWidth + 'px';
11115 if(this.labelWidth < 13 && this.labelmd == 0){
11116 this.labelmd = this.labelWidth;
11119 if(this.labellg > 0){
11120 labelCfg.cls += ' col-lg-' + this.labellg;
11121 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11124 if(this.labelmd > 0){
11125 labelCfg.cls += ' col-md-' + this.labelmd;
11126 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11129 if(this.labelsm > 0){
11130 labelCfg.cls += ' col-sm-' + this.labelsm;
11131 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11134 if(this.labelxs > 0){
11135 labelCfg.cls += ' col-xs-' + this.labelxs;
11136 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11139 } else if ( this.fieldLabel.length) {
11140 // Roo.log(" label");
11145 //cls : 'input-group-addon',
11146 html : this.fieldLabel
11154 if(this.indicatorpos == 'right'){
11162 html : this.fieldLabel
11176 // Roo.log(" no label && no align");
11183 ['xs','sm','md','lg'].map(function(size){
11184 if (settings[size]) {
11185 cfg.cls += ' col-' + size + '-' + settings[size];
11196 onResize : function(w, h){
11197 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
11198 // if(typeof w == 'number'){
11199 // var x = w - this.trigger.getWidth();
11200 // this.inputEl().setWidth(this.adjustWidth('input', x));
11201 // this.trigger.setStyle('left', x+'px');
11206 adjustSize : Roo.BoxComponent.prototype.adjustSize,
11209 getResizeEl : function(){
11210 return this.inputEl();
11214 getPositionEl : function(){
11215 return this.inputEl();
11219 alignErrorIcon : function(){
11220 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
11224 initEvents : function(){
11228 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
11229 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
11230 if(!this.multiple && this.showToggleBtn){
11231 this.trigger = this.el.select('span.dropdown-toggle',true).first();
11232 if(this.hideTrigger){
11233 this.trigger.setDisplayed(false);
11235 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
11239 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
11242 if(this.removable && !this.editable && !this.tickable){
11243 var close = this.closeTriggerEl();
11246 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
11247 close.on('click', this.removeBtnClick, this, close);
11251 //this.trigger.addClassOnOver('x-form-trigger-over');
11252 //this.trigger.addClassOnClick('x-form-trigger-click');
11255 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
11259 closeTriggerEl : function()
11261 var close = this.el.select('.roo-combo-removable-btn', true).first();
11262 return close ? close : false;
11265 removeBtnClick : function(e, h, el)
11267 e.preventDefault();
11269 if(this.fireEvent("remove", this) !== false){
11271 this.fireEvent("afterremove", this)
11275 createList : function()
11277 this.list = Roo.get(document.body).createChild({
11278 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
11279 cls: 'typeahead typeahead-long dropdown-menu',
11280 style: 'display:none'
11283 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
11288 initTrigger : function(){
11293 onDestroy : function(){
11295 this.trigger.removeAllListeners();
11296 // this.trigger.remove();
11299 // this.wrap.remove();
11301 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
11305 onFocus : function(){
11306 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
11308 if(!this.mimicing){
11309 this.wrap.addClass('x-trigger-wrap-focus');
11310 this.mimicing = true;
11311 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
11312 if(this.monitorTab){
11313 this.el.on("keydown", this.checkTab, this);
11320 checkTab : function(e){
11321 if(e.getKey() == e.TAB){
11322 this.triggerBlur();
11327 onBlur : function(){
11332 mimicBlur : function(e, t){
11334 if(!this.wrap.contains(t) && this.validateBlur()){
11335 this.triggerBlur();
11341 triggerBlur : function(){
11342 this.mimicing = false;
11343 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
11344 if(this.monitorTab){
11345 this.el.un("keydown", this.checkTab, this);
11347 //this.wrap.removeClass('x-trigger-wrap-focus');
11348 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11352 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11353 validateBlur : function(e, t){
11358 onDisable : function(){
11359 this.inputEl().dom.disabled = true;
11360 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11362 // this.wrap.addClass('x-item-disabled');
11367 onEnable : function(){
11368 this.inputEl().dom.disabled = false;
11369 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11371 // this.el.removeClass('x-item-disabled');
11376 onShow : function(){
11377 var ae = this.getActionEl();
11380 ae.dom.style.display = '';
11381 ae.dom.style.visibility = 'visible';
11387 onHide : function(){
11388 var ae = this.getActionEl();
11389 ae.dom.style.display = 'none';
11393 * The function that should handle the trigger's click event. This method does nothing by default until overridden
11394 * by an implementing function.
11396 * @param {EventObject} e
11398 onTriggerClick : Roo.emptyFn
11402 * Ext JS Library 1.1.1
11403 * Copyright(c) 2006-2007, Ext JS, LLC.
11405 * Originally Released Under LGPL - original licence link has changed is not relivant.
11408 * <script type="text/javascript">
11413 * @class Roo.data.SortTypes
11415 * Defines the default sorting (casting?) comparison functions used when sorting data.
11417 Roo.data.SortTypes = {
11419 * Default sort that does nothing
11420 * @param {Mixed} s The value being converted
11421 * @return {Mixed} The comparison value
11423 none : function(s){
11428 * The regular expression used to strip tags
11432 stripTagsRE : /<\/?[^>]+>/gi,
11435 * Strips all HTML tags to sort on text only
11436 * @param {Mixed} s The value being converted
11437 * @return {String} The comparison value
11439 asText : function(s){
11440 return String(s).replace(this.stripTagsRE, "");
11444 * Strips all HTML tags to sort on text only - Case insensitive
11445 * @param {Mixed} s The value being converted
11446 * @return {String} The comparison value
11448 asUCText : function(s){
11449 return String(s).toUpperCase().replace(this.stripTagsRE, "");
11453 * Case insensitive string
11454 * @param {Mixed} s The value being converted
11455 * @return {String} The comparison value
11457 asUCString : function(s) {
11458 return String(s).toUpperCase();
11463 * @param {Mixed} s The value being converted
11464 * @return {Number} The comparison value
11466 asDate : function(s) {
11470 if(s instanceof Date){
11471 return s.getTime();
11473 return Date.parse(String(s));
11478 * @param {Mixed} s The value being converted
11479 * @return {Float} The comparison value
11481 asFloat : function(s) {
11482 var val = parseFloat(String(s).replace(/,/g, ""));
11491 * @param {Mixed} s The value being converted
11492 * @return {Number} The comparison value
11494 asInt : function(s) {
11495 var val = parseInt(String(s).replace(/,/g, ""));
11503 * Ext JS Library 1.1.1
11504 * Copyright(c) 2006-2007, Ext JS, LLC.
11506 * Originally Released Under LGPL - original licence link has changed is not relivant.
11509 * <script type="text/javascript">
11513 * @class Roo.data.Record
11514 * Instances of this class encapsulate both record <em>definition</em> information, and record
11515 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11516 * to access Records cached in an {@link Roo.data.Store} object.<br>
11518 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11519 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11522 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11524 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11525 * {@link #create}. The parameters are the same.
11526 * @param {Array} data An associative Array of data values keyed by the field name.
11527 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11528 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11529 * not specified an integer id is generated.
11531 Roo.data.Record = function(data, id){
11532 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11537 * Generate a constructor for a specific record layout.
11538 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11539 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11540 * Each field definition object may contain the following properties: <ul>
11541 * <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,
11542 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11543 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11544 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11545 * is being used, then this is a string containing the javascript expression to reference the data relative to
11546 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11547 * to the data item relative to the record element. If the mapping expression is the same as the field name,
11548 * this may be omitted.</p></li>
11549 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11550 * <ul><li>auto (Default, implies no conversion)</li>
11555 * <li>date</li></ul></p></li>
11556 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11557 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11558 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11559 * by the Reader into an object that will be stored in the Record. It is passed the
11560 * following parameters:<ul>
11561 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11563 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11565 * <br>usage:<br><pre><code>
11566 var TopicRecord = Roo.data.Record.create(
11567 {name: 'title', mapping: 'topic_title'},
11568 {name: 'author', mapping: 'username'},
11569 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11570 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11571 {name: 'lastPoster', mapping: 'user2'},
11572 {name: 'excerpt', mapping: 'post_text'}
11575 var myNewRecord = new TopicRecord({
11576 title: 'Do my job please',
11579 lastPost: new Date(),
11580 lastPoster: 'Animal',
11581 excerpt: 'No way dude!'
11583 myStore.add(myNewRecord);
11588 Roo.data.Record.create = function(o){
11589 var f = function(){
11590 f.superclass.constructor.apply(this, arguments);
11592 Roo.extend(f, Roo.data.Record);
11593 var p = f.prototype;
11594 p.fields = new Roo.util.MixedCollection(false, function(field){
11597 for(var i = 0, len = o.length; i < len; i++){
11598 p.fields.add(new Roo.data.Field(o[i]));
11600 f.getField = function(name){
11601 return p.fields.get(name);
11606 Roo.data.Record.AUTO_ID = 1000;
11607 Roo.data.Record.EDIT = 'edit';
11608 Roo.data.Record.REJECT = 'reject';
11609 Roo.data.Record.COMMIT = 'commit';
11611 Roo.data.Record.prototype = {
11613 * Readonly flag - true if this record has been modified.
11622 join : function(store){
11623 this.store = store;
11627 * Set the named field to the specified value.
11628 * @param {String} name The name of the field to set.
11629 * @param {Object} value The value to set the field to.
11631 set : function(name, value){
11632 if(this.data[name] == value){
11636 if(!this.modified){
11637 this.modified = {};
11639 if(typeof this.modified[name] == 'undefined'){
11640 this.modified[name] = this.data[name];
11642 this.data[name] = value;
11643 if(!this.editing && this.store){
11644 this.store.afterEdit(this);
11649 * Get the value of the named field.
11650 * @param {String} name The name of the field to get the value of.
11651 * @return {Object} The value of the field.
11653 get : function(name){
11654 return this.data[name];
11658 beginEdit : function(){
11659 this.editing = true;
11660 this.modified = {};
11664 cancelEdit : function(){
11665 this.editing = false;
11666 delete this.modified;
11670 endEdit : function(){
11671 this.editing = false;
11672 if(this.dirty && this.store){
11673 this.store.afterEdit(this);
11678 * Usually called by the {@link Roo.data.Store} which owns the Record.
11679 * Rejects all changes made to the Record since either creation, or the last commit operation.
11680 * Modified fields are reverted to their original values.
11682 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11683 * of reject operations.
11685 reject : function(){
11686 var m = this.modified;
11688 if(typeof m[n] != "function"){
11689 this.data[n] = m[n];
11692 this.dirty = false;
11693 delete this.modified;
11694 this.editing = false;
11696 this.store.afterReject(this);
11701 * Usually called by the {@link Roo.data.Store} which owns the Record.
11702 * Commits all changes made to the Record since either creation, or the last commit operation.
11704 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11705 * of commit operations.
11707 commit : function(){
11708 this.dirty = false;
11709 delete this.modified;
11710 this.editing = false;
11712 this.store.afterCommit(this);
11717 hasError : function(){
11718 return this.error != null;
11722 clearError : function(){
11727 * Creates a copy of this record.
11728 * @param {String} id (optional) A new record id if you don't want to use this record's id
11731 copy : function(newId) {
11732 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11736 * Ext JS Library 1.1.1
11737 * Copyright(c) 2006-2007, Ext JS, LLC.
11739 * Originally Released Under LGPL - original licence link has changed is not relivant.
11742 * <script type="text/javascript">
11748 * @class Roo.data.Store
11749 * @extends Roo.util.Observable
11750 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11751 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11753 * 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
11754 * has no knowledge of the format of the data returned by the Proxy.<br>
11756 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11757 * instances from the data object. These records are cached and made available through accessor functions.
11759 * Creates a new Store.
11760 * @param {Object} config A config object containing the objects needed for the Store to access data,
11761 * and read the data into Records.
11763 Roo.data.Store = function(config){
11764 this.data = new Roo.util.MixedCollection(false);
11765 this.data.getKey = function(o){
11768 this.baseParams = {};
11770 this.paramNames = {
11775 "multisort" : "_multisort"
11778 if(config && config.data){
11779 this.inlineData = config.data;
11780 delete config.data;
11783 Roo.apply(this, config);
11785 if(this.reader){ // reader passed
11786 this.reader = Roo.factory(this.reader, Roo.data);
11787 this.reader.xmodule = this.xmodule || false;
11788 if(!this.recordType){
11789 this.recordType = this.reader.recordType;
11791 if(this.reader.onMetaChange){
11792 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11796 if(this.recordType){
11797 this.fields = this.recordType.prototype.fields;
11799 this.modified = [];
11803 * @event datachanged
11804 * Fires when the data cache has changed, and a widget which is using this Store
11805 * as a Record cache should refresh its view.
11806 * @param {Store} this
11808 datachanged : true,
11810 * @event metachange
11811 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11812 * @param {Store} this
11813 * @param {Object} meta The JSON metadata
11818 * Fires when Records have been added to the Store
11819 * @param {Store} this
11820 * @param {Roo.data.Record[]} records The array of Records added
11821 * @param {Number} index The index at which the record(s) were added
11826 * Fires when a Record has been removed from the Store
11827 * @param {Store} this
11828 * @param {Roo.data.Record} record The Record that was removed
11829 * @param {Number} index The index at which the record was removed
11834 * Fires when a Record has been updated
11835 * @param {Store} this
11836 * @param {Roo.data.Record} record The Record that was updated
11837 * @param {String} operation The update operation being performed. Value may be one of:
11839 Roo.data.Record.EDIT
11840 Roo.data.Record.REJECT
11841 Roo.data.Record.COMMIT
11847 * Fires when the data cache has been cleared.
11848 * @param {Store} this
11852 * @event beforeload
11853 * Fires before a request is made for a new data object. If the beforeload handler returns false
11854 * the load action will be canceled.
11855 * @param {Store} this
11856 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11860 * @event beforeloadadd
11861 * Fires after a new set of Records has been loaded.
11862 * @param {Store} this
11863 * @param {Roo.data.Record[]} records The Records that were loaded
11864 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11866 beforeloadadd : true,
11869 * Fires after a new set of Records has been loaded, before they are added to the store.
11870 * @param {Store} this
11871 * @param {Roo.data.Record[]} records The Records that were loaded
11872 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11873 * @params {Object} return from reader
11877 * @event loadexception
11878 * Fires if an exception occurs in the Proxy during loading.
11879 * Called with the signature of the Proxy's "loadexception" event.
11880 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11883 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11884 * @param {Object} load options
11885 * @param {Object} jsonData from your request (normally this contains the Exception)
11887 loadexception : true
11891 this.proxy = Roo.factory(this.proxy, Roo.data);
11892 this.proxy.xmodule = this.xmodule || false;
11893 this.relayEvents(this.proxy, ["loadexception"]);
11895 this.sortToggle = {};
11896 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11898 Roo.data.Store.superclass.constructor.call(this);
11900 if(this.inlineData){
11901 this.loadData(this.inlineData);
11902 delete this.inlineData;
11906 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11908 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11909 * without a remote query - used by combo/forms at present.
11913 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11916 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11919 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11920 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11923 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11924 * on any HTTP request
11927 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11930 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11934 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11935 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11937 remoteSort : false,
11940 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11941 * loaded or when a record is removed. (defaults to false).
11943 pruneModifiedRecords : false,
11946 lastOptions : null,
11949 * Add Records to the Store and fires the add event.
11950 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11952 add : function(records){
11953 records = [].concat(records);
11954 for(var i = 0, len = records.length; i < len; i++){
11955 records[i].join(this);
11957 var index = this.data.length;
11958 this.data.addAll(records);
11959 this.fireEvent("add", this, records, index);
11963 * Remove a Record from the Store and fires the remove event.
11964 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11966 remove : function(record){
11967 var index = this.data.indexOf(record);
11968 this.data.removeAt(index);
11970 if(this.pruneModifiedRecords){
11971 this.modified.remove(record);
11973 this.fireEvent("remove", this, record, index);
11977 * Remove all Records from the Store and fires the clear event.
11979 removeAll : function(){
11981 if(this.pruneModifiedRecords){
11982 this.modified = [];
11984 this.fireEvent("clear", this);
11988 * Inserts Records to the Store at the given index and fires the add event.
11989 * @param {Number} index The start index at which to insert the passed Records.
11990 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11992 insert : function(index, records){
11993 records = [].concat(records);
11994 for(var i = 0, len = records.length; i < len; i++){
11995 this.data.insert(index, records[i]);
11996 records[i].join(this);
11998 this.fireEvent("add", this, records, index);
12002 * Get the index within the cache of the passed Record.
12003 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
12004 * @return {Number} The index of the passed Record. Returns -1 if not found.
12006 indexOf : function(record){
12007 return this.data.indexOf(record);
12011 * Get the index within the cache of the Record with the passed id.
12012 * @param {String} id The id of the Record to find.
12013 * @return {Number} The index of the Record. Returns -1 if not found.
12015 indexOfId : function(id){
12016 return this.data.indexOfKey(id);
12020 * Get the Record with the specified id.
12021 * @param {String} id The id of the Record to find.
12022 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
12024 getById : function(id){
12025 return this.data.key(id);
12029 * Get the Record at the specified index.
12030 * @param {Number} index The index of the Record to find.
12031 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
12033 getAt : function(index){
12034 return this.data.itemAt(index);
12038 * Returns a range of Records between specified indices.
12039 * @param {Number} startIndex (optional) The starting index (defaults to 0)
12040 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
12041 * @return {Roo.data.Record[]} An array of Records
12043 getRange : function(start, end){
12044 return this.data.getRange(start, end);
12048 storeOptions : function(o){
12049 o = Roo.apply({}, o);
12052 this.lastOptions = o;
12056 * Loads the Record cache from the configured Proxy using the configured Reader.
12058 * If using remote paging, then the first load call must specify the <em>start</em>
12059 * and <em>limit</em> properties in the options.params property to establish the initial
12060 * position within the dataset, and the number of Records to cache on each read from the Proxy.
12062 * <strong>It is important to note that for remote data sources, loading is asynchronous,
12063 * and this call will return before the new data has been loaded. Perform any post-processing
12064 * in a callback function, or in a "load" event handler.</strong>
12066 * @param {Object} options An object containing properties which control loading options:<ul>
12067 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
12068 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
12069 * passed the following arguments:<ul>
12070 * <li>r : Roo.data.Record[]</li>
12071 * <li>options: Options object from the load call</li>
12072 * <li>success: Boolean success indicator</li></ul></li>
12073 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
12074 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
12077 load : function(options){
12078 options = options || {};
12079 if(this.fireEvent("beforeload", this, options) !== false){
12080 this.storeOptions(options);
12081 var p = Roo.apply(options.params || {}, this.baseParams);
12082 // if meta was not loaded from remote source.. try requesting it.
12083 if (!this.reader.metaFromRemote) {
12084 p._requestMeta = 1;
12086 if(this.sortInfo && this.remoteSort){
12087 var pn = this.paramNames;
12088 p[pn["sort"]] = this.sortInfo.field;
12089 p[pn["dir"]] = this.sortInfo.direction;
12091 if (this.multiSort) {
12092 var pn = this.paramNames;
12093 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
12096 this.proxy.load(p, this.reader, this.loadRecords, this, options);
12101 * Reloads the Record cache from the configured Proxy using the configured Reader and
12102 * the options from the last load operation performed.
12103 * @param {Object} options (optional) An object containing properties which may override the options
12104 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
12105 * the most recently used options are reused).
12107 reload : function(options){
12108 this.load(Roo.applyIf(options||{}, this.lastOptions));
12112 // Called as a callback by the Reader during a load operation.
12113 loadRecords : function(o, options, success){
12114 if(!o || success === false){
12115 if(success !== false){
12116 this.fireEvent("load", this, [], options, o);
12118 if(options.callback){
12119 options.callback.call(options.scope || this, [], options, false);
12123 // if data returned failure - throw an exception.
12124 if (o.success === false) {
12125 // show a message if no listener is registered.
12126 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
12127 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
12129 // loadmask wil be hooked into this..
12130 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
12133 var r = o.records, t = o.totalRecords || r.length;
12135 this.fireEvent("beforeloadadd", this, r, options, o);
12137 if(!options || options.add !== true){
12138 if(this.pruneModifiedRecords){
12139 this.modified = [];
12141 for(var i = 0, len = r.length; i < len; i++){
12145 this.data = this.snapshot;
12146 delete this.snapshot;
12149 this.data.addAll(r);
12150 this.totalLength = t;
12152 this.fireEvent("datachanged", this);
12154 this.totalLength = Math.max(t, this.data.length+r.length);
12158 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
12160 var e = new Roo.data.Record({});
12162 e.set(this.parent.displayField, this.parent.emptyTitle);
12163 e.set(this.parent.valueField, '');
12168 this.fireEvent("load", this, r, options, o);
12169 if(options.callback){
12170 options.callback.call(options.scope || this, r, options, true);
12176 * Loads data from a passed data block. A Reader which understands the format of the data
12177 * must have been configured in the constructor.
12178 * @param {Object} data The data block from which to read the Records. The format of the data expected
12179 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
12180 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
12182 loadData : function(o, append){
12183 var r = this.reader.readRecords(o);
12184 this.loadRecords(r, {add: append}, true);
12188 * using 'cn' the nested child reader read the child array into it's child stores.
12189 * @param {Object} rec The record with a 'children array
12191 loadDataFromChildren : function(rec)
12193 this.loadData(this.reader.toLoadData(rec));
12198 * Gets the number of cached records.
12200 * <em>If using paging, this may not be the total size of the dataset. If the data object
12201 * used by the Reader contains the dataset size, then the getTotalCount() function returns
12202 * the data set size</em>
12204 getCount : function(){
12205 return this.data.length || 0;
12209 * Gets the total number of records in the dataset as returned by the server.
12211 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
12212 * the dataset size</em>
12214 getTotalCount : function(){
12215 return this.totalLength || 0;
12219 * Returns the sort state of the Store as an object with two properties:
12221 field {String} The name of the field by which the Records are sorted
12222 direction {String} The sort order, "ASC" or "DESC"
12225 getSortState : function(){
12226 return this.sortInfo;
12230 applySort : function(){
12231 if(this.sortInfo && !this.remoteSort){
12232 var s = this.sortInfo, f = s.field;
12233 var st = this.fields.get(f).sortType;
12234 var fn = function(r1, r2){
12235 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
12236 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12238 this.data.sort(s.direction, fn);
12239 if(this.snapshot && this.snapshot != this.data){
12240 this.snapshot.sort(s.direction, fn);
12246 * Sets the default sort column and order to be used by the next load operation.
12247 * @param {String} fieldName The name of the field to sort by.
12248 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12250 setDefaultSort : function(field, dir){
12251 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
12255 * Sort the Records.
12256 * If remote sorting is used, the sort is performed on the server, and the cache is
12257 * reloaded. If local sorting is used, the cache is sorted internally.
12258 * @param {String} fieldName The name of the field to sort by.
12259 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12261 sort : function(fieldName, dir){
12262 var f = this.fields.get(fieldName);
12264 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
12266 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
12267 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
12272 this.sortToggle[f.name] = dir;
12273 this.sortInfo = {field: f.name, direction: dir};
12274 if(!this.remoteSort){
12276 this.fireEvent("datachanged", this);
12278 this.load(this.lastOptions);
12283 * Calls the specified function for each of the Records in the cache.
12284 * @param {Function} fn The function to call. The Record is passed as the first parameter.
12285 * Returning <em>false</em> aborts and exits the iteration.
12286 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
12288 each : function(fn, scope){
12289 this.data.each(fn, scope);
12293 * Gets all records modified since the last commit. Modified records are persisted across load operations
12294 * (e.g., during paging).
12295 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
12297 getModifiedRecords : function(){
12298 return this.modified;
12302 createFilterFn : function(property, value, anyMatch){
12303 if(!value.exec){ // not a regex
12304 value = String(value);
12305 if(value.length == 0){
12308 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
12310 return function(r){
12311 return value.test(r.data[property]);
12316 * Sums the value of <i>property</i> for each record between start and end and returns the result.
12317 * @param {String} property A field on your records
12318 * @param {Number} start The record index to start at (defaults to 0)
12319 * @param {Number} end The last record index to include (defaults to length - 1)
12320 * @return {Number} The sum
12322 sum : function(property, start, end){
12323 var rs = this.data.items, v = 0;
12324 start = start || 0;
12325 end = (end || end === 0) ? end : rs.length-1;
12327 for(var i = start; i <= end; i++){
12328 v += (rs[i].data[property] || 0);
12334 * Filter the records by a specified property.
12335 * @param {String} field A field on your records
12336 * @param {String/RegExp} value Either a string that the field
12337 * should start with or a RegExp to test against the field
12338 * @param {Boolean} anyMatch True to match any part not just the beginning
12340 filter : function(property, value, anyMatch){
12341 var fn = this.createFilterFn(property, value, anyMatch);
12342 return fn ? this.filterBy(fn) : this.clearFilter();
12346 * Filter by a function. The specified function will be called with each
12347 * record in this data source. If the function returns true the record is included,
12348 * otherwise it is filtered.
12349 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12350 * @param {Object} scope (optional) The scope of the function (defaults to this)
12352 filterBy : function(fn, scope){
12353 this.snapshot = this.snapshot || this.data;
12354 this.data = this.queryBy(fn, scope||this);
12355 this.fireEvent("datachanged", this);
12359 * Query the records by a specified property.
12360 * @param {String} field A field on your records
12361 * @param {String/RegExp} value Either a string that the field
12362 * should start with or a RegExp to test against the field
12363 * @param {Boolean} anyMatch True to match any part not just the beginning
12364 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12366 query : function(property, value, anyMatch){
12367 var fn = this.createFilterFn(property, value, anyMatch);
12368 return fn ? this.queryBy(fn) : this.data.clone();
12372 * Query by a function. The specified function will be called with each
12373 * record in this data source. If the function returns true the record is included
12375 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12376 * @param {Object} scope (optional) The scope of the function (defaults to this)
12377 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12379 queryBy : function(fn, scope){
12380 var data = this.snapshot || this.data;
12381 return data.filterBy(fn, scope||this);
12385 * Collects unique values for a particular dataIndex from this store.
12386 * @param {String} dataIndex The property to collect
12387 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12388 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12389 * @return {Array} An array of the unique values
12391 collect : function(dataIndex, allowNull, bypassFilter){
12392 var d = (bypassFilter === true && this.snapshot) ?
12393 this.snapshot.items : this.data.items;
12394 var v, sv, r = [], l = {};
12395 for(var i = 0, len = d.length; i < len; i++){
12396 v = d[i].data[dataIndex];
12398 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12407 * Revert to a view of the Record cache with no filtering applied.
12408 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12410 clearFilter : function(suppressEvent){
12411 if(this.snapshot && this.snapshot != this.data){
12412 this.data = this.snapshot;
12413 delete this.snapshot;
12414 if(suppressEvent !== true){
12415 this.fireEvent("datachanged", this);
12421 afterEdit : function(record){
12422 if(this.modified.indexOf(record) == -1){
12423 this.modified.push(record);
12425 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12429 afterReject : function(record){
12430 this.modified.remove(record);
12431 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12435 afterCommit : function(record){
12436 this.modified.remove(record);
12437 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12441 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12442 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12444 commitChanges : function(){
12445 var m = this.modified.slice(0);
12446 this.modified = [];
12447 for(var i = 0, len = m.length; i < len; i++){
12453 * Cancel outstanding changes on all changed records.
12455 rejectChanges : function(){
12456 var m = this.modified.slice(0);
12457 this.modified = [];
12458 for(var i = 0, len = m.length; i < len; i++){
12463 onMetaChange : function(meta, rtype, o){
12464 this.recordType = rtype;
12465 this.fields = rtype.prototype.fields;
12466 delete this.snapshot;
12467 this.sortInfo = meta.sortInfo || this.sortInfo;
12468 this.modified = [];
12469 this.fireEvent('metachange', this, this.reader.meta);
12472 moveIndex : function(data, type)
12474 var index = this.indexOf(data);
12476 var newIndex = index + type;
12480 this.insert(newIndex, data);
12485 * Ext JS Library 1.1.1
12486 * Copyright(c) 2006-2007, Ext JS, LLC.
12488 * Originally Released Under LGPL - original licence link has changed is not relivant.
12491 * <script type="text/javascript">
12495 * @class Roo.data.SimpleStore
12496 * @extends Roo.data.Store
12497 * Small helper class to make creating Stores from Array data easier.
12498 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12499 * @cfg {Array} fields An array of field definition objects, or field name strings.
12500 * @cfg {Object} an existing reader (eg. copied from another store)
12501 * @cfg {Array} data The multi-dimensional array of data
12503 * @param {Object} config
12505 Roo.data.SimpleStore = function(config)
12507 Roo.data.SimpleStore.superclass.constructor.call(this, {
12509 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
12512 Roo.data.Record.create(config.fields)
12514 proxy : new Roo.data.MemoryProxy(config.data)
12518 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12520 * Ext JS Library 1.1.1
12521 * Copyright(c) 2006-2007, Ext JS, LLC.
12523 * Originally Released Under LGPL - original licence link has changed is not relivant.
12526 * <script type="text/javascript">
12531 * @extends Roo.data.Store
12532 * @class Roo.data.JsonStore
12533 * Small helper class to make creating Stores for JSON data easier. <br/>
12535 var store = new Roo.data.JsonStore({
12536 url: 'get-images.php',
12538 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12541 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12542 * JsonReader and HttpProxy (unless inline data is provided).</b>
12543 * @cfg {Array} fields An array of field definition objects, or field name strings.
12545 * @param {Object} config
12547 Roo.data.JsonStore = function(c){
12548 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12549 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12550 reader: new Roo.data.JsonReader(c, c.fields)
12553 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12555 * Ext JS Library 1.1.1
12556 * Copyright(c) 2006-2007, Ext JS, LLC.
12558 * Originally Released Under LGPL - original licence link has changed is not relivant.
12561 * <script type="text/javascript">
12565 Roo.data.Field = function(config){
12566 if(typeof config == "string"){
12567 config = {name: config};
12569 Roo.apply(this, config);
12572 this.type = "auto";
12575 var st = Roo.data.SortTypes;
12576 // named sortTypes are supported, here we look them up
12577 if(typeof this.sortType == "string"){
12578 this.sortType = st[this.sortType];
12581 // set default sortType for strings and dates
12582 if(!this.sortType){
12585 this.sortType = st.asUCString;
12588 this.sortType = st.asDate;
12591 this.sortType = st.none;
12596 var stripRe = /[\$,%]/g;
12598 // prebuilt conversion function for this field, instead of
12599 // switching every time we're reading a value
12601 var cv, dateFormat = this.dateFormat;
12606 cv = function(v){ return v; };
12609 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12613 return v !== undefined && v !== null && v !== '' ?
12614 parseInt(String(v).replace(stripRe, ""), 10) : '';
12619 return v !== undefined && v !== null && v !== '' ?
12620 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12625 cv = function(v){ return v === true || v === "true" || v == 1; };
12632 if(v instanceof Date){
12636 if(dateFormat == "timestamp"){
12637 return new Date(v*1000);
12639 return Date.parseDate(v, dateFormat);
12641 var parsed = Date.parse(v);
12642 return parsed ? new Date(parsed) : null;
12651 Roo.data.Field.prototype = {
12659 * Ext JS Library 1.1.1
12660 * Copyright(c) 2006-2007, Ext JS, LLC.
12662 * Originally Released Under LGPL - original licence link has changed is not relivant.
12665 * <script type="text/javascript">
12668 // Base class for reading structured data from a data source. This class is intended to be
12669 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12672 * @class Roo.data.DataReader
12673 * Base class for reading structured data from a data source. This class is intended to be
12674 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12677 Roo.data.DataReader = function(meta, recordType){
12681 this.recordType = recordType instanceof Array ?
12682 Roo.data.Record.create(recordType) : recordType;
12685 Roo.data.DataReader.prototype = {
12688 readerType : 'Data',
12690 * Create an empty record
12691 * @param {Object} data (optional) - overlay some values
12692 * @return {Roo.data.Record} record created.
12694 newRow : function(d) {
12696 this.recordType.prototype.fields.each(function(c) {
12698 case 'int' : da[c.name] = 0; break;
12699 case 'date' : da[c.name] = new Date(); break;
12700 case 'float' : da[c.name] = 0.0; break;
12701 case 'boolean' : da[c.name] = false; break;
12702 default : da[c.name] = ""; break;
12706 return new this.recordType(Roo.apply(da, d));
12712 * Ext JS Library 1.1.1
12713 * Copyright(c) 2006-2007, Ext JS, LLC.
12715 * Originally Released Under LGPL - original licence link has changed is not relivant.
12718 * <script type="text/javascript">
12722 * @class Roo.data.DataProxy
12723 * @extends Roo.data.Observable
12724 * This class is an abstract base class for implementations which provide retrieval of
12725 * unformatted data objects.<br>
12727 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12728 * (of the appropriate type which knows how to parse the data object) to provide a block of
12729 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12731 * Custom implementations must implement the load method as described in
12732 * {@link Roo.data.HttpProxy#load}.
12734 Roo.data.DataProxy = function(){
12737 * @event beforeload
12738 * Fires before a network request is made to retrieve a data object.
12739 * @param {Object} This DataProxy object.
12740 * @param {Object} params The params parameter to the load function.
12745 * Fires before the load method's callback is called.
12746 * @param {Object} This DataProxy object.
12747 * @param {Object} o The data object.
12748 * @param {Object} arg The callback argument object passed to the load function.
12752 * @event loadexception
12753 * Fires if an Exception occurs during data retrieval.
12754 * @param {Object} This DataProxy object.
12755 * @param {Object} o The data object.
12756 * @param {Object} arg The callback argument object passed to the load function.
12757 * @param {Object} e The Exception.
12759 loadexception : true
12761 Roo.data.DataProxy.superclass.constructor.call(this);
12764 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12767 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12771 * Ext JS Library 1.1.1
12772 * Copyright(c) 2006-2007, Ext JS, LLC.
12774 * Originally Released Under LGPL - original licence link has changed is not relivant.
12777 * <script type="text/javascript">
12780 * @class Roo.data.MemoryProxy
12781 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12782 * to the Reader when its load method is called.
12784 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12786 Roo.data.MemoryProxy = function(data){
12790 Roo.data.MemoryProxy.superclass.constructor.call(this);
12794 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12797 * Load data from the requested source (in this case an in-memory
12798 * data object passed to the constructor), read the data object into
12799 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12800 * process that block using the passed callback.
12801 * @param {Object} params This parameter is not used by the MemoryProxy class.
12802 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12803 * object into a block of Roo.data.Records.
12804 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12805 * The function must be passed <ul>
12806 * <li>The Record block object</li>
12807 * <li>The "arg" argument from the load function</li>
12808 * <li>A boolean success indicator</li>
12810 * @param {Object} scope The scope in which to call the callback
12811 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12813 load : function(params, reader, callback, scope, arg){
12814 params = params || {};
12817 result = reader.readRecords(params.data ? params.data :this.data);
12819 this.fireEvent("loadexception", this, arg, null, e);
12820 callback.call(scope, null, arg, false);
12823 callback.call(scope, result, arg, true);
12827 update : function(params, records){
12832 * Ext JS Library 1.1.1
12833 * Copyright(c) 2006-2007, Ext JS, LLC.
12835 * Originally Released Under LGPL - original licence link has changed is not relivant.
12838 * <script type="text/javascript">
12841 * @class Roo.data.HttpProxy
12842 * @extends Roo.data.DataProxy
12843 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12844 * configured to reference a certain URL.<br><br>
12846 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12847 * from which the running page was served.<br><br>
12849 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12851 * Be aware that to enable the browser to parse an XML document, the server must set
12852 * the Content-Type header in the HTTP response to "text/xml".
12854 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12855 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12856 * will be used to make the request.
12858 Roo.data.HttpProxy = function(conn){
12859 Roo.data.HttpProxy.superclass.constructor.call(this);
12860 // is conn a conn config or a real conn?
12862 this.useAjax = !conn || !conn.events;
12866 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12867 // thse are take from connection...
12870 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12873 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12874 * extra parameters to each request made by this object. (defaults to undefined)
12877 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12878 * to each request made by this object. (defaults to undefined)
12881 * @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)
12884 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12887 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12893 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12897 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12898 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12899 * a finer-grained basis than the DataProxy events.
12901 getConnection : function(){
12902 return this.useAjax ? Roo.Ajax : this.conn;
12906 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12907 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12908 * process that block using the passed callback.
12909 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12910 * for the request to the remote server.
12911 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12912 * object into a block of Roo.data.Records.
12913 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12914 * The function must be passed <ul>
12915 * <li>The Record block object</li>
12916 * <li>The "arg" argument from the load function</li>
12917 * <li>A boolean success indicator</li>
12919 * @param {Object} scope The scope in which to call the callback
12920 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12922 load : function(params, reader, callback, scope, arg){
12923 if(this.fireEvent("beforeload", this, params) !== false){
12925 params : params || {},
12927 callback : callback,
12932 callback : this.loadResponse,
12936 Roo.applyIf(o, this.conn);
12937 if(this.activeRequest){
12938 Roo.Ajax.abort(this.activeRequest);
12940 this.activeRequest = Roo.Ajax.request(o);
12942 this.conn.request(o);
12945 callback.call(scope||this, null, arg, false);
12950 loadResponse : function(o, success, response){
12951 delete this.activeRequest;
12953 this.fireEvent("loadexception", this, o, response);
12954 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12959 result = o.reader.read(response);
12961 this.fireEvent("loadexception", this, o, response, e);
12962 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12966 this.fireEvent("load", this, o, o.request.arg);
12967 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12971 update : function(dataSet){
12976 updateResponse : function(dataSet){
12981 * Ext JS Library 1.1.1
12982 * Copyright(c) 2006-2007, Ext JS, LLC.
12984 * Originally Released Under LGPL - original licence link has changed is not relivant.
12987 * <script type="text/javascript">
12991 * @class Roo.data.ScriptTagProxy
12992 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12993 * other than the originating domain of the running page.<br><br>
12995 * <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
12996 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12998 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12999 * source code that is used as the source inside a <script> tag.<br><br>
13001 * In order for the browser to process the returned data, the server must wrap the data object
13002 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
13003 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
13004 * depending on whether the callback name was passed:
13007 boolean scriptTag = false;
13008 String cb = request.getParameter("callback");
13011 response.setContentType("text/javascript");
13013 response.setContentType("application/x-json");
13015 Writer out = response.getWriter();
13017 out.write(cb + "(");
13019 out.print(dataBlock.toJsonString());
13026 * @param {Object} config A configuration object.
13028 Roo.data.ScriptTagProxy = function(config){
13029 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
13030 Roo.apply(this, config);
13031 this.head = document.getElementsByTagName("head")[0];
13034 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
13036 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
13038 * @cfg {String} url The URL from which to request the data object.
13041 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
13045 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
13046 * the server the name of the callback function set up by the load call to process the returned data object.
13047 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
13048 * javascript output which calls this named function passing the data object as its only parameter.
13050 callbackParam : "callback",
13052 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
13053 * name to the request.
13058 * Load data from the configured URL, read the data object into
13059 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13060 * process that block using the passed callback.
13061 * @param {Object} params An object containing properties which are to be used as HTTP parameters
13062 * for the request to the remote server.
13063 * @param {Roo.data.DataReader} reader The Reader object which converts the data
13064 * object into a block of Roo.data.Records.
13065 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
13066 * The function must be passed <ul>
13067 * <li>The Record block object</li>
13068 * <li>The "arg" argument from the load function</li>
13069 * <li>A boolean success indicator</li>
13071 * @param {Object} scope The scope in which to call the callback
13072 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13074 load : function(params, reader, callback, scope, arg){
13075 if(this.fireEvent("beforeload", this, params) !== false){
13077 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
13079 var url = this.url;
13080 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
13082 url += "&_dc=" + (new Date().getTime());
13084 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
13087 cb : "stcCallback"+transId,
13088 scriptId : "stcScript"+transId,
13092 callback : callback,
13098 window[trans.cb] = function(o){
13099 conn.handleResponse(o, trans);
13102 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
13104 if(this.autoAbort !== false){
13108 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
13110 var script = document.createElement("script");
13111 script.setAttribute("src", url);
13112 script.setAttribute("type", "text/javascript");
13113 script.setAttribute("id", trans.scriptId);
13114 this.head.appendChild(script);
13116 this.trans = trans;
13118 callback.call(scope||this, null, arg, false);
13123 isLoading : function(){
13124 return this.trans ? true : false;
13128 * Abort the current server request.
13130 abort : function(){
13131 if(this.isLoading()){
13132 this.destroyTrans(this.trans);
13137 destroyTrans : function(trans, isLoaded){
13138 this.head.removeChild(document.getElementById(trans.scriptId));
13139 clearTimeout(trans.timeoutId);
13141 window[trans.cb] = undefined;
13143 delete window[trans.cb];
13146 // if hasn't been loaded, wait for load to remove it to prevent script error
13147 window[trans.cb] = function(){
13148 window[trans.cb] = undefined;
13150 delete window[trans.cb];
13157 handleResponse : function(o, trans){
13158 this.trans = false;
13159 this.destroyTrans(trans, true);
13162 result = trans.reader.readRecords(o);
13164 this.fireEvent("loadexception", this, o, trans.arg, e);
13165 trans.callback.call(trans.scope||window, null, trans.arg, false);
13168 this.fireEvent("load", this, o, trans.arg);
13169 trans.callback.call(trans.scope||window, result, trans.arg, true);
13173 handleFailure : function(trans){
13174 this.trans = false;
13175 this.destroyTrans(trans, false);
13176 this.fireEvent("loadexception", this, null, trans.arg);
13177 trans.callback.call(trans.scope||window, null, trans.arg, false);
13181 * Ext JS Library 1.1.1
13182 * Copyright(c) 2006-2007, Ext JS, LLC.
13184 * Originally Released Under LGPL - original licence link has changed is not relivant.
13187 * <script type="text/javascript">
13191 * @class Roo.data.JsonReader
13192 * @extends Roo.data.DataReader
13193 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
13194 * based on mappings in a provided Roo.data.Record constructor.
13196 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
13197 * in the reply previously.
13202 var RecordDef = Roo.data.Record.create([
13203 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
13204 {name: 'occupation'} // This field will use "occupation" as the mapping.
13206 var myReader = new Roo.data.JsonReader({
13207 totalProperty: "results", // The property which contains the total dataset size (optional)
13208 root: "rows", // The property which contains an Array of row objects
13209 id: "id" // The property within each row object that provides an ID for the record (optional)
13213 * This would consume a JSON file like this:
13215 { 'results': 2, 'rows': [
13216 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
13217 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
13220 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
13221 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
13222 * paged from the remote server.
13223 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
13224 * @cfg {String} root name of the property which contains the Array of row objects.
13225 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13226 * @cfg {Array} fields Array of field definition objects
13228 * Create a new JsonReader
13229 * @param {Object} meta Metadata configuration options
13230 * @param {Object} recordType Either an Array of field definition objects,
13231 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
13233 Roo.data.JsonReader = function(meta, recordType){
13236 // set some defaults:
13237 Roo.applyIf(meta, {
13238 totalProperty: 'total',
13239 successProperty : 'success',
13244 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13246 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
13248 readerType : 'Json',
13251 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
13252 * Used by Store query builder to append _requestMeta to params.
13255 metaFromRemote : false,
13257 * This method is only used by a DataProxy which has retrieved data from a remote server.
13258 * @param {Object} response The XHR object which contains the JSON data in its responseText.
13259 * @return {Object} data A data block which is used by an Roo.data.Store object as
13260 * a cache of Roo.data.Records.
13262 read : function(response){
13263 var json = response.responseText;
13265 var o = /* eval:var:o */ eval("("+json+")");
13267 throw {message: "JsonReader.read: Json object not found"};
13273 this.metaFromRemote = true;
13274 this.meta = o.metaData;
13275 this.recordType = Roo.data.Record.create(o.metaData.fields);
13276 this.onMetaChange(this.meta, this.recordType, o);
13278 return this.readRecords(o);
13281 // private function a store will implement
13282 onMetaChange : function(meta, recordType, o){
13289 simpleAccess: function(obj, subsc) {
13296 getJsonAccessor: function(){
13298 return function(expr) {
13300 return(re.test(expr))
13301 ? new Function("obj", "return obj." + expr)
13306 return Roo.emptyFn;
13311 * Create a data block containing Roo.data.Records from an XML document.
13312 * @param {Object} o An object which contains an Array of row objects in the property specified
13313 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
13314 * which contains the total size of the dataset.
13315 * @return {Object} data A data block which is used by an Roo.data.Store object as
13316 * a cache of Roo.data.Records.
13318 readRecords : function(o){
13320 * After any data loads, the raw JSON data is available for further custom processing.
13324 var s = this.meta, Record = this.recordType,
13325 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
13327 // Generate extraction functions for the totalProperty, the root, the id, and for each field
13329 if(s.totalProperty) {
13330 this.getTotal = this.getJsonAccessor(s.totalProperty);
13332 if(s.successProperty) {
13333 this.getSuccess = this.getJsonAccessor(s.successProperty);
13335 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
13337 var g = this.getJsonAccessor(s.id);
13338 this.getId = function(rec) {
13340 return (r === undefined || r === "") ? null : r;
13343 this.getId = function(){return null;};
13346 for(var jj = 0; jj < fl; jj++){
13348 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
13349 this.ef[jj] = this.getJsonAccessor(map);
13353 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
13354 if(s.totalProperty){
13355 var vt = parseInt(this.getTotal(o), 10);
13360 if(s.successProperty){
13361 var vs = this.getSuccess(o);
13362 if(vs === false || vs === 'false'){
13367 for(var i = 0; i < c; i++){
13370 var id = this.getId(n);
13371 for(var j = 0; j < fl; j++){
13373 var v = this.ef[j](n);
13375 Roo.log('missing convert for ' + f.name);
13379 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13381 var record = new Record(values, id);
13383 records[i] = record;
13389 totalRecords : totalRecords
13392 // used when loading children.. @see loadDataFromChildren
13393 toLoadData: function(rec)
13395 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13396 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13397 return { data : data, total : data.length };
13402 * Ext JS Library 1.1.1
13403 * Copyright(c) 2006-2007, Ext JS, LLC.
13405 * Originally Released Under LGPL - original licence link has changed is not relivant.
13408 * <script type="text/javascript">
13412 * @class Roo.data.ArrayReader
13413 * @extends Roo.data.DataReader
13414 * Data reader class to create an Array of Roo.data.Record objects from an Array.
13415 * Each element of that Array represents a row of data fields. The
13416 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13417 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13421 var RecordDef = Roo.data.Record.create([
13422 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
13423 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
13425 var myReader = new Roo.data.ArrayReader({
13426 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
13430 * This would consume an Array like this:
13432 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13436 * Create a new JsonReader
13437 * @param {Object} meta Metadata configuration options.
13438 * @param {Object|Array} recordType Either an Array of field definition objects
13440 * @cfg {Array} fields Array of field definition objects
13441 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13442 * as specified to {@link Roo.data.Record#create},
13443 * or an {@link Roo.data.Record} object
13446 * created using {@link Roo.data.Record#create}.
13448 Roo.data.ArrayReader = function(meta, recordType)
13450 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13453 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13456 * Create a data block containing Roo.data.Records from an XML document.
13457 * @param {Object} o An Array of row objects which represents the dataset.
13458 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13459 * a cache of Roo.data.Records.
13461 readRecords : function(o)
13463 var sid = this.meta ? this.meta.id : null;
13464 var recordType = this.recordType, fields = recordType.prototype.fields;
13467 for(var i = 0; i < root.length; i++){
13470 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13471 for(var j = 0, jlen = fields.length; j < jlen; j++){
13472 var f = fields.items[j];
13473 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13474 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13476 values[f.name] = v;
13478 var record = new recordType(values, id);
13480 records[records.length] = record;
13484 totalRecords : records.length
13487 // used when loading children.. @see loadDataFromChildren
13488 toLoadData: function(rec)
13490 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13491 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13502 * @class Roo.bootstrap.ComboBox
13503 * @extends Roo.bootstrap.TriggerField
13504 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13505 * @cfg {Boolean} append (true|false) default false
13506 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13507 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13508 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13509 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13510 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13511 * @cfg {Boolean} animate default true
13512 * @cfg {Boolean} emptyResultText only for touch device
13513 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13514 * @cfg {String} emptyTitle default ''
13516 * Create a new ComboBox.
13517 * @param {Object} config Configuration options
13519 Roo.bootstrap.ComboBox = function(config){
13520 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13524 * Fires when the dropdown list is expanded
13525 * @param {Roo.bootstrap.ComboBox} combo This combo box
13530 * Fires when the dropdown list is collapsed
13531 * @param {Roo.bootstrap.ComboBox} combo This combo box
13535 * @event beforeselect
13536 * Fires before a list item is selected. Return false to cancel the selection.
13537 * @param {Roo.bootstrap.ComboBox} combo This combo box
13538 * @param {Roo.data.Record} record The data record returned from the underlying store
13539 * @param {Number} index The index of the selected item in the dropdown list
13541 'beforeselect' : true,
13544 * Fires when a list item is selected
13545 * @param {Roo.bootstrap.ComboBox} combo This combo box
13546 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13547 * @param {Number} index The index of the selected item in the dropdown list
13551 * @event beforequery
13552 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13553 * The event object passed has these properties:
13554 * @param {Roo.bootstrap.ComboBox} combo This combo box
13555 * @param {String} query The query
13556 * @param {Boolean} forceAll true to force "all" query
13557 * @param {Boolean} cancel true to cancel the query
13558 * @param {Object} e The query event object
13560 'beforequery': true,
13563 * Fires when the 'add' icon is pressed (add a listener to enable add button)
13564 * @param {Roo.bootstrap.ComboBox} combo This combo box
13569 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13570 * @param {Roo.bootstrap.ComboBox} combo This combo box
13571 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13576 * Fires when the remove value from the combobox array
13577 * @param {Roo.bootstrap.ComboBox} combo This combo box
13581 * @event afterremove
13582 * Fires when the remove value from the combobox array
13583 * @param {Roo.bootstrap.ComboBox} combo This combo box
13585 'afterremove' : true,
13587 * @event specialfilter
13588 * Fires when specialfilter
13589 * @param {Roo.bootstrap.ComboBox} combo This combo box
13591 'specialfilter' : true,
13594 * Fires when tick the element
13595 * @param {Roo.bootstrap.ComboBox} combo This combo box
13599 * @event touchviewdisplay
13600 * Fires when touch view require special display (default is using displayField)
13601 * @param {Roo.bootstrap.ComboBox} combo This combo box
13602 * @param {Object} cfg set html .
13604 'touchviewdisplay' : true
13609 this.tickItems = [];
13611 this.selectedIndex = -1;
13612 if(this.mode == 'local'){
13613 if(config.queryDelay === undefined){
13614 this.queryDelay = 10;
13616 if(config.minChars === undefined){
13622 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13625 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13626 * rendering into an Roo.Editor, defaults to false)
13629 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13630 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13633 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13636 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13637 * the dropdown list (defaults to undefined, with no header element)
13641 * @cfg {String/Roo.Template} tpl The template to use to render the output
13645 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13647 listWidth: undefined,
13649 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13650 * mode = 'remote' or 'text' if mode = 'local')
13652 displayField: undefined,
13655 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13656 * mode = 'remote' or 'value' if mode = 'local').
13657 * Note: use of a valueField requires the user make a selection
13658 * in order for a value to be mapped.
13660 valueField: undefined,
13662 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13667 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13668 * field's data value (defaults to the underlying DOM element's name)
13670 hiddenName: undefined,
13672 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13676 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13678 selectedClass: 'active',
13681 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13685 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13686 * anchor positions (defaults to 'tl-bl')
13688 listAlign: 'tl-bl?',
13690 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13694 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13695 * query specified by the allQuery config option (defaults to 'query')
13697 triggerAction: 'query',
13699 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13700 * (defaults to 4, does not apply if editable = false)
13704 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13705 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13709 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13710 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13714 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13715 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13719 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13720 * when editable = true (defaults to false)
13722 selectOnFocus:false,
13724 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13726 queryParam: 'query',
13728 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13729 * when mode = 'remote' (defaults to 'Loading...')
13731 loadingText: 'Loading...',
13733 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13737 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13741 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13742 * traditional select (defaults to true)
13746 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13750 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13754 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13755 * listWidth has a higher value)
13759 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13760 * allow the user to set arbitrary text into the field (defaults to false)
13762 forceSelection:false,
13764 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13765 * if typeAhead = true (defaults to 250)
13767 typeAheadDelay : 250,
13769 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13770 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13772 valueNotFoundText : undefined,
13774 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13776 blockFocus : false,
13779 * @cfg {Boolean} disableClear Disable showing of clear button.
13781 disableClear : false,
13783 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13785 alwaysQuery : false,
13788 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13793 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13795 invalidClass : "has-warning",
13798 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13800 validClass : "has-success",
13803 * @cfg {Boolean} specialFilter (true|false) special filter default false
13805 specialFilter : false,
13808 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13810 mobileTouchView : true,
13813 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13815 useNativeIOS : false,
13818 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13820 mobile_restrict_height : false,
13822 ios_options : false,
13834 btnPosition : 'right',
13835 triggerList : true,
13836 showToggleBtn : true,
13838 emptyResultText: 'Empty',
13839 triggerText : 'Select',
13842 // element that contains real text value.. (when hidden is used..)
13844 getAutoCreate : function()
13849 * Render classic select for iso
13852 if(Roo.isIOS && this.useNativeIOS){
13853 cfg = this.getAutoCreateNativeIOS();
13861 if(Roo.isTouch && this.mobileTouchView){
13862 cfg = this.getAutoCreateTouchView();
13869 if(!this.tickable){
13870 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13875 * ComboBox with tickable selections
13878 var align = this.labelAlign || this.parentLabelAlign();
13881 cls : 'form-group roo-combobox-tickable' //input-group
13884 var btn_text_select = '';
13885 var btn_text_done = '';
13886 var btn_text_cancel = '';
13888 if (this.btn_text_show) {
13889 btn_text_select = 'Select';
13890 btn_text_done = 'Done';
13891 btn_text_cancel = 'Cancel';
13896 cls : 'tickable-buttons',
13901 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13902 //html : this.triggerText
13903 html: btn_text_select
13909 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13911 html: btn_text_done
13917 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13919 html: btn_text_cancel
13925 buttons.cn.unshift({
13927 cls: 'roo-select2-search-field-input'
13933 Roo.each(buttons.cn, function(c){
13935 c.cls += ' btn-' + _this.size;
13938 if (_this.disabled) {
13945 style : 'display: contents',
13950 cls: 'form-hidden-field'
13954 cls: 'roo-select2-choices',
13958 cls: 'roo-select2-search-field',
13969 cls: 'roo-select2-container input-group roo-select2-container-multi',
13975 // cls: 'typeahead typeahead-long dropdown-menu',
13976 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13981 if(this.hasFeedback && !this.allowBlank){
13985 cls: 'glyphicon form-control-feedback'
13988 combobox.cn.push(feedback);
13993 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13994 tooltip : 'This field is required'
13996 if (Roo.bootstrap.version == 4) {
13999 style : 'display:none'
14002 if (align ==='left' && this.fieldLabel.length) {
14004 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
14011 cls : 'control-label col-form-label',
14012 html : this.fieldLabel
14024 var labelCfg = cfg.cn[1];
14025 var contentCfg = cfg.cn[2];
14028 if(this.indicatorpos == 'right'){
14034 cls : 'control-label col-form-label',
14038 html : this.fieldLabel
14054 labelCfg = cfg.cn[0];
14055 contentCfg = cfg.cn[1];
14059 if(this.labelWidth > 12){
14060 labelCfg.style = "width: " + this.labelWidth + 'px';
14063 if(this.labelWidth < 13 && this.labelmd == 0){
14064 this.labelmd = this.labelWidth;
14067 if(this.labellg > 0){
14068 labelCfg.cls += ' col-lg-' + this.labellg;
14069 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14072 if(this.labelmd > 0){
14073 labelCfg.cls += ' col-md-' + this.labelmd;
14074 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14077 if(this.labelsm > 0){
14078 labelCfg.cls += ' col-sm-' + this.labelsm;
14079 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14082 if(this.labelxs > 0){
14083 labelCfg.cls += ' col-xs-' + this.labelxs;
14084 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14088 } else if ( this.fieldLabel.length) {
14089 // Roo.log(" label");
14094 //cls : 'input-group-addon',
14095 html : this.fieldLabel
14100 if(this.indicatorpos == 'right'){
14104 //cls : 'input-group-addon',
14105 html : this.fieldLabel
14115 // Roo.log(" no label && no align");
14122 ['xs','sm','md','lg'].map(function(size){
14123 if (settings[size]) {
14124 cfg.cls += ' col-' + size + '-' + settings[size];
14132 _initEventsCalled : false,
14135 initEvents: function()
14137 if (this._initEventsCalled) { // as we call render... prevent looping...
14140 this._initEventsCalled = true;
14143 throw "can not find store for combo";
14146 this.indicator = this.indicatorEl();
14148 this.store = Roo.factory(this.store, Roo.data);
14149 this.store.parent = this;
14151 // if we are building from html. then this element is so complex, that we can not really
14152 // use the rendered HTML.
14153 // so we have to trash and replace the previous code.
14154 if (Roo.XComponent.build_from_html) {
14155 // remove this element....
14156 var e = this.el.dom, k=0;
14157 while (e ) { e = e.previousSibling; ++k;}
14162 this.rendered = false;
14164 this.render(this.parent().getChildContainer(true), k);
14167 if(Roo.isIOS && this.useNativeIOS){
14168 this.initIOSView();
14176 if(Roo.isTouch && this.mobileTouchView){
14177 this.initTouchView();
14182 this.initTickableEvents();
14186 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
14188 if(this.hiddenName){
14190 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14192 this.hiddenField.dom.value =
14193 this.hiddenValue !== undefined ? this.hiddenValue :
14194 this.value !== undefined ? this.value : '';
14196 // prevent input submission
14197 this.el.dom.removeAttribute('name');
14198 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14203 // this.el.dom.setAttribute('autocomplete', 'off');
14206 var cls = 'x-combo-list';
14208 //this.list = new Roo.Layer({
14209 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
14215 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14216 _this.list.setWidth(lw);
14219 this.list.on('mouseover', this.onViewOver, this);
14220 this.list.on('mousemove', this.onViewMove, this);
14221 this.list.on('scroll', this.onViewScroll, this);
14224 this.list.swallowEvent('mousewheel');
14225 this.assetHeight = 0;
14228 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
14229 this.assetHeight += this.header.getHeight();
14232 this.innerList = this.list.createChild({cls:cls+'-inner'});
14233 this.innerList.on('mouseover', this.onViewOver, this);
14234 this.innerList.on('mousemove', this.onViewMove, this);
14235 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14237 if(this.allowBlank && !this.pageSize && !this.disableClear){
14238 this.footer = this.list.createChild({cls:cls+'-ft'});
14239 this.pageTb = new Roo.Toolbar(this.footer);
14243 this.footer = this.list.createChild({cls:cls+'-ft'});
14244 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
14245 {pageSize: this.pageSize});
14249 if (this.pageTb && this.allowBlank && !this.disableClear) {
14251 this.pageTb.add(new Roo.Toolbar.Fill(), {
14252 cls: 'x-btn-icon x-btn-clear',
14254 handler: function()
14257 _this.clearValue();
14258 _this.onSelect(false, -1);
14263 this.assetHeight += this.footer.getHeight();
14268 this.tpl = Roo.bootstrap.version == 4 ?
14269 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
14270 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
14273 this.view = new Roo.View(this.list, this.tpl, {
14274 singleSelect:true, store: this.store, selectedClass: this.selectedClass
14276 //this.view.wrapEl.setDisplayed(false);
14277 this.view.on('click', this.onViewClick, this);
14280 this.store.on('beforeload', this.onBeforeLoad, this);
14281 this.store.on('load', this.onLoad, this);
14282 this.store.on('loadexception', this.onLoadException, this);
14284 if(this.resizable){
14285 this.resizer = new Roo.Resizable(this.list, {
14286 pinned:true, handles:'se'
14288 this.resizer.on('resize', function(r, w, h){
14289 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
14290 this.listWidth = w;
14291 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
14292 this.restrictHeight();
14294 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
14297 if(!this.editable){
14298 this.editable = true;
14299 this.setEditable(false);
14304 if (typeof(this.events.add.listeners) != 'undefined') {
14306 this.addicon = this.wrap.createChild(
14307 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
14309 this.addicon.on('click', function(e) {
14310 this.fireEvent('add', this);
14313 if (typeof(this.events.edit.listeners) != 'undefined') {
14315 this.editicon = this.wrap.createChild(
14316 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
14317 if (this.addicon) {
14318 this.editicon.setStyle('margin-left', '40px');
14320 this.editicon.on('click', function(e) {
14322 // we fire even if inothing is selected..
14323 this.fireEvent('edit', this, this.lastData );
14329 this.keyNav = new Roo.KeyNav(this.inputEl(), {
14330 "up" : function(e){
14331 this.inKeyMode = true;
14335 "down" : function(e){
14336 if(!this.isExpanded()){
14337 this.onTriggerClick();
14339 this.inKeyMode = true;
14344 "enter" : function(e){
14345 // this.onViewClick();
14349 if(this.fireEvent("specialkey", this, e)){
14350 this.onViewClick(false);
14356 "esc" : function(e){
14360 "tab" : function(e){
14363 if(this.fireEvent("specialkey", this, e)){
14364 this.onViewClick(false);
14372 doRelay : function(foo, bar, hname){
14373 if(hname == 'down' || this.scope.isExpanded()){
14374 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14383 this.queryDelay = Math.max(this.queryDelay || 10,
14384 this.mode == 'local' ? 10 : 250);
14387 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14389 if(this.typeAhead){
14390 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14392 if(this.editable !== false){
14393 this.inputEl().on("keyup", this.onKeyUp, this);
14395 if(this.forceSelection){
14396 this.inputEl().on('blur', this.doForce, this);
14400 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14401 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14405 initTickableEvents: function()
14409 if(this.hiddenName){
14411 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14413 this.hiddenField.dom.value =
14414 this.hiddenValue !== undefined ? this.hiddenValue :
14415 this.value !== undefined ? this.value : '';
14417 // prevent input submission
14418 this.el.dom.removeAttribute('name');
14419 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14424 // this.list = this.el.select('ul.dropdown-menu',true).first();
14426 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14427 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14428 if(this.triggerList){
14429 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14432 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14433 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14435 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14436 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14438 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14439 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14441 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14442 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14443 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14446 this.cancelBtn.hide();
14451 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14452 _this.list.setWidth(lw);
14455 this.list.on('mouseover', this.onViewOver, this);
14456 this.list.on('mousemove', this.onViewMove, this);
14458 this.list.on('scroll', this.onViewScroll, this);
14461 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
14462 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14465 this.view = new Roo.View(this.list, this.tpl, {
14470 selectedClass: this.selectedClass
14473 //this.view.wrapEl.setDisplayed(false);
14474 this.view.on('click', this.onViewClick, this);
14478 this.store.on('beforeload', this.onBeforeLoad, this);
14479 this.store.on('load', this.onLoad, this);
14480 this.store.on('loadexception', this.onLoadException, this);
14483 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14484 "up" : function(e){
14485 this.inKeyMode = true;
14489 "down" : function(e){
14490 this.inKeyMode = true;
14494 "enter" : function(e){
14495 if(this.fireEvent("specialkey", this, e)){
14496 this.onViewClick(false);
14502 "esc" : function(e){
14503 this.onTickableFooterButtonClick(e, false, false);
14506 "tab" : function(e){
14507 this.fireEvent("specialkey", this, e);
14509 this.onTickableFooterButtonClick(e, false, false);
14516 doRelay : function(e, fn, key){
14517 if(this.scope.isExpanded()){
14518 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14527 this.queryDelay = Math.max(this.queryDelay || 10,
14528 this.mode == 'local' ? 10 : 250);
14531 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14533 if(this.typeAhead){
14534 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14537 if(this.editable !== false){
14538 this.tickableInputEl().on("keyup", this.onKeyUp, this);
14541 this.indicator = this.indicatorEl();
14543 if(this.indicator){
14544 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14545 this.indicator.hide();
14550 onDestroy : function(){
14552 this.view.setStore(null);
14553 this.view.el.removeAllListeners();
14554 this.view.el.remove();
14555 this.view.purgeListeners();
14558 this.list.dom.innerHTML = '';
14562 this.store.un('beforeload', this.onBeforeLoad, this);
14563 this.store.un('load', this.onLoad, this);
14564 this.store.un('loadexception', this.onLoadException, this);
14566 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14570 fireKey : function(e){
14571 if(e.isNavKeyPress() && !this.list.isVisible()){
14572 this.fireEvent("specialkey", this, e);
14577 onResize: function(w, h){
14578 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14580 // if(typeof w != 'number'){
14581 // // we do not handle it!?!?
14584 // var tw = this.trigger.getWidth();
14585 // // tw += this.addicon ? this.addicon.getWidth() : 0;
14586 // // tw += this.editicon ? this.editicon.getWidth() : 0;
14588 // this.inputEl().setWidth( this.adjustWidth('input', x));
14590 // //this.trigger.setStyle('left', x+'px');
14592 // if(this.list && this.listWidth === undefined){
14593 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14594 // this.list.setWidth(lw);
14595 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14603 * Allow or prevent the user from directly editing the field text. If false is passed,
14604 * the user will only be able to select from the items defined in the dropdown list. This method
14605 * is the runtime equivalent of setting the 'editable' config option at config time.
14606 * @param {Boolean} value True to allow the user to directly edit the field text
14608 setEditable : function(value){
14609 if(value == this.editable){
14612 this.editable = value;
14614 this.inputEl().dom.setAttribute('readOnly', true);
14615 this.inputEl().on('mousedown', this.onTriggerClick, this);
14616 this.inputEl().addClass('x-combo-noedit');
14618 this.inputEl().dom.setAttribute('readOnly', false);
14619 this.inputEl().un('mousedown', this.onTriggerClick, this);
14620 this.inputEl().removeClass('x-combo-noedit');
14626 onBeforeLoad : function(combo,opts){
14627 if(!this.hasFocus){
14631 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14633 this.restrictHeight();
14634 this.selectedIndex = -1;
14638 onLoad : function(){
14640 this.hasQuery = false;
14642 if(!this.hasFocus){
14646 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14647 this.loading.hide();
14650 if(this.store.getCount() > 0){
14653 this.restrictHeight();
14654 if(this.lastQuery == this.allQuery){
14655 if(this.editable && !this.tickable){
14656 this.inputEl().dom.select();
14660 !this.selectByValue(this.value, true) &&
14663 !this.store.lastOptions ||
14664 typeof(this.store.lastOptions.add) == 'undefined' ||
14665 this.store.lastOptions.add != true
14668 this.select(0, true);
14671 if(this.autoFocus){
14674 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14675 this.taTask.delay(this.typeAheadDelay);
14679 this.onEmptyResults();
14685 onLoadException : function()
14687 this.hasQuery = false;
14689 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14690 this.loading.hide();
14693 if(this.tickable && this.editable){
14698 // only causes errors at present
14699 //Roo.log(this.store.reader.jsonData);
14700 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14702 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14708 onTypeAhead : function(){
14709 if(this.store.getCount() > 0){
14710 var r = this.store.getAt(0);
14711 var newValue = r.data[this.displayField];
14712 var len = newValue.length;
14713 var selStart = this.getRawValue().length;
14715 if(selStart != len){
14716 this.setRawValue(newValue);
14717 this.selectText(selStart, newValue.length);
14723 onSelect : function(record, index){
14725 if(this.fireEvent('beforeselect', this, record, index) !== false){
14727 this.setFromData(index > -1 ? record.data : false);
14730 this.fireEvent('select', this, record, index);
14735 * Returns the currently selected field value or empty string if no value is set.
14736 * @return {String} value The selected value
14738 getValue : function()
14740 if(Roo.isIOS && this.useNativeIOS){
14741 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14745 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14748 if(this.valueField){
14749 return typeof this.value != 'undefined' ? this.value : '';
14751 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14755 getRawValue : function()
14757 if(Roo.isIOS && this.useNativeIOS){
14758 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14761 var v = this.inputEl().getValue();
14767 * Clears any text/value currently set in the field
14769 clearValue : function(){
14771 if(this.hiddenField){
14772 this.hiddenField.dom.value = '';
14775 this.setRawValue('');
14776 this.lastSelectionText = '';
14777 this.lastData = false;
14779 var close = this.closeTriggerEl();
14790 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14791 * will be displayed in the field. If the value does not match the data value of an existing item,
14792 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14793 * Otherwise the field will be blank (although the value will still be set).
14794 * @param {String} value The value to match
14796 setValue : function(v)
14798 if(Roo.isIOS && this.useNativeIOS){
14799 this.setIOSValue(v);
14809 if(this.valueField){
14810 var r = this.findRecord(this.valueField, v);
14812 text = r.data[this.displayField];
14813 }else if(this.valueNotFoundText !== undefined){
14814 text = this.valueNotFoundText;
14817 this.lastSelectionText = text;
14818 if(this.hiddenField){
14819 this.hiddenField.dom.value = v;
14821 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14824 var close = this.closeTriggerEl();
14827 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14833 * @property {Object} the last set data for the element
14838 * Sets the value of the field based on a object which is related to the record format for the store.
14839 * @param {Object} value the value to set as. or false on reset?
14841 setFromData : function(o){
14848 var dv = ''; // display value
14849 var vv = ''; // value value..
14851 if (this.displayField) {
14852 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14854 // this is an error condition!!!
14855 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14858 if(this.valueField){
14859 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14862 var close = this.closeTriggerEl();
14865 if(dv.length || vv * 1 > 0){
14867 this.blockFocus=true;
14873 if(this.hiddenField){
14874 this.hiddenField.dom.value = vv;
14876 this.lastSelectionText = dv;
14877 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14881 // no hidden field.. - we store the value in 'value', but still display
14882 // display field!!!!
14883 this.lastSelectionText = dv;
14884 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14891 reset : function(){
14892 // overridden so that last data is reset..
14899 this.setValue(this.originalValue);
14900 //this.clearInvalid();
14901 this.lastData = false;
14903 this.view.clearSelections();
14909 findRecord : function(prop, value){
14911 if(this.store.getCount() > 0){
14912 this.store.each(function(r){
14913 if(r.data[prop] == value){
14923 getName: function()
14925 // returns hidden if it's set..
14926 if (!this.rendered) {return ''};
14927 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14931 onViewMove : function(e, t){
14932 this.inKeyMode = false;
14936 onViewOver : function(e, t){
14937 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14940 var item = this.view.findItemFromChild(t);
14943 var index = this.view.indexOf(item);
14944 this.select(index, false);
14949 onViewClick : function(view, doFocus, el, e)
14951 var index = this.view.getSelectedIndexes()[0];
14953 var r = this.store.getAt(index);
14957 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14964 Roo.each(this.tickItems, function(v,k){
14966 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14968 _this.tickItems.splice(k, 1);
14970 if(typeof(e) == 'undefined' && view == false){
14971 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14983 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14984 this.tickItems.push(r.data);
14987 if(typeof(e) == 'undefined' && view == false){
14988 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14995 this.onSelect(r, index);
14997 if(doFocus !== false && !this.blockFocus){
14998 this.inputEl().focus();
15003 restrictHeight : function(){
15004 //this.innerList.dom.style.height = '';
15005 //var inner = this.innerList.dom;
15006 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
15007 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
15008 //this.list.beginUpdate();
15009 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
15010 this.list.alignTo(this.inputEl(), this.listAlign);
15011 this.list.alignTo(this.inputEl(), this.listAlign);
15012 //this.list.endUpdate();
15016 onEmptyResults : function(){
15018 if(this.tickable && this.editable){
15019 this.hasFocus = false;
15020 this.restrictHeight();
15028 * Returns true if the dropdown list is expanded, else false.
15030 isExpanded : function(){
15031 return this.list.isVisible();
15035 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
15036 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15037 * @param {String} value The data value of the item to select
15038 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15039 * selected item if it is not currently in view (defaults to true)
15040 * @return {Boolean} True if the value matched an item in the list, else false
15042 selectByValue : function(v, scrollIntoView){
15043 if(v !== undefined && v !== null){
15044 var r = this.findRecord(this.valueField || this.displayField, v);
15046 this.select(this.store.indexOf(r), scrollIntoView);
15054 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
15055 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15056 * @param {Number} index The zero-based index of the list item to select
15057 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15058 * selected item if it is not currently in view (defaults to true)
15060 select : function(index, scrollIntoView){
15061 this.selectedIndex = index;
15062 this.view.select(index);
15063 if(scrollIntoView !== false){
15064 var el = this.view.getNode(index);
15066 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
15069 this.list.scrollChildIntoView(el, false);
15075 selectNext : function(){
15076 var ct = this.store.getCount();
15078 if(this.selectedIndex == -1){
15080 }else if(this.selectedIndex < ct-1){
15081 this.select(this.selectedIndex+1);
15087 selectPrev : function(){
15088 var ct = this.store.getCount();
15090 if(this.selectedIndex == -1){
15092 }else if(this.selectedIndex != 0){
15093 this.select(this.selectedIndex-1);
15099 onKeyUp : function(e){
15100 if(this.editable !== false && !e.isSpecialKey()){
15101 this.lastKey = e.getKey();
15102 this.dqTask.delay(this.queryDelay);
15107 validateBlur : function(){
15108 return !this.list || !this.list.isVisible();
15112 initQuery : function(){
15114 var v = this.getRawValue();
15116 if(this.tickable && this.editable){
15117 v = this.tickableInputEl().getValue();
15124 doForce : function(){
15125 if(this.inputEl().dom.value.length > 0){
15126 this.inputEl().dom.value =
15127 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
15133 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
15134 * query allowing the query action to be canceled if needed.
15135 * @param {String} query The SQL query to execute
15136 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
15137 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
15138 * saved in the current store (defaults to false)
15140 doQuery : function(q, forceAll){
15142 if(q === undefined || q === null){
15147 forceAll: forceAll,
15151 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
15156 forceAll = qe.forceAll;
15157 if(forceAll === true || (q.length >= this.minChars)){
15159 this.hasQuery = true;
15161 if(this.lastQuery != q || this.alwaysQuery){
15162 this.lastQuery = q;
15163 if(this.mode == 'local'){
15164 this.selectedIndex = -1;
15166 this.store.clearFilter();
15169 if(this.specialFilter){
15170 this.fireEvent('specialfilter', this);
15175 this.store.filter(this.displayField, q);
15178 this.store.fireEvent("datachanged", this.store);
15185 this.store.baseParams[this.queryParam] = q;
15187 var options = {params : this.getParams(q)};
15190 options.add = true;
15191 options.params.start = this.page * this.pageSize;
15194 this.store.load(options);
15197 * this code will make the page width larger, at the beginning, the list not align correctly,
15198 * we should expand the list on onLoad
15199 * so command out it
15204 this.selectedIndex = -1;
15209 this.loadNext = false;
15213 getParams : function(q){
15215 //p[this.queryParam] = q;
15219 p.limit = this.pageSize;
15225 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
15227 collapse : function(){
15228 if(!this.isExpanded()){
15234 this.hasFocus = false;
15238 this.cancelBtn.hide();
15239 this.trigger.show();
15242 this.tickableInputEl().dom.value = '';
15243 this.tickableInputEl().blur();
15248 Roo.get(document).un('mousedown', this.collapseIf, this);
15249 Roo.get(document).un('mousewheel', this.collapseIf, this);
15250 if (!this.editable) {
15251 Roo.get(document).un('keydown', this.listKeyPress, this);
15253 this.fireEvent('collapse', this);
15259 collapseIf : function(e){
15260 var in_combo = e.within(this.el);
15261 var in_list = e.within(this.list);
15262 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
15264 if (in_combo || in_list || is_list) {
15265 //e.stopPropagation();
15270 this.onTickableFooterButtonClick(e, false, false);
15278 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
15280 expand : function(){
15282 if(this.isExpanded() || !this.hasFocus){
15286 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
15287 this.list.setWidth(lw);
15293 this.restrictHeight();
15297 this.tickItems = Roo.apply([], this.item);
15300 this.cancelBtn.show();
15301 this.trigger.hide();
15304 this.tickableInputEl().focus();
15309 Roo.get(document).on('mousedown', this.collapseIf, this);
15310 Roo.get(document).on('mousewheel', this.collapseIf, this);
15311 if (!this.editable) {
15312 Roo.get(document).on('keydown', this.listKeyPress, this);
15315 this.fireEvent('expand', this);
15319 // Implements the default empty TriggerField.onTriggerClick function
15320 onTriggerClick : function(e)
15322 Roo.log('trigger click');
15324 if(this.disabled || !this.triggerList){
15329 this.loadNext = false;
15331 if(this.isExpanded()){
15333 if (!this.blockFocus) {
15334 this.inputEl().focus();
15338 this.hasFocus = true;
15339 if(this.triggerAction == 'all') {
15340 this.doQuery(this.allQuery, true);
15342 this.doQuery(this.getRawValue());
15344 if (!this.blockFocus) {
15345 this.inputEl().focus();
15350 onTickableTriggerClick : function(e)
15357 this.loadNext = false;
15358 this.hasFocus = true;
15360 if(this.triggerAction == 'all') {
15361 this.doQuery(this.allQuery, true);
15363 this.doQuery(this.getRawValue());
15367 onSearchFieldClick : function(e)
15369 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15370 this.onTickableFooterButtonClick(e, false, false);
15374 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15379 this.loadNext = false;
15380 this.hasFocus = true;
15382 if(this.triggerAction == 'all') {
15383 this.doQuery(this.allQuery, true);
15385 this.doQuery(this.getRawValue());
15389 listKeyPress : function(e)
15391 //Roo.log('listkeypress');
15392 // scroll to first matching element based on key pres..
15393 if (e.isSpecialKey()) {
15396 var k = String.fromCharCode(e.getKey()).toUpperCase();
15399 var csel = this.view.getSelectedNodes();
15400 var cselitem = false;
15402 var ix = this.view.indexOf(csel[0]);
15403 cselitem = this.store.getAt(ix);
15404 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15410 this.store.each(function(v) {
15412 // start at existing selection.
15413 if (cselitem.id == v.id) {
15419 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15420 match = this.store.indexOf(v);
15426 if (match === false) {
15427 return true; // no more action?
15430 this.view.select(match);
15431 var sn = Roo.get(this.view.getSelectedNodes()[0]);
15432 sn.scrollIntoView(sn.dom.parentNode, false);
15435 onViewScroll : function(e, t){
15437 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){
15441 this.hasQuery = true;
15443 this.loading = this.list.select('.loading', true).first();
15445 if(this.loading === null){
15446 this.list.createChild({
15448 cls: 'loading roo-select2-more-results roo-select2-active',
15449 html: 'Loading more results...'
15452 this.loading = this.list.select('.loading', true).first();
15454 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15456 this.loading.hide();
15459 this.loading.show();
15464 this.loadNext = true;
15466 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15471 addItem : function(o)
15473 var dv = ''; // display value
15475 if (this.displayField) {
15476 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15478 // this is an error condition!!!
15479 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
15486 var choice = this.choices.createChild({
15488 cls: 'roo-select2-search-choice',
15497 cls: 'roo-select2-search-choice-close fa fa-times',
15502 }, this.searchField);
15504 var close = choice.select('a.roo-select2-search-choice-close', true).first();
15506 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15514 this.inputEl().dom.value = '';
15519 onRemoveItem : function(e, _self, o)
15521 e.preventDefault();
15523 this.lastItem = Roo.apply([], this.item);
15525 var index = this.item.indexOf(o.data) * 1;
15528 Roo.log('not this item?!');
15532 this.item.splice(index, 1);
15537 this.fireEvent('remove', this, e);
15543 syncValue : function()
15545 if(!this.item.length){
15552 Roo.each(this.item, function(i){
15553 if(_this.valueField){
15554 value.push(i[_this.valueField]);
15561 this.value = value.join(',');
15563 if(this.hiddenField){
15564 this.hiddenField.dom.value = this.value;
15567 this.store.fireEvent("datachanged", this.store);
15572 clearItem : function()
15574 if(!this.multiple){
15580 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15588 if(this.tickable && !Roo.isTouch){
15589 this.view.refresh();
15593 inputEl: function ()
15595 if(Roo.isIOS && this.useNativeIOS){
15596 return this.el.select('select.roo-ios-select', true).first();
15599 if(Roo.isTouch && this.mobileTouchView){
15600 return this.el.select('input.form-control',true).first();
15604 return this.searchField;
15607 return this.el.select('input.form-control',true).first();
15610 onTickableFooterButtonClick : function(e, btn, el)
15612 e.preventDefault();
15614 this.lastItem = Roo.apply([], this.item);
15616 if(btn && btn.name == 'cancel'){
15617 this.tickItems = Roo.apply([], this.item);
15626 Roo.each(this.tickItems, function(o){
15634 validate : function()
15636 if(this.getVisibilityEl().hasClass('hidden')){
15640 var v = this.getRawValue();
15643 v = this.getValue();
15646 if(this.disabled || this.allowBlank || v.length){
15651 this.markInvalid();
15655 tickableInputEl : function()
15657 if(!this.tickable || !this.editable){
15658 return this.inputEl();
15661 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15665 getAutoCreateTouchView : function()
15670 cls: 'form-group' //input-group
15676 type : this.inputType,
15677 cls : 'form-control x-combo-noedit',
15678 autocomplete: 'new-password',
15679 placeholder : this.placeholder || '',
15684 input.name = this.name;
15688 input.cls += ' input-' + this.size;
15691 if (this.disabled) {
15692 input.disabled = true;
15703 inputblock.cls += ' input-group';
15705 inputblock.cn.unshift({
15707 cls : 'input-group-addon input-group-prepend input-group-text',
15712 if(this.removable && !this.multiple){
15713 inputblock.cls += ' roo-removable';
15715 inputblock.cn.push({
15718 cls : 'roo-combo-removable-btn close'
15722 if(this.hasFeedback && !this.allowBlank){
15724 inputblock.cls += ' has-feedback';
15726 inputblock.cn.push({
15728 cls: 'glyphicon form-control-feedback'
15735 inputblock.cls += (this.before) ? '' : ' input-group';
15737 inputblock.cn.push({
15739 cls : 'input-group-addon input-group-append input-group-text',
15745 var ibwrap = inputblock;
15750 cls: 'roo-select2-choices',
15754 cls: 'roo-select2-search-field',
15767 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15772 cls: 'form-hidden-field'
15778 if(!this.multiple && this.showToggleBtn){
15784 if (this.caret != false) {
15787 cls: 'fa fa-' + this.caret
15794 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15796 Roo.bootstrap.version == 3 ? caret : '',
15799 cls: 'combobox-clear',
15813 combobox.cls += ' roo-select2-container-multi';
15816 var align = this.labelAlign || this.parentLabelAlign();
15818 if (align ==='left' && this.fieldLabel.length) {
15823 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15824 tooltip : 'This field is required'
15828 cls : 'control-label col-form-label',
15829 html : this.fieldLabel
15840 var labelCfg = cfg.cn[1];
15841 var contentCfg = cfg.cn[2];
15844 if(this.indicatorpos == 'right'){
15849 cls : 'control-label col-form-label',
15853 html : this.fieldLabel
15857 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15858 tooltip : 'This field is required'
15871 labelCfg = cfg.cn[0];
15872 contentCfg = cfg.cn[1];
15877 if(this.labelWidth > 12){
15878 labelCfg.style = "width: " + this.labelWidth + 'px';
15881 if(this.labelWidth < 13 && this.labelmd == 0){
15882 this.labelmd = this.labelWidth;
15885 if(this.labellg > 0){
15886 labelCfg.cls += ' col-lg-' + this.labellg;
15887 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15890 if(this.labelmd > 0){
15891 labelCfg.cls += ' col-md-' + this.labelmd;
15892 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15895 if(this.labelsm > 0){
15896 labelCfg.cls += ' col-sm-' + this.labelsm;
15897 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15900 if(this.labelxs > 0){
15901 labelCfg.cls += ' col-xs-' + this.labelxs;
15902 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15906 } else if ( this.fieldLabel.length) {
15910 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15911 tooltip : 'This field is required'
15915 cls : 'control-label',
15916 html : this.fieldLabel
15927 if(this.indicatorpos == 'right'){
15931 cls : 'control-label',
15932 html : this.fieldLabel,
15936 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15937 tooltip : 'This field is required'
15954 var settings = this;
15956 ['xs','sm','md','lg'].map(function(size){
15957 if (settings[size]) {
15958 cfg.cls += ' col-' + size + '-' + settings[size];
15965 initTouchView : function()
15967 this.renderTouchView();
15969 this.touchViewEl.on('scroll', function(){
15970 this.el.dom.scrollTop = 0;
15973 this.originalValue = this.getValue();
15975 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15977 this.inputEl().on("click", this.showTouchView, this);
15978 if (this.triggerEl) {
15979 this.triggerEl.on("click", this.showTouchView, this);
15983 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15984 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15986 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15988 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15989 this.store.on('load', this.onTouchViewLoad, this);
15990 this.store.on('loadexception', this.onTouchViewLoadException, this);
15992 if(this.hiddenName){
15994 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15996 this.hiddenField.dom.value =
15997 this.hiddenValue !== undefined ? this.hiddenValue :
15998 this.value !== undefined ? this.value : '';
16000 this.el.dom.removeAttribute('name');
16001 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16005 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16006 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16009 if(this.removable && !this.multiple){
16010 var close = this.closeTriggerEl();
16012 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
16013 close.on('click', this.removeBtnClick, this, close);
16017 * fix the bug in Safari iOS8
16019 this.inputEl().on("focus", function(e){
16020 document.activeElement.blur();
16023 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
16030 renderTouchView : function()
16032 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
16033 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16035 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
16036 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16038 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
16039 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16040 this.touchViewBodyEl.setStyle('overflow', 'auto');
16042 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
16043 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16045 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
16046 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16050 showTouchView : function()
16056 this.touchViewHeaderEl.hide();
16058 if(this.modalTitle.length){
16059 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
16060 this.touchViewHeaderEl.show();
16063 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
16064 this.touchViewEl.show();
16066 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
16068 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
16069 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
16071 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16073 if(this.modalTitle.length){
16074 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16077 this.touchViewBodyEl.setHeight(bodyHeight);
16081 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
16083 this.touchViewEl.addClass('in');
16086 if(this._touchViewMask){
16087 Roo.get(document.body).addClass("x-body-masked");
16088 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
16089 this._touchViewMask.setStyle('z-index', 10000);
16090 this._touchViewMask.addClass('show');
16093 this.doTouchViewQuery();
16097 hideTouchView : function()
16099 this.touchViewEl.removeClass('in');
16103 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
16105 this.touchViewEl.setStyle('display', 'none');
16108 if(this._touchViewMask){
16109 this._touchViewMask.removeClass('show');
16110 Roo.get(document.body).removeClass("x-body-masked");
16114 setTouchViewValue : function()
16121 Roo.each(this.tickItems, function(o){
16126 this.hideTouchView();
16129 doTouchViewQuery : function()
16138 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
16142 if(!this.alwaysQuery || this.mode == 'local'){
16143 this.onTouchViewLoad();
16150 onTouchViewBeforeLoad : function(combo,opts)
16156 onTouchViewLoad : function()
16158 if(this.store.getCount() < 1){
16159 this.onTouchViewEmptyResults();
16163 this.clearTouchView();
16165 var rawValue = this.getRawValue();
16167 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
16169 this.tickItems = [];
16171 this.store.data.each(function(d, rowIndex){
16172 var row = this.touchViewListGroup.createChild(template);
16174 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
16175 row.addClass(d.data.cls);
16178 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16181 html : d.data[this.displayField]
16184 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
16185 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
16188 row.removeClass('selected');
16189 if(!this.multiple && this.valueField &&
16190 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
16193 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16194 row.addClass('selected');
16197 if(this.multiple && this.valueField &&
16198 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
16202 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16203 this.tickItems.push(d.data);
16206 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
16210 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
16212 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16214 if(this.modalTitle.length){
16215 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16218 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
16220 if(this.mobile_restrict_height && listHeight < bodyHeight){
16221 this.touchViewBodyEl.setHeight(listHeight);
16226 if(firstChecked && listHeight > bodyHeight){
16227 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
16232 onTouchViewLoadException : function()
16234 this.hideTouchView();
16237 onTouchViewEmptyResults : function()
16239 this.clearTouchView();
16241 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
16243 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
16247 clearTouchView : function()
16249 this.touchViewListGroup.dom.innerHTML = '';
16252 onTouchViewClick : function(e, el, o)
16254 e.preventDefault();
16257 var rowIndex = o.rowIndex;
16259 var r = this.store.getAt(rowIndex);
16261 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
16263 if(!this.multiple){
16264 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
16265 c.dom.removeAttribute('checked');
16268 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16270 this.setFromData(r.data);
16272 var close = this.closeTriggerEl();
16278 this.hideTouchView();
16280 this.fireEvent('select', this, r, rowIndex);
16285 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
16286 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
16287 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
16291 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16292 this.addItem(r.data);
16293 this.tickItems.push(r.data);
16297 getAutoCreateNativeIOS : function()
16300 cls: 'form-group' //input-group,
16305 cls : 'roo-ios-select'
16309 combobox.name = this.name;
16312 if (this.disabled) {
16313 combobox.disabled = true;
16316 var settings = this;
16318 ['xs','sm','md','lg'].map(function(size){
16319 if (settings[size]) {
16320 cfg.cls += ' col-' + size + '-' + settings[size];
16330 initIOSView : function()
16332 this.store.on('load', this.onIOSViewLoad, this);
16337 onIOSViewLoad : function()
16339 if(this.store.getCount() < 1){
16343 this.clearIOSView();
16345 if(this.allowBlank) {
16347 var default_text = '-- SELECT --';
16349 if(this.placeholder.length){
16350 default_text = this.placeholder;
16353 if(this.emptyTitle.length){
16354 default_text += ' - ' + this.emptyTitle + ' -';
16357 var opt = this.inputEl().createChild({
16360 html : default_text
16364 o[this.valueField] = 0;
16365 o[this.displayField] = default_text;
16367 this.ios_options.push({
16374 this.store.data.each(function(d, rowIndex){
16378 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16379 html = d.data[this.displayField];
16384 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16385 value = d.data[this.valueField];
16394 if(this.value == d.data[this.valueField]){
16395 option['selected'] = true;
16398 var opt = this.inputEl().createChild(option);
16400 this.ios_options.push({
16407 this.inputEl().on('change', function(){
16408 this.fireEvent('select', this);
16413 clearIOSView: function()
16415 this.inputEl().dom.innerHTML = '';
16417 this.ios_options = [];
16420 setIOSValue: function(v)
16424 if(!this.ios_options){
16428 Roo.each(this.ios_options, function(opts){
16430 opts.el.dom.removeAttribute('selected');
16432 if(opts.data[this.valueField] != v){
16436 opts.el.dom.setAttribute('selected', true);
16442 * @cfg {Boolean} grow
16446 * @cfg {Number} growMin
16450 * @cfg {Number} growMax
16459 Roo.apply(Roo.bootstrap.ComboBox, {
16463 cls: 'modal-header',
16485 cls: 'list-group-item',
16489 cls: 'roo-combobox-list-group-item-value'
16493 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16507 listItemCheckbox : {
16509 cls: 'list-group-item',
16513 cls: 'roo-combobox-list-group-item-value'
16517 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16533 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16538 cls: 'modal-footer',
16546 cls: 'col-xs-6 text-left',
16549 cls: 'btn btn-danger roo-touch-view-cancel',
16555 cls: 'col-xs-6 text-right',
16558 cls: 'btn btn-success roo-touch-view-ok',
16569 Roo.apply(Roo.bootstrap.ComboBox, {
16571 touchViewTemplate : {
16573 cls: 'modal fade roo-combobox-touch-view',
16577 cls: 'modal-dialog',
16578 style : 'position:fixed', // we have to fix position....
16582 cls: 'modal-content',
16584 Roo.bootstrap.ComboBox.header,
16585 Roo.bootstrap.ComboBox.body,
16586 Roo.bootstrap.ComboBox.footer
16595 * Ext JS Library 1.1.1
16596 * Copyright(c) 2006-2007, Ext JS, LLC.
16598 * Originally Released Under LGPL - original licence link has changed is not relivant.
16601 * <script type="text/javascript">
16606 * @extends Roo.util.Observable
16607 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16608 * This class also supports single and multi selection modes. <br>
16609 * Create a data model bound view:
16611 var store = new Roo.data.Store(...);
16613 var view = new Roo.View({
16615 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16617 singleSelect: true,
16618 selectedClass: "ydataview-selected",
16622 // listen for node click?
16623 view.on("click", function(vw, index, node, e){
16624 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16628 dataModel.load("foobar.xml");
16630 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16632 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16633 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16635 * Note: old style constructor is still suported (container, template, config)
16638 * Create a new View
16639 * @param {Object} config The config object
16642 Roo.View = function(config, depreciated_tpl, depreciated_config){
16644 this.parent = false;
16646 if (typeof(depreciated_tpl) == 'undefined') {
16647 // new way.. - universal constructor.
16648 Roo.apply(this, config);
16649 this.el = Roo.get(this.el);
16652 this.el = Roo.get(config);
16653 this.tpl = depreciated_tpl;
16654 Roo.apply(this, depreciated_config);
16656 this.wrapEl = this.el.wrap().wrap();
16657 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16660 if(typeof(this.tpl) == "string"){
16661 this.tpl = new Roo.Template(this.tpl);
16663 // support xtype ctors..
16664 this.tpl = new Roo.factory(this.tpl, Roo);
16668 this.tpl.compile();
16673 * @event beforeclick
16674 * Fires before a click is processed. Returns false to cancel the default action.
16675 * @param {Roo.View} this
16676 * @param {Number} index The index of the target node
16677 * @param {HTMLElement} node The target node
16678 * @param {Roo.EventObject} e The raw event object
16680 "beforeclick" : true,
16683 * Fires when a template node is clicked.
16684 * @param {Roo.View} this
16685 * @param {Number} index The index of the target node
16686 * @param {HTMLElement} node The target node
16687 * @param {Roo.EventObject} e The raw event object
16692 * Fires when a template node is double clicked.
16693 * @param {Roo.View} this
16694 * @param {Number} index The index of the target node
16695 * @param {HTMLElement} node The target node
16696 * @param {Roo.EventObject} e The raw event object
16700 * @event contextmenu
16701 * Fires when a template node is right clicked.
16702 * @param {Roo.View} this
16703 * @param {Number} index The index of the target node
16704 * @param {HTMLElement} node The target node
16705 * @param {Roo.EventObject} e The raw event object
16707 "contextmenu" : true,
16709 * @event selectionchange
16710 * Fires when the selected nodes change.
16711 * @param {Roo.View} this
16712 * @param {Array} selections Array of the selected nodes
16714 "selectionchange" : true,
16717 * @event beforeselect
16718 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16719 * @param {Roo.View} this
16720 * @param {HTMLElement} node The node to be selected
16721 * @param {Array} selections Array of currently selected nodes
16723 "beforeselect" : true,
16725 * @event preparedata
16726 * Fires on every row to render, to allow you to change the data.
16727 * @param {Roo.View} this
16728 * @param {Object} data to be rendered (change this)
16730 "preparedata" : true
16738 "click": this.onClick,
16739 "dblclick": this.onDblClick,
16740 "contextmenu": this.onContextMenu,
16744 this.selections = [];
16746 this.cmp = new Roo.CompositeElementLite([]);
16748 this.store = Roo.factory(this.store, Roo.data);
16749 this.setStore(this.store, true);
16752 if ( this.footer && this.footer.xtype) {
16754 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16756 this.footer.dataSource = this.store;
16757 this.footer.container = fctr;
16758 this.footer = Roo.factory(this.footer, Roo);
16759 fctr.insertFirst(this.el);
16761 // this is a bit insane - as the paging toolbar seems to detach the el..
16762 // dom.parentNode.parentNode.parentNode
16763 // they get detached?
16767 Roo.View.superclass.constructor.call(this);
16772 Roo.extend(Roo.View, Roo.util.Observable, {
16775 * @cfg {Roo.data.Store} store Data store to load data from.
16780 * @cfg {String|Roo.Element} el The container element.
16785 * @cfg {String|Roo.Template} tpl The template used by this View
16789 * @cfg {String} dataName the named area of the template to use as the data area
16790 * Works with domtemplates roo-name="name"
16794 * @cfg {String} selectedClass The css class to add to selected nodes
16796 selectedClass : "x-view-selected",
16798 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16803 * @cfg {String} text to display on mask (default Loading)
16807 * @cfg {Boolean} multiSelect Allow multiple selection
16809 multiSelect : false,
16811 * @cfg {Boolean} singleSelect Allow single selection
16813 singleSelect: false,
16816 * @cfg {Boolean} toggleSelect - selecting
16818 toggleSelect : false,
16821 * @cfg {Boolean} tickable - selecting
16826 * Returns the element this view is bound to.
16827 * @return {Roo.Element}
16829 getEl : function(){
16830 return this.wrapEl;
16836 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16838 refresh : function(){
16839 //Roo.log('refresh');
16842 // if we are using something like 'domtemplate', then
16843 // the what gets used is:
16844 // t.applySubtemplate(NAME, data, wrapping data..)
16845 // the outer template then get' applied with
16846 // the store 'extra data'
16847 // and the body get's added to the
16848 // roo-name="data" node?
16849 // <span class='roo-tpl-{name}'></span> ?????
16853 this.clearSelections();
16854 this.el.update("");
16856 var records = this.store.getRange();
16857 if(records.length < 1) {
16859 // is this valid?? = should it render a template??
16861 this.el.update(this.emptyText);
16865 if (this.dataName) {
16866 this.el.update(t.apply(this.store.meta)); //????
16867 el = this.el.child('.roo-tpl-' + this.dataName);
16870 for(var i = 0, len = records.length; i < len; i++){
16871 var data = this.prepareData(records[i].data, i, records[i]);
16872 this.fireEvent("preparedata", this, data, i, records[i]);
16874 var d = Roo.apply({}, data);
16877 Roo.apply(d, {'roo-id' : Roo.id()});
16881 Roo.each(this.parent.item, function(item){
16882 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16885 Roo.apply(d, {'roo-data-checked' : 'checked'});
16889 html[html.length] = Roo.util.Format.trim(
16891 t.applySubtemplate(this.dataName, d, this.store.meta) :
16898 el.update(html.join(""));
16899 this.nodes = el.dom.childNodes;
16900 this.updateIndexes(0);
16905 * Function to override to reformat the data that is sent to
16906 * the template for each node.
16907 * DEPRICATED - use the preparedata event handler.
16908 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16909 * a JSON object for an UpdateManager bound view).
16911 prepareData : function(data, index, record)
16913 this.fireEvent("preparedata", this, data, index, record);
16917 onUpdate : function(ds, record){
16918 // Roo.log('on update');
16919 this.clearSelections();
16920 var index = this.store.indexOf(record);
16921 var n = this.nodes[index];
16922 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16923 n.parentNode.removeChild(n);
16924 this.updateIndexes(index, index);
16930 onAdd : function(ds, records, index)
16932 //Roo.log(['on Add', ds, records, index] );
16933 this.clearSelections();
16934 if(this.nodes.length == 0){
16938 var n = this.nodes[index];
16939 for(var i = 0, len = records.length; i < len; i++){
16940 var d = this.prepareData(records[i].data, i, records[i]);
16942 this.tpl.insertBefore(n, d);
16945 this.tpl.append(this.el, d);
16948 this.updateIndexes(index);
16951 onRemove : function(ds, record, index){
16952 // Roo.log('onRemove');
16953 this.clearSelections();
16954 var el = this.dataName ?
16955 this.el.child('.roo-tpl-' + this.dataName) :
16958 el.dom.removeChild(this.nodes[index]);
16959 this.updateIndexes(index);
16963 * Refresh an individual node.
16964 * @param {Number} index
16966 refreshNode : function(index){
16967 this.onUpdate(this.store, this.store.getAt(index));
16970 updateIndexes : function(startIndex, endIndex){
16971 var ns = this.nodes;
16972 startIndex = startIndex || 0;
16973 endIndex = endIndex || ns.length - 1;
16974 for(var i = startIndex; i <= endIndex; i++){
16975 ns[i].nodeIndex = i;
16980 * Changes the data store this view uses and refresh the view.
16981 * @param {Store} store
16983 setStore : function(store, initial){
16984 if(!initial && this.store){
16985 this.store.un("datachanged", this.refresh);
16986 this.store.un("add", this.onAdd);
16987 this.store.un("remove", this.onRemove);
16988 this.store.un("update", this.onUpdate);
16989 this.store.un("clear", this.refresh);
16990 this.store.un("beforeload", this.onBeforeLoad);
16991 this.store.un("load", this.onLoad);
16992 this.store.un("loadexception", this.onLoad);
16996 store.on("datachanged", this.refresh, this);
16997 store.on("add", this.onAdd, this);
16998 store.on("remove", this.onRemove, this);
16999 store.on("update", this.onUpdate, this);
17000 store.on("clear", this.refresh, this);
17001 store.on("beforeload", this.onBeforeLoad, this);
17002 store.on("load", this.onLoad, this);
17003 store.on("loadexception", this.onLoad, this);
17011 * onbeforeLoad - masks the loading area.
17014 onBeforeLoad : function(store,opts)
17016 //Roo.log('onBeforeLoad');
17018 this.el.update("");
17020 this.el.mask(this.mask ? this.mask : "Loading" );
17022 onLoad : function ()
17029 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
17030 * @param {HTMLElement} node
17031 * @return {HTMLElement} The template node
17033 findItemFromChild : function(node){
17034 var el = this.dataName ?
17035 this.el.child('.roo-tpl-' + this.dataName,true) :
17038 if(!node || node.parentNode == el){
17041 var p = node.parentNode;
17042 while(p && p != el){
17043 if(p.parentNode == el){
17052 onClick : function(e){
17053 var item = this.findItemFromChild(e.getTarget());
17055 var index = this.indexOf(item);
17056 if(this.onItemClick(item, index, e) !== false){
17057 this.fireEvent("click", this, index, item, e);
17060 this.clearSelections();
17065 onContextMenu : function(e){
17066 var item = this.findItemFromChild(e.getTarget());
17068 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
17073 onDblClick : function(e){
17074 var item = this.findItemFromChild(e.getTarget());
17076 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
17080 onItemClick : function(item, index, e)
17082 if(this.fireEvent("beforeclick", this, index, item, e) === false){
17085 if (this.toggleSelect) {
17086 var m = this.isSelected(item) ? 'unselect' : 'select';
17089 _t[m](item, true, false);
17092 if(this.multiSelect || this.singleSelect){
17093 if(this.multiSelect && e.shiftKey && this.lastSelection){
17094 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
17096 this.select(item, this.multiSelect && e.ctrlKey);
17097 this.lastSelection = item;
17100 if(!this.tickable){
17101 e.preventDefault();
17109 * Get the number of selected nodes.
17112 getSelectionCount : function(){
17113 return this.selections.length;
17117 * Get the currently selected nodes.
17118 * @return {Array} An array of HTMLElements
17120 getSelectedNodes : function(){
17121 return this.selections;
17125 * Get the indexes of the selected nodes.
17128 getSelectedIndexes : function(){
17129 var indexes = [], s = this.selections;
17130 for(var i = 0, len = s.length; i < len; i++){
17131 indexes.push(s[i].nodeIndex);
17137 * Clear all selections
17138 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
17140 clearSelections : function(suppressEvent){
17141 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
17142 this.cmp.elements = this.selections;
17143 this.cmp.removeClass(this.selectedClass);
17144 this.selections = [];
17145 if(!suppressEvent){
17146 this.fireEvent("selectionchange", this, this.selections);
17152 * Returns true if the passed node is selected
17153 * @param {HTMLElement/Number} node The node or node index
17154 * @return {Boolean}
17156 isSelected : function(node){
17157 var s = this.selections;
17161 node = this.getNode(node);
17162 return s.indexOf(node) !== -1;
17167 * @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
17168 * @param {Boolean} keepExisting (optional) true to keep existing selections
17169 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17171 select : function(nodeInfo, keepExisting, suppressEvent){
17172 if(nodeInfo instanceof Array){
17174 this.clearSelections(true);
17176 for(var i = 0, len = nodeInfo.length; i < len; i++){
17177 this.select(nodeInfo[i], true, true);
17181 var node = this.getNode(nodeInfo);
17182 if(!node || this.isSelected(node)){
17183 return; // already selected.
17186 this.clearSelections(true);
17189 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
17190 Roo.fly(node).addClass(this.selectedClass);
17191 this.selections.push(node);
17192 if(!suppressEvent){
17193 this.fireEvent("selectionchange", this, this.selections);
17201 * @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
17202 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
17203 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17205 unselect : function(nodeInfo, keepExisting, suppressEvent)
17207 if(nodeInfo instanceof Array){
17208 Roo.each(this.selections, function(s) {
17209 this.unselect(s, nodeInfo);
17213 var node = this.getNode(nodeInfo);
17214 if(!node || !this.isSelected(node)){
17215 //Roo.log("not selected");
17216 return; // not selected.
17220 Roo.each(this.selections, function(s) {
17222 Roo.fly(node).removeClass(this.selectedClass);
17229 this.selections= ns;
17230 this.fireEvent("selectionchange", this, this.selections);
17234 * Gets a template node.
17235 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17236 * @return {HTMLElement} The node or null if it wasn't found
17238 getNode : function(nodeInfo){
17239 if(typeof nodeInfo == "string"){
17240 return document.getElementById(nodeInfo);
17241 }else if(typeof nodeInfo == "number"){
17242 return this.nodes[nodeInfo];
17248 * Gets a range template nodes.
17249 * @param {Number} startIndex
17250 * @param {Number} endIndex
17251 * @return {Array} An array of nodes
17253 getNodes : function(start, end){
17254 var ns = this.nodes;
17255 start = start || 0;
17256 end = typeof end == "undefined" ? ns.length - 1 : end;
17259 for(var i = start; i <= end; i++){
17263 for(var i = start; i >= end; i--){
17271 * Finds the index of the passed node
17272 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17273 * @return {Number} The index of the node or -1
17275 indexOf : function(node){
17276 node = this.getNode(node);
17277 if(typeof node.nodeIndex == "number"){
17278 return node.nodeIndex;
17280 var ns = this.nodes;
17281 for(var i = 0, len = ns.length; i < len; i++){
17292 * based on jquery fullcalendar
17296 Roo.bootstrap = Roo.bootstrap || {};
17298 * @class Roo.bootstrap.Calendar
17299 * @extends Roo.bootstrap.Component
17300 * Bootstrap Calendar class
17301 * @cfg {Boolean} loadMask (true|false) default false
17302 * @cfg {Object} header generate the user specific header of the calendar, default false
17305 * Create a new Container
17306 * @param {Object} config The config object
17311 Roo.bootstrap.Calendar = function(config){
17312 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
17316 * Fires when a date is selected
17317 * @param {DatePicker} this
17318 * @param {Date} date The selected date
17322 * @event monthchange
17323 * Fires when the displayed month changes
17324 * @param {DatePicker} this
17325 * @param {Date} date The selected month
17327 'monthchange': true,
17329 * @event evententer
17330 * Fires when mouse over an event
17331 * @param {Calendar} this
17332 * @param {event} Event
17334 'evententer': true,
17336 * @event eventleave
17337 * Fires when the mouse leaves an
17338 * @param {Calendar} this
17341 'eventleave': true,
17343 * @event eventclick
17344 * Fires when the mouse click an
17345 * @param {Calendar} this
17354 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
17357 * @cfg {Number} startDay
17358 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17366 getAutoCreate : function(){
17369 var fc_button = function(name, corner, style, content ) {
17370 return Roo.apply({},{
17372 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
17374 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17377 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17388 style : 'width:100%',
17395 cls : 'fc-header-left',
17397 fc_button('prev', 'left', 'arrow', '‹' ),
17398 fc_button('next', 'right', 'arrow', '›' ),
17399 { tag: 'span', cls: 'fc-header-space' },
17400 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
17408 cls : 'fc-header-center',
17412 cls: 'fc-header-title',
17415 html : 'month / year'
17423 cls : 'fc-header-right',
17425 /* fc_button('month', 'left', '', 'month' ),
17426 fc_button('week', '', '', 'week' ),
17427 fc_button('day', 'right', '', 'day' )
17439 header = this.header;
17442 var cal_heads = function() {
17444 // fixme - handle this.
17446 for (var i =0; i < Date.dayNames.length; i++) {
17447 var d = Date.dayNames[i];
17450 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17451 html : d.substring(0,3)
17455 ret[0].cls += ' fc-first';
17456 ret[6].cls += ' fc-last';
17459 var cal_cell = function(n) {
17462 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17467 cls: 'fc-day-number',
17471 cls: 'fc-day-content',
17475 style: 'position: relative;' // height: 17px;
17487 var cal_rows = function() {
17490 for (var r = 0; r < 6; r++) {
17497 for (var i =0; i < Date.dayNames.length; i++) {
17498 var d = Date.dayNames[i];
17499 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17502 row.cn[0].cls+=' fc-first';
17503 row.cn[0].cn[0].style = 'min-height:90px';
17504 row.cn[6].cls+=' fc-last';
17508 ret[0].cls += ' fc-first';
17509 ret[4].cls += ' fc-prev-last';
17510 ret[5].cls += ' fc-last';
17517 cls: 'fc-border-separate',
17518 style : 'width:100%',
17526 cls : 'fc-first fc-last',
17544 cls : 'fc-content',
17545 style : "position: relative;",
17548 cls : 'fc-view fc-view-month fc-grid',
17549 style : 'position: relative',
17550 unselectable : 'on',
17553 cls : 'fc-event-container',
17554 style : 'position:absolute;z-index:8;top:0;left:0;'
17572 initEvents : function()
17575 throw "can not find store for calendar";
17581 style: "text-align:center",
17585 style: "background-color:white;width:50%;margin:250 auto",
17589 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17600 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17602 var size = this.el.select('.fc-content', true).first().getSize();
17603 this.maskEl.setSize(size.width, size.height);
17604 this.maskEl.enableDisplayMode("block");
17605 if(!this.loadMask){
17606 this.maskEl.hide();
17609 this.store = Roo.factory(this.store, Roo.data);
17610 this.store.on('load', this.onLoad, this);
17611 this.store.on('beforeload', this.onBeforeLoad, this);
17615 this.cells = this.el.select('.fc-day',true);
17616 //Roo.log(this.cells);
17617 this.textNodes = this.el.query('.fc-day-number');
17618 this.cells.addClassOnOver('fc-state-hover');
17620 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17621 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17622 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17623 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17625 this.on('monthchange', this.onMonthChange, this);
17627 this.update(new Date().clearTime());
17630 resize : function() {
17631 var sz = this.el.getSize();
17633 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17634 this.el.select('.fc-day-content div',true).setHeight(34);
17639 showPrevMonth : function(e){
17640 this.update(this.activeDate.add("mo", -1));
17642 showToday : function(e){
17643 this.update(new Date().clearTime());
17646 showNextMonth : function(e){
17647 this.update(this.activeDate.add("mo", 1));
17651 showPrevYear : function(){
17652 this.update(this.activeDate.add("y", -1));
17656 showNextYear : function(){
17657 this.update(this.activeDate.add("y", 1));
17662 update : function(date)
17664 var vd = this.activeDate;
17665 this.activeDate = date;
17666 // if(vd && this.el){
17667 // var t = date.getTime();
17668 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17669 // Roo.log('using add remove');
17671 // this.fireEvent('monthchange', this, date);
17673 // this.cells.removeClass("fc-state-highlight");
17674 // this.cells.each(function(c){
17675 // if(c.dateValue == t){
17676 // c.addClass("fc-state-highlight");
17677 // setTimeout(function(){
17678 // try{c.dom.firstChild.focus();}catch(e){}
17688 var days = date.getDaysInMonth();
17690 var firstOfMonth = date.getFirstDateOfMonth();
17691 var startingPos = firstOfMonth.getDay()-this.startDay;
17693 if(startingPos < this.startDay){
17697 var pm = date.add(Date.MONTH, -1);
17698 var prevStart = pm.getDaysInMonth()-startingPos;
17700 this.cells = this.el.select('.fc-day',true);
17701 this.textNodes = this.el.query('.fc-day-number');
17702 this.cells.addClassOnOver('fc-state-hover');
17704 var cells = this.cells.elements;
17705 var textEls = this.textNodes;
17707 Roo.each(cells, function(cell){
17708 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17711 days += startingPos;
17713 // convert everything to numbers so it's fast
17714 var day = 86400000;
17715 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17718 //Roo.log(prevStart);
17720 var today = new Date().clearTime().getTime();
17721 var sel = date.clearTime().getTime();
17722 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17723 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17724 var ddMatch = this.disabledDatesRE;
17725 var ddText = this.disabledDatesText;
17726 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17727 var ddaysText = this.disabledDaysText;
17728 var format = this.format;
17730 var setCellClass = function(cal, cell){
17734 //Roo.log('set Cell Class');
17736 var t = d.getTime();
17740 cell.dateValue = t;
17742 cell.className += " fc-today";
17743 cell.className += " fc-state-highlight";
17744 cell.title = cal.todayText;
17747 // disable highlight in other month..
17748 //cell.className += " fc-state-highlight";
17753 cell.className = " fc-state-disabled";
17754 cell.title = cal.minText;
17758 cell.className = " fc-state-disabled";
17759 cell.title = cal.maxText;
17763 if(ddays.indexOf(d.getDay()) != -1){
17764 cell.title = ddaysText;
17765 cell.className = " fc-state-disabled";
17768 if(ddMatch && format){
17769 var fvalue = d.dateFormat(format);
17770 if(ddMatch.test(fvalue)){
17771 cell.title = ddText.replace("%0", fvalue);
17772 cell.className = " fc-state-disabled";
17776 if (!cell.initialClassName) {
17777 cell.initialClassName = cell.dom.className;
17780 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17785 for(; i < startingPos; i++) {
17786 textEls[i].innerHTML = (++prevStart);
17787 d.setDate(d.getDate()+1);
17789 cells[i].className = "fc-past fc-other-month";
17790 setCellClass(this, cells[i]);
17795 for(; i < days; i++){
17796 intDay = i - startingPos + 1;
17797 textEls[i].innerHTML = (intDay);
17798 d.setDate(d.getDate()+1);
17800 cells[i].className = ''; // "x-date-active";
17801 setCellClass(this, cells[i]);
17805 for(; i < 42; i++) {
17806 textEls[i].innerHTML = (++extraDays);
17807 d.setDate(d.getDate()+1);
17809 cells[i].className = "fc-future fc-other-month";
17810 setCellClass(this, cells[i]);
17813 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17815 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17817 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17818 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17820 if(totalRows != 6){
17821 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17822 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17825 this.fireEvent('monthchange', this, date);
17829 if(!this.internalRender){
17830 var main = this.el.dom.firstChild;
17831 var w = main.offsetWidth;
17832 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17833 Roo.fly(main).setWidth(w);
17834 this.internalRender = true;
17835 // opera does not respect the auto grow header center column
17836 // then, after it gets a width opera refuses to recalculate
17837 // without a second pass
17838 if(Roo.isOpera && !this.secondPass){
17839 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17840 this.secondPass = true;
17841 this.update.defer(10, this, [date]);
17848 findCell : function(dt) {
17849 dt = dt.clearTime().getTime();
17851 this.cells.each(function(c){
17852 //Roo.log("check " +c.dateValue + '?=' + dt);
17853 if(c.dateValue == dt){
17863 findCells : function(ev) {
17864 var s = ev.start.clone().clearTime().getTime();
17866 var e= ev.end.clone().clearTime().getTime();
17869 this.cells.each(function(c){
17870 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17872 if(c.dateValue > e){
17875 if(c.dateValue < s){
17884 // findBestRow: function(cells)
17888 // for (var i =0 ; i < cells.length;i++) {
17889 // ret = Math.max(cells[i].rows || 0,ret);
17896 addItem : function(ev)
17898 // look for vertical location slot in
17899 var cells = this.findCells(ev);
17901 // ev.row = this.findBestRow(cells);
17903 // work out the location.
17907 for(var i =0; i < cells.length; i++) {
17909 cells[i].row = cells[0].row;
17912 cells[i].row = cells[i].row + 1;
17922 if (crow.start.getY() == cells[i].getY()) {
17924 crow.end = cells[i];
17941 cells[0].events.push(ev);
17943 this.calevents.push(ev);
17946 clearEvents: function() {
17948 if(!this.calevents){
17952 Roo.each(this.cells.elements, function(c){
17958 Roo.each(this.calevents, function(e) {
17959 Roo.each(e.els, function(el) {
17960 el.un('mouseenter' ,this.onEventEnter, this);
17961 el.un('mouseleave' ,this.onEventLeave, this);
17966 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17972 renderEvents: function()
17976 this.cells.each(function(c) {
17985 if(c.row != c.events.length){
17986 r = 4 - (4 - (c.row - c.events.length));
17989 c.events = ev.slice(0, r);
17990 c.more = ev.slice(r);
17992 if(c.more.length && c.more.length == 1){
17993 c.events.push(c.more.pop());
17996 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
18000 this.cells.each(function(c) {
18002 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
18005 for (var e = 0; e < c.events.length; e++){
18006 var ev = c.events[e];
18007 var rows = ev.rows;
18009 for(var i = 0; i < rows.length; i++) {
18011 // how many rows should it span..
18014 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
18015 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
18017 unselectable : "on",
18020 cls: 'fc-event-inner',
18024 // cls: 'fc-event-time',
18025 // html : cells.length > 1 ? '' : ev.time
18029 cls: 'fc-event-title',
18030 html : String.format('{0}', ev.title)
18037 cls: 'ui-resizable-handle ui-resizable-e',
18038 html : '  '
18045 cfg.cls += ' fc-event-start';
18047 if ((i+1) == rows.length) {
18048 cfg.cls += ' fc-event-end';
18051 var ctr = _this.el.select('.fc-event-container',true).first();
18052 var cg = ctr.createChild(cfg);
18054 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
18055 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
18057 var r = (c.more.length) ? 1 : 0;
18058 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
18059 cg.setWidth(ebox.right - sbox.x -2);
18061 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
18062 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
18063 cg.on('click', _this.onEventClick, _this, ev);
18074 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
18075 style : 'position: absolute',
18076 unselectable : "on",
18079 cls: 'fc-event-inner',
18083 cls: 'fc-event-title',
18091 cls: 'ui-resizable-handle ui-resizable-e',
18092 html : '  '
18098 var ctr = _this.el.select('.fc-event-container',true).first();
18099 var cg = ctr.createChild(cfg);
18101 var sbox = c.select('.fc-day-content',true).first().getBox();
18102 var ebox = c.select('.fc-day-content',true).first().getBox();
18104 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
18105 cg.setWidth(ebox.right - sbox.x -2);
18107 cg.on('click', _this.onMoreEventClick, _this, c.more);
18117 onEventEnter: function (e, el,event,d) {
18118 this.fireEvent('evententer', this, el, event);
18121 onEventLeave: function (e, el,event,d) {
18122 this.fireEvent('eventleave', this, el, event);
18125 onEventClick: function (e, el,event,d) {
18126 this.fireEvent('eventclick', this, el, event);
18129 onMonthChange: function () {
18133 onMoreEventClick: function(e, el, more)
18137 this.calpopover.placement = 'right';
18138 this.calpopover.setTitle('More');
18140 this.calpopover.setContent('');
18142 var ctr = this.calpopover.el.select('.popover-content', true).first();
18144 Roo.each(more, function(m){
18146 cls : 'fc-event-hori fc-event-draggable',
18149 var cg = ctr.createChild(cfg);
18151 cg.on('click', _this.onEventClick, _this, m);
18154 this.calpopover.show(el);
18159 onLoad: function ()
18161 this.calevents = [];
18164 if(this.store.getCount() > 0){
18165 this.store.data.each(function(d){
18168 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
18169 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
18170 time : d.data.start_time,
18171 title : d.data.title,
18172 description : d.data.description,
18173 venue : d.data.venue
18178 this.renderEvents();
18180 if(this.calevents.length && this.loadMask){
18181 this.maskEl.hide();
18185 onBeforeLoad: function()
18187 this.clearEvents();
18189 this.maskEl.show();
18203 * @class Roo.bootstrap.Popover
18204 * @extends Roo.bootstrap.Component
18205 * Bootstrap Popover class
18206 * @cfg {String} html contents of the popover (or false to use children..)
18207 * @cfg {String} title of popover (or false to hide)
18208 * @cfg {String} placement how it is placed
18209 * @cfg {String} trigger click || hover (or false to trigger manually)
18210 * @cfg {String} over what (parent or false to trigger manually.)
18211 * @cfg {Number} delay - delay before showing
18214 * Create a new Popover
18215 * @param {Object} config The config object
18218 Roo.bootstrap.Popover = function(config){
18219 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
18225 * After the popover show
18227 * @param {Roo.bootstrap.Popover} this
18232 * After the popover hide
18234 * @param {Roo.bootstrap.Popover} this
18240 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
18242 title: 'Fill in a title',
18245 placement : 'right',
18246 trigger : 'hover', // hover
18252 can_build_overlaid : false,
18254 getChildContainer : function()
18256 return this.el.select('.popover-content',true).first();
18259 getAutoCreate : function(){
18262 cls : 'popover roo-dynamic',
18263 style: 'display:block',
18269 cls : 'popover-inner',
18273 cls: 'popover-title popover-header',
18277 cls : 'popover-content popover-body',
18288 setTitle: function(str)
18291 this.el.select('.popover-title',true).first().dom.innerHTML = str;
18293 setContent: function(str)
18296 this.el.select('.popover-content',true).first().dom.innerHTML = str;
18298 // as it get's added to the bottom of the page.
18299 onRender : function(ct, position)
18301 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18303 var cfg = Roo.apply({}, this.getAutoCreate());
18307 cfg.cls += ' ' + this.cls;
18310 cfg.style = this.style;
18312 //Roo.log("adding to ");
18313 this.el = Roo.get(document.body).createChild(cfg, position);
18314 // Roo.log(this.el);
18319 initEvents : function()
18321 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
18322 this.el.enableDisplayMode('block');
18324 if (this.over === false) {
18327 if (this.triggers === false) {
18330 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18331 var triggers = this.trigger ? this.trigger.split(' ') : [];
18332 Roo.each(triggers, function(trigger) {
18334 if (trigger == 'click') {
18335 on_el.on('click', this.toggle, this);
18336 } else if (trigger != 'manual') {
18337 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
18338 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
18340 on_el.on(eventIn ,this.enter, this);
18341 on_el.on(eventOut, this.leave, this);
18352 toggle : function () {
18353 this.hoverState == 'in' ? this.leave() : this.enter();
18356 enter : function () {
18358 clearTimeout(this.timeout);
18360 this.hoverState = 'in';
18362 if (!this.delay || !this.delay.show) {
18367 this.timeout = setTimeout(function () {
18368 if (_t.hoverState == 'in') {
18371 }, this.delay.show)
18374 leave : function() {
18375 clearTimeout(this.timeout);
18377 this.hoverState = 'out';
18379 if (!this.delay || !this.delay.hide) {
18384 this.timeout = setTimeout(function () {
18385 if (_t.hoverState == 'out') {
18388 }, this.delay.hide)
18391 show : function (on_el)
18394 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18398 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18399 if (this.html !== false) {
18400 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18402 this.el.removeClass([
18403 'fade','top','bottom', 'left', 'right','in',
18404 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18406 if (!this.title.length) {
18407 this.el.select('.popover-title',true).hide();
18410 var placement = typeof this.placement == 'function' ?
18411 this.placement.call(this, this.el, on_el) :
18414 var autoToken = /\s?auto?\s?/i;
18415 var autoPlace = autoToken.test(placement);
18417 placement = placement.replace(autoToken, '') || 'top';
18421 //this.el.setXY([0,0]);
18423 this.el.dom.style.display='block';
18424 this.el.addClass(placement);
18426 //this.el.appendTo(on_el);
18428 var p = this.getPosition();
18429 var box = this.el.getBox();
18434 var align = Roo.bootstrap.Popover.alignment[placement];
18437 this.el.alignTo(on_el, align[0],align[1]);
18438 //var arrow = this.el.select('.arrow',true).first();
18439 //arrow.set(align[2],
18441 this.el.addClass('in');
18444 if (this.el.hasClass('fade')) {
18448 this.hoverState = 'in';
18450 this.fireEvent('show', this);
18455 this.el.setXY([0,0]);
18456 this.el.removeClass('in');
18458 this.hoverState = null;
18460 this.fireEvent('hide', this);
18465 Roo.bootstrap.Popover.alignment = {
18466 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18467 'right' : ['l-r', [10,0], 'left bs-popover-left'],
18468 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18469 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18480 * @class Roo.bootstrap.Progress
18481 * @extends Roo.bootstrap.Component
18482 * Bootstrap Progress class
18483 * @cfg {Boolean} striped striped of the progress bar
18484 * @cfg {Boolean} active animated of the progress bar
18488 * Create a new Progress
18489 * @param {Object} config The config object
18492 Roo.bootstrap.Progress = function(config){
18493 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18496 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
18501 getAutoCreate : function(){
18509 cfg.cls += ' progress-striped';
18513 cfg.cls += ' active';
18532 * @class Roo.bootstrap.ProgressBar
18533 * @extends Roo.bootstrap.Component
18534 * Bootstrap ProgressBar class
18535 * @cfg {Number} aria_valuenow aria-value now
18536 * @cfg {Number} aria_valuemin aria-value min
18537 * @cfg {Number} aria_valuemax aria-value max
18538 * @cfg {String} label label for the progress bar
18539 * @cfg {String} panel (success | info | warning | danger )
18540 * @cfg {String} role role of the progress bar
18541 * @cfg {String} sr_only text
18545 * Create a new ProgressBar
18546 * @param {Object} config The config object
18549 Roo.bootstrap.ProgressBar = function(config){
18550 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18553 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
18557 aria_valuemax : 100,
18563 getAutoCreate : function()
18568 cls: 'progress-bar',
18569 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18581 cfg.role = this.role;
18584 if(this.aria_valuenow){
18585 cfg['aria-valuenow'] = this.aria_valuenow;
18588 if(this.aria_valuemin){
18589 cfg['aria-valuemin'] = this.aria_valuemin;
18592 if(this.aria_valuemax){
18593 cfg['aria-valuemax'] = this.aria_valuemax;
18596 if(this.label && !this.sr_only){
18597 cfg.html = this.label;
18601 cfg.cls += ' progress-bar-' + this.panel;
18607 update : function(aria_valuenow)
18609 this.aria_valuenow = aria_valuenow;
18611 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18626 * @class Roo.bootstrap.TabGroup
18627 * @extends Roo.bootstrap.Column
18628 * Bootstrap Column class
18629 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18630 * @cfg {Boolean} carousel true to make the group behave like a carousel
18631 * @cfg {Boolean} bullets show bullets for the panels
18632 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18633 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18634 * @cfg {Boolean} showarrow (true|false) show arrow default true
18637 * Create a new TabGroup
18638 * @param {Object} config The config object
18641 Roo.bootstrap.TabGroup = function(config){
18642 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18644 this.navId = Roo.id();
18647 Roo.bootstrap.TabGroup.register(this);
18651 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18654 transition : false,
18659 slideOnTouch : false,
18662 getAutoCreate : function()
18664 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18666 cfg.cls += ' tab-content';
18668 if (this.carousel) {
18669 cfg.cls += ' carousel slide';
18672 cls : 'carousel-inner',
18676 if(this.bullets && !Roo.isTouch){
18679 cls : 'carousel-bullets',
18683 if(this.bullets_cls){
18684 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18691 cfg.cn[0].cn.push(bullets);
18694 if(this.showarrow){
18695 cfg.cn[0].cn.push({
18697 class : 'carousel-arrow',
18701 class : 'carousel-prev',
18705 class : 'fa fa-chevron-left'
18711 class : 'carousel-next',
18715 class : 'fa fa-chevron-right'
18728 initEvents: function()
18730 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18731 // this.el.on("touchstart", this.onTouchStart, this);
18734 if(this.autoslide){
18737 this.slideFn = window.setInterval(function() {
18738 _this.showPanelNext();
18742 if(this.showarrow){
18743 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18744 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18750 // onTouchStart : function(e, el, o)
18752 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18756 // this.showPanelNext();
18760 getChildContainer : function()
18762 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18766 * register a Navigation item
18767 * @param {Roo.bootstrap.NavItem} the navitem to add
18769 register : function(item)
18771 this.tabs.push( item);
18772 item.navId = this.navId; // not really needed..
18777 getActivePanel : function()
18780 Roo.each(this.tabs, function(t) {
18790 getPanelByName : function(n)
18793 Roo.each(this.tabs, function(t) {
18794 if (t.tabId == n) {
18802 indexOfPanel : function(p)
18805 Roo.each(this.tabs, function(t,i) {
18806 if (t.tabId == p.tabId) {
18815 * show a specific panel
18816 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18817 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18819 showPanel : function (pan)
18821 if(this.transition || typeof(pan) == 'undefined'){
18822 Roo.log("waiting for the transitionend");
18826 if (typeof(pan) == 'number') {
18827 pan = this.tabs[pan];
18830 if (typeof(pan) == 'string') {
18831 pan = this.getPanelByName(pan);
18834 var cur = this.getActivePanel();
18837 Roo.log('pan or acitve pan is undefined');
18841 if (pan.tabId == this.getActivePanel().tabId) {
18845 if (false === cur.fireEvent('beforedeactivate')) {
18849 if(this.bullets > 0 && !Roo.isTouch){
18850 this.setActiveBullet(this.indexOfPanel(pan));
18853 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18855 //class="carousel-item carousel-item-next carousel-item-left"
18857 this.transition = true;
18858 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18859 var lr = dir == 'next' ? 'left' : 'right';
18860 pan.el.addClass(dir); // or prev
18861 pan.el.addClass('carousel-item-' + dir); // or prev
18862 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18863 cur.el.addClass(lr); // or right
18864 pan.el.addClass(lr);
18865 cur.el.addClass('carousel-item-' +lr); // or right
18866 pan.el.addClass('carousel-item-' +lr);
18870 cur.el.on('transitionend', function() {
18871 Roo.log("trans end?");
18873 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18874 pan.setActive(true);
18876 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18877 cur.setActive(false);
18879 _this.transition = false;
18881 }, this, { single: true } );
18886 cur.setActive(false);
18887 pan.setActive(true);
18892 showPanelNext : function()
18894 var i = this.indexOfPanel(this.getActivePanel());
18896 if (i >= this.tabs.length - 1 && !this.autoslide) {
18900 if (i >= this.tabs.length - 1 && this.autoslide) {
18904 this.showPanel(this.tabs[i+1]);
18907 showPanelPrev : function()
18909 var i = this.indexOfPanel(this.getActivePanel());
18911 if (i < 1 && !this.autoslide) {
18915 if (i < 1 && this.autoslide) {
18916 i = this.tabs.length;
18919 this.showPanel(this.tabs[i-1]);
18923 addBullet: function()
18925 if(!this.bullets || Roo.isTouch){
18928 var ctr = this.el.select('.carousel-bullets',true).first();
18929 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18930 var bullet = ctr.createChild({
18931 cls : 'bullet bullet-' + i
18932 },ctr.dom.lastChild);
18937 bullet.on('click', (function(e, el, o, ii, t){
18939 e.preventDefault();
18941 this.showPanel(ii);
18943 if(this.autoslide && this.slideFn){
18944 clearInterval(this.slideFn);
18945 this.slideFn = window.setInterval(function() {
18946 _this.showPanelNext();
18950 }).createDelegate(this, [i, bullet], true));
18955 setActiveBullet : function(i)
18961 Roo.each(this.el.select('.bullet', true).elements, function(el){
18962 el.removeClass('selected');
18965 var bullet = this.el.select('.bullet-' + i, true).first();
18971 bullet.addClass('selected');
18982 Roo.apply(Roo.bootstrap.TabGroup, {
18986 * register a Navigation Group
18987 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18989 register : function(navgrp)
18991 this.groups[navgrp.navId] = navgrp;
18995 * fetch a Navigation Group based on the navigation ID
18996 * if one does not exist , it will get created.
18997 * @param {string} the navgroup to add
18998 * @returns {Roo.bootstrap.NavGroup} the navgroup
19000 get: function(navId) {
19001 if (typeof(this.groups[navId]) == 'undefined') {
19002 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
19004 return this.groups[navId] ;
19019 * @class Roo.bootstrap.TabPanel
19020 * @extends Roo.bootstrap.Component
19021 * Bootstrap TabPanel class
19022 * @cfg {Boolean} active panel active
19023 * @cfg {String} html panel content
19024 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
19025 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
19026 * @cfg {String} href click to link..
19030 * Create a new TabPanel
19031 * @param {Object} config The config object
19034 Roo.bootstrap.TabPanel = function(config){
19035 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
19039 * Fires when the active status changes
19040 * @param {Roo.bootstrap.TabPanel} this
19041 * @param {Boolean} state the new state
19046 * @event beforedeactivate
19047 * Fires before a tab is de-activated - can be used to do validation on a form.
19048 * @param {Roo.bootstrap.TabPanel} this
19049 * @return {Boolean} false if there is an error
19052 'beforedeactivate': true
19055 this.tabId = this.tabId || Roo.id();
19059 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
19067 getAutoCreate : function(){
19072 // item is needed for carousel - not sure if it has any effect otherwise
19073 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
19074 html: this.html || ''
19078 cfg.cls += ' active';
19082 cfg.tabId = this.tabId;
19090 initEvents: function()
19092 var p = this.parent();
19094 this.navId = this.navId || p.navId;
19096 if (typeof(this.navId) != 'undefined') {
19097 // not really needed.. but just in case.. parent should be a NavGroup.
19098 var tg = Roo.bootstrap.TabGroup.get(this.navId);
19102 var i = tg.tabs.length - 1;
19104 if(this.active && tg.bullets > 0 && i < tg.bullets){
19105 tg.setActiveBullet(i);
19109 this.el.on('click', this.onClick, this);
19112 this.el.on("touchstart", this.onTouchStart, this);
19113 this.el.on("touchmove", this.onTouchMove, this);
19114 this.el.on("touchend", this.onTouchEnd, this);
19119 onRender : function(ct, position)
19121 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
19124 setActive : function(state)
19126 Roo.log("panel - set active " + this.tabId + "=" + state);
19128 this.active = state;
19130 this.el.removeClass('active');
19132 } else if (!this.el.hasClass('active')) {
19133 this.el.addClass('active');
19136 this.fireEvent('changed', this, state);
19139 onClick : function(e)
19141 e.preventDefault();
19143 if(!this.href.length){
19147 window.location.href = this.href;
19156 onTouchStart : function(e)
19158 this.swiping = false;
19160 this.startX = e.browserEvent.touches[0].clientX;
19161 this.startY = e.browserEvent.touches[0].clientY;
19164 onTouchMove : function(e)
19166 this.swiping = true;
19168 this.endX = e.browserEvent.touches[0].clientX;
19169 this.endY = e.browserEvent.touches[0].clientY;
19172 onTouchEnd : function(e)
19179 var tabGroup = this.parent();
19181 if(this.endX > this.startX){ // swiping right
19182 tabGroup.showPanelPrev();
19186 if(this.startX > this.endX){ // swiping left
19187 tabGroup.showPanelNext();
19206 * @class Roo.bootstrap.DateField
19207 * @extends Roo.bootstrap.Input
19208 * Bootstrap DateField class
19209 * @cfg {Number} weekStart default 0
19210 * @cfg {String} viewMode default empty, (months|years)
19211 * @cfg {String} minViewMode default empty, (months|years)
19212 * @cfg {Number} startDate default -Infinity
19213 * @cfg {Number} endDate default Infinity
19214 * @cfg {Boolean} todayHighlight default false
19215 * @cfg {Boolean} todayBtn default false
19216 * @cfg {Boolean} calendarWeeks default false
19217 * @cfg {Object} daysOfWeekDisabled default empty
19218 * @cfg {Boolean} singleMode default false (true | false)
19220 * @cfg {Boolean} keyboardNavigation default true
19221 * @cfg {String} language default en
19224 * Create a new DateField
19225 * @param {Object} config The config object
19228 Roo.bootstrap.DateField = function(config){
19229 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
19233 * Fires when this field show.
19234 * @param {Roo.bootstrap.DateField} this
19235 * @param {Mixed} date The date value
19240 * Fires when this field hide.
19241 * @param {Roo.bootstrap.DateField} this
19242 * @param {Mixed} date The date value
19247 * Fires when select a date.
19248 * @param {Roo.bootstrap.DateField} this
19249 * @param {Mixed} date The date value
19253 * @event beforeselect
19254 * Fires when before select a date.
19255 * @param {Roo.bootstrap.DateField} this
19256 * @param {Mixed} date The date value
19258 beforeselect : true
19262 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
19265 * @cfg {String} format
19266 * The default date format string which can be overriden for localization support. The format must be
19267 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
19271 * @cfg {String} altFormats
19272 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
19273 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
19275 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
19283 todayHighlight : false,
19289 keyboardNavigation: true,
19291 calendarWeeks: false,
19293 startDate: -Infinity,
19297 daysOfWeekDisabled: [],
19301 singleMode : false,
19303 UTCDate: function()
19305 return new Date(Date.UTC.apply(Date, arguments));
19308 UTCToday: function()
19310 var today = new Date();
19311 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
19314 getDate: function() {
19315 var d = this.getUTCDate();
19316 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
19319 getUTCDate: function() {
19323 setDate: function(d) {
19324 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
19327 setUTCDate: function(d) {
19329 this.setValue(this.formatDate(this.date));
19332 onRender: function(ct, position)
19335 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
19337 this.language = this.language || 'en';
19338 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
19339 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
19341 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
19342 this.format = this.format || 'm/d/y';
19343 this.isInline = false;
19344 this.isInput = true;
19345 this.component = this.el.select('.add-on', true).first() || false;
19346 this.component = (this.component && this.component.length === 0) ? false : this.component;
19347 this.hasInput = this.component && this.inputEl().length;
19349 if (typeof(this.minViewMode === 'string')) {
19350 switch (this.minViewMode) {
19352 this.minViewMode = 1;
19355 this.minViewMode = 2;
19358 this.minViewMode = 0;
19363 if (typeof(this.viewMode === 'string')) {
19364 switch (this.viewMode) {
19377 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19379 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19381 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19383 this.picker().on('mousedown', this.onMousedown, this);
19384 this.picker().on('click', this.onClick, this);
19386 this.picker().addClass('datepicker-dropdown');
19388 this.startViewMode = this.viewMode;
19390 if(this.singleMode){
19391 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19392 v.setVisibilityMode(Roo.Element.DISPLAY);
19396 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19397 v.setStyle('width', '189px');
19401 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19402 if(!this.calendarWeeks){
19407 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19408 v.attr('colspan', function(i, val){
19409 return parseInt(val) + 1;
19414 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19416 this.setStartDate(this.startDate);
19417 this.setEndDate(this.endDate);
19419 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19426 if(this.isInline) {
19431 picker : function()
19433 return this.pickerEl;
19434 // return this.el.select('.datepicker', true).first();
19437 fillDow: function()
19439 var dowCnt = this.weekStart;
19448 if(this.calendarWeeks){
19456 while (dowCnt < this.weekStart + 7) {
19460 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19464 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19467 fillMonths: function()
19470 var months = this.picker().select('>.datepicker-months td', true).first();
19472 months.dom.innerHTML = '';
19478 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19481 months.createChild(month);
19488 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;
19490 if (this.date < this.startDate) {
19491 this.viewDate = new Date(this.startDate);
19492 } else if (this.date > this.endDate) {
19493 this.viewDate = new Date(this.endDate);
19495 this.viewDate = new Date(this.date);
19503 var d = new Date(this.viewDate),
19504 year = d.getUTCFullYear(),
19505 month = d.getUTCMonth(),
19506 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19507 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19508 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19509 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19510 currentDate = this.date && this.date.valueOf(),
19511 today = this.UTCToday();
19513 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19515 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19517 // this.picker.select('>tfoot th.today').
19518 // .text(dates[this.language].today)
19519 // .toggle(this.todayBtn !== false);
19521 this.updateNavArrows();
19524 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19526 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19528 prevMonth.setUTCDate(day);
19530 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19532 var nextMonth = new Date(prevMonth);
19534 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19536 nextMonth = nextMonth.valueOf();
19538 var fillMonths = false;
19540 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19542 while(prevMonth.valueOf() <= nextMonth) {
19545 if (prevMonth.getUTCDay() === this.weekStart) {
19547 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19555 if(this.calendarWeeks){
19556 // ISO 8601: First week contains first thursday.
19557 // ISO also states week starts on Monday, but we can be more abstract here.
19559 // Start of current week: based on weekstart/current date
19560 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19561 // Thursday of this week
19562 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19563 // First Thursday of year, year from thursday
19564 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19565 // Calendar week: ms between thursdays, div ms per day, div 7 days
19566 calWeek = (th - yth) / 864e5 / 7 + 1;
19568 fillMonths.cn.push({
19576 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19578 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19581 if (this.todayHighlight &&
19582 prevMonth.getUTCFullYear() == today.getFullYear() &&
19583 prevMonth.getUTCMonth() == today.getMonth() &&
19584 prevMonth.getUTCDate() == today.getDate()) {
19585 clsName += ' today';
19588 if (currentDate && prevMonth.valueOf() === currentDate) {
19589 clsName += ' active';
19592 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19593 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19594 clsName += ' disabled';
19597 fillMonths.cn.push({
19599 cls: 'day ' + clsName,
19600 html: prevMonth.getDate()
19603 prevMonth.setDate(prevMonth.getDate()+1);
19606 var currentYear = this.date && this.date.getUTCFullYear();
19607 var currentMonth = this.date && this.date.getUTCMonth();
19609 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19611 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19612 v.removeClass('active');
19614 if(currentYear === year && k === currentMonth){
19615 v.addClass('active');
19618 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19619 v.addClass('disabled');
19625 year = parseInt(year/10, 10) * 10;
19627 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19629 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19632 for (var i = -1; i < 11; i++) {
19633 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19635 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19643 showMode: function(dir)
19646 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19649 Roo.each(this.picker().select('>div',true).elements, function(v){
19650 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19653 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19658 if(this.isInline) {
19662 this.picker().removeClass(['bottom', 'top']);
19664 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19666 * place to the top of element!
19670 this.picker().addClass('top');
19671 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19676 this.picker().addClass('bottom');
19678 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19681 parseDate : function(value)
19683 if(!value || value instanceof Date){
19686 var v = Date.parseDate(value, this.format);
19687 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19688 v = Date.parseDate(value, 'Y-m-d');
19690 if(!v && this.altFormats){
19691 if(!this.altFormatsArray){
19692 this.altFormatsArray = this.altFormats.split("|");
19694 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19695 v = Date.parseDate(value, this.altFormatsArray[i]);
19701 formatDate : function(date, fmt)
19703 return (!date || !(date instanceof Date)) ?
19704 date : date.dateFormat(fmt || this.format);
19707 onFocus : function()
19709 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19713 onBlur : function()
19715 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19717 var d = this.inputEl().getValue();
19724 showPopup : function()
19726 this.picker().show();
19730 this.fireEvent('showpopup', this, this.date);
19733 hidePopup : function()
19735 if(this.isInline) {
19738 this.picker().hide();
19739 this.viewMode = this.startViewMode;
19742 this.fireEvent('hidepopup', this, this.date);
19746 onMousedown: function(e)
19748 e.stopPropagation();
19749 e.preventDefault();
19754 Roo.bootstrap.DateField.superclass.keyup.call(this);
19758 setValue: function(v)
19760 if(this.fireEvent('beforeselect', this, v) !== false){
19761 var d = new Date(this.parseDate(v) ).clearTime();
19763 if(isNaN(d.getTime())){
19764 this.date = this.viewDate = '';
19765 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19769 v = this.formatDate(d);
19771 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19773 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19777 this.fireEvent('select', this, this.date);
19781 getValue: function()
19783 return this.formatDate(this.date);
19786 fireKey: function(e)
19788 if (!this.picker().isVisible()){
19789 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19795 var dateChanged = false,
19797 newDate, newViewDate;
19802 e.preventDefault();
19806 if (!this.keyboardNavigation) {
19809 dir = e.keyCode == 37 ? -1 : 1;
19812 newDate = this.moveYear(this.date, dir);
19813 newViewDate = this.moveYear(this.viewDate, dir);
19814 } else if (e.shiftKey){
19815 newDate = this.moveMonth(this.date, dir);
19816 newViewDate = this.moveMonth(this.viewDate, dir);
19818 newDate = new Date(this.date);
19819 newDate.setUTCDate(this.date.getUTCDate() + dir);
19820 newViewDate = new Date(this.viewDate);
19821 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19823 if (this.dateWithinRange(newDate)){
19824 this.date = newDate;
19825 this.viewDate = newViewDate;
19826 this.setValue(this.formatDate(this.date));
19828 e.preventDefault();
19829 dateChanged = true;
19834 if (!this.keyboardNavigation) {
19837 dir = e.keyCode == 38 ? -1 : 1;
19839 newDate = this.moveYear(this.date, dir);
19840 newViewDate = this.moveYear(this.viewDate, dir);
19841 } else if (e.shiftKey){
19842 newDate = this.moveMonth(this.date, dir);
19843 newViewDate = this.moveMonth(this.viewDate, dir);
19845 newDate = new Date(this.date);
19846 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19847 newViewDate = new Date(this.viewDate);
19848 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19850 if (this.dateWithinRange(newDate)){
19851 this.date = newDate;
19852 this.viewDate = newViewDate;
19853 this.setValue(this.formatDate(this.date));
19855 e.preventDefault();
19856 dateChanged = true;
19860 this.setValue(this.formatDate(this.date));
19862 e.preventDefault();
19865 this.setValue(this.formatDate(this.date));
19879 onClick: function(e)
19881 e.stopPropagation();
19882 e.preventDefault();
19884 var target = e.getTarget();
19886 if(target.nodeName.toLowerCase() === 'i'){
19887 target = Roo.get(target).dom.parentNode;
19890 var nodeName = target.nodeName;
19891 var className = target.className;
19892 var html = target.innerHTML;
19893 //Roo.log(nodeName);
19895 switch(nodeName.toLowerCase()) {
19897 switch(className) {
19903 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19904 switch(this.viewMode){
19906 this.viewDate = this.moveMonth(this.viewDate, dir);
19910 this.viewDate = this.moveYear(this.viewDate, dir);
19916 var date = new Date();
19917 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19919 this.setValue(this.formatDate(this.date));
19926 if (className.indexOf('disabled') < 0) {
19927 this.viewDate.setUTCDate(1);
19928 if (className.indexOf('month') > -1) {
19929 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19931 var year = parseInt(html, 10) || 0;
19932 this.viewDate.setUTCFullYear(year);
19936 if(this.singleMode){
19937 this.setValue(this.formatDate(this.viewDate));
19948 //Roo.log(className);
19949 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19950 var day = parseInt(html, 10) || 1;
19951 var year = this.viewDate.getUTCFullYear(),
19952 month = this.viewDate.getUTCMonth();
19954 if (className.indexOf('old') > -1) {
19961 } else if (className.indexOf('new') > -1) {
19969 //Roo.log([year,month,day]);
19970 this.date = this.UTCDate(year, month, day,0,0,0,0);
19971 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19973 //Roo.log(this.formatDate(this.date));
19974 this.setValue(this.formatDate(this.date));
19981 setStartDate: function(startDate)
19983 this.startDate = startDate || -Infinity;
19984 if (this.startDate !== -Infinity) {
19985 this.startDate = this.parseDate(this.startDate);
19988 this.updateNavArrows();
19991 setEndDate: function(endDate)
19993 this.endDate = endDate || Infinity;
19994 if (this.endDate !== Infinity) {
19995 this.endDate = this.parseDate(this.endDate);
19998 this.updateNavArrows();
20001 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
20003 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
20004 if (typeof(this.daysOfWeekDisabled) !== 'object') {
20005 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
20007 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
20008 return parseInt(d, 10);
20011 this.updateNavArrows();
20014 updateNavArrows: function()
20016 if(this.singleMode){
20020 var d = new Date(this.viewDate),
20021 year = d.getUTCFullYear(),
20022 month = d.getUTCMonth();
20024 Roo.each(this.picker().select('.prev', true).elements, function(v){
20026 switch (this.viewMode) {
20029 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
20035 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
20042 Roo.each(this.picker().select('.next', true).elements, function(v){
20044 switch (this.viewMode) {
20047 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
20053 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
20061 moveMonth: function(date, dir)
20066 var new_date = new Date(date.valueOf()),
20067 day = new_date.getUTCDate(),
20068 month = new_date.getUTCMonth(),
20069 mag = Math.abs(dir),
20071 dir = dir > 0 ? 1 : -1;
20074 // If going back one month, make sure month is not current month
20075 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
20077 return new_date.getUTCMonth() == month;
20079 // If going forward one month, make sure month is as expected
20080 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
20082 return new_date.getUTCMonth() != new_month;
20084 new_month = month + dir;
20085 new_date.setUTCMonth(new_month);
20086 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
20087 if (new_month < 0 || new_month > 11) {
20088 new_month = (new_month + 12) % 12;
20091 // For magnitudes >1, move one month at a time...
20092 for (var i=0; i<mag; i++) {
20093 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
20094 new_date = this.moveMonth(new_date, dir);
20096 // ...then reset the day, keeping it in the new month
20097 new_month = new_date.getUTCMonth();
20098 new_date.setUTCDate(day);
20100 return new_month != new_date.getUTCMonth();
20103 // Common date-resetting loop -- if date is beyond end of month, make it
20106 new_date.setUTCDate(--day);
20107 new_date.setUTCMonth(new_month);
20112 moveYear: function(date, dir)
20114 return this.moveMonth(date, dir*12);
20117 dateWithinRange: function(date)
20119 return date >= this.startDate && date <= this.endDate;
20125 this.picker().remove();
20128 validateValue : function(value)
20130 if(this.getVisibilityEl().hasClass('hidden')){
20134 if(value.length < 1) {
20135 if(this.allowBlank){
20141 if(value.length < this.minLength){
20144 if(value.length > this.maxLength){
20148 var vt = Roo.form.VTypes;
20149 if(!vt[this.vtype](value, this)){
20153 if(typeof this.validator == "function"){
20154 var msg = this.validator(value);
20160 if(this.regex && !this.regex.test(value)){
20164 if(typeof(this.parseDate(value)) == 'undefined'){
20168 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
20172 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
20182 this.date = this.viewDate = '';
20184 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20189 Roo.apply(Roo.bootstrap.DateField, {
20200 html: '<i class="fa fa-arrow-left"/>'
20210 html: '<i class="fa fa-arrow-right"/>'
20252 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
20253 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
20254 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
20255 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20256 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
20269 navFnc: 'FullYear',
20274 navFnc: 'FullYear',
20279 Roo.apply(Roo.bootstrap.DateField, {
20283 cls: 'datepicker dropdown-menu roo-dynamic',
20287 cls: 'datepicker-days',
20291 cls: 'table-condensed',
20293 Roo.bootstrap.DateField.head,
20297 Roo.bootstrap.DateField.footer
20304 cls: 'datepicker-months',
20308 cls: 'table-condensed',
20310 Roo.bootstrap.DateField.head,
20311 Roo.bootstrap.DateField.content,
20312 Roo.bootstrap.DateField.footer
20319 cls: 'datepicker-years',
20323 cls: 'table-condensed',
20325 Roo.bootstrap.DateField.head,
20326 Roo.bootstrap.DateField.content,
20327 Roo.bootstrap.DateField.footer
20346 * @class Roo.bootstrap.TimeField
20347 * @extends Roo.bootstrap.Input
20348 * Bootstrap DateField class
20352 * Create a new TimeField
20353 * @param {Object} config The config object
20356 Roo.bootstrap.TimeField = function(config){
20357 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20361 * Fires when this field show.
20362 * @param {Roo.bootstrap.DateField} thisthis
20363 * @param {Mixed} date The date value
20368 * Fires when this field hide.
20369 * @param {Roo.bootstrap.DateField} this
20370 * @param {Mixed} date The date value
20375 * Fires when select a date.
20376 * @param {Roo.bootstrap.DateField} this
20377 * @param {Mixed} date The date value
20383 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
20386 * @cfg {String} format
20387 * The default time format string which can be overriden for localization support. The format must be
20388 * valid according to {@link Date#parseDate} (defaults to 'H:i').
20392 onRender: function(ct, position)
20395 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20397 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20399 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20401 this.pop = this.picker().select('>.datepicker-time',true).first();
20402 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20404 this.picker().on('mousedown', this.onMousedown, this);
20405 this.picker().on('click', this.onClick, this);
20407 this.picker().addClass('datepicker-dropdown');
20412 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20413 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20414 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20415 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20416 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20417 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20421 fireKey: function(e){
20422 if (!this.picker().isVisible()){
20423 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20429 e.preventDefault();
20437 this.onTogglePeriod();
20440 this.onIncrementMinutes();
20443 this.onDecrementMinutes();
20452 onClick: function(e) {
20453 e.stopPropagation();
20454 e.preventDefault();
20457 picker : function()
20459 return this.el.select('.datepicker', true).first();
20462 fillTime: function()
20464 var time = this.pop.select('tbody', true).first();
20466 time.dom.innerHTML = '';
20481 cls: 'hours-up glyphicon glyphicon-chevron-up'
20501 cls: 'minutes-up glyphicon glyphicon-chevron-up'
20522 cls: 'timepicker-hour',
20537 cls: 'timepicker-minute',
20552 cls: 'btn btn-primary period',
20574 cls: 'hours-down glyphicon glyphicon-chevron-down'
20594 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20612 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20619 var hours = this.time.getHours();
20620 var minutes = this.time.getMinutes();
20633 hours = hours - 12;
20637 hours = '0' + hours;
20641 minutes = '0' + minutes;
20644 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20645 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20646 this.pop.select('button', true).first().dom.innerHTML = period;
20652 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20654 var cls = ['bottom'];
20656 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20663 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20668 this.picker().addClass(cls.join('-'));
20672 Roo.each(cls, function(c){
20674 _this.picker().setTop(_this.inputEl().getHeight());
20678 _this.picker().setTop(0 - _this.picker().getHeight());
20683 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20687 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20694 onFocus : function()
20696 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20700 onBlur : function()
20702 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20708 this.picker().show();
20713 this.fireEvent('show', this, this.date);
20718 this.picker().hide();
20721 this.fireEvent('hide', this, this.date);
20724 setTime : function()
20727 this.setValue(this.time.format(this.format));
20729 this.fireEvent('select', this, this.date);
20734 onMousedown: function(e){
20735 e.stopPropagation();
20736 e.preventDefault();
20739 onIncrementHours: function()
20741 Roo.log('onIncrementHours');
20742 this.time = this.time.add(Date.HOUR, 1);
20747 onDecrementHours: function()
20749 Roo.log('onDecrementHours');
20750 this.time = this.time.add(Date.HOUR, -1);
20754 onIncrementMinutes: function()
20756 Roo.log('onIncrementMinutes');
20757 this.time = this.time.add(Date.MINUTE, 1);
20761 onDecrementMinutes: function()
20763 Roo.log('onDecrementMinutes');
20764 this.time = this.time.add(Date.MINUTE, -1);
20768 onTogglePeriod: function()
20770 Roo.log('onTogglePeriod');
20771 this.time = this.time.add(Date.HOUR, 12);
20778 Roo.apply(Roo.bootstrap.TimeField, {
20808 cls: 'btn btn-info ok',
20820 Roo.apply(Roo.bootstrap.TimeField, {
20824 cls: 'datepicker dropdown-menu',
20828 cls: 'datepicker-time',
20832 cls: 'table-condensed',
20834 Roo.bootstrap.TimeField.content,
20835 Roo.bootstrap.TimeField.footer
20854 * @class Roo.bootstrap.MonthField
20855 * @extends Roo.bootstrap.Input
20856 * Bootstrap MonthField class
20858 * @cfg {String} language default en
20861 * Create a new MonthField
20862 * @param {Object} config The config object
20865 Roo.bootstrap.MonthField = function(config){
20866 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20871 * Fires when this field show.
20872 * @param {Roo.bootstrap.MonthField} this
20873 * @param {Mixed} date The date value
20878 * Fires when this field hide.
20879 * @param {Roo.bootstrap.MonthField} this
20880 * @param {Mixed} date The date value
20885 * Fires when select a date.
20886 * @param {Roo.bootstrap.MonthField} this
20887 * @param {String} oldvalue The old value
20888 * @param {String} newvalue The new value
20894 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20896 onRender: function(ct, position)
20899 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20901 this.language = this.language || 'en';
20902 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20903 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20905 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20906 this.isInline = false;
20907 this.isInput = true;
20908 this.component = this.el.select('.add-on', true).first() || false;
20909 this.component = (this.component && this.component.length === 0) ? false : this.component;
20910 this.hasInput = this.component && this.inputEL().length;
20912 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20914 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20916 this.picker().on('mousedown', this.onMousedown, this);
20917 this.picker().on('click', this.onClick, this);
20919 this.picker().addClass('datepicker-dropdown');
20921 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20922 v.setStyle('width', '189px');
20929 if(this.isInline) {
20935 setValue: function(v, suppressEvent)
20937 var o = this.getValue();
20939 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20943 if(suppressEvent !== true){
20944 this.fireEvent('select', this, o, v);
20949 getValue: function()
20954 onClick: function(e)
20956 e.stopPropagation();
20957 e.preventDefault();
20959 var target = e.getTarget();
20961 if(target.nodeName.toLowerCase() === 'i'){
20962 target = Roo.get(target).dom.parentNode;
20965 var nodeName = target.nodeName;
20966 var className = target.className;
20967 var html = target.innerHTML;
20969 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20973 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20975 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20981 picker : function()
20983 return this.pickerEl;
20986 fillMonths: function()
20989 var months = this.picker().select('>.datepicker-months td', true).first();
20991 months.dom.innerHTML = '';
20997 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
21000 months.createChild(month);
21009 if(typeof(this.vIndex) == 'undefined' && this.value.length){
21010 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
21013 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
21014 e.removeClass('active');
21016 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
21017 e.addClass('active');
21024 if(this.isInline) {
21028 this.picker().removeClass(['bottom', 'top']);
21030 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21032 * place to the top of element!
21036 this.picker().addClass('top');
21037 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21042 this.picker().addClass('bottom');
21044 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21047 onFocus : function()
21049 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
21053 onBlur : function()
21055 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
21057 var d = this.inputEl().getValue();
21066 this.picker().show();
21067 this.picker().select('>.datepicker-months', true).first().show();
21071 this.fireEvent('show', this, this.date);
21076 if(this.isInline) {
21079 this.picker().hide();
21080 this.fireEvent('hide', this, this.date);
21084 onMousedown: function(e)
21086 e.stopPropagation();
21087 e.preventDefault();
21092 Roo.bootstrap.MonthField.superclass.keyup.call(this);
21096 fireKey: function(e)
21098 if (!this.picker().isVisible()){
21099 if (e.keyCode == 27) {// allow escape to hide and re-show picker
21110 e.preventDefault();
21114 dir = e.keyCode == 37 ? -1 : 1;
21116 this.vIndex = this.vIndex + dir;
21118 if(this.vIndex < 0){
21122 if(this.vIndex > 11){
21126 if(isNaN(this.vIndex)){
21130 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21136 dir = e.keyCode == 38 ? -1 : 1;
21138 this.vIndex = this.vIndex + dir * 4;
21140 if(this.vIndex < 0){
21144 if(this.vIndex > 11){
21148 if(isNaN(this.vIndex)){
21152 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21157 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21158 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21162 e.preventDefault();
21165 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21166 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21182 this.picker().remove();
21187 Roo.apply(Roo.bootstrap.MonthField, {
21206 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21207 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
21212 Roo.apply(Roo.bootstrap.MonthField, {
21216 cls: 'datepicker dropdown-menu roo-dynamic',
21220 cls: 'datepicker-months',
21224 cls: 'table-condensed',
21226 Roo.bootstrap.DateField.content
21246 * @class Roo.bootstrap.CheckBox
21247 * @extends Roo.bootstrap.Input
21248 * Bootstrap CheckBox class
21250 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
21251 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
21252 * @cfg {String} boxLabel The text that appears beside the checkbox
21253 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
21254 * @cfg {Boolean} checked initnal the element
21255 * @cfg {Boolean} inline inline the element (default false)
21256 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
21257 * @cfg {String} tooltip label tooltip
21260 * Create a new CheckBox
21261 * @param {Object} config The config object
21264 Roo.bootstrap.CheckBox = function(config){
21265 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
21270 * Fires when the element is checked or unchecked.
21271 * @param {Roo.bootstrap.CheckBox} this This input
21272 * @param {Boolean} checked The new checked value
21277 * Fires when the element is click.
21278 * @param {Roo.bootstrap.CheckBox} this This input
21285 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
21287 inputType: 'checkbox',
21296 // checkbox success does not make any sense really..
21301 getAutoCreate : function()
21303 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
21309 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
21312 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
21318 type : this.inputType,
21319 value : this.inputValue,
21320 cls : 'roo-' + this.inputType, //'form-box',
21321 placeholder : this.placeholder || ''
21325 if(this.inputType != 'radio'){
21329 cls : 'roo-hidden-value',
21330 value : this.checked ? this.inputValue : this.valueOff
21335 if (this.weight) { // Validity check?
21336 cfg.cls += " " + this.inputType + "-" + this.weight;
21339 if (this.disabled) {
21340 input.disabled=true;
21344 input.checked = this.checked;
21349 input.name = this.name;
21351 if(this.inputType != 'radio'){
21352 hidden.name = this.name;
21353 input.name = '_hidden_' + this.name;
21358 input.cls += ' input-' + this.size;
21363 ['xs','sm','md','lg'].map(function(size){
21364 if (settings[size]) {
21365 cfg.cls += ' col-' + size + '-' + settings[size];
21369 var inputblock = input;
21371 if (this.before || this.after) {
21374 cls : 'input-group',
21379 inputblock.cn.push({
21381 cls : 'input-group-addon',
21386 inputblock.cn.push(input);
21388 if(this.inputType != 'radio'){
21389 inputblock.cn.push(hidden);
21393 inputblock.cn.push({
21395 cls : 'input-group-addon',
21401 var boxLabelCfg = false;
21407 //'for': id, // box label is handled by onclick - so no for...
21409 html: this.boxLabel
21412 boxLabelCfg.tooltip = this.tooltip;
21418 if (align ==='left' && this.fieldLabel.length) {
21419 // Roo.log("left and has label");
21424 cls : 'control-label',
21425 html : this.fieldLabel
21436 cfg.cn[1].cn.push(boxLabelCfg);
21439 if(this.labelWidth > 12){
21440 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21443 if(this.labelWidth < 13 && this.labelmd == 0){
21444 this.labelmd = this.labelWidth;
21447 if(this.labellg > 0){
21448 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21449 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21452 if(this.labelmd > 0){
21453 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21454 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21457 if(this.labelsm > 0){
21458 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21459 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21462 if(this.labelxs > 0){
21463 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21464 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21467 } else if ( this.fieldLabel.length) {
21468 // Roo.log(" label");
21472 tag: this.boxLabel ? 'span' : 'label',
21474 cls: 'control-label box-input-label',
21475 //cls : 'input-group-addon',
21476 html : this.fieldLabel
21483 cfg.cn.push(boxLabelCfg);
21488 // Roo.log(" no label && no align");
21489 cfg.cn = [ inputblock ] ;
21491 cfg.cn.push(boxLabelCfg);
21499 if(this.inputType != 'radio'){
21500 cfg.cn.push(hidden);
21508 * return the real input element.
21510 inputEl: function ()
21512 return this.el.select('input.roo-' + this.inputType,true).first();
21514 hiddenEl: function ()
21516 return this.el.select('input.roo-hidden-value',true).first();
21519 labelEl: function()
21521 return this.el.select('label.control-label',true).first();
21523 /* depricated... */
21527 return this.labelEl();
21530 boxLabelEl: function()
21532 return this.el.select('label.box-label',true).first();
21535 initEvents : function()
21537 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21539 this.inputEl().on('click', this.onClick, this);
21541 if (this.boxLabel) {
21542 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
21545 this.startValue = this.getValue();
21548 Roo.bootstrap.CheckBox.register(this);
21552 onClick : function(e)
21554 if(this.fireEvent('click', this, e) !== false){
21555 this.setChecked(!this.checked);
21560 setChecked : function(state,suppressEvent)
21562 this.startValue = this.getValue();
21564 if(this.inputType == 'radio'){
21566 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21567 e.dom.checked = false;
21570 this.inputEl().dom.checked = true;
21572 this.inputEl().dom.value = this.inputValue;
21574 if(suppressEvent !== true){
21575 this.fireEvent('check', this, true);
21583 this.checked = state;
21585 this.inputEl().dom.checked = state;
21588 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21590 if(suppressEvent !== true){
21591 this.fireEvent('check', this, state);
21597 getValue : function()
21599 if(this.inputType == 'radio'){
21600 return this.getGroupValue();
21603 return this.hiddenEl().dom.value;
21607 getGroupValue : function()
21609 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21613 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21616 setValue : function(v,suppressEvent)
21618 if(this.inputType == 'radio'){
21619 this.setGroupValue(v, suppressEvent);
21623 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21628 setGroupValue : function(v, suppressEvent)
21630 this.startValue = this.getValue();
21632 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21633 e.dom.checked = false;
21635 if(e.dom.value == v){
21636 e.dom.checked = true;
21640 if(suppressEvent !== true){
21641 this.fireEvent('check', this, true);
21649 validate : function()
21651 if(this.getVisibilityEl().hasClass('hidden')){
21657 (this.inputType == 'radio' && this.validateRadio()) ||
21658 (this.inputType == 'checkbox' && this.validateCheckbox())
21664 this.markInvalid();
21668 validateRadio : function()
21670 if(this.getVisibilityEl().hasClass('hidden')){
21674 if(this.allowBlank){
21680 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21681 if(!e.dom.checked){
21693 validateCheckbox : function()
21696 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21697 //return (this.getValue() == this.inputValue) ? true : false;
21700 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21708 for(var i in group){
21709 if(group[i].el.isVisible(true)){
21717 for(var i in group){
21722 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21729 * Mark this field as valid
21731 markValid : function()
21735 this.fireEvent('valid', this);
21737 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21740 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21747 if(this.inputType == 'radio'){
21748 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21749 var fg = e.findParent('.form-group', false, true);
21750 if (Roo.bootstrap.version == 3) {
21751 fg.removeClass([_this.invalidClass, _this.validClass]);
21752 fg.addClass(_this.validClass);
21754 fg.removeClass(['is-valid', 'is-invalid']);
21755 fg.addClass('is-valid');
21763 var fg = this.el.findParent('.form-group', false, true);
21764 if (Roo.bootstrap.version == 3) {
21765 fg.removeClass([this.invalidClass, this.validClass]);
21766 fg.addClass(this.validClass);
21768 fg.removeClass(['is-valid', 'is-invalid']);
21769 fg.addClass('is-valid');
21774 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21780 for(var i in group){
21781 var fg = group[i].el.findParent('.form-group', false, true);
21782 if (Roo.bootstrap.version == 3) {
21783 fg.removeClass([this.invalidClass, this.validClass]);
21784 fg.addClass(this.validClass);
21786 fg.removeClass(['is-valid', 'is-invalid']);
21787 fg.addClass('is-valid');
21793 * Mark this field as invalid
21794 * @param {String} msg The validation message
21796 markInvalid : function(msg)
21798 if(this.allowBlank){
21804 this.fireEvent('invalid', this, msg);
21806 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21809 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21813 label.markInvalid();
21816 if(this.inputType == 'radio'){
21818 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21819 var fg = e.findParent('.form-group', false, true);
21820 if (Roo.bootstrap.version == 3) {
21821 fg.removeClass([_this.invalidClass, _this.validClass]);
21822 fg.addClass(_this.invalidClass);
21824 fg.removeClass(['is-invalid', 'is-valid']);
21825 fg.addClass('is-invalid');
21833 var fg = this.el.findParent('.form-group', false, true);
21834 if (Roo.bootstrap.version == 3) {
21835 fg.removeClass([_this.invalidClass, _this.validClass]);
21836 fg.addClass(_this.invalidClass);
21838 fg.removeClass(['is-invalid', 'is-valid']);
21839 fg.addClass('is-invalid');
21844 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21850 for(var i in group){
21851 var fg = group[i].el.findParent('.form-group', false, true);
21852 if (Roo.bootstrap.version == 3) {
21853 fg.removeClass([_this.invalidClass, _this.validClass]);
21854 fg.addClass(_this.invalidClass);
21856 fg.removeClass(['is-invalid', 'is-valid']);
21857 fg.addClass('is-invalid');
21863 clearInvalid : function()
21865 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21867 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21869 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21871 if (label && label.iconEl) {
21872 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21873 label.iconEl.removeClass(['is-invalid', 'is-valid']);
21877 disable : function()
21879 if(this.inputType != 'radio'){
21880 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21887 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21888 _this.getActionEl().addClass(this.disabledClass);
21889 e.dom.disabled = true;
21893 this.disabled = true;
21894 this.fireEvent("disable", this);
21898 enable : function()
21900 if(this.inputType != 'radio'){
21901 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21908 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21909 _this.getActionEl().removeClass(this.disabledClass);
21910 e.dom.disabled = false;
21914 this.disabled = false;
21915 this.fireEvent("enable", this);
21919 setBoxLabel : function(v)
21924 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21930 Roo.apply(Roo.bootstrap.CheckBox, {
21935 * register a CheckBox Group
21936 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21938 register : function(checkbox)
21940 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21941 this.groups[checkbox.groupId] = {};
21944 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21948 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21952 * fetch a CheckBox Group based on the group ID
21953 * @param {string} the group ID
21954 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21956 get: function(groupId) {
21957 if (typeof(this.groups[groupId]) == 'undefined') {
21961 return this.groups[groupId] ;
21974 * @class Roo.bootstrap.Radio
21975 * @extends Roo.bootstrap.Component
21976 * Bootstrap Radio class
21977 * @cfg {String} boxLabel - the label associated
21978 * @cfg {String} value - the value of radio
21981 * Create a new Radio
21982 * @param {Object} config The config object
21984 Roo.bootstrap.Radio = function(config){
21985 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21989 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21995 getAutoCreate : function()
21999 cls : 'form-group radio',
22004 html : this.boxLabel
22012 initEvents : function()
22014 this.parent().register(this);
22016 this.el.on('click', this.onClick, this);
22020 onClick : function(e)
22022 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
22023 this.setChecked(true);
22027 setChecked : function(state, suppressEvent)
22029 this.parent().setValue(this.value, suppressEvent);
22033 setBoxLabel : function(v)
22038 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
22053 * @class Roo.bootstrap.SecurePass
22054 * @extends Roo.bootstrap.Input
22055 * Bootstrap SecurePass class
22059 * Create a new SecurePass
22060 * @param {Object} config The config object
22063 Roo.bootstrap.SecurePass = function (config) {
22064 // these go here, so the translation tool can replace them..
22066 PwdEmpty: "Please type a password, and then retype it to confirm.",
22067 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22068 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22069 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22070 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22071 FNInPwd: "Your password can't contain your first name. Please type a different password.",
22072 LNInPwd: "Your password can't contain your last name. Please type a different password.",
22073 TooWeak: "Your password is Too Weak."
22075 this.meterLabel = "Password strength:";
22076 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
22077 this.meterClass = [
22078 "roo-password-meter-tooweak",
22079 "roo-password-meter-weak",
22080 "roo-password-meter-medium",
22081 "roo-password-meter-strong",
22082 "roo-password-meter-grey"
22087 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
22090 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
22092 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
22094 * PwdEmpty: "Please type a password, and then retype it to confirm.",
22095 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22096 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22097 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22098 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22099 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
22100 * LNInPwd: "Your password can't contain your last name. Please type a different password."
22110 * @cfg {String/Object} Label for the strength meter (defaults to
22111 * 'Password strength:')
22116 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
22117 * ['Weak', 'Medium', 'Strong'])
22120 pwdStrengths: false,
22133 initEvents: function ()
22135 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
22137 if (this.el.is('input[type=password]') && Roo.isSafari) {
22138 this.el.on('keydown', this.SafariOnKeyDown, this);
22141 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
22144 onRender: function (ct, position)
22146 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
22147 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
22148 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
22150 this.trigger.createChild({
22155 cls: 'roo-password-meter-grey col-xs-12',
22158 //width: this.meterWidth + 'px'
22162 cls: 'roo-password-meter-text'
22168 if (this.hideTrigger) {
22169 this.trigger.setDisplayed(false);
22171 this.setSize(this.width || '', this.height || '');
22174 onDestroy: function ()
22176 if (this.trigger) {
22177 this.trigger.removeAllListeners();
22178 this.trigger.remove();
22181 this.wrap.remove();
22183 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
22186 checkStrength: function ()
22188 var pwd = this.inputEl().getValue();
22189 if (pwd == this._lastPwd) {
22194 if (this.ClientSideStrongPassword(pwd)) {
22196 } else if (this.ClientSideMediumPassword(pwd)) {
22198 } else if (this.ClientSideWeakPassword(pwd)) {
22204 Roo.log('strength1: ' + strength);
22206 //var pm = this.trigger.child('div/div/div').dom;
22207 var pm = this.trigger.child('div/div');
22208 pm.removeClass(this.meterClass);
22209 pm.addClass(this.meterClass[strength]);
22212 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22214 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
22216 this._lastPwd = pwd;
22220 Roo.bootstrap.SecurePass.superclass.reset.call(this);
22222 this._lastPwd = '';
22224 var pm = this.trigger.child('div/div');
22225 pm.removeClass(this.meterClass);
22226 pm.addClass('roo-password-meter-grey');
22229 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22232 this.inputEl().dom.type='password';
22235 validateValue: function (value)
22238 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
22241 if (value.length == 0) {
22242 if (this.allowBlank) {
22243 this.clearInvalid();
22247 this.markInvalid(this.errors.PwdEmpty);
22248 this.errorMsg = this.errors.PwdEmpty;
22256 if ('[\x21-\x7e]*'.match(value)) {
22257 this.markInvalid(this.errors.PwdBadChar);
22258 this.errorMsg = this.errors.PwdBadChar;
22261 if (value.length < 6) {
22262 this.markInvalid(this.errors.PwdShort);
22263 this.errorMsg = this.errors.PwdShort;
22266 if (value.length > 16) {
22267 this.markInvalid(this.errors.PwdLong);
22268 this.errorMsg = this.errors.PwdLong;
22272 if (this.ClientSideStrongPassword(value)) {
22274 } else if (this.ClientSideMediumPassword(value)) {
22276 } else if (this.ClientSideWeakPassword(value)) {
22283 if (strength < 2) {
22284 //this.markInvalid(this.errors.TooWeak);
22285 this.errorMsg = this.errors.TooWeak;
22290 console.log('strength2: ' + strength);
22292 //var pm = this.trigger.child('div/div/div').dom;
22294 var pm = this.trigger.child('div/div');
22295 pm.removeClass(this.meterClass);
22296 pm.addClass(this.meterClass[strength]);
22298 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22300 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
22302 this.errorMsg = '';
22306 CharacterSetChecks: function (type)
22309 this.fResult = false;
22312 isctype: function (character, type)
22315 case this.kCapitalLetter:
22316 if (character >= 'A' && character <= 'Z') {
22321 case this.kSmallLetter:
22322 if (character >= 'a' && character <= 'z') {
22328 if (character >= '0' && character <= '9') {
22333 case this.kPunctuation:
22334 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
22345 IsLongEnough: function (pwd, size)
22347 return !(pwd == null || isNaN(size) || pwd.length < size);
22350 SpansEnoughCharacterSets: function (word, nb)
22352 if (!this.IsLongEnough(word, nb))
22357 var characterSetChecks = new Array(
22358 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22359 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22362 for (var index = 0; index < word.length; ++index) {
22363 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22364 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22365 characterSetChecks[nCharSet].fResult = true;
22372 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22373 if (characterSetChecks[nCharSet].fResult) {
22378 if (nCharSets < nb) {
22384 ClientSideStrongPassword: function (pwd)
22386 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22389 ClientSideMediumPassword: function (pwd)
22391 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22394 ClientSideWeakPassword: function (pwd)
22396 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22399 })//<script type="text/javascript">
22402 * Based Ext JS Library 1.1.1
22403 * Copyright(c) 2006-2007, Ext JS, LLC.
22409 * @class Roo.HtmlEditorCore
22410 * @extends Roo.Component
22411 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22413 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22416 Roo.HtmlEditorCore = function(config){
22419 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22424 * @event initialize
22425 * Fires when the editor is fully initialized (including the iframe)
22426 * @param {Roo.HtmlEditorCore} this
22431 * Fires when the editor is first receives the focus. Any insertion must wait
22432 * until after this event.
22433 * @param {Roo.HtmlEditorCore} this
22437 * @event beforesync
22438 * Fires before the textarea is updated with content from the editor iframe. Return false
22439 * to cancel the sync.
22440 * @param {Roo.HtmlEditorCore} this
22441 * @param {String} html
22445 * @event beforepush
22446 * Fires before the iframe editor is updated with content from the textarea. Return false
22447 * to cancel the push.
22448 * @param {Roo.HtmlEditorCore} this
22449 * @param {String} html
22454 * Fires when the textarea is updated with content from the editor iframe.
22455 * @param {Roo.HtmlEditorCore} this
22456 * @param {String} html
22461 * Fires when the iframe editor is updated with content from the textarea.
22462 * @param {Roo.HtmlEditorCore} this
22463 * @param {String} html
22468 * @event editorevent
22469 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22470 * @param {Roo.HtmlEditorCore} this
22476 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22478 // defaults : white / black...
22479 this.applyBlacklists();
22486 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
22490 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
22496 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22501 * @cfg {Number} height (in pixels)
22505 * @cfg {Number} width (in pixels)
22510 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22513 stylesheets: false,
22518 // private properties
22519 validationEvent : false,
22521 initialized : false,
22523 sourceEditMode : false,
22524 onFocus : Roo.emptyFn,
22526 hideMode:'offsets',
22530 // blacklist + whitelisted elements..
22537 * Protected method that will not generally be called directly. It
22538 * is called when the editor initializes the iframe with HTML contents. Override this method if you
22539 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22541 getDocMarkup : function(){
22545 // inherit styels from page...??
22546 if (this.stylesheets === false) {
22548 Roo.get(document.head).select('style').each(function(node) {
22549 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22552 Roo.get(document.head).select('link').each(function(node) {
22553 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22556 } else if (!this.stylesheets.length) {
22558 st = '<style type="text/css">' +
22559 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22562 st = '<style type="text/css">' +
22567 st += '<style type="text/css">' +
22568 'IMG { cursor: pointer } ' +
22571 var cls = 'roo-htmleditor-body';
22573 if(this.bodyCls.length){
22574 cls += ' ' + this.bodyCls;
22577 return '<html><head>' + st +
22578 //<style type="text/css">' +
22579 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22581 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
22585 onRender : function(ct, position)
22588 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22589 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22592 this.el.dom.style.border = '0 none';
22593 this.el.dom.setAttribute('tabIndex', -1);
22594 this.el.addClass('x-hidden hide');
22598 if(Roo.isIE){ // fix IE 1px bogus margin
22599 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22603 this.frameId = Roo.id();
22607 var iframe = this.owner.wrap.createChild({
22609 cls: 'form-control', // bootstrap..
22611 name: this.frameId,
22612 frameBorder : 'no',
22613 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
22618 this.iframe = iframe.dom;
22620 this.assignDocWin();
22622 this.doc.designMode = 'on';
22625 this.doc.write(this.getDocMarkup());
22629 var task = { // must defer to wait for browser to be ready
22631 //console.log("run task?" + this.doc.readyState);
22632 this.assignDocWin();
22633 if(this.doc.body || this.doc.readyState == 'complete'){
22635 this.doc.designMode="on";
22639 Roo.TaskMgr.stop(task);
22640 this.initEditor.defer(10, this);
22647 Roo.TaskMgr.start(task);
22652 onResize : function(w, h)
22654 Roo.log('resize: ' +w + ',' + h );
22655 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22659 if(typeof w == 'number'){
22661 this.iframe.style.width = w + 'px';
22663 if(typeof h == 'number'){
22665 this.iframe.style.height = h + 'px';
22667 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22674 * Toggles the editor between standard and source edit mode.
22675 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22677 toggleSourceEdit : function(sourceEditMode){
22679 this.sourceEditMode = sourceEditMode === true;
22681 if(this.sourceEditMode){
22683 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22686 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22687 //this.iframe.className = '';
22690 //this.setSize(this.owner.wrap.getSize());
22691 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22698 * Protected method that will not generally be called directly. If you need/want
22699 * custom HTML cleanup, this is the method you should override.
22700 * @param {String} html The HTML to be cleaned
22701 * return {String} The cleaned HTML
22703 cleanHtml : function(html){
22704 html = String(html);
22705 if(html.length > 5){
22706 if(Roo.isSafari){ // strip safari nonsense
22707 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22710 if(html == ' '){
22717 * HTML Editor -> Textarea
22718 * Protected method that will not generally be called directly. Syncs the contents
22719 * of the editor iframe with the textarea.
22721 syncValue : function(){
22722 if(this.initialized){
22723 var bd = (this.doc.body || this.doc.documentElement);
22724 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22725 var html = bd.innerHTML;
22727 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22728 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22730 html = '<div style="'+m[0]+'">' + html + '</div>';
22733 html = this.cleanHtml(html);
22734 // fix up the special chars.. normaly like back quotes in word...
22735 // however we do not want to do this with chinese..
22736 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
22738 var cc = match.charCodeAt();
22740 // Get the character value, handling surrogate pairs
22741 if (match.length == 2) {
22742 // It's a surrogate pair, calculate the Unicode code point
22743 var high = match.charCodeAt(0) - 0xD800;
22744 var low = match.charCodeAt(1) - 0xDC00;
22745 cc = (high * 0x400) + low + 0x10000;
22747 (cc >= 0x4E00 && cc < 0xA000 ) ||
22748 (cc >= 0x3400 && cc < 0x4E00 ) ||
22749 (cc >= 0xf900 && cc < 0xfb00 )
22754 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
22755 return "&#" + cc + ";";
22762 if(this.owner.fireEvent('beforesync', this, html) !== false){
22763 this.el.dom.value = html;
22764 this.owner.fireEvent('sync', this, html);
22770 * Protected method that will not generally be called directly. Pushes the value of the textarea
22771 * into the iframe editor.
22773 pushValue : function(){
22774 if(this.initialized){
22775 var v = this.el.dom.value.trim();
22777 // if(v.length < 1){
22781 if(this.owner.fireEvent('beforepush', this, v) !== false){
22782 var d = (this.doc.body || this.doc.documentElement);
22784 this.cleanUpPaste();
22785 this.el.dom.value = d.innerHTML;
22786 this.owner.fireEvent('push', this, v);
22792 deferFocus : function(){
22793 this.focus.defer(10, this);
22797 focus : function(){
22798 if(this.win && !this.sourceEditMode){
22805 assignDocWin: function()
22807 var iframe = this.iframe;
22810 this.doc = iframe.contentWindow.document;
22811 this.win = iframe.contentWindow;
22813 // if (!Roo.get(this.frameId)) {
22816 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22817 // this.win = Roo.get(this.frameId).dom.contentWindow;
22819 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22823 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22824 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22829 initEditor : function(){
22830 //console.log("INIT EDITOR");
22831 this.assignDocWin();
22835 this.doc.designMode="on";
22837 this.doc.write(this.getDocMarkup());
22840 var dbody = (this.doc.body || this.doc.documentElement);
22841 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22842 // this copies styles from the containing element into thsi one..
22843 // not sure why we need all of this..
22844 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22846 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22847 //ss['background-attachment'] = 'fixed'; // w3c
22848 dbody.bgProperties = 'fixed'; // ie
22849 //Roo.DomHelper.applyStyles(dbody, ss);
22850 Roo.EventManager.on(this.doc, {
22851 //'mousedown': this.onEditorEvent,
22852 'mouseup': this.onEditorEvent,
22853 'dblclick': this.onEditorEvent,
22854 'click': this.onEditorEvent,
22855 'keyup': this.onEditorEvent,
22860 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22862 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22863 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22865 this.initialized = true;
22867 this.owner.fireEvent('initialize', this);
22872 onDestroy : function(){
22878 //for (var i =0; i < this.toolbars.length;i++) {
22879 // // fixme - ask toolbars for heights?
22880 // this.toolbars[i].onDestroy();
22883 //this.wrap.dom.innerHTML = '';
22884 //this.wrap.remove();
22889 onFirstFocus : function(){
22891 this.assignDocWin();
22894 this.activated = true;
22897 if(Roo.isGecko){ // prevent silly gecko errors
22899 var s = this.win.getSelection();
22900 if(!s.focusNode || s.focusNode.nodeType != 3){
22901 var r = s.getRangeAt(0);
22902 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22907 this.execCmd('useCSS', true);
22908 this.execCmd('styleWithCSS', false);
22911 this.owner.fireEvent('activate', this);
22915 adjustFont: function(btn){
22916 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22917 //if(Roo.isSafari){ // safari
22920 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22921 if(Roo.isSafari){ // safari
22922 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22923 v = (v < 10) ? 10 : v;
22924 v = (v > 48) ? 48 : v;
22925 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22930 v = Math.max(1, v+adjust);
22932 this.execCmd('FontSize', v );
22935 onEditorEvent : function(e)
22937 this.owner.fireEvent('editorevent', this, e);
22938 // this.updateToolbar();
22939 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22942 insertTag : function(tg)
22944 // could be a bit smarter... -> wrap the current selected tRoo..
22945 if (tg.toLowerCase() == 'span' ||
22946 tg.toLowerCase() == 'code' ||
22947 tg.toLowerCase() == 'sup' ||
22948 tg.toLowerCase() == 'sub'
22951 range = this.createRange(this.getSelection());
22952 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22953 wrappingNode.appendChild(range.extractContents());
22954 range.insertNode(wrappingNode);
22961 this.execCmd("formatblock", tg);
22965 insertText : function(txt)
22969 var range = this.createRange();
22970 range.deleteContents();
22971 //alert(Sender.getAttribute('label'));
22973 range.insertNode(this.doc.createTextNode(txt));
22979 * Executes a Midas editor command on the editor document and performs necessary focus and
22980 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22981 * @param {String} cmd The Midas command
22982 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22984 relayCmd : function(cmd, value){
22986 this.execCmd(cmd, value);
22987 this.owner.fireEvent('editorevent', this);
22988 //this.updateToolbar();
22989 this.owner.deferFocus();
22993 * Executes a Midas editor command directly on the editor document.
22994 * For visual commands, you should use {@link #relayCmd} instead.
22995 * <b>This should only be called after the editor is initialized.</b>
22996 * @param {String} cmd The Midas command
22997 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22999 execCmd : function(cmd, value){
23000 this.doc.execCommand(cmd, false, value === undefined ? null : value);
23007 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
23009 * @param {String} text | dom node..
23011 insertAtCursor : function(text)
23014 if(!this.activated){
23020 var r = this.doc.selection.createRange();
23031 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
23035 // from jquery ui (MIT licenced)
23037 var win = this.win;
23039 if (win.getSelection && win.getSelection().getRangeAt) {
23040 range = win.getSelection().getRangeAt(0);
23041 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
23042 range.insertNode(node);
23043 } else if (win.document.selection && win.document.selection.createRange) {
23044 // no firefox support
23045 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23046 win.document.selection.createRange().pasteHTML(txt);
23048 // no firefox support
23049 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23050 this.execCmd('InsertHTML', txt);
23059 mozKeyPress : function(e){
23061 var c = e.getCharCode(), cmd;
23064 c = String.fromCharCode(c).toLowerCase();
23078 this.cleanUpPaste.defer(100, this);
23086 e.preventDefault();
23094 fixKeys : function(){ // load time branching for fastest keydown performance
23096 return function(e){
23097 var k = e.getKey(), r;
23100 r = this.doc.selection.createRange();
23103 r.pasteHTML('    ');
23110 r = this.doc.selection.createRange();
23112 var target = r.parentElement();
23113 if(!target || target.tagName.toLowerCase() != 'li'){
23115 r.pasteHTML('<br />');
23121 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23122 this.cleanUpPaste.defer(100, this);
23128 }else if(Roo.isOpera){
23129 return function(e){
23130 var k = e.getKey();
23134 this.execCmd('InsertHTML','    ');
23137 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23138 this.cleanUpPaste.defer(100, this);
23143 }else if(Roo.isSafari){
23144 return function(e){
23145 var k = e.getKey();
23149 this.execCmd('InsertText','\t');
23153 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23154 this.cleanUpPaste.defer(100, this);
23162 getAllAncestors: function()
23164 var p = this.getSelectedNode();
23167 a.push(p); // push blank onto stack..
23168 p = this.getParentElement();
23172 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
23176 a.push(this.doc.body);
23180 lastSelNode : false,
23183 getSelection : function()
23185 this.assignDocWin();
23186 return Roo.isIE ? this.doc.selection : this.win.getSelection();
23189 getSelectedNode: function()
23191 // this may only work on Gecko!!!
23193 // should we cache this!!!!
23198 var range = this.createRange(this.getSelection()).cloneRange();
23201 var parent = range.parentElement();
23203 var testRange = range.duplicate();
23204 testRange.moveToElementText(parent);
23205 if (testRange.inRange(range)) {
23208 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
23211 parent = parent.parentElement;
23216 // is ancestor a text element.
23217 var ac = range.commonAncestorContainer;
23218 if (ac.nodeType == 3) {
23219 ac = ac.parentNode;
23222 var ar = ac.childNodes;
23225 var other_nodes = [];
23226 var has_other_nodes = false;
23227 for (var i=0;i<ar.length;i++) {
23228 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
23231 // fullly contained node.
23233 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
23238 // probably selected..
23239 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
23240 other_nodes.push(ar[i]);
23244 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
23249 has_other_nodes = true;
23251 if (!nodes.length && other_nodes.length) {
23252 nodes= other_nodes;
23254 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
23260 createRange: function(sel)
23262 // this has strange effects when using with
23263 // top toolbar - not sure if it's a great idea.
23264 //this.editor.contentWindow.focus();
23265 if (typeof sel != "undefined") {
23267 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
23269 return this.doc.createRange();
23272 return this.doc.createRange();
23275 getParentElement: function()
23278 this.assignDocWin();
23279 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
23281 var range = this.createRange(sel);
23284 var p = range.commonAncestorContainer;
23285 while (p.nodeType == 3) { // text node
23296 * Range intersection.. the hard stuff...
23300 * [ -- selected range --- ]
23304 * if end is before start or hits it. fail.
23305 * if start is after end or hits it fail.
23307 * if either hits (but other is outside. - then it's not
23313 // @see http://www.thismuchiknow.co.uk/?p=64.
23314 rangeIntersectsNode : function(range, node)
23316 var nodeRange = node.ownerDocument.createRange();
23318 nodeRange.selectNode(node);
23320 nodeRange.selectNodeContents(node);
23323 var rangeStartRange = range.cloneRange();
23324 rangeStartRange.collapse(true);
23326 var rangeEndRange = range.cloneRange();
23327 rangeEndRange.collapse(false);
23329 var nodeStartRange = nodeRange.cloneRange();
23330 nodeStartRange.collapse(true);
23332 var nodeEndRange = nodeRange.cloneRange();
23333 nodeEndRange.collapse(false);
23335 return rangeStartRange.compareBoundaryPoints(
23336 Range.START_TO_START, nodeEndRange) == -1 &&
23337 rangeEndRange.compareBoundaryPoints(
23338 Range.START_TO_START, nodeStartRange) == 1;
23342 rangeCompareNode : function(range, node)
23344 var nodeRange = node.ownerDocument.createRange();
23346 nodeRange.selectNode(node);
23348 nodeRange.selectNodeContents(node);
23352 range.collapse(true);
23354 nodeRange.collapse(true);
23356 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23357 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
23359 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23361 var nodeIsBefore = ss == 1;
23362 var nodeIsAfter = ee == -1;
23364 if (nodeIsBefore && nodeIsAfter) {
23367 if (!nodeIsBefore && nodeIsAfter) {
23368 return 1; //right trailed.
23371 if (nodeIsBefore && !nodeIsAfter) {
23372 return 2; // left trailed.
23378 // private? - in a new class?
23379 cleanUpPaste : function()
23381 // cleans up the whole document..
23382 Roo.log('cleanuppaste');
23384 this.cleanUpChildren(this.doc.body);
23385 var clean = this.cleanWordChars(this.doc.body.innerHTML);
23386 if (clean != this.doc.body.innerHTML) {
23387 this.doc.body.innerHTML = clean;
23392 cleanWordChars : function(input) {// change the chars to hex code
23393 var he = Roo.HtmlEditorCore;
23395 var output = input;
23396 Roo.each(he.swapCodes, function(sw) {
23397 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23399 output = output.replace(swapper, sw[1]);
23406 cleanUpChildren : function (n)
23408 if (!n.childNodes.length) {
23411 for (var i = n.childNodes.length-1; i > -1 ; i--) {
23412 this.cleanUpChild(n.childNodes[i]);
23419 cleanUpChild : function (node)
23422 //console.log(node);
23423 if (node.nodeName == "#text") {
23424 // clean up silly Windows -- stuff?
23427 if (node.nodeName == "#comment") {
23428 node.parentNode.removeChild(node);
23429 // clean up silly Windows -- stuff?
23432 var lcname = node.tagName.toLowerCase();
23433 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23434 // whitelist of tags..
23436 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23438 node.parentNode.removeChild(node);
23443 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23445 // spans with no attributes - just remove them..
23446 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
23447 remove_keep_children = true;
23450 // remove <a name=....> as rendering on yahoo mailer is borked with this.
23451 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23453 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23454 // remove_keep_children = true;
23457 if (remove_keep_children) {
23458 this.cleanUpChildren(node);
23459 // inserts everything just before this node...
23460 while (node.childNodes.length) {
23461 var cn = node.childNodes[0];
23462 node.removeChild(cn);
23463 node.parentNode.insertBefore(cn, node);
23465 node.parentNode.removeChild(node);
23469 if (!node.attributes || !node.attributes.length) {
23474 this.cleanUpChildren(node);
23478 function cleanAttr(n,v)
23481 if (v.match(/^\./) || v.match(/^\//)) {
23484 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23487 if (v.match(/^#/)) {
23490 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23491 node.removeAttribute(n);
23495 var cwhite = this.cwhite;
23496 var cblack = this.cblack;
23498 function cleanStyle(n,v)
23500 if (v.match(/expression/)) { //XSS?? should we even bother..
23501 node.removeAttribute(n);
23505 var parts = v.split(/;/);
23508 Roo.each(parts, function(p) {
23509 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23513 var l = p.split(':').shift().replace(/\s+/g,'');
23514 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23516 if ( cwhite.length && cblack.indexOf(l) > -1) {
23517 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23518 //node.removeAttribute(n);
23522 // only allow 'c whitelisted system attributes'
23523 if ( cwhite.length && cwhite.indexOf(l) < 0) {
23524 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23525 //node.removeAttribute(n);
23535 if (clean.length) {
23536 node.setAttribute(n, clean.join(';'));
23538 node.removeAttribute(n);
23544 for (var i = node.attributes.length-1; i > -1 ; i--) {
23545 var a = node.attributes[i];
23548 if (a.name.toLowerCase().substr(0,2)=='on') {
23549 node.removeAttribute(a.name);
23552 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23553 node.removeAttribute(a.name);
23556 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23557 cleanAttr(a.name,a.value); // fixme..
23560 if (a.name == 'style') {
23561 cleanStyle(a.name,a.value);
23564 /// clean up MS crap..
23565 // tecnically this should be a list of valid class'es..
23568 if (a.name == 'class') {
23569 if (a.value.match(/^Mso/)) {
23570 node.removeAttribute('class');
23573 if (a.value.match(/^body$/)) {
23574 node.removeAttribute('class');
23585 this.cleanUpChildren(node);
23591 * Clean up MS wordisms...
23593 cleanWord : function(node)
23596 this.cleanWord(this.doc.body);
23601 node.nodeName == 'SPAN' &&
23602 !node.hasAttributes() &&
23603 node.childNodes.length == 1 &&
23604 node.firstChild.nodeName == "#text"
23606 var textNode = node.firstChild;
23607 node.removeChild(textNode);
23608 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
23609 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
23611 node.parentNode.insertBefore(textNode, node);
23612 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
23613 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
23615 node.parentNode.removeChild(node);
23618 if (node.nodeName == "#text") {
23619 // clean up silly Windows -- stuff?
23622 if (node.nodeName == "#comment") {
23623 node.parentNode.removeChild(node);
23624 // clean up silly Windows -- stuff?
23628 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23629 node.parentNode.removeChild(node);
23632 //Roo.log(node.tagName);
23633 // remove - but keep children..
23634 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
23635 //Roo.log('-- removed');
23636 while (node.childNodes.length) {
23637 var cn = node.childNodes[0];
23638 node.removeChild(cn);
23639 node.parentNode.insertBefore(cn, node);
23640 // move node to parent - and clean it..
23641 this.cleanWord(cn);
23643 node.parentNode.removeChild(node);
23644 /// no need to iterate chidlren = it's got none..
23645 //this.iterateChildren(node, this.cleanWord);
23649 if (node.className.length) {
23651 var cn = node.className.split(/\W+/);
23653 Roo.each(cn, function(cls) {
23654 if (cls.match(/Mso[a-zA-Z]+/)) {
23659 node.className = cna.length ? cna.join(' ') : '';
23661 node.removeAttribute("class");
23665 if (node.hasAttribute("lang")) {
23666 node.removeAttribute("lang");
23669 if (node.hasAttribute("style")) {
23671 var styles = node.getAttribute("style").split(";");
23673 Roo.each(styles, function(s) {
23674 if (!s.match(/:/)) {
23677 var kv = s.split(":");
23678 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23681 // what ever is left... we allow.
23684 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23685 if (!nstyle.length) {
23686 node.removeAttribute('style');
23689 this.iterateChildren(node, this.cleanWord);
23695 * iterateChildren of a Node, calling fn each time, using this as the scole..
23696 * @param {DomNode} node node to iterate children of.
23697 * @param {Function} fn method of this class to call on each item.
23699 iterateChildren : function(node, fn)
23701 if (!node.childNodes.length) {
23704 for (var i = node.childNodes.length-1; i > -1 ; i--) {
23705 fn.call(this, node.childNodes[i])
23711 * cleanTableWidths.
23713 * Quite often pasting from word etc.. results in tables with column and widths.
23714 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23717 cleanTableWidths : function(node)
23722 this.cleanTableWidths(this.doc.body);
23727 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23730 Roo.log(node.tagName);
23731 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23732 this.iterateChildren(node, this.cleanTableWidths);
23735 if (node.hasAttribute('width')) {
23736 node.removeAttribute('width');
23740 if (node.hasAttribute("style")) {
23743 var styles = node.getAttribute("style").split(";");
23745 Roo.each(styles, function(s) {
23746 if (!s.match(/:/)) {
23749 var kv = s.split(":");
23750 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23753 // what ever is left... we allow.
23756 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23757 if (!nstyle.length) {
23758 node.removeAttribute('style');
23762 this.iterateChildren(node, this.cleanTableWidths);
23770 domToHTML : function(currentElement, depth, nopadtext) {
23772 depth = depth || 0;
23773 nopadtext = nopadtext || false;
23775 if (!currentElement) {
23776 return this.domToHTML(this.doc.body);
23779 //Roo.log(currentElement);
23781 var allText = false;
23782 var nodeName = currentElement.nodeName;
23783 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23785 if (nodeName == '#text') {
23787 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23792 if (nodeName != 'BODY') {
23795 // Prints the node tagName, such as <A>, <IMG>, etc
23798 for(i = 0; i < currentElement.attributes.length;i++) {
23800 var aname = currentElement.attributes.item(i).name;
23801 if (!currentElement.attributes.item(i).value.length) {
23804 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23807 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23816 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23819 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23824 // Traverse the tree
23826 var currentElementChild = currentElement.childNodes.item(i);
23827 var allText = true;
23828 var innerHTML = '';
23830 while (currentElementChild) {
23831 // Formatting code (indent the tree so it looks nice on the screen)
23832 var nopad = nopadtext;
23833 if (lastnode == 'SPAN') {
23837 if (currentElementChild.nodeName == '#text') {
23838 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23839 toadd = nopadtext ? toadd : toadd.trim();
23840 if (!nopad && toadd.length > 80) {
23841 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23843 innerHTML += toadd;
23846 currentElementChild = currentElement.childNodes.item(i);
23852 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23854 // Recursively traverse the tree structure of the child node
23855 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23856 lastnode = currentElementChild.nodeName;
23858 currentElementChild=currentElement.childNodes.item(i);
23864 // The remaining code is mostly for formatting the tree
23865 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23870 ret+= "</"+tagName+">";
23876 applyBlacklists : function()
23878 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23879 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23883 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23884 if (b.indexOf(tag) > -1) {
23887 this.white.push(tag);
23891 Roo.each(w, function(tag) {
23892 if (b.indexOf(tag) > -1) {
23895 if (this.white.indexOf(tag) > -1) {
23898 this.white.push(tag);
23903 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23904 if (w.indexOf(tag) > -1) {
23907 this.black.push(tag);
23911 Roo.each(b, function(tag) {
23912 if (w.indexOf(tag) > -1) {
23915 if (this.black.indexOf(tag) > -1) {
23918 this.black.push(tag);
23923 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23924 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23928 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23929 if (b.indexOf(tag) > -1) {
23932 this.cwhite.push(tag);
23936 Roo.each(w, function(tag) {
23937 if (b.indexOf(tag) > -1) {
23940 if (this.cwhite.indexOf(tag) > -1) {
23943 this.cwhite.push(tag);
23948 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23949 if (w.indexOf(tag) > -1) {
23952 this.cblack.push(tag);
23956 Roo.each(b, function(tag) {
23957 if (w.indexOf(tag) > -1) {
23960 if (this.cblack.indexOf(tag) > -1) {
23963 this.cblack.push(tag);
23968 setStylesheets : function(stylesheets)
23970 if(typeof(stylesheets) == 'string'){
23971 Roo.get(this.iframe.contentDocument.head).createChild({
23973 rel : 'stylesheet',
23982 Roo.each(stylesheets, function(s) {
23987 Roo.get(_this.iframe.contentDocument.head).createChild({
23989 rel : 'stylesheet',
23998 removeStylesheets : function()
24002 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
24007 setStyle : function(style)
24009 Roo.get(this.iframe.contentDocument.head).createChild({
24018 // hide stuff that is not compatible
24032 * @event specialkey
24036 * @cfg {String} fieldClass @hide
24039 * @cfg {String} focusClass @hide
24042 * @cfg {String} autoCreate @hide
24045 * @cfg {String} inputType @hide
24048 * @cfg {String} invalidClass @hide
24051 * @cfg {String} invalidText @hide
24054 * @cfg {String} msgFx @hide
24057 * @cfg {String} validateOnBlur @hide
24061 Roo.HtmlEditorCore.white = [
24062 'area', 'br', 'img', 'input', 'hr', 'wbr',
24064 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
24065 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
24066 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
24067 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
24068 'table', 'ul', 'xmp',
24070 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
24073 'dir', 'menu', 'ol', 'ul', 'dl',
24079 Roo.HtmlEditorCore.black = [
24080 // 'embed', 'object', // enable - backend responsiblity to clean thiese
24082 'base', 'basefont', 'bgsound', 'blink', 'body',
24083 'frame', 'frameset', 'head', 'html', 'ilayer',
24084 'iframe', 'layer', 'link', 'meta', 'object',
24085 'script', 'style' ,'title', 'xml' // clean later..
24087 Roo.HtmlEditorCore.clean = [
24088 'script', 'style', 'title', 'xml'
24090 Roo.HtmlEditorCore.remove = [
24095 Roo.HtmlEditorCore.ablack = [
24099 Roo.HtmlEditorCore.aclean = [
24100 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24104 Roo.HtmlEditorCore.pwhite= [
24105 'http', 'https', 'mailto'
24108 // white listed style attributes.
24109 Roo.HtmlEditorCore.cwhite= [
24110 // 'text-align', /// default is to allow most things..
24116 // black listed style attributes.
24117 Roo.HtmlEditorCore.cblack= [
24118 // 'font-size' -- this can be set by the project
24122 Roo.HtmlEditorCore.swapCodes =[
24141 * @class Roo.bootstrap.HtmlEditor
24142 * @extends Roo.bootstrap.TextArea
24143 * Bootstrap HtmlEditor class
24146 * Create a new HtmlEditor
24147 * @param {Object} config The config object
24150 Roo.bootstrap.HtmlEditor = function(config){
24151 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
24152 if (!this.toolbars) {
24153 this.toolbars = [];
24156 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
24159 * @event initialize
24160 * Fires when the editor is fully initialized (including the iframe)
24161 * @param {HtmlEditor} this
24166 * Fires when the editor is first receives the focus. Any insertion must wait
24167 * until after this event.
24168 * @param {HtmlEditor} this
24172 * @event beforesync
24173 * Fires before the textarea is updated with content from the editor iframe. Return false
24174 * to cancel the sync.
24175 * @param {HtmlEditor} this
24176 * @param {String} html
24180 * @event beforepush
24181 * Fires before the iframe editor is updated with content from the textarea. Return false
24182 * to cancel the push.
24183 * @param {HtmlEditor} this
24184 * @param {String} html
24189 * Fires when the textarea is updated with content from the editor iframe.
24190 * @param {HtmlEditor} this
24191 * @param {String} html
24196 * Fires when the iframe editor is updated with content from the textarea.
24197 * @param {HtmlEditor} this
24198 * @param {String} html
24202 * @event editmodechange
24203 * Fires when the editor switches edit modes
24204 * @param {HtmlEditor} this
24205 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24207 editmodechange: true,
24209 * @event editorevent
24210 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24211 * @param {HtmlEditor} this
24215 * @event firstfocus
24216 * Fires when on first focus - needed by toolbars..
24217 * @param {HtmlEditor} this
24222 * Auto save the htmlEditor value as a file into Events
24223 * @param {HtmlEditor} this
24227 * @event savedpreview
24228 * preview the saved version of htmlEditor
24229 * @param {HtmlEditor} this
24236 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
24240 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24245 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
24250 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24255 * @cfg {Number} height (in pixels)
24259 * @cfg {Number} width (in pixels)
24264 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24267 stylesheets: false,
24272 // private properties
24273 validationEvent : false,
24275 initialized : false,
24278 onFocus : Roo.emptyFn,
24280 hideMode:'offsets',
24282 tbContainer : false,
24286 toolbarContainer :function() {
24287 return this.wrap.select('.x-html-editor-tb',true).first();
24291 * Protected method that will not generally be called directly. It
24292 * is called when the editor creates its toolbar. Override this method if you need to
24293 * add custom toolbar buttons.
24294 * @param {HtmlEditor} editor
24296 createToolbar : function(){
24297 Roo.log('renewing');
24298 Roo.log("create toolbars");
24300 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
24301 this.toolbars[0].render(this.toolbarContainer());
24305 // if (!editor.toolbars || !editor.toolbars.length) {
24306 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
24309 // for (var i =0 ; i < editor.toolbars.length;i++) {
24310 // editor.toolbars[i] = Roo.factory(
24311 // typeof(editor.toolbars[i]) == 'string' ?
24312 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
24313 // Roo.bootstrap.HtmlEditor);
24314 // editor.toolbars[i].init(editor);
24320 onRender : function(ct, position)
24322 // Roo.log("Call onRender: " + this.xtype);
24324 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
24326 this.wrap = this.inputEl().wrap({
24327 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24330 this.editorcore.onRender(ct, position);
24332 if (this.resizable) {
24333 this.resizeEl = new Roo.Resizable(this.wrap, {
24337 minHeight : this.height,
24338 height: this.height,
24339 handles : this.resizable,
24342 resize : function(r, w, h) {
24343 _t.onResize(w,h); // -something
24349 this.createToolbar(this);
24352 if(!this.width && this.resizable){
24353 this.setSize(this.wrap.getSize());
24355 if (this.resizeEl) {
24356 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24357 // should trigger onReize..
24363 onResize : function(w, h)
24365 Roo.log('resize: ' +w + ',' + h );
24366 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
24370 if(this.inputEl() ){
24371 if(typeof w == 'number'){
24372 var aw = w - this.wrap.getFrameWidth('lr');
24373 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24376 if(typeof h == 'number'){
24377 var tbh = -11; // fixme it needs to tool bar size!
24378 for (var i =0; i < this.toolbars.length;i++) {
24379 // fixme - ask toolbars for heights?
24380 tbh += this.toolbars[i].el.getHeight();
24381 //if (this.toolbars[i].footer) {
24382 // tbh += this.toolbars[i].footer.el.getHeight();
24390 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24391 ah -= 5; // knock a few pixes off for look..
24392 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24396 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24397 this.editorcore.onResize(ew,eh);
24402 * Toggles the editor between standard and source edit mode.
24403 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24405 toggleSourceEdit : function(sourceEditMode)
24407 this.editorcore.toggleSourceEdit(sourceEditMode);
24409 if(this.editorcore.sourceEditMode){
24410 Roo.log('editor - showing textarea');
24413 // Roo.log(this.syncValue());
24415 this.inputEl().removeClass(['hide', 'x-hidden']);
24416 this.inputEl().dom.removeAttribute('tabIndex');
24417 this.inputEl().focus();
24419 Roo.log('editor - hiding textarea');
24421 // Roo.log(this.pushValue());
24424 this.inputEl().addClass(['hide', 'x-hidden']);
24425 this.inputEl().dom.setAttribute('tabIndex', -1);
24426 //this.deferFocus();
24429 if(this.resizable){
24430 this.setSize(this.wrap.getSize());
24433 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24436 // private (for BoxComponent)
24437 adjustSize : Roo.BoxComponent.prototype.adjustSize,
24439 // private (for BoxComponent)
24440 getResizeEl : function(){
24444 // private (for BoxComponent)
24445 getPositionEl : function(){
24450 initEvents : function(){
24451 this.originalValue = this.getValue();
24455 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24458 // markInvalid : Roo.emptyFn,
24460 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24463 // clearInvalid : Roo.emptyFn,
24465 setValue : function(v){
24466 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24467 this.editorcore.pushValue();
24472 deferFocus : function(){
24473 this.focus.defer(10, this);
24477 focus : function(){
24478 this.editorcore.focus();
24484 onDestroy : function(){
24490 for (var i =0; i < this.toolbars.length;i++) {
24491 // fixme - ask toolbars for heights?
24492 this.toolbars[i].onDestroy();
24495 this.wrap.dom.innerHTML = '';
24496 this.wrap.remove();
24501 onFirstFocus : function(){
24502 //Roo.log("onFirstFocus");
24503 this.editorcore.onFirstFocus();
24504 for (var i =0; i < this.toolbars.length;i++) {
24505 this.toolbars[i].onFirstFocus();
24511 syncValue : function()
24513 this.editorcore.syncValue();
24516 pushValue : function()
24518 this.editorcore.pushValue();
24522 // hide stuff that is not compatible
24536 * @event specialkey
24540 * @cfg {String} fieldClass @hide
24543 * @cfg {String} focusClass @hide
24546 * @cfg {String} autoCreate @hide
24549 * @cfg {String} inputType @hide
24553 * @cfg {String} invalidText @hide
24556 * @cfg {String} msgFx @hide
24559 * @cfg {String} validateOnBlur @hide
24568 Roo.namespace('Roo.bootstrap.htmleditor');
24570 * @class Roo.bootstrap.HtmlEditorToolbar1
24576 new Roo.bootstrap.HtmlEditor({
24579 new Roo.bootstrap.HtmlEditorToolbar1({
24580 disable : { fonts: 1 , format: 1, ..., ... , ...],
24586 * @cfg {Object} disable List of elements to disable..
24587 * @cfg {Array} btns List of additional buttons.
24591 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24594 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24597 Roo.apply(this, config);
24599 // default disabled, based on 'good practice'..
24600 this.disable = this.disable || {};
24601 Roo.applyIf(this.disable, {
24604 specialElements : true
24606 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24608 this.editor = config.editor;
24609 this.editorcore = config.editor.editorcore;
24611 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24613 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24614 // dont call parent... till later.
24616 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
24621 editorcore : false,
24626 "h1","h2","h3","h4","h5","h6",
24628 "abbr", "acronym", "address", "cite", "samp", "var",
24632 onRender : function(ct, position)
24634 // Roo.log("Call onRender: " + this.xtype);
24636 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24638 this.el.dom.style.marginBottom = '0';
24640 var editorcore = this.editorcore;
24641 var editor= this.editor;
24644 var btn = function(id,cmd , toggle, handler, html){
24646 var event = toggle ? 'toggle' : 'click';
24651 xns: Roo.bootstrap,
24655 enableToggle:toggle !== false,
24657 pressed : toggle ? false : null,
24660 a.listeners[toggle ? 'toggle' : 'click'] = function() {
24661 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
24667 // var cb_box = function...
24672 xns: Roo.bootstrap,
24677 xns: Roo.bootstrap,
24681 Roo.each(this.formats, function(f) {
24682 style.menu.items.push({
24684 xns: Roo.bootstrap,
24685 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24690 editorcore.insertTag(this.tagname);
24697 children.push(style);
24699 btn('bold',false,true);
24700 btn('italic',false,true);
24701 btn('align-left', 'justifyleft',true);
24702 btn('align-center', 'justifycenter',true);
24703 btn('align-right' , 'justifyright',true);
24704 btn('link', false, false, function(btn) {
24705 //Roo.log("create link?");
24706 var url = prompt(this.createLinkText, this.defaultLinkValue);
24707 if(url && url != 'http:/'+'/'){
24708 this.editorcore.relayCmd('createlink', url);
24711 btn('list','insertunorderedlist',true);
24712 btn('pencil', false,true, function(btn){
24714 this.toggleSourceEdit(btn.pressed);
24717 if (this.editor.btns.length > 0) {
24718 for (var i = 0; i<this.editor.btns.length; i++) {
24719 children.push(this.editor.btns[i]);
24727 xns: Roo.bootstrap,
24732 xns: Roo.bootstrap,
24737 cog.menu.items.push({
24739 xns: Roo.bootstrap,
24740 html : Clean styles,
24745 editorcore.insertTag(this.tagname);
24754 this.xtype = 'NavSimplebar';
24756 for(var i=0;i< children.length;i++) {
24758 this.buttons.add(this.addxtypeChild(children[i]));
24762 editor.on('editorevent', this.updateToolbar, this);
24764 onBtnClick : function(id)
24766 this.editorcore.relayCmd(id);
24767 this.editorcore.focus();
24771 * Protected method that will not generally be called directly. It triggers
24772 * a toolbar update by reading the markup state of the current selection in the editor.
24774 updateToolbar: function(){
24776 if(!this.editorcore.activated){
24777 this.editor.onFirstFocus(); // is this neeed?
24781 var btns = this.buttons;
24782 var doc = this.editorcore.doc;
24783 btns.get('bold').setActive(doc.queryCommandState('bold'));
24784 btns.get('italic').setActive(doc.queryCommandState('italic'));
24785 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24787 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24788 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24789 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24791 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24792 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24795 var ans = this.editorcore.getAllAncestors();
24796 if (this.formatCombo) {
24799 var store = this.formatCombo.store;
24800 this.formatCombo.setValue("");
24801 for (var i =0; i < ans.length;i++) {
24802 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24804 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24812 // hides menus... - so this cant be on a menu...
24813 Roo.bootstrap.MenuMgr.hideAll();
24815 Roo.bootstrap.MenuMgr.hideAll();
24816 //this.editorsyncValue();
24818 onFirstFocus: function() {
24819 this.buttons.each(function(item){
24823 toggleSourceEdit : function(sourceEditMode){
24826 if(sourceEditMode){
24827 Roo.log("disabling buttons");
24828 this.buttons.each( function(item){
24829 if(item.cmd != 'pencil'){
24835 Roo.log("enabling buttons");
24836 if(this.editorcore.initialized){
24837 this.buttons.each( function(item){
24843 Roo.log("calling toggole on editor");
24844 // tell the editor that it's been pressed..
24845 this.editor.toggleSourceEdit(sourceEditMode);
24855 * @class Roo.bootstrap.Table.AbstractSelectionModel
24856 * @extends Roo.util.Observable
24857 * Abstract base class for grid SelectionModels. It provides the interface that should be
24858 * implemented by descendant classes. This class should not be directly instantiated.
24861 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24862 this.locked = false;
24863 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24867 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24868 /** @ignore Called by the grid automatically. Do not call directly. */
24869 init : function(grid){
24875 * Locks the selections.
24878 this.locked = true;
24882 * Unlocks the selections.
24884 unlock : function(){
24885 this.locked = false;
24889 * Returns true if the selections are locked.
24890 * @return {Boolean}
24892 isLocked : function(){
24893 return this.locked;
24897 initEvents : function ()
24903 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24904 * @class Roo.bootstrap.Table.RowSelectionModel
24905 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24906 * It supports multiple selections and keyboard selection/navigation.
24908 * @param {Object} config
24911 Roo.bootstrap.Table.RowSelectionModel = function(config){
24912 Roo.apply(this, config);
24913 this.selections = new Roo.util.MixedCollection(false, function(o){
24918 this.lastActive = false;
24922 * @event selectionchange
24923 * Fires when the selection changes
24924 * @param {SelectionModel} this
24926 "selectionchange" : true,
24928 * @event afterselectionchange
24929 * Fires after the selection changes (eg. by key press or clicking)
24930 * @param {SelectionModel} this
24932 "afterselectionchange" : true,
24934 * @event beforerowselect
24935 * Fires when a row is selected being selected, return false to cancel.
24936 * @param {SelectionModel} this
24937 * @param {Number} rowIndex The selected index
24938 * @param {Boolean} keepExisting False if other selections will be cleared
24940 "beforerowselect" : true,
24943 * Fires when a row is selected.
24944 * @param {SelectionModel} this
24945 * @param {Number} rowIndex The selected index
24946 * @param {Roo.data.Record} r The record
24948 "rowselect" : true,
24950 * @event rowdeselect
24951 * Fires when a row is deselected.
24952 * @param {SelectionModel} this
24953 * @param {Number} rowIndex The selected index
24955 "rowdeselect" : true
24957 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24958 this.locked = false;
24961 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24963 * @cfg {Boolean} singleSelect
24964 * True to allow selection of only one row at a time (defaults to false)
24966 singleSelect : false,
24969 initEvents : function()
24972 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24973 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24974 //}else{ // allow click to work like normal
24975 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24977 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24978 this.grid.on("rowclick", this.handleMouseDown, this);
24980 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24981 "up" : function(e){
24983 this.selectPrevious(e.shiftKey);
24984 }else if(this.last !== false && this.lastActive !== false){
24985 var last = this.last;
24986 this.selectRange(this.last, this.lastActive-1);
24987 this.grid.getView().focusRow(this.lastActive);
24988 if(last !== false){
24992 this.selectFirstRow();
24994 this.fireEvent("afterselectionchange", this);
24996 "down" : function(e){
24998 this.selectNext(e.shiftKey);
24999 }else if(this.last !== false && this.lastActive !== false){
25000 var last = this.last;
25001 this.selectRange(this.last, this.lastActive+1);
25002 this.grid.getView().focusRow(this.lastActive);
25003 if(last !== false){
25007 this.selectFirstRow();
25009 this.fireEvent("afterselectionchange", this);
25013 this.grid.store.on('load', function(){
25014 this.selections.clear();
25017 var view = this.grid.view;
25018 view.on("refresh", this.onRefresh, this);
25019 view.on("rowupdated", this.onRowUpdated, this);
25020 view.on("rowremoved", this.onRemove, this);
25025 onRefresh : function()
25027 var ds = this.grid.store, i, v = this.grid.view;
25028 var s = this.selections;
25029 s.each(function(r){
25030 if((i = ds.indexOfId(r.id)) != -1){
25039 onRemove : function(v, index, r){
25040 this.selections.remove(r);
25044 onRowUpdated : function(v, index, r){
25045 if(this.isSelected(r)){
25046 v.onRowSelect(index);
25052 * @param {Array} records The records to select
25053 * @param {Boolean} keepExisting (optional) True to keep existing selections
25055 selectRecords : function(records, keepExisting)
25058 this.clearSelections();
25060 var ds = this.grid.store;
25061 for(var i = 0, len = records.length; i < len; i++){
25062 this.selectRow(ds.indexOf(records[i]), true);
25067 * Gets the number of selected rows.
25070 getCount : function(){
25071 return this.selections.length;
25075 * Selects the first row in the grid.
25077 selectFirstRow : function(){
25082 * Select the last row.
25083 * @param {Boolean} keepExisting (optional) True to keep existing selections
25085 selectLastRow : function(keepExisting){
25086 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
25087 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
25091 * Selects the row immediately following the last selected row.
25092 * @param {Boolean} keepExisting (optional) True to keep existing selections
25094 selectNext : function(keepExisting)
25096 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
25097 this.selectRow(this.last+1, keepExisting);
25098 this.grid.getView().focusRow(this.last);
25103 * Selects the row that precedes the last selected row.
25104 * @param {Boolean} keepExisting (optional) True to keep existing selections
25106 selectPrevious : function(keepExisting){
25108 this.selectRow(this.last-1, keepExisting);
25109 this.grid.getView().focusRow(this.last);
25114 * Returns the selected records
25115 * @return {Array} Array of selected records
25117 getSelections : function(){
25118 return [].concat(this.selections.items);
25122 * Returns the first selected record.
25125 getSelected : function(){
25126 return this.selections.itemAt(0);
25131 * Clears all selections.
25133 clearSelections : function(fast)
25139 var ds = this.grid.store;
25140 var s = this.selections;
25141 s.each(function(r){
25142 this.deselectRow(ds.indexOfId(r.id));
25146 this.selections.clear();
25153 * Selects all rows.
25155 selectAll : function(){
25159 this.selections.clear();
25160 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
25161 this.selectRow(i, true);
25166 * Returns True if there is a selection.
25167 * @return {Boolean}
25169 hasSelection : function(){
25170 return this.selections.length > 0;
25174 * Returns True if the specified row is selected.
25175 * @param {Number/Record} record The record or index of the record to check
25176 * @return {Boolean}
25178 isSelected : function(index){
25179 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
25180 return (r && this.selections.key(r.id) ? true : false);
25184 * Returns True if the specified record id is selected.
25185 * @param {String} id The id of record to check
25186 * @return {Boolean}
25188 isIdSelected : function(id){
25189 return (this.selections.key(id) ? true : false);
25194 handleMouseDBClick : function(e, t){
25198 handleMouseDown : function(e, t)
25200 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
25201 if(this.isLocked() || rowIndex < 0 ){
25204 if(e.shiftKey && this.last !== false){
25205 var last = this.last;
25206 this.selectRange(last, rowIndex, e.ctrlKey);
25207 this.last = last; // reset the last
25211 var isSelected = this.isSelected(rowIndex);
25212 //Roo.log("select row:" + rowIndex);
25214 this.deselectRow(rowIndex);
25216 this.selectRow(rowIndex, true);
25220 if(e.button !== 0 && isSelected){
25221 alert('rowIndex 2: ' + rowIndex);
25222 view.focusRow(rowIndex);
25223 }else if(e.ctrlKey && isSelected){
25224 this.deselectRow(rowIndex);
25225 }else if(!isSelected){
25226 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
25227 view.focusRow(rowIndex);
25231 this.fireEvent("afterselectionchange", this);
25234 handleDragableRowClick : function(grid, rowIndex, e)
25236 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
25237 this.selectRow(rowIndex, false);
25238 grid.view.focusRow(rowIndex);
25239 this.fireEvent("afterselectionchange", this);
25244 * Selects multiple rows.
25245 * @param {Array} rows Array of the indexes of the row to select
25246 * @param {Boolean} keepExisting (optional) True to keep existing selections
25248 selectRows : function(rows, keepExisting){
25250 this.clearSelections();
25252 for(var i = 0, len = rows.length; i < len; i++){
25253 this.selectRow(rows[i], true);
25258 * Selects a range of rows. All rows in between startRow and endRow are also selected.
25259 * @param {Number} startRow The index of the first row in the range
25260 * @param {Number} endRow The index of the last row in the range
25261 * @param {Boolean} keepExisting (optional) True to retain existing selections
25263 selectRange : function(startRow, endRow, keepExisting){
25268 this.clearSelections();
25270 if(startRow <= endRow){
25271 for(var i = startRow; i <= endRow; i++){
25272 this.selectRow(i, true);
25275 for(var i = startRow; i >= endRow; i--){
25276 this.selectRow(i, true);
25282 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
25283 * @param {Number} startRow The index of the first row in the range
25284 * @param {Number} endRow The index of the last row in the range
25286 deselectRange : function(startRow, endRow, preventViewNotify){
25290 for(var i = startRow; i <= endRow; i++){
25291 this.deselectRow(i, preventViewNotify);
25297 * @param {Number} row The index of the row to select
25298 * @param {Boolean} keepExisting (optional) True to keep existing selections
25300 selectRow : function(index, keepExisting, preventViewNotify)
25302 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
25305 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
25306 if(!keepExisting || this.singleSelect){
25307 this.clearSelections();
25310 var r = this.grid.store.getAt(index);
25311 //console.log('selectRow - record id :' + r.id);
25313 this.selections.add(r);
25314 this.last = this.lastActive = index;
25315 if(!preventViewNotify){
25316 var proxy = new Roo.Element(
25317 this.grid.getRowDom(index)
25319 proxy.addClass('bg-info info');
25321 this.fireEvent("rowselect", this, index, r);
25322 this.fireEvent("selectionchange", this);
25328 * @param {Number} row The index of the row to deselect
25330 deselectRow : function(index, preventViewNotify)
25335 if(this.last == index){
25338 if(this.lastActive == index){
25339 this.lastActive = false;
25342 var r = this.grid.store.getAt(index);
25347 this.selections.remove(r);
25348 //.console.log('deselectRow - record id :' + r.id);
25349 if(!preventViewNotify){
25351 var proxy = new Roo.Element(
25352 this.grid.getRowDom(index)
25354 proxy.removeClass('bg-info info');
25356 this.fireEvent("rowdeselect", this, index);
25357 this.fireEvent("selectionchange", this);
25361 restoreLast : function(){
25363 this.last = this._last;
25368 acceptsNav : function(row, col, cm){
25369 return !cm.isHidden(col) && cm.isCellEditable(col, row);
25373 onEditorKey : function(field, e){
25374 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25379 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25381 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25383 }else if(k == e.ENTER && !e.ctrlKey){
25387 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25389 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25391 }else if(k == e.ESC){
25395 g.startEditing(newCell[0], newCell[1]);
25401 * Ext JS Library 1.1.1
25402 * Copyright(c) 2006-2007, Ext JS, LLC.
25404 * Originally Released Under LGPL - original licence link has changed is not relivant.
25407 * <script type="text/javascript">
25411 * @class Roo.bootstrap.PagingToolbar
25412 * @extends Roo.bootstrap.NavSimplebar
25413 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25415 * Create a new PagingToolbar
25416 * @param {Object} config The config object
25417 * @param {Roo.data.Store} store
25419 Roo.bootstrap.PagingToolbar = function(config)
25421 // old args format still supported... - xtype is prefered..
25422 // created from xtype...
25424 this.ds = config.dataSource;
25426 if (config.store && !this.ds) {
25427 this.store= Roo.factory(config.store, Roo.data);
25428 this.ds = this.store;
25429 this.ds.xmodule = this.xmodule || false;
25432 this.toolbarItems = [];
25433 if (config.items) {
25434 this.toolbarItems = config.items;
25437 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25442 this.bind(this.ds);
25445 if (Roo.bootstrap.version == 4) {
25446 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25448 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25453 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25455 * @cfg {Roo.data.Store} dataSource
25456 * The underlying data store providing the paged data
25459 * @cfg {String/HTMLElement/Element} container
25460 * container The id or element that will contain the toolbar
25463 * @cfg {Boolean} displayInfo
25464 * True to display the displayMsg (defaults to false)
25467 * @cfg {Number} pageSize
25468 * The number of records to display per page (defaults to 20)
25472 * @cfg {String} displayMsg
25473 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25475 displayMsg : 'Displaying {0} - {1} of {2}',
25477 * @cfg {String} emptyMsg
25478 * The message to display when no records are found (defaults to "No data to display")
25480 emptyMsg : 'No data to display',
25482 * Customizable piece of the default paging text (defaults to "Page")
25485 beforePageText : "Page",
25487 * Customizable piece of the default paging text (defaults to "of %0")
25490 afterPageText : "of {0}",
25492 * Customizable piece of the default paging text (defaults to "First Page")
25495 firstText : "First Page",
25497 * Customizable piece of the default paging text (defaults to "Previous Page")
25500 prevText : "Previous Page",
25502 * Customizable piece of the default paging text (defaults to "Next Page")
25505 nextText : "Next Page",
25507 * Customizable piece of the default paging text (defaults to "Last Page")
25510 lastText : "Last Page",
25512 * Customizable piece of the default paging text (defaults to "Refresh")
25515 refreshText : "Refresh",
25519 onRender : function(ct, position)
25521 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25522 this.navgroup.parentId = this.id;
25523 this.navgroup.onRender(this.el, null);
25524 // add the buttons to the navgroup
25526 if(this.displayInfo){
25527 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25528 this.displayEl = this.el.select('.x-paging-info', true).first();
25529 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25530 // this.displayEl = navel.el.select('span',true).first();
25536 Roo.each(_this.buttons, function(e){ // this might need to use render????
25537 Roo.factory(e).render(_this.el);
25541 Roo.each(_this.toolbarItems, function(e) {
25542 _this.navgroup.addItem(e);
25546 this.first = this.navgroup.addItem({
25547 tooltip: this.firstText,
25548 cls: "prev btn-outline-secondary",
25549 html : ' <i class="fa fa-step-backward"></i>',
25551 preventDefault: true,
25552 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25555 this.prev = this.navgroup.addItem({
25556 tooltip: this.prevText,
25557 cls: "prev btn-outline-secondary",
25558 html : ' <i class="fa fa-backward"></i>',
25560 preventDefault: true,
25561 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
25563 //this.addSeparator();
25566 var field = this.navgroup.addItem( {
25568 cls : 'x-paging-position btn-outline-secondary',
25570 html : this.beforePageText +
25571 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25572 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
25575 this.field = field.el.select('input', true).first();
25576 this.field.on("keydown", this.onPagingKeydown, this);
25577 this.field.on("focus", function(){this.dom.select();});
25580 this.afterTextEl = field.el.select('.x-paging-after',true).first();
25581 //this.field.setHeight(18);
25582 //this.addSeparator();
25583 this.next = this.navgroup.addItem({
25584 tooltip: this.nextText,
25585 cls: "next btn-outline-secondary",
25586 html : ' <i class="fa fa-forward"></i>',
25588 preventDefault: true,
25589 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
25591 this.last = this.navgroup.addItem({
25592 tooltip: this.lastText,
25593 html : ' <i class="fa fa-step-forward"></i>',
25594 cls: "next btn-outline-secondary",
25596 preventDefault: true,
25597 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
25599 //this.addSeparator();
25600 this.loading = this.navgroup.addItem({
25601 tooltip: this.refreshText,
25602 cls: "btn-outline-secondary",
25603 html : ' <i class="fa fa-refresh"></i>',
25604 preventDefault: true,
25605 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25611 updateInfo : function(){
25612 if(this.displayEl){
25613 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25614 var msg = count == 0 ?
25618 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
25620 this.displayEl.update(msg);
25625 onLoad : function(ds, r, o)
25627 this.cursor = o.params.start ? o.params.start : 0;
25629 var d = this.getPageData(),
25634 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25635 this.field.dom.value = ap;
25636 this.first.setDisabled(ap == 1);
25637 this.prev.setDisabled(ap == 1);
25638 this.next.setDisabled(ap == ps);
25639 this.last.setDisabled(ap == ps);
25640 this.loading.enable();
25645 getPageData : function(){
25646 var total = this.ds.getTotalCount();
25649 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25650 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25655 onLoadError : function(){
25656 this.loading.enable();
25660 onPagingKeydown : function(e){
25661 var k = e.getKey();
25662 var d = this.getPageData();
25664 var v = this.field.dom.value, pageNum;
25665 if(!v || isNaN(pageNum = parseInt(v, 10))){
25666 this.field.dom.value = d.activePage;
25669 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25670 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25673 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))
25675 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25676 this.field.dom.value = pageNum;
25677 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25680 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25682 var v = this.field.dom.value, pageNum;
25683 var increment = (e.shiftKey) ? 10 : 1;
25684 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25687 if(!v || isNaN(pageNum = parseInt(v, 10))) {
25688 this.field.dom.value = d.activePage;
25691 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25693 this.field.dom.value = parseInt(v, 10) + increment;
25694 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25695 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25702 beforeLoad : function(){
25704 this.loading.disable();
25709 onClick : function(which){
25718 ds.load({params:{start: 0, limit: this.pageSize}});
25721 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25724 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25727 var total = ds.getTotalCount();
25728 var extra = total % this.pageSize;
25729 var lastStart = extra ? (total - extra) : total-this.pageSize;
25730 ds.load({params:{start: lastStart, limit: this.pageSize}});
25733 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25739 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25740 * @param {Roo.data.Store} store The data store to unbind
25742 unbind : function(ds){
25743 ds.un("beforeload", this.beforeLoad, this);
25744 ds.un("load", this.onLoad, this);
25745 ds.un("loadexception", this.onLoadError, this);
25746 ds.un("remove", this.updateInfo, this);
25747 ds.un("add", this.updateInfo, this);
25748 this.ds = undefined;
25752 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25753 * @param {Roo.data.Store} store The data store to bind
25755 bind : function(ds){
25756 ds.on("beforeload", this.beforeLoad, this);
25757 ds.on("load", this.onLoad, this);
25758 ds.on("loadexception", this.onLoadError, this);
25759 ds.on("remove", this.updateInfo, this);
25760 ds.on("add", this.updateInfo, this);
25771 * @class Roo.bootstrap.MessageBar
25772 * @extends Roo.bootstrap.Component
25773 * Bootstrap MessageBar class
25774 * @cfg {String} html contents of the MessageBar
25775 * @cfg {String} weight (info | success | warning | danger) default info
25776 * @cfg {String} beforeClass insert the bar before the given class
25777 * @cfg {Boolean} closable (true | false) default false
25778 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25781 * Create a new Element
25782 * @param {Object} config The config object
25785 Roo.bootstrap.MessageBar = function(config){
25786 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25789 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25795 beforeClass: 'bootstrap-sticky-wrap',
25797 getAutoCreate : function(){
25801 cls: 'alert alert-dismissable alert-' + this.weight,
25806 html: this.html || ''
25812 cfg.cls += ' alert-messages-fixed';
25826 onRender : function(ct, position)
25828 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25831 var cfg = Roo.apply({}, this.getAutoCreate());
25835 cfg.cls += ' ' + this.cls;
25838 cfg.style = this.style;
25840 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25842 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25845 this.el.select('>button.close').on('click', this.hide, this);
25851 if (!this.rendered) {
25857 this.fireEvent('show', this);
25863 if (!this.rendered) {
25869 this.fireEvent('hide', this);
25872 update : function()
25874 // var e = this.el.dom.firstChild;
25876 // if(this.closable){
25877 // e = e.nextSibling;
25880 // e.data = this.html || '';
25882 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25898 * @class Roo.bootstrap.Graph
25899 * @extends Roo.bootstrap.Component
25900 * Bootstrap Graph class
25904 @cfg {String} graphtype bar | vbar | pie
25905 @cfg {number} g_x coodinator | centre x (pie)
25906 @cfg {number} g_y coodinator | centre y (pie)
25907 @cfg {number} g_r radius (pie)
25908 @cfg {number} g_height height of the chart (respected by all elements in the set)
25909 @cfg {number} g_width width of the chart (respected by all elements in the set)
25910 @cfg {Object} title The title of the chart
25913 -opts (object) options for the chart
25915 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25916 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25918 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.
25919 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25921 o stretch (boolean)
25923 -opts (object) options for the pie
25926 o startAngle (number)
25927 o endAngle (number)
25931 * Create a new Input
25932 * @param {Object} config The config object
25935 Roo.bootstrap.Graph = function(config){
25936 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25942 * The img click event for the img.
25943 * @param {Roo.EventObject} e
25949 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25960 //g_colors: this.colors,
25967 getAutoCreate : function(){
25978 onRender : function(ct,position){
25981 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25983 if (typeof(Raphael) == 'undefined') {
25984 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25988 this.raphael = Raphael(this.el.dom);
25990 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25991 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25992 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25993 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25995 r.text(160, 10, "Single Series Chart").attr(txtattr);
25996 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25997 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25998 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
26000 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
26001 r.barchart(330, 10, 300, 220, data1);
26002 r.barchart(10, 250, 300, 220, data2, {stacked: true});
26003 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
26006 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
26007 // r.barchart(30, 30, 560, 250, xdata, {
26008 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
26009 // axis : "0 0 1 1",
26010 // axisxlabels : xdata
26011 // //yvalues : cols,
26014 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
26016 // this.load(null,xdata,{
26017 // axis : "0 0 1 1",
26018 // axisxlabels : xdata
26023 load : function(graphtype,xdata,opts)
26025 this.raphael.clear();
26027 graphtype = this.graphtype;
26032 var r = this.raphael,
26033 fin = function () {
26034 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
26036 fout = function () {
26037 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
26039 pfin = function() {
26040 this.sector.stop();
26041 this.sector.scale(1.1, 1.1, this.cx, this.cy);
26044 this.label[0].stop();
26045 this.label[0].attr({ r: 7.5 });
26046 this.label[1].attr({ "font-weight": 800 });
26049 pfout = function() {
26050 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
26053 this.label[0].animate({ r: 5 }, 500, "bounce");
26054 this.label[1].attr({ "font-weight": 400 });
26060 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26063 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26066 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
26067 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
26069 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
26076 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
26081 setTitle: function(o)
26086 initEvents: function() {
26089 this.el.on('click', this.onClick, this);
26093 onClick : function(e)
26095 Roo.log('img onclick');
26096 this.fireEvent('click', this, e);
26108 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26111 * @class Roo.bootstrap.dash.NumberBox
26112 * @extends Roo.bootstrap.Component
26113 * Bootstrap NumberBox class
26114 * @cfg {String} headline Box headline
26115 * @cfg {String} content Box content
26116 * @cfg {String} icon Box icon
26117 * @cfg {String} footer Footer text
26118 * @cfg {String} fhref Footer href
26121 * Create a new NumberBox
26122 * @param {Object} config The config object
26126 Roo.bootstrap.dash.NumberBox = function(config){
26127 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
26131 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
26140 getAutoCreate : function(){
26144 cls : 'small-box ',
26152 cls : 'roo-headline',
26153 html : this.headline
26157 cls : 'roo-content',
26158 html : this.content
26172 cls : 'ion ' + this.icon
26181 cls : 'small-box-footer',
26182 href : this.fhref || '#',
26186 cfg.cn.push(footer);
26193 onRender : function(ct,position){
26194 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
26201 setHeadline: function (value)
26203 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
26206 setFooter: function (value, href)
26208 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
26211 this.el.select('a.small-box-footer',true).first().attr('href', href);
26216 setContent: function (value)
26218 this.el.select('.roo-content',true).first().dom.innerHTML = value;
26221 initEvents: function()
26235 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26238 * @class Roo.bootstrap.dash.TabBox
26239 * @extends Roo.bootstrap.Component
26240 * Bootstrap TabBox class
26241 * @cfg {String} title Title of the TabBox
26242 * @cfg {String} icon Icon of the TabBox
26243 * @cfg {Boolean} showtabs (true|false) show the tabs default true
26244 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
26247 * Create a new TabBox
26248 * @param {Object} config The config object
26252 Roo.bootstrap.dash.TabBox = function(config){
26253 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
26258 * When a pane is added
26259 * @param {Roo.bootstrap.dash.TabPane} pane
26263 * @event activatepane
26264 * When a pane is activated
26265 * @param {Roo.bootstrap.dash.TabPane} pane
26267 "activatepane" : true
26275 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
26280 tabScrollable : false,
26282 getChildContainer : function()
26284 return this.el.select('.tab-content', true).first();
26287 getAutoCreate : function(){
26291 cls: 'pull-left header',
26299 cls: 'fa ' + this.icon
26305 cls: 'nav nav-tabs pull-right',
26311 if(this.tabScrollable){
26318 cls: 'nav nav-tabs pull-right',
26329 cls: 'nav-tabs-custom',
26334 cls: 'tab-content no-padding',
26342 initEvents : function()
26344 //Roo.log('add add pane handler');
26345 this.on('addpane', this.onAddPane, this);
26348 * Updates the box title
26349 * @param {String} html to set the title to.
26351 setTitle : function(value)
26353 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
26355 onAddPane : function(pane)
26357 this.panes.push(pane);
26358 //Roo.log('addpane');
26360 // tabs are rendere left to right..
26361 if(!this.showtabs){
26365 var ctr = this.el.select('.nav-tabs', true).first();
26368 var existing = ctr.select('.nav-tab',true);
26369 var qty = existing.getCount();;
26372 var tab = ctr.createChild({
26374 cls : 'nav-tab' + (qty ? '' : ' active'),
26382 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26385 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26387 pane.el.addClass('active');
26392 onTabClick : function(ev,un,ob,pane)
26394 //Roo.log('tab - prev default');
26395 ev.preventDefault();
26398 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26399 pane.tab.addClass('active');
26400 //Roo.log(pane.title);
26401 this.getChildContainer().select('.tab-pane',true).removeClass('active');
26402 // technically we should have a deactivate event.. but maybe add later.
26403 // and it should not de-activate the selected tab...
26404 this.fireEvent('activatepane', pane);
26405 pane.el.addClass('active');
26406 pane.fireEvent('activate');
26411 getActivePane : function()
26414 Roo.each(this.panes, function(p) {
26415 if(p.el.hasClass('active')){
26436 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26438 * @class Roo.bootstrap.TabPane
26439 * @extends Roo.bootstrap.Component
26440 * Bootstrap TabPane class
26441 * @cfg {Boolean} active (false | true) Default false
26442 * @cfg {String} title title of panel
26446 * Create a new TabPane
26447 * @param {Object} config The config object
26450 Roo.bootstrap.dash.TabPane = function(config){
26451 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26457 * When a pane is activated
26458 * @param {Roo.bootstrap.dash.TabPane} pane
26465 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
26470 // the tabBox that this is attached to.
26473 getAutoCreate : function()
26481 cfg.cls += ' active';
26486 initEvents : function()
26488 //Roo.log('trigger add pane handler');
26489 this.parent().fireEvent('addpane', this)
26493 * Updates the tab title
26494 * @param {String} html to set the title to.
26496 setTitle: function(str)
26502 this.tab.select('a', true).first().dom.innerHTML = str;
26519 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26522 * @class Roo.bootstrap.menu.Menu
26523 * @extends Roo.bootstrap.Component
26524 * Bootstrap Menu class - container for Menu
26525 * @cfg {String} html Text of the menu
26526 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26527 * @cfg {String} icon Font awesome icon
26528 * @cfg {String} pos Menu align to (top | bottom) default bottom
26532 * Create a new Menu
26533 * @param {Object} config The config object
26537 Roo.bootstrap.menu.Menu = function(config){
26538 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26542 * @event beforeshow
26543 * Fires before this menu is displayed
26544 * @param {Roo.bootstrap.menu.Menu} this
26548 * @event beforehide
26549 * Fires before this menu is hidden
26550 * @param {Roo.bootstrap.menu.Menu} this
26555 * Fires after this menu is displayed
26556 * @param {Roo.bootstrap.menu.Menu} this
26561 * Fires after this menu is hidden
26562 * @param {Roo.bootstrap.menu.Menu} this
26567 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26568 * @param {Roo.bootstrap.menu.Menu} this
26569 * @param {Roo.EventObject} e
26576 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
26580 weight : 'default',
26585 getChildContainer : function() {
26586 if(this.isSubMenu){
26590 return this.el.select('ul.dropdown-menu', true).first();
26593 getAutoCreate : function()
26598 cls : 'roo-menu-text',
26606 cls : 'fa ' + this.icon
26617 cls : 'dropdown-button btn btn-' + this.weight,
26622 cls : 'dropdown-toggle btn btn-' + this.weight,
26632 cls : 'dropdown-menu'
26638 if(this.pos == 'top'){
26639 cfg.cls += ' dropup';
26642 if(this.isSubMenu){
26645 cls : 'dropdown-menu'
26652 onRender : function(ct, position)
26654 this.isSubMenu = ct.hasClass('dropdown-submenu');
26656 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26659 initEvents : function()
26661 if(this.isSubMenu){
26665 this.hidden = true;
26667 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26668 this.triggerEl.on('click', this.onTriggerPress, this);
26670 this.buttonEl = this.el.select('button.dropdown-button', true).first();
26671 this.buttonEl.on('click', this.onClick, this);
26677 if(this.isSubMenu){
26681 return this.el.select('ul.dropdown-menu', true).first();
26684 onClick : function(e)
26686 this.fireEvent("click", this, e);
26689 onTriggerPress : function(e)
26691 if (this.isVisible()) {
26698 isVisible : function(){
26699 return !this.hidden;
26704 this.fireEvent("beforeshow", this);
26706 this.hidden = false;
26707 this.el.addClass('open');
26709 Roo.get(document).on("mouseup", this.onMouseUp, this);
26711 this.fireEvent("show", this);
26718 this.fireEvent("beforehide", this);
26720 this.hidden = true;
26721 this.el.removeClass('open');
26723 Roo.get(document).un("mouseup", this.onMouseUp);
26725 this.fireEvent("hide", this);
26728 onMouseUp : function()
26742 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26745 * @class Roo.bootstrap.menu.Item
26746 * @extends Roo.bootstrap.Component
26747 * Bootstrap MenuItem class
26748 * @cfg {Boolean} submenu (true | false) default false
26749 * @cfg {String} html text of the item
26750 * @cfg {String} href the link
26751 * @cfg {Boolean} disable (true | false) default false
26752 * @cfg {Boolean} preventDefault (true | false) default true
26753 * @cfg {String} icon Font awesome icon
26754 * @cfg {String} pos Submenu align to (left | right) default right
26758 * Create a new Item
26759 * @param {Object} config The config object
26763 Roo.bootstrap.menu.Item = function(config){
26764 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26768 * Fires when the mouse is hovering over this menu
26769 * @param {Roo.bootstrap.menu.Item} this
26770 * @param {Roo.EventObject} e
26775 * Fires when the mouse exits this menu
26776 * @param {Roo.bootstrap.menu.Item} this
26777 * @param {Roo.EventObject} e
26783 * The raw click event for the entire grid.
26784 * @param {Roo.EventObject} e
26790 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26795 preventDefault: true,
26800 getAutoCreate : function()
26805 cls : 'roo-menu-item-text',
26813 cls : 'fa ' + this.icon
26822 href : this.href || '#',
26829 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26833 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26835 if(this.pos == 'left'){
26836 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26843 initEvents : function()
26845 this.el.on('mouseover', this.onMouseOver, this);
26846 this.el.on('mouseout', this.onMouseOut, this);
26848 this.el.select('a', true).first().on('click', this.onClick, this);
26852 onClick : function(e)
26854 if(this.preventDefault){
26855 e.preventDefault();
26858 this.fireEvent("click", this, e);
26861 onMouseOver : function(e)
26863 if(this.submenu && this.pos == 'left'){
26864 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26867 this.fireEvent("mouseover", this, e);
26870 onMouseOut : function(e)
26872 this.fireEvent("mouseout", this, e);
26884 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26887 * @class Roo.bootstrap.menu.Separator
26888 * @extends Roo.bootstrap.Component
26889 * Bootstrap Separator class
26892 * Create a new Separator
26893 * @param {Object} config The config object
26897 Roo.bootstrap.menu.Separator = function(config){
26898 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26901 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26903 getAutoCreate : function(){
26924 * @class Roo.bootstrap.Tooltip
26925 * Bootstrap Tooltip class
26926 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26927 * to determine which dom element triggers the tooltip.
26929 * It needs to add support for additional attributes like tooltip-position
26932 * Create a new Toolti
26933 * @param {Object} config The config object
26936 Roo.bootstrap.Tooltip = function(config){
26937 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26939 this.alignment = Roo.bootstrap.Tooltip.alignment;
26941 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26942 this.alignment = config.alignment;
26947 Roo.apply(Roo.bootstrap.Tooltip, {
26949 * @function init initialize tooltip monitoring.
26953 currentTip : false,
26954 currentRegion : false,
26960 Roo.get(document).on('mouseover', this.enter ,this);
26961 Roo.get(document).on('mouseout', this.leave, this);
26964 this.currentTip = new Roo.bootstrap.Tooltip();
26967 enter : function(ev)
26969 var dom = ev.getTarget();
26971 //Roo.log(['enter',dom]);
26972 var el = Roo.fly(dom);
26973 if (this.currentEl) {
26975 //Roo.log(this.currentEl);
26976 //Roo.log(this.currentEl.contains(dom));
26977 if (this.currentEl == el) {
26980 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26986 if (this.currentTip.el) {
26987 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26991 if(!el || el.dom == document){
26997 // you can not look for children, as if el is the body.. then everythign is the child..
26998 if (!el.attr('tooltip')) { //
26999 if (!el.select("[tooltip]").elements.length) {
27002 // is the mouse over this child...?
27003 bindEl = el.select("[tooltip]").first();
27004 var xy = ev.getXY();
27005 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
27006 //Roo.log("not in region.");
27009 //Roo.log("child element over..");
27012 this.currentEl = bindEl;
27013 this.currentTip.bind(bindEl);
27014 this.currentRegion = Roo.lib.Region.getRegion(dom);
27015 this.currentTip.enter();
27018 leave : function(ev)
27020 var dom = ev.getTarget();
27021 //Roo.log(['leave',dom]);
27022 if (!this.currentEl) {
27027 if (dom != this.currentEl.dom) {
27030 var xy = ev.getXY();
27031 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
27034 // only activate leave if mouse cursor is outside... bounding box..
27039 if (this.currentTip) {
27040 this.currentTip.leave();
27042 //Roo.log('clear currentEl');
27043 this.currentEl = false;
27048 'left' : ['r-l', [-2,0], 'right'],
27049 'right' : ['l-r', [2,0], 'left'],
27050 'bottom' : ['t-b', [0,2], 'top'],
27051 'top' : [ 'b-t', [0,-2], 'bottom']
27057 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
27062 delay : null, // can be { show : 300 , hide: 500}
27066 hoverState : null, //???
27068 placement : 'bottom',
27072 getAutoCreate : function(){
27079 cls : 'tooltip-arrow'
27082 cls : 'tooltip-inner'
27089 bind : function(el)
27095 enter : function () {
27097 if (this.timeout != null) {
27098 clearTimeout(this.timeout);
27101 this.hoverState = 'in';
27102 //Roo.log("enter - show");
27103 if (!this.delay || !this.delay.show) {
27108 this.timeout = setTimeout(function () {
27109 if (_t.hoverState == 'in') {
27112 }, this.delay.show);
27116 clearTimeout(this.timeout);
27118 this.hoverState = 'out';
27119 if (!this.delay || !this.delay.hide) {
27125 this.timeout = setTimeout(function () {
27126 //Roo.log("leave - timeout");
27128 if (_t.hoverState == 'out') {
27130 Roo.bootstrap.Tooltip.currentEl = false;
27135 show : function (msg)
27138 this.render(document.body);
27141 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
27143 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
27145 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
27147 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
27149 var placement = typeof this.placement == 'function' ?
27150 this.placement.call(this, this.el, on_el) :
27153 var autoToken = /\s?auto?\s?/i;
27154 var autoPlace = autoToken.test(placement);
27156 placement = placement.replace(autoToken, '') || 'top';
27160 //this.el.setXY([0,0]);
27162 //this.el.dom.style.display='block';
27164 //this.el.appendTo(on_el);
27166 var p = this.getPosition();
27167 var box = this.el.getBox();
27173 var align = this.alignment[placement];
27175 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
27177 if(placement == 'top' || placement == 'bottom'){
27179 placement = 'right';
27182 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
27183 placement = 'left';
27186 var scroll = Roo.select('body', true).first().getScroll();
27188 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
27192 align = this.alignment[placement];
27195 this.el.alignTo(this.bindEl, align[0],align[1]);
27196 //var arrow = this.el.select('.arrow',true).first();
27197 //arrow.set(align[2],
27199 this.el.addClass(placement);
27201 this.el.addClass('in fade');
27203 this.hoverState = null;
27205 if (this.el.hasClass('fade')) {
27216 //this.el.setXY([0,0]);
27217 this.el.removeClass('in');
27233 * @class Roo.bootstrap.LocationPicker
27234 * @extends Roo.bootstrap.Component
27235 * Bootstrap LocationPicker class
27236 * @cfg {Number} latitude Position when init default 0
27237 * @cfg {Number} longitude Position when init default 0
27238 * @cfg {Number} zoom default 15
27239 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
27240 * @cfg {Boolean} mapTypeControl default false
27241 * @cfg {Boolean} disableDoubleClickZoom default false
27242 * @cfg {Boolean} scrollwheel default true
27243 * @cfg {Boolean} streetViewControl default false
27244 * @cfg {Number} radius default 0
27245 * @cfg {String} locationName
27246 * @cfg {Boolean} draggable default true
27247 * @cfg {Boolean} enableAutocomplete default false
27248 * @cfg {Boolean} enableReverseGeocode default true
27249 * @cfg {String} markerTitle
27252 * Create a new LocationPicker
27253 * @param {Object} config The config object
27257 Roo.bootstrap.LocationPicker = function(config){
27259 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
27264 * Fires when the picker initialized.
27265 * @param {Roo.bootstrap.LocationPicker} this
27266 * @param {Google Location} location
27270 * @event positionchanged
27271 * Fires when the picker position changed.
27272 * @param {Roo.bootstrap.LocationPicker} this
27273 * @param {Google Location} location
27275 positionchanged : true,
27278 * Fires when the map resize.
27279 * @param {Roo.bootstrap.LocationPicker} this
27284 * Fires when the map show.
27285 * @param {Roo.bootstrap.LocationPicker} this
27290 * Fires when the map hide.
27291 * @param {Roo.bootstrap.LocationPicker} this
27296 * Fires when click the map.
27297 * @param {Roo.bootstrap.LocationPicker} this
27298 * @param {Map event} e
27302 * @event mapRightClick
27303 * Fires when right click the map.
27304 * @param {Roo.bootstrap.LocationPicker} this
27305 * @param {Map event} e
27307 mapRightClick : true,
27309 * @event markerClick
27310 * Fires when click the marker.
27311 * @param {Roo.bootstrap.LocationPicker} this
27312 * @param {Map event} e
27314 markerClick : true,
27316 * @event markerRightClick
27317 * Fires when right click the marker.
27318 * @param {Roo.bootstrap.LocationPicker} this
27319 * @param {Map event} e
27321 markerRightClick : true,
27323 * @event OverlayViewDraw
27324 * Fires when OverlayView Draw
27325 * @param {Roo.bootstrap.LocationPicker} this
27327 OverlayViewDraw : true,
27329 * @event OverlayViewOnAdd
27330 * Fires when OverlayView Draw
27331 * @param {Roo.bootstrap.LocationPicker} this
27333 OverlayViewOnAdd : true,
27335 * @event OverlayViewOnRemove
27336 * Fires when OverlayView Draw
27337 * @param {Roo.bootstrap.LocationPicker} this
27339 OverlayViewOnRemove : true,
27341 * @event OverlayViewShow
27342 * Fires when OverlayView Draw
27343 * @param {Roo.bootstrap.LocationPicker} this
27344 * @param {Pixel} cpx
27346 OverlayViewShow : true,
27348 * @event OverlayViewHide
27349 * Fires when OverlayView Draw
27350 * @param {Roo.bootstrap.LocationPicker} this
27352 OverlayViewHide : true,
27354 * @event loadexception
27355 * Fires when load google lib failed.
27356 * @param {Roo.bootstrap.LocationPicker} this
27358 loadexception : true
27363 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
27365 gMapContext: false,
27371 mapTypeControl: false,
27372 disableDoubleClickZoom: false,
27374 streetViewControl: false,
27378 enableAutocomplete: false,
27379 enableReverseGeocode: true,
27382 getAutoCreate: function()
27387 cls: 'roo-location-picker'
27393 initEvents: function(ct, position)
27395 if(!this.el.getWidth() || this.isApplied()){
27399 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27404 initial: function()
27406 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27407 this.fireEvent('loadexception', this);
27411 if(!this.mapTypeId){
27412 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27415 this.gMapContext = this.GMapContext();
27417 this.initOverlayView();
27419 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27423 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27424 _this.setPosition(_this.gMapContext.marker.position);
27427 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27428 _this.fireEvent('mapClick', this, event);
27432 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27433 _this.fireEvent('mapRightClick', this, event);
27437 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27438 _this.fireEvent('markerClick', this, event);
27442 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27443 _this.fireEvent('markerRightClick', this, event);
27447 this.setPosition(this.gMapContext.location);
27449 this.fireEvent('initial', this, this.gMapContext.location);
27452 initOverlayView: function()
27456 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27460 _this.fireEvent('OverlayViewDraw', _this);
27465 _this.fireEvent('OverlayViewOnAdd', _this);
27468 onRemove: function()
27470 _this.fireEvent('OverlayViewOnRemove', _this);
27473 show: function(cpx)
27475 _this.fireEvent('OverlayViewShow', _this, cpx);
27480 _this.fireEvent('OverlayViewHide', _this);
27486 fromLatLngToContainerPixel: function(event)
27488 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27491 isApplied: function()
27493 return this.getGmapContext() == false ? false : true;
27496 getGmapContext: function()
27498 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27501 GMapContext: function()
27503 var position = new google.maps.LatLng(this.latitude, this.longitude);
27505 var _map = new google.maps.Map(this.el.dom, {
27508 mapTypeId: this.mapTypeId,
27509 mapTypeControl: this.mapTypeControl,
27510 disableDoubleClickZoom: this.disableDoubleClickZoom,
27511 scrollwheel: this.scrollwheel,
27512 streetViewControl: this.streetViewControl,
27513 locationName: this.locationName,
27514 draggable: this.draggable,
27515 enableAutocomplete: this.enableAutocomplete,
27516 enableReverseGeocode: this.enableReverseGeocode
27519 var _marker = new google.maps.Marker({
27520 position: position,
27522 title: this.markerTitle,
27523 draggable: this.draggable
27530 location: position,
27531 radius: this.radius,
27532 locationName: this.locationName,
27533 addressComponents: {
27534 formatted_address: null,
27535 addressLine1: null,
27536 addressLine2: null,
27538 streetNumber: null,
27542 stateOrProvince: null
27545 domContainer: this.el.dom,
27546 geodecoder: new google.maps.Geocoder()
27550 drawCircle: function(center, radius, options)
27552 if (this.gMapContext.circle != null) {
27553 this.gMapContext.circle.setMap(null);
27557 options = Roo.apply({}, options, {
27558 strokeColor: "#0000FF",
27559 strokeOpacity: .35,
27561 fillColor: "#0000FF",
27565 options.map = this.gMapContext.map;
27566 options.radius = radius;
27567 options.center = center;
27568 this.gMapContext.circle = new google.maps.Circle(options);
27569 return this.gMapContext.circle;
27575 setPosition: function(location)
27577 this.gMapContext.location = location;
27578 this.gMapContext.marker.setPosition(location);
27579 this.gMapContext.map.panTo(location);
27580 this.drawCircle(location, this.gMapContext.radius, {});
27584 if (this.gMapContext.settings.enableReverseGeocode) {
27585 this.gMapContext.geodecoder.geocode({
27586 latLng: this.gMapContext.location
27587 }, function(results, status) {
27589 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27590 _this.gMapContext.locationName = results[0].formatted_address;
27591 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27593 _this.fireEvent('positionchanged', this, location);
27600 this.fireEvent('positionchanged', this, location);
27605 google.maps.event.trigger(this.gMapContext.map, "resize");
27607 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27609 this.fireEvent('resize', this);
27612 setPositionByLatLng: function(latitude, longitude)
27614 this.setPosition(new google.maps.LatLng(latitude, longitude));
27617 getCurrentPosition: function()
27620 latitude: this.gMapContext.location.lat(),
27621 longitude: this.gMapContext.location.lng()
27625 getAddressName: function()
27627 return this.gMapContext.locationName;
27630 getAddressComponents: function()
27632 return this.gMapContext.addressComponents;
27635 address_component_from_google_geocode: function(address_components)
27639 for (var i = 0; i < address_components.length; i++) {
27640 var component = address_components[i];
27641 if (component.types.indexOf("postal_code") >= 0) {
27642 result.postalCode = component.short_name;
27643 } else if (component.types.indexOf("street_number") >= 0) {
27644 result.streetNumber = component.short_name;
27645 } else if (component.types.indexOf("route") >= 0) {
27646 result.streetName = component.short_name;
27647 } else if (component.types.indexOf("neighborhood") >= 0) {
27648 result.city = component.short_name;
27649 } else if (component.types.indexOf("locality") >= 0) {
27650 result.city = component.short_name;
27651 } else if (component.types.indexOf("sublocality") >= 0) {
27652 result.district = component.short_name;
27653 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27654 result.stateOrProvince = component.short_name;
27655 } else if (component.types.indexOf("country") >= 0) {
27656 result.country = component.short_name;
27660 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27661 result.addressLine2 = "";
27665 setZoomLevel: function(zoom)
27667 this.gMapContext.map.setZoom(zoom);
27680 this.fireEvent('show', this);
27691 this.fireEvent('hide', this);
27696 Roo.apply(Roo.bootstrap.LocationPicker, {
27698 OverlayView : function(map, options)
27700 options = options || {};
27707 * @class Roo.bootstrap.Alert
27708 * @extends Roo.bootstrap.Component
27709 * Bootstrap Alert class - shows an alert area box
27711 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27712 Enter a valid email address
27715 * @cfg {String} title The title of alert
27716 * @cfg {String} html The content of alert
27717 * @cfg {String} weight ( success | info | warning | danger )
27718 * @cfg {String} faicon font-awesomeicon
27721 * Create a new alert
27722 * @param {Object} config The config object
27726 Roo.bootstrap.Alert = function(config){
27727 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27731 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27738 getAutoCreate : function()
27747 cls : 'roo-alert-icon'
27752 cls : 'roo-alert-title',
27757 cls : 'roo-alert-text',
27764 cfg.cn[0].cls += ' fa ' + this.faicon;
27768 cfg.cls += ' alert-' + this.weight;
27774 initEvents: function()
27776 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27779 setTitle : function(str)
27781 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27784 setText : function(str)
27786 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27789 setWeight : function(weight)
27792 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27795 this.weight = weight;
27797 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27800 setIcon : function(icon)
27803 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27806 this.faicon = icon;
27808 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27829 * @class Roo.bootstrap.UploadCropbox
27830 * @extends Roo.bootstrap.Component
27831 * Bootstrap UploadCropbox class
27832 * @cfg {String} emptyText show when image has been loaded
27833 * @cfg {String} rotateNotify show when image too small to rotate
27834 * @cfg {Number} errorTimeout default 3000
27835 * @cfg {Number} minWidth default 300
27836 * @cfg {Number} minHeight default 300
27837 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27838 * @cfg {Boolean} isDocument (true|false) default false
27839 * @cfg {String} url action url
27840 * @cfg {String} paramName default 'imageUpload'
27841 * @cfg {String} method default POST
27842 * @cfg {Boolean} loadMask (true|false) default true
27843 * @cfg {Boolean} loadingText default 'Loading...'
27846 * Create a new UploadCropbox
27847 * @param {Object} config The config object
27850 Roo.bootstrap.UploadCropbox = function(config){
27851 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27855 * @event beforeselectfile
27856 * Fire before select file
27857 * @param {Roo.bootstrap.UploadCropbox} this
27859 "beforeselectfile" : true,
27862 * Fire after initEvent
27863 * @param {Roo.bootstrap.UploadCropbox} this
27868 * Fire after initEvent
27869 * @param {Roo.bootstrap.UploadCropbox} this
27870 * @param {String} data
27875 * Fire when preparing the file data
27876 * @param {Roo.bootstrap.UploadCropbox} this
27877 * @param {Object} file
27882 * Fire when get exception
27883 * @param {Roo.bootstrap.UploadCropbox} this
27884 * @param {XMLHttpRequest} xhr
27886 "exception" : true,
27888 * @event beforeloadcanvas
27889 * Fire before load the canvas
27890 * @param {Roo.bootstrap.UploadCropbox} this
27891 * @param {String} src
27893 "beforeloadcanvas" : true,
27896 * Fire when trash image
27897 * @param {Roo.bootstrap.UploadCropbox} this
27902 * Fire when download the image
27903 * @param {Roo.bootstrap.UploadCropbox} this
27907 * @event footerbuttonclick
27908 * Fire when footerbuttonclick
27909 * @param {Roo.bootstrap.UploadCropbox} this
27910 * @param {String} type
27912 "footerbuttonclick" : true,
27916 * @param {Roo.bootstrap.UploadCropbox} this
27921 * Fire when rotate the image
27922 * @param {Roo.bootstrap.UploadCropbox} this
27923 * @param {String} pos
27928 * Fire when inspect the file
27929 * @param {Roo.bootstrap.UploadCropbox} this
27930 * @param {Object} file
27935 * Fire when xhr upload the file
27936 * @param {Roo.bootstrap.UploadCropbox} this
27937 * @param {Object} data
27942 * Fire when arrange the file data
27943 * @param {Roo.bootstrap.UploadCropbox} this
27944 * @param {Object} formData
27949 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27952 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27954 emptyText : 'Click to upload image',
27955 rotateNotify : 'Image is too small to rotate',
27956 errorTimeout : 3000,
27970 cropType : 'image/jpeg',
27972 canvasLoaded : false,
27973 isDocument : false,
27975 paramName : 'imageUpload',
27977 loadingText : 'Loading...',
27980 getAutoCreate : function()
27984 cls : 'roo-upload-cropbox',
27988 cls : 'roo-upload-cropbox-selector',
27993 cls : 'roo-upload-cropbox-body',
27994 style : 'cursor:pointer',
27998 cls : 'roo-upload-cropbox-preview'
28002 cls : 'roo-upload-cropbox-thumb'
28006 cls : 'roo-upload-cropbox-empty-notify',
28007 html : this.emptyText
28011 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
28012 html : this.rotateNotify
28018 cls : 'roo-upload-cropbox-footer',
28021 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
28031 onRender : function(ct, position)
28033 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
28035 if (this.buttons.length) {
28037 Roo.each(this.buttons, function(bb) {
28039 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
28041 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
28047 this.maskEl = this.el;
28051 initEvents : function()
28053 this.urlAPI = (window.createObjectURL && window) ||
28054 (window.URL && URL.revokeObjectURL && URL) ||
28055 (window.webkitURL && webkitURL);
28057 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
28058 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28060 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
28061 this.selectorEl.hide();
28063 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
28064 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28066 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
28067 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28068 this.thumbEl.hide();
28070 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
28071 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28073 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
28074 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28075 this.errorEl.hide();
28077 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
28078 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28079 this.footerEl.hide();
28081 this.setThumbBoxSize();
28087 this.fireEvent('initial', this);
28094 window.addEventListener("resize", function() { _this.resize(); } );
28096 this.bodyEl.on('click', this.beforeSelectFile, this);
28099 this.bodyEl.on('touchstart', this.onTouchStart, this);
28100 this.bodyEl.on('touchmove', this.onTouchMove, this);
28101 this.bodyEl.on('touchend', this.onTouchEnd, this);
28105 this.bodyEl.on('mousedown', this.onMouseDown, this);
28106 this.bodyEl.on('mousemove', this.onMouseMove, this);
28107 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
28108 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
28109 Roo.get(document).on('mouseup', this.onMouseUp, this);
28112 this.selectorEl.on('change', this.onFileSelected, this);
28118 this.baseScale = 1;
28120 this.baseRotate = 1;
28121 this.dragable = false;
28122 this.pinching = false;
28125 this.cropData = false;
28126 this.notifyEl.dom.innerHTML = this.emptyText;
28128 this.selectorEl.dom.value = '';
28132 resize : function()
28134 if(this.fireEvent('resize', this) != false){
28135 this.setThumbBoxPosition();
28136 this.setCanvasPosition();
28140 onFooterButtonClick : function(e, el, o, type)
28143 case 'rotate-left' :
28144 this.onRotateLeft(e);
28146 case 'rotate-right' :
28147 this.onRotateRight(e);
28150 this.beforeSelectFile(e);
28165 this.fireEvent('footerbuttonclick', this, type);
28168 beforeSelectFile : function(e)
28170 e.preventDefault();
28172 if(this.fireEvent('beforeselectfile', this) != false){
28173 this.selectorEl.dom.click();
28177 onFileSelected : function(e)
28179 e.preventDefault();
28181 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28185 var file = this.selectorEl.dom.files[0];
28187 if(this.fireEvent('inspect', this, file) != false){
28188 this.prepare(file);
28193 trash : function(e)
28195 this.fireEvent('trash', this);
28198 download : function(e)
28200 this.fireEvent('download', this);
28203 loadCanvas : function(src)
28205 if(this.fireEvent('beforeloadcanvas', this, src) != false){
28209 this.imageEl = document.createElement('img');
28213 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
28215 this.imageEl.src = src;
28219 onLoadCanvas : function()
28221 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
28222 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
28224 this.bodyEl.un('click', this.beforeSelectFile, this);
28226 this.notifyEl.hide();
28227 this.thumbEl.show();
28228 this.footerEl.show();
28230 this.baseRotateLevel();
28232 if(this.isDocument){
28233 this.setThumbBoxSize();
28236 this.setThumbBoxPosition();
28238 this.baseScaleLevel();
28244 this.canvasLoaded = true;
28247 this.maskEl.unmask();
28252 setCanvasPosition : function()
28254 if(!this.canvasEl){
28258 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
28259 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
28261 this.previewEl.setLeft(pw);
28262 this.previewEl.setTop(ph);
28266 onMouseDown : function(e)
28270 this.dragable = true;
28271 this.pinching = false;
28273 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
28274 this.dragable = false;
28278 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28279 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28283 onMouseMove : function(e)
28287 if(!this.canvasLoaded){
28291 if (!this.dragable){
28295 var minX = Math.ceil(this.thumbEl.getLeft(true));
28296 var minY = Math.ceil(this.thumbEl.getTop(true));
28298 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
28299 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
28301 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28302 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28304 x = x - this.mouseX;
28305 y = y - this.mouseY;
28307 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
28308 var bgY = Math.ceil(y + this.previewEl.getTop(true));
28310 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
28311 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
28313 this.previewEl.setLeft(bgX);
28314 this.previewEl.setTop(bgY);
28316 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28317 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28320 onMouseUp : function(e)
28324 this.dragable = false;
28327 onMouseWheel : function(e)
28331 this.startScale = this.scale;
28333 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
28335 if(!this.zoomable()){
28336 this.scale = this.startScale;
28345 zoomable : function()
28347 var minScale = this.thumbEl.getWidth() / this.minWidth;
28349 if(this.minWidth < this.minHeight){
28350 minScale = this.thumbEl.getHeight() / this.minHeight;
28353 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
28354 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
28358 (this.rotate == 0 || this.rotate == 180) &&
28360 width > this.imageEl.OriginWidth ||
28361 height > this.imageEl.OriginHeight ||
28362 (width < this.minWidth && height < this.minHeight)
28370 (this.rotate == 90 || this.rotate == 270) &&
28372 width > this.imageEl.OriginWidth ||
28373 height > this.imageEl.OriginHeight ||
28374 (width < this.minHeight && height < this.minWidth)
28381 !this.isDocument &&
28382 (this.rotate == 0 || this.rotate == 180) &&
28384 width < this.minWidth ||
28385 width > this.imageEl.OriginWidth ||
28386 height < this.minHeight ||
28387 height > this.imageEl.OriginHeight
28394 !this.isDocument &&
28395 (this.rotate == 90 || this.rotate == 270) &&
28397 width < this.minHeight ||
28398 width > this.imageEl.OriginWidth ||
28399 height < this.minWidth ||
28400 height > this.imageEl.OriginHeight
28410 onRotateLeft : function(e)
28412 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28414 var minScale = this.thumbEl.getWidth() / this.minWidth;
28416 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28417 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28419 this.startScale = this.scale;
28421 while (this.getScaleLevel() < minScale){
28423 this.scale = this.scale + 1;
28425 if(!this.zoomable()){
28430 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28431 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28436 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28443 this.scale = this.startScale;
28445 this.onRotateFail();
28450 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28452 if(this.isDocument){
28453 this.setThumbBoxSize();
28454 this.setThumbBoxPosition();
28455 this.setCanvasPosition();
28460 this.fireEvent('rotate', this, 'left');
28464 onRotateRight : function(e)
28466 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28468 var minScale = this.thumbEl.getWidth() / this.minWidth;
28470 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28471 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28473 this.startScale = this.scale;
28475 while (this.getScaleLevel() < minScale){
28477 this.scale = this.scale + 1;
28479 if(!this.zoomable()){
28484 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28485 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28490 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28497 this.scale = this.startScale;
28499 this.onRotateFail();
28504 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28506 if(this.isDocument){
28507 this.setThumbBoxSize();
28508 this.setThumbBoxPosition();
28509 this.setCanvasPosition();
28514 this.fireEvent('rotate', this, 'right');
28517 onRotateFail : function()
28519 this.errorEl.show(true);
28523 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28528 this.previewEl.dom.innerHTML = '';
28530 var canvasEl = document.createElement("canvas");
28532 var contextEl = canvasEl.getContext("2d");
28534 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28535 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28536 var center = this.imageEl.OriginWidth / 2;
28538 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28539 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28540 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28541 center = this.imageEl.OriginHeight / 2;
28544 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28546 contextEl.translate(center, center);
28547 contextEl.rotate(this.rotate * Math.PI / 180);
28549 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28551 this.canvasEl = document.createElement("canvas");
28553 this.contextEl = this.canvasEl.getContext("2d");
28555 switch (this.rotate) {
28558 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28559 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28561 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28566 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28567 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28569 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28570 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);
28574 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28579 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28580 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28582 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28583 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);
28587 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);
28592 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28593 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28595 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28596 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28600 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);
28607 this.previewEl.appendChild(this.canvasEl);
28609 this.setCanvasPosition();
28614 if(!this.canvasLoaded){
28618 var imageCanvas = document.createElement("canvas");
28620 var imageContext = imageCanvas.getContext("2d");
28622 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28623 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28625 var center = imageCanvas.width / 2;
28627 imageContext.translate(center, center);
28629 imageContext.rotate(this.rotate * Math.PI / 180);
28631 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28633 var canvas = document.createElement("canvas");
28635 var context = canvas.getContext("2d");
28637 canvas.width = this.minWidth;
28638 canvas.height = this.minHeight;
28640 switch (this.rotate) {
28643 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28644 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28646 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28647 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28649 var targetWidth = this.minWidth - 2 * x;
28650 var targetHeight = this.minHeight - 2 * y;
28654 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28655 scale = targetWidth / width;
28658 if(x > 0 && y == 0){
28659 scale = targetHeight / height;
28662 if(x > 0 && y > 0){
28663 scale = targetWidth / width;
28665 if(width < height){
28666 scale = targetHeight / height;
28670 context.scale(scale, scale);
28672 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28673 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28675 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28676 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28678 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28683 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28684 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (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 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28720 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28725 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28726 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28728 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28729 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28731 var targetWidth = this.minWidth - 2 * x;
28732 var targetHeight = this.minHeight - 2 * y;
28736 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28737 scale = targetWidth / width;
28740 if(x > 0 && y == 0){
28741 scale = targetHeight / height;
28744 if(x > 0 && y > 0){
28745 scale = targetWidth / width;
28747 if(width < height){
28748 scale = targetHeight / height;
28752 context.scale(scale, scale);
28754 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28755 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28757 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28758 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28760 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28761 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28763 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28768 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28769 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28771 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28772 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28774 var targetWidth = this.minWidth - 2 * x;
28775 var targetHeight = this.minHeight - 2 * y;
28779 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28780 scale = targetWidth / width;
28783 if(x > 0 && y == 0){
28784 scale = targetHeight / height;
28787 if(x > 0 && y > 0){
28788 scale = targetWidth / width;
28790 if(width < height){
28791 scale = targetHeight / height;
28795 context.scale(scale, scale);
28797 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28798 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28800 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28801 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28803 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28805 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28812 this.cropData = canvas.toDataURL(this.cropType);
28814 if(this.fireEvent('crop', this, this.cropData) !== false){
28815 this.process(this.file, this.cropData);
28822 setThumbBoxSize : function()
28826 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28827 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28828 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28830 this.minWidth = width;
28831 this.minHeight = height;
28833 if(this.rotate == 90 || this.rotate == 270){
28834 this.minWidth = height;
28835 this.minHeight = width;
28840 width = Math.ceil(this.minWidth * height / this.minHeight);
28842 if(this.minWidth > this.minHeight){
28844 height = Math.ceil(this.minHeight * width / this.minWidth);
28847 this.thumbEl.setStyle({
28848 width : width + 'px',
28849 height : height + 'px'
28856 setThumbBoxPosition : function()
28858 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28859 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28861 this.thumbEl.setLeft(x);
28862 this.thumbEl.setTop(y);
28866 baseRotateLevel : function()
28868 this.baseRotate = 1;
28871 typeof(this.exif) != 'undefined' &&
28872 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28873 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28875 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28878 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28882 baseScaleLevel : function()
28886 if(this.isDocument){
28888 if(this.baseRotate == 6 || this.baseRotate == 8){
28890 height = this.thumbEl.getHeight();
28891 this.baseScale = height / this.imageEl.OriginWidth;
28893 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28894 width = this.thumbEl.getWidth();
28895 this.baseScale = width / this.imageEl.OriginHeight;
28901 height = this.thumbEl.getHeight();
28902 this.baseScale = height / this.imageEl.OriginHeight;
28904 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28905 width = this.thumbEl.getWidth();
28906 this.baseScale = width / this.imageEl.OriginWidth;
28912 if(this.baseRotate == 6 || this.baseRotate == 8){
28914 width = this.thumbEl.getHeight();
28915 this.baseScale = width / this.imageEl.OriginHeight;
28917 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28918 height = this.thumbEl.getWidth();
28919 this.baseScale = height / this.imageEl.OriginHeight;
28922 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28923 height = this.thumbEl.getWidth();
28924 this.baseScale = height / this.imageEl.OriginHeight;
28926 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28927 width = this.thumbEl.getHeight();
28928 this.baseScale = width / this.imageEl.OriginWidth;
28935 width = this.thumbEl.getWidth();
28936 this.baseScale = width / this.imageEl.OriginWidth;
28938 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28939 height = this.thumbEl.getHeight();
28940 this.baseScale = height / this.imageEl.OriginHeight;
28943 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28945 height = this.thumbEl.getHeight();
28946 this.baseScale = height / this.imageEl.OriginHeight;
28948 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28949 width = this.thumbEl.getWidth();
28950 this.baseScale = width / this.imageEl.OriginWidth;
28958 getScaleLevel : function()
28960 return this.baseScale * Math.pow(1.1, this.scale);
28963 onTouchStart : function(e)
28965 if(!this.canvasLoaded){
28966 this.beforeSelectFile(e);
28970 var touches = e.browserEvent.touches;
28976 if(touches.length == 1){
28977 this.onMouseDown(e);
28981 if(touches.length != 2){
28987 for(var i = 0, finger; finger = touches[i]; i++){
28988 coords.push(finger.pageX, finger.pageY);
28991 var x = Math.pow(coords[0] - coords[2], 2);
28992 var y = Math.pow(coords[1] - coords[3], 2);
28994 this.startDistance = Math.sqrt(x + y);
28996 this.startScale = this.scale;
28998 this.pinching = true;
28999 this.dragable = false;
29003 onTouchMove : function(e)
29005 if(!this.pinching && !this.dragable){
29009 var touches = e.browserEvent.touches;
29016 this.onMouseMove(e);
29022 for(var i = 0, finger; finger = touches[i]; i++){
29023 coords.push(finger.pageX, finger.pageY);
29026 var x = Math.pow(coords[0] - coords[2], 2);
29027 var y = Math.pow(coords[1] - coords[3], 2);
29029 this.endDistance = Math.sqrt(x + y);
29031 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
29033 if(!this.zoomable()){
29034 this.scale = this.startScale;
29042 onTouchEnd : function(e)
29044 this.pinching = false;
29045 this.dragable = false;
29049 process : function(file, crop)
29052 this.maskEl.mask(this.loadingText);
29055 this.xhr = new XMLHttpRequest();
29057 file.xhr = this.xhr;
29059 this.xhr.open(this.method, this.url, true);
29062 "Accept": "application/json",
29063 "Cache-Control": "no-cache",
29064 "X-Requested-With": "XMLHttpRequest"
29067 for (var headerName in headers) {
29068 var headerValue = headers[headerName];
29070 this.xhr.setRequestHeader(headerName, headerValue);
29076 this.xhr.onload = function()
29078 _this.xhrOnLoad(_this.xhr);
29081 this.xhr.onerror = function()
29083 _this.xhrOnError(_this.xhr);
29086 var formData = new FormData();
29088 formData.append('returnHTML', 'NO');
29091 formData.append('crop', crop);
29094 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
29095 formData.append(this.paramName, file, file.name);
29098 if(typeof(file.filename) != 'undefined'){
29099 formData.append('filename', file.filename);
29102 if(typeof(file.mimetype) != 'undefined'){
29103 formData.append('mimetype', file.mimetype);
29106 if(this.fireEvent('arrange', this, formData) != false){
29107 this.xhr.send(formData);
29111 xhrOnLoad : function(xhr)
29114 this.maskEl.unmask();
29117 if (xhr.readyState !== 4) {
29118 this.fireEvent('exception', this, xhr);
29122 var response = Roo.decode(xhr.responseText);
29124 if(!response.success){
29125 this.fireEvent('exception', this, xhr);
29129 var response = Roo.decode(xhr.responseText);
29131 this.fireEvent('upload', this, response);
29135 xhrOnError : function()
29138 this.maskEl.unmask();
29141 Roo.log('xhr on error');
29143 var response = Roo.decode(xhr.responseText);
29149 prepare : function(file)
29152 this.maskEl.mask(this.loadingText);
29158 if(typeof(file) === 'string'){
29159 this.loadCanvas(file);
29163 if(!file || !this.urlAPI){
29168 this.cropType = file.type;
29172 if(this.fireEvent('prepare', this, this.file) != false){
29174 var reader = new FileReader();
29176 reader.onload = function (e) {
29177 if (e.target.error) {
29178 Roo.log(e.target.error);
29182 var buffer = e.target.result,
29183 dataView = new DataView(buffer),
29185 maxOffset = dataView.byteLength - 4,
29189 if (dataView.getUint16(0) === 0xffd8) {
29190 while (offset < maxOffset) {
29191 markerBytes = dataView.getUint16(offset);
29193 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
29194 markerLength = dataView.getUint16(offset + 2) + 2;
29195 if (offset + markerLength > dataView.byteLength) {
29196 Roo.log('Invalid meta data: Invalid segment size.');
29200 if(markerBytes == 0xffe1){
29201 _this.parseExifData(
29208 offset += markerLength;
29218 var url = _this.urlAPI.createObjectURL(_this.file);
29220 _this.loadCanvas(url);
29225 reader.readAsArrayBuffer(this.file);
29231 parseExifData : function(dataView, offset, length)
29233 var tiffOffset = offset + 10,
29237 if (dataView.getUint32(offset + 4) !== 0x45786966) {
29238 // No Exif data, might be XMP data instead
29242 // Check for the ASCII code for "Exif" (0x45786966):
29243 if (dataView.getUint32(offset + 4) !== 0x45786966) {
29244 // No Exif data, might be XMP data instead
29247 if (tiffOffset + 8 > dataView.byteLength) {
29248 Roo.log('Invalid Exif data: Invalid segment size.');
29251 // Check for the two null bytes:
29252 if (dataView.getUint16(offset + 8) !== 0x0000) {
29253 Roo.log('Invalid Exif data: Missing byte alignment offset.');
29256 // Check the byte alignment:
29257 switch (dataView.getUint16(tiffOffset)) {
29259 littleEndian = true;
29262 littleEndian = false;
29265 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
29268 // Check for the TIFF tag marker (0x002A):
29269 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
29270 Roo.log('Invalid Exif data: Missing TIFF marker.');
29273 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
29274 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
29276 this.parseExifTags(
29279 tiffOffset + dirOffset,
29284 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
29289 if (dirOffset + 6 > dataView.byteLength) {
29290 Roo.log('Invalid Exif data: Invalid directory offset.');
29293 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
29294 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
29295 if (dirEndOffset + 4 > dataView.byteLength) {
29296 Roo.log('Invalid Exif data: Invalid directory size.');
29299 for (i = 0; i < tagsNumber; i += 1) {
29303 dirOffset + 2 + 12 * i, // tag offset
29307 // Return the offset to the next directory:
29308 return dataView.getUint32(dirEndOffset, littleEndian);
29311 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
29313 var tag = dataView.getUint16(offset, littleEndian);
29315 this.exif[tag] = this.getExifValue(
29319 dataView.getUint16(offset + 2, littleEndian), // tag type
29320 dataView.getUint32(offset + 4, littleEndian), // tag length
29325 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
29327 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
29336 Roo.log('Invalid Exif data: Invalid tag type.');
29340 tagSize = tagType.size * length;
29341 // Determine if the value is contained in the dataOffset bytes,
29342 // or if the value at the dataOffset is a pointer to the actual data:
29343 dataOffset = tagSize > 4 ?
29344 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
29345 if (dataOffset + tagSize > dataView.byteLength) {
29346 Roo.log('Invalid Exif data: Invalid data offset.');
29349 if (length === 1) {
29350 return tagType.getValue(dataView, dataOffset, littleEndian);
29353 for (i = 0; i < length; i += 1) {
29354 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
29357 if (tagType.ascii) {
29359 // Concatenate the chars:
29360 for (i = 0; i < values.length; i += 1) {
29362 // Ignore the terminating NULL byte(s):
29363 if (c === '\u0000') {
29375 Roo.apply(Roo.bootstrap.UploadCropbox, {
29377 'Orientation': 0x0112
29381 1: 0, //'top-left',
29383 3: 180, //'bottom-right',
29384 // 4: 'bottom-left',
29386 6: 90, //'right-top',
29387 // 7: 'right-bottom',
29388 8: 270 //'left-bottom'
29392 // byte, 8-bit unsigned int:
29394 getValue: function (dataView, dataOffset) {
29395 return dataView.getUint8(dataOffset);
29399 // ascii, 8-bit byte:
29401 getValue: function (dataView, dataOffset) {
29402 return String.fromCharCode(dataView.getUint8(dataOffset));
29407 // short, 16 bit int:
29409 getValue: function (dataView, dataOffset, littleEndian) {
29410 return dataView.getUint16(dataOffset, littleEndian);
29414 // long, 32 bit int:
29416 getValue: function (dataView, dataOffset, littleEndian) {
29417 return dataView.getUint32(dataOffset, littleEndian);
29421 // rational = two long values, first is numerator, second is denominator:
29423 getValue: function (dataView, dataOffset, littleEndian) {
29424 return dataView.getUint32(dataOffset, littleEndian) /
29425 dataView.getUint32(dataOffset + 4, littleEndian);
29429 // slong, 32 bit signed int:
29431 getValue: function (dataView, dataOffset, littleEndian) {
29432 return dataView.getInt32(dataOffset, littleEndian);
29436 // srational, two slongs, first is numerator, second is denominator:
29438 getValue: function (dataView, dataOffset, littleEndian) {
29439 return dataView.getInt32(dataOffset, littleEndian) /
29440 dataView.getInt32(dataOffset + 4, littleEndian);
29450 cls : 'btn-group roo-upload-cropbox-rotate-left',
29451 action : 'rotate-left',
29455 cls : 'btn btn-default',
29456 html : '<i class="fa fa-undo"></i>'
29462 cls : 'btn-group roo-upload-cropbox-picture',
29463 action : 'picture',
29467 cls : 'btn btn-default',
29468 html : '<i class="fa fa-picture-o"></i>'
29474 cls : 'btn-group roo-upload-cropbox-rotate-right',
29475 action : 'rotate-right',
29479 cls : 'btn btn-default',
29480 html : '<i class="fa fa-repeat"></i>'
29488 cls : 'btn-group roo-upload-cropbox-rotate-left',
29489 action : 'rotate-left',
29493 cls : 'btn btn-default',
29494 html : '<i class="fa fa-undo"></i>'
29500 cls : 'btn-group roo-upload-cropbox-download',
29501 action : 'download',
29505 cls : 'btn btn-default',
29506 html : '<i class="fa fa-download"></i>'
29512 cls : 'btn-group roo-upload-cropbox-crop',
29517 cls : 'btn btn-default',
29518 html : '<i class="fa fa-crop"></i>'
29524 cls : 'btn-group roo-upload-cropbox-trash',
29529 cls : 'btn btn-default',
29530 html : '<i class="fa fa-trash"></i>'
29536 cls : 'btn-group roo-upload-cropbox-rotate-right',
29537 action : 'rotate-right',
29541 cls : 'btn btn-default',
29542 html : '<i class="fa fa-repeat"></i>'
29550 cls : 'btn-group roo-upload-cropbox-rotate-left',
29551 action : 'rotate-left',
29555 cls : 'btn btn-default',
29556 html : '<i class="fa fa-undo"></i>'
29562 cls : 'btn-group roo-upload-cropbox-rotate-right',
29563 action : 'rotate-right',
29567 cls : 'btn btn-default',
29568 html : '<i class="fa fa-repeat"></i>'
29581 * @class Roo.bootstrap.DocumentManager
29582 * @extends Roo.bootstrap.Component
29583 * Bootstrap DocumentManager class
29584 * @cfg {String} paramName default 'imageUpload'
29585 * @cfg {String} toolTipName default 'filename'
29586 * @cfg {String} method default POST
29587 * @cfg {String} url action url
29588 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29589 * @cfg {Boolean} multiple multiple upload default true
29590 * @cfg {Number} thumbSize default 300
29591 * @cfg {String} fieldLabel
29592 * @cfg {Number} labelWidth default 4
29593 * @cfg {String} labelAlign (left|top) default left
29594 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29595 * @cfg {Number} labellg set the width of label (1-12)
29596 * @cfg {Number} labelmd set the width of label (1-12)
29597 * @cfg {Number} labelsm set the width of label (1-12)
29598 * @cfg {Number} labelxs set the width of label (1-12)
29601 * Create a new DocumentManager
29602 * @param {Object} config The config object
29605 Roo.bootstrap.DocumentManager = function(config){
29606 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29609 this.delegates = [];
29614 * Fire when initial the DocumentManager
29615 * @param {Roo.bootstrap.DocumentManager} this
29620 * inspect selected file
29621 * @param {Roo.bootstrap.DocumentManager} this
29622 * @param {File} file
29627 * Fire when xhr load exception
29628 * @param {Roo.bootstrap.DocumentManager} this
29629 * @param {XMLHttpRequest} xhr
29631 "exception" : true,
29633 * @event afterupload
29634 * Fire when xhr load exception
29635 * @param {Roo.bootstrap.DocumentManager} this
29636 * @param {XMLHttpRequest} xhr
29638 "afterupload" : true,
29641 * prepare the form data
29642 * @param {Roo.bootstrap.DocumentManager} this
29643 * @param {Object} formData
29648 * Fire when remove the file
29649 * @param {Roo.bootstrap.DocumentManager} this
29650 * @param {Object} file
29655 * Fire after refresh the file
29656 * @param {Roo.bootstrap.DocumentManager} this
29661 * Fire after click the image
29662 * @param {Roo.bootstrap.DocumentManager} this
29663 * @param {Object} file
29668 * Fire when upload a image and editable set to true
29669 * @param {Roo.bootstrap.DocumentManager} this
29670 * @param {Object} file
29674 * @event beforeselectfile
29675 * Fire before select file
29676 * @param {Roo.bootstrap.DocumentManager} this
29678 "beforeselectfile" : true,
29681 * Fire before process file
29682 * @param {Roo.bootstrap.DocumentManager} this
29683 * @param {Object} file
29687 * @event previewrendered
29688 * Fire when preview rendered
29689 * @param {Roo.bootstrap.DocumentManager} this
29690 * @param {Object} file
29692 "previewrendered" : true,
29695 "previewResize" : true
29700 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
29709 paramName : 'imageUpload',
29710 toolTipName : 'filename',
29713 labelAlign : 'left',
29723 getAutoCreate : function()
29725 var managerWidget = {
29727 cls : 'roo-document-manager',
29731 cls : 'roo-document-manager-selector',
29736 cls : 'roo-document-manager-uploader',
29740 cls : 'roo-document-manager-upload-btn',
29741 html : '<i class="fa fa-plus"></i>'
29752 cls : 'column col-md-12',
29757 if(this.fieldLabel.length){
29762 cls : 'column col-md-12',
29763 html : this.fieldLabel
29767 cls : 'column col-md-12',
29772 if(this.labelAlign == 'left'){
29777 html : this.fieldLabel
29786 if(this.labelWidth > 12){
29787 content[0].style = "width: " + this.labelWidth + 'px';
29790 if(this.labelWidth < 13 && this.labelmd == 0){
29791 this.labelmd = this.labelWidth;
29794 if(this.labellg > 0){
29795 content[0].cls += ' col-lg-' + this.labellg;
29796 content[1].cls += ' col-lg-' + (12 - this.labellg);
29799 if(this.labelmd > 0){
29800 content[0].cls += ' col-md-' + this.labelmd;
29801 content[1].cls += ' col-md-' + (12 - this.labelmd);
29804 if(this.labelsm > 0){
29805 content[0].cls += ' col-sm-' + this.labelsm;
29806 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29809 if(this.labelxs > 0){
29810 content[0].cls += ' col-xs-' + this.labelxs;
29811 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29819 cls : 'row clearfix',
29827 initEvents : function()
29829 this.managerEl = this.el.select('.roo-document-manager', true).first();
29830 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29832 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29833 this.selectorEl.hide();
29836 this.selectorEl.attr('multiple', 'multiple');
29839 this.selectorEl.on('change', this.onFileSelected, this);
29841 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29842 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29844 this.uploader.on('click', this.onUploaderClick, this);
29846 this.renderProgressDialog();
29850 window.addEventListener("resize", function() { _this.refresh(); } );
29852 this.fireEvent('initial', this);
29855 renderProgressDialog : function()
29859 this.progressDialog = new Roo.bootstrap.Modal({
29860 cls : 'roo-document-manager-progress-dialog',
29861 allow_close : false,
29872 btnclick : function() {
29873 _this.uploadCancel();
29879 this.progressDialog.render(Roo.get(document.body));
29881 this.progress = new Roo.bootstrap.Progress({
29882 cls : 'roo-document-manager-progress',
29887 this.progress.render(this.progressDialog.getChildContainer());
29889 this.progressBar = new Roo.bootstrap.ProgressBar({
29890 cls : 'roo-document-manager-progress-bar',
29893 aria_valuemax : 12,
29897 this.progressBar.render(this.progress.getChildContainer());
29900 onUploaderClick : function(e)
29902 e.preventDefault();
29904 if(this.fireEvent('beforeselectfile', this) != false){
29905 this.selectorEl.dom.click();
29910 onFileSelected : function(e)
29912 e.preventDefault();
29914 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29918 Roo.each(this.selectorEl.dom.files, function(file){
29919 if(this.fireEvent('inspect', this, file) != false){
29920 this.files.push(file);
29930 this.selectorEl.dom.value = '';
29932 if(!this.files || !this.files.length){
29936 if(this.boxes > 0 && this.files.length > this.boxes){
29937 this.files = this.files.slice(0, this.boxes);
29940 this.uploader.show();
29942 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29943 this.uploader.hide();
29952 Roo.each(this.files, function(file){
29954 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29955 var f = this.renderPreview(file);
29960 if(file.type.indexOf('image') != -1){
29961 this.delegates.push(
29963 _this.process(file);
29964 }).createDelegate(this)
29972 _this.process(file);
29973 }).createDelegate(this)
29978 this.files = files;
29980 this.delegates = this.delegates.concat(docs);
29982 if(!this.delegates.length){
29987 this.progressBar.aria_valuemax = this.delegates.length;
29994 arrange : function()
29996 if(!this.delegates.length){
29997 this.progressDialog.hide();
30002 var delegate = this.delegates.shift();
30004 this.progressDialog.show();
30006 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
30008 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
30013 refresh : function()
30015 this.uploader.show();
30017 if(this.boxes > 0 && this.files.length > this.boxes - 1){
30018 this.uploader.hide();
30021 Roo.isTouch ? this.closable(false) : this.closable(true);
30023 this.fireEvent('refresh', this);
30026 onRemove : function(e, el, o)
30028 e.preventDefault();
30030 this.fireEvent('remove', this, o);
30034 remove : function(o)
30038 Roo.each(this.files, function(file){
30039 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
30048 this.files = files;
30055 Roo.each(this.files, function(file){
30060 file.target.remove();
30069 onClick : function(e, el, o)
30071 e.preventDefault();
30073 this.fireEvent('click', this, o);
30077 closable : function(closable)
30079 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
30081 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30093 xhrOnLoad : function(xhr)
30095 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30099 if (xhr.readyState !== 4) {
30101 this.fireEvent('exception', this, xhr);
30105 var response = Roo.decode(xhr.responseText);
30107 if(!response.success){
30109 this.fireEvent('exception', this, xhr);
30113 var file = this.renderPreview(response.data);
30115 this.files.push(file);
30119 this.fireEvent('afterupload', this, xhr);
30123 xhrOnError : function(xhr)
30125 Roo.log('xhr on error');
30127 var response = Roo.decode(xhr.responseText);
30134 process : function(file)
30136 if(this.fireEvent('process', this, file) !== false){
30137 if(this.editable && file.type.indexOf('image') != -1){
30138 this.fireEvent('edit', this, file);
30142 this.uploadStart(file, false);
30149 uploadStart : function(file, crop)
30151 this.xhr = new XMLHttpRequest();
30153 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30158 file.xhr = this.xhr;
30160 this.managerEl.createChild({
30162 cls : 'roo-document-manager-loading',
30166 tooltip : file.name,
30167 cls : 'roo-document-manager-thumb',
30168 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30174 this.xhr.open(this.method, this.url, true);
30177 "Accept": "application/json",
30178 "Cache-Control": "no-cache",
30179 "X-Requested-With": "XMLHttpRequest"
30182 for (var headerName in headers) {
30183 var headerValue = headers[headerName];
30185 this.xhr.setRequestHeader(headerName, headerValue);
30191 this.xhr.onload = function()
30193 _this.xhrOnLoad(_this.xhr);
30196 this.xhr.onerror = function()
30198 _this.xhrOnError(_this.xhr);
30201 var formData = new FormData();
30203 formData.append('returnHTML', 'NO');
30206 formData.append('crop', crop);
30209 formData.append(this.paramName, file, file.name);
30216 if(this.fireEvent('prepare', this, formData, options) != false){
30218 if(options.manually){
30222 this.xhr.send(formData);
30226 this.uploadCancel();
30229 uploadCancel : function()
30235 this.delegates = [];
30237 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30244 renderPreview : function(file)
30246 if(typeof(file.target) != 'undefined' && file.target){
30250 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
30252 var previewEl = this.managerEl.createChild({
30254 cls : 'roo-document-manager-preview',
30258 tooltip : file[this.toolTipName],
30259 cls : 'roo-document-manager-thumb',
30260 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
30265 html : '<i class="fa fa-times-circle"></i>'
30270 var close = previewEl.select('button.close', true).first();
30272 close.on('click', this.onRemove, this, file);
30274 file.target = previewEl;
30276 var image = previewEl.select('img', true).first();
30280 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
30282 image.on('click', this.onClick, this, file);
30284 this.fireEvent('previewrendered', this, file);
30290 onPreviewLoad : function(file, image)
30292 if(typeof(file.target) == 'undefined' || !file.target){
30296 var width = image.dom.naturalWidth || image.dom.width;
30297 var height = image.dom.naturalHeight || image.dom.height;
30299 if(!this.previewResize) {
30303 if(width > height){
30304 file.target.addClass('wide');
30308 file.target.addClass('tall');
30313 uploadFromSource : function(file, crop)
30315 this.xhr = new XMLHttpRequest();
30317 this.managerEl.createChild({
30319 cls : 'roo-document-manager-loading',
30323 tooltip : file.name,
30324 cls : 'roo-document-manager-thumb',
30325 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30331 this.xhr.open(this.method, this.url, true);
30334 "Accept": "application/json",
30335 "Cache-Control": "no-cache",
30336 "X-Requested-With": "XMLHttpRequest"
30339 for (var headerName in headers) {
30340 var headerValue = headers[headerName];
30342 this.xhr.setRequestHeader(headerName, headerValue);
30348 this.xhr.onload = function()
30350 _this.xhrOnLoad(_this.xhr);
30353 this.xhr.onerror = function()
30355 _this.xhrOnError(_this.xhr);
30358 var formData = new FormData();
30360 formData.append('returnHTML', 'NO');
30362 formData.append('crop', crop);
30364 if(typeof(file.filename) != 'undefined'){
30365 formData.append('filename', file.filename);
30368 if(typeof(file.mimetype) != 'undefined'){
30369 formData.append('mimetype', file.mimetype);
30374 if(this.fireEvent('prepare', this, formData) != false){
30375 this.xhr.send(formData);
30385 * @class Roo.bootstrap.DocumentViewer
30386 * @extends Roo.bootstrap.Component
30387 * Bootstrap DocumentViewer class
30388 * @cfg {Boolean} showDownload (true|false) show download button (default true)
30389 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30392 * Create a new DocumentViewer
30393 * @param {Object} config The config object
30396 Roo.bootstrap.DocumentViewer = function(config){
30397 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30402 * Fire after initEvent
30403 * @param {Roo.bootstrap.DocumentViewer} this
30409 * @param {Roo.bootstrap.DocumentViewer} this
30414 * Fire after download button
30415 * @param {Roo.bootstrap.DocumentViewer} this
30420 * Fire after trash button
30421 * @param {Roo.bootstrap.DocumentViewer} this
30428 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
30430 showDownload : true,
30434 getAutoCreate : function()
30438 cls : 'roo-document-viewer',
30442 cls : 'roo-document-viewer-body',
30446 cls : 'roo-document-viewer-thumb',
30450 cls : 'roo-document-viewer-image'
30458 cls : 'roo-document-viewer-footer',
30461 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30465 cls : 'btn-group roo-document-viewer-download',
30469 cls : 'btn btn-default',
30470 html : '<i class="fa fa-download"></i>'
30476 cls : 'btn-group roo-document-viewer-trash',
30480 cls : 'btn btn-default',
30481 html : '<i class="fa fa-trash"></i>'
30494 initEvents : function()
30496 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30497 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30499 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30500 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30502 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30503 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30505 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30506 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30508 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30509 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30511 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30512 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30514 this.bodyEl.on('click', this.onClick, this);
30515 this.downloadBtn.on('click', this.onDownload, this);
30516 this.trashBtn.on('click', this.onTrash, this);
30518 this.downloadBtn.hide();
30519 this.trashBtn.hide();
30521 if(this.showDownload){
30522 this.downloadBtn.show();
30525 if(this.showTrash){
30526 this.trashBtn.show();
30529 if(!this.showDownload && !this.showTrash) {
30530 this.footerEl.hide();
30535 initial : function()
30537 this.fireEvent('initial', this);
30541 onClick : function(e)
30543 e.preventDefault();
30545 this.fireEvent('click', this);
30548 onDownload : function(e)
30550 e.preventDefault();
30552 this.fireEvent('download', this);
30555 onTrash : function(e)
30557 e.preventDefault();
30559 this.fireEvent('trash', this);
30571 * @class Roo.bootstrap.NavProgressBar
30572 * @extends Roo.bootstrap.Component
30573 * Bootstrap NavProgressBar class
30576 * Create a new nav progress bar
30577 * @param {Object} config The config object
30580 Roo.bootstrap.NavProgressBar = function(config){
30581 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30583 this.bullets = this.bullets || [];
30585 // Roo.bootstrap.NavProgressBar.register(this);
30589 * Fires when the active item changes
30590 * @param {Roo.bootstrap.NavProgressBar} this
30591 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30592 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
30599 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
30604 getAutoCreate : function()
30606 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30610 cls : 'roo-navigation-bar-group',
30614 cls : 'roo-navigation-top-bar'
30618 cls : 'roo-navigation-bullets-bar',
30622 cls : 'roo-navigation-bar'
30629 cls : 'roo-navigation-bottom-bar'
30639 initEvents: function()
30644 onRender : function(ct, position)
30646 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30648 if(this.bullets.length){
30649 Roo.each(this.bullets, function(b){
30658 addItem : function(cfg)
30660 var item = new Roo.bootstrap.NavProgressItem(cfg);
30662 item.parentId = this.id;
30663 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30666 var top = new Roo.bootstrap.Element({
30668 cls : 'roo-navigation-bar-text'
30671 var bottom = new Roo.bootstrap.Element({
30673 cls : 'roo-navigation-bar-text'
30676 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30677 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30679 var topText = new Roo.bootstrap.Element({
30681 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30684 var bottomText = new Roo.bootstrap.Element({
30686 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30689 topText.onRender(top.el, null);
30690 bottomText.onRender(bottom.el, null);
30693 item.bottomEl = bottom;
30696 this.barItems.push(item);
30701 getActive : function()
30703 var active = false;
30705 Roo.each(this.barItems, function(v){
30707 if (!v.isActive()) {
30719 setActiveItem : function(item)
30723 Roo.each(this.barItems, function(v){
30724 if (v.rid == item.rid) {
30728 if (v.isActive()) {
30729 v.setActive(false);
30734 item.setActive(true);
30736 this.fireEvent('changed', this, item, prev);
30739 getBarItem: function(rid)
30743 Roo.each(this.barItems, function(e) {
30744 if (e.rid != rid) {
30755 indexOfItem : function(item)
30759 Roo.each(this.barItems, function(v, i){
30761 if (v.rid != item.rid) {
30772 setActiveNext : function()
30774 var i = this.indexOfItem(this.getActive());
30776 if (i > this.barItems.length) {
30780 this.setActiveItem(this.barItems[i+1]);
30783 setActivePrev : function()
30785 var i = this.indexOfItem(this.getActive());
30791 this.setActiveItem(this.barItems[i-1]);
30794 format : function()
30796 if(!this.barItems.length){
30800 var width = 100 / this.barItems.length;
30802 Roo.each(this.barItems, function(i){
30803 i.el.setStyle('width', width + '%');
30804 i.topEl.el.setStyle('width', width + '%');
30805 i.bottomEl.el.setStyle('width', width + '%');
30814 * Nav Progress Item
30819 * @class Roo.bootstrap.NavProgressItem
30820 * @extends Roo.bootstrap.Component
30821 * Bootstrap NavProgressItem class
30822 * @cfg {String} rid the reference id
30823 * @cfg {Boolean} active (true|false) Is item active default false
30824 * @cfg {Boolean} disabled (true|false) Is item active default false
30825 * @cfg {String} html
30826 * @cfg {String} position (top|bottom) text position default bottom
30827 * @cfg {String} icon show icon instead of number
30830 * Create a new NavProgressItem
30831 * @param {Object} config The config object
30833 Roo.bootstrap.NavProgressItem = function(config){
30834 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30839 * The raw click event for the entire grid.
30840 * @param {Roo.bootstrap.NavProgressItem} this
30841 * @param {Roo.EventObject} e
30848 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30854 position : 'bottom',
30857 getAutoCreate : function()
30859 var iconCls = 'roo-navigation-bar-item-icon';
30861 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30865 cls: 'roo-navigation-bar-item',
30875 cfg.cls += ' active';
30878 cfg.cls += ' disabled';
30884 disable : function()
30886 this.setDisabled(true);
30889 enable : function()
30891 this.setDisabled(false);
30894 initEvents: function()
30896 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30898 this.iconEl.on('click', this.onClick, this);
30901 onClick : function(e)
30903 e.preventDefault();
30909 if(this.fireEvent('click', this, e) === false){
30913 this.parent().setActiveItem(this);
30916 isActive: function ()
30918 return this.active;
30921 setActive : function(state)
30923 if(this.active == state){
30927 this.active = state;
30930 this.el.addClass('active');
30934 this.el.removeClass('active');
30939 setDisabled : function(state)
30941 if(this.disabled == state){
30945 this.disabled = state;
30948 this.el.addClass('disabled');
30952 this.el.removeClass('disabled');
30955 tooltipEl : function()
30957 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30970 * @class Roo.bootstrap.FieldLabel
30971 * @extends Roo.bootstrap.Component
30972 * Bootstrap FieldLabel class
30973 * @cfg {String} html contents of the element
30974 * @cfg {String} tag tag of the element default label
30975 * @cfg {String} cls class of the element
30976 * @cfg {String} target label target
30977 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30978 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30979 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30980 * @cfg {String} iconTooltip default "This field is required"
30981 * @cfg {String} indicatorpos (left|right) default left
30984 * Create a new FieldLabel
30985 * @param {Object} config The config object
30988 Roo.bootstrap.FieldLabel = function(config){
30989 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30994 * Fires after the field has been marked as invalid.
30995 * @param {Roo.form.FieldLabel} this
30996 * @param {String} msg The validation message
31001 * Fires after the field has been validated with no errors.
31002 * @param {Roo.form.FieldLabel} this
31008 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
31015 invalidClass : 'has-warning',
31016 validClass : 'has-success',
31017 iconTooltip : 'This field is required',
31018 indicatorpos : 'left',
31020 getAutoCreate : function(){
31023 if (!this.allowBlank) {
31029 cls : 'roo-bootstrap-field-label ' + this.cls,
31034 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
31035 tooltip : this.iconTooltip
31044 if(this.indicatorpos == 'right'){
31047 cls : 'roo-bootstrap-field-label ' + this.cls,
31056 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
31057 tooltip : this.iconTooltip
31066 initEvents: function()
31068 Roo.bootstrap.Element.superclass.initEvents.call(this);
31070 this.indicator = this.indicatorEl();
31072 if(this.indicator){
31073 this.indicator.removeClass('visible');
31074 this.indicator.addClass('invisible');
31077 Roo.bootstrap.FieldLabel.register(this);
31080 indicatorEl : function()
31082 var indicator = this.el.select('i.roo-required-indicator',true).first();
31093 * Mark this field as valid
31095 markValid : function()
31097 if(this.indicator){
31098 this.indicator.removeClass('visible');
31099 this.indicator.addClass('invisible');
31101 if (Roo.bootstrap.version == 3) {
31102 this.el.removeClass(this.invalidClass);
31103 this.el.addClass(this.validClass);
31105 this.el.removeClass('is-invalid');
31106 this.el.addClass('is-valid');
31110 this.fireEvent('valid', this);
31114 * Mark this field as invalid
31115 * @param {String} msg The validation message
31117 markInvalid : function(msg)
31119 if(this.indicator){
31120 this.indicator.removeClass('invisible');
31121 this.indicator.addClass('visible');
31123 if (Roo.bootstrap.version == 3) {
31124 this.el.removeClass(this.validClass);
31125 this.el.addClass(this.invalidClass);
31127 this.el.removeClass('is-valid');
31128 this.el.addClass('is-invalid');
31132 this.fireEvent('invalid', this, msg);
31138 Roo.apply(Roo.bootstrap.FieldLabel, {
31143 * register a FieldLabel Group
31144 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
31146 register : function(label)
31148 if(this.groups.hasOwnProperty(label.target)){
31152 this.groups[label.target] = label;
31156 * fetch a FieldLabel Group based on the target
31157 * @param {string} target
31158 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
31160 get: function(target) {
31161 if (typeof(this.groups[target]) == 'undefined') {
31165 return this.groups[target] ;
31174 * page DateSplitField.
31180 * @class Roo.bootstrap.DateSplitField
31181 * @extends Roo.bootstrap.Component
31182 * Bootstrap DateSplitField class
31183 * @cfg {string} fieldLabel - the label associated
31184 * @cfg {Number} labelWidth set the width of label (0-12)
31185 * @cfg {String} labelAlign (top|left)
31186 * @cfg {Boolean} dayAllowBlank (true|false) default false
31187 * @cfg {Boolean} monthAllowBlank (true|false) default false
31188 * @cfg {Boolean} yearAllowBlank (true|false) default false
31189 * @cfg {string} dayPlaceholder
31190 * @cfg {string} monthPlaceholder
31191 * @cfg {string} yearPlaceholder
31192 * @cfg {string} dayFormat default 'd'
31193 * @cfg {string} monthFormat default 'm'
31194 * @cfg {string} yearFormat default 'Y'
31195 * @cfg {Number} labellg set the width of label (1-12)
31196 * @cfg {Number} labelmd set the width of label (1-12)
31197 * @cfg {Number} labelsm set the width of label (1-12)
31198 * @cfg {Number} labelxs set the width of label (1-12)
31202 * Create a new DateSplitField
31203 * @param {Object} config The config object
31206 Roo.bootstrap.DateSplitField = function(config){
31207 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
31213 * getting the data of years
31214 * @param {Roo.bootstrap.DateSplitField} this
31215 * @param {Object} years
31220 * getting the data of days
31221 * @param {Roo.bootstrap.DateSplitField} this
31222 * @param {Object} days
31227 * Fires after the field has been marked as invalid.
31228 * @param {Roo.form.Field} this
31229 * @param {String} msg The validation message
31234 * Fires after the field has been validated with no errors.
31235 * @param {Roo.form.Field} this
31241 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
31244 labelAlign : 'top',
31246 dayAllowBlank : false,
31247 monthAllowBlank : false,
31248 yearAllowBlank : false,
31249 dayPlaceholder : '',
31250 monthPlaceholder : '',
31251 yearPlaceholder : '',
31255 isFormField : true,
31261 getAutoCreate : function()
31265 cls : 'row roo-date-split-field-group',
31270 cls : 'form-hidden-field roo-date-split-field-group-value',
31276 var labelCls = 'col-md-12';
31277 var contentCls = 'col-md-4';
31279 if(this.fieldLabel){
31283 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
31287 html : this.fieldLabel
31292 if(this.labelAlign == 'left'){
31294 if(this.labelWidth > 12){
31295 label.style = "width: " + this.labelWidth + 'px';
31298 if(this.labelWidth < 13 && this.labelmd == 0){
31299 this.labelmd = this.labelWidth;
31302 if(this.labellg > 0){
31303 labelCls = ' col-lg-' + this.labellg;
31304 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
31307 if(this.labelmd > 0){
31308 labelCls = ' col-md-' + this.labelmd;
31309 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
31312 if(this.labelsm > 0){
31313 labelCls = ' col-sm-' + this.labelsm;
31314 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
31317 if(this.labelxs > 0){
31318 labelCls = ' col-xs-' + this.labelxs;
31319 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
31323 label.cls += ' ' + labelCls;
31325 cfg.cn.push(label);
31328 Roo.each(['day', 'month', 'year'], function(t){
31331 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
31338 inputEl: function ()
31340 return this.el.select('.roo-date-split-field-group-value', true).first();
31343 onRender : function(ct, position)
31347 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31349 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
31351 this.dayField = new Roo.bootstrap.ComboBox({
31352 allowBlank : this.dayAllowBlank,
31353 alwaysQuery : true,
31354 displayField : 'value',
31357 forceSelection : true,
31359 placeholder : this.dayPlaceholder,
31360 selectOnFocus : true,
31361 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31362 triggerAction : 'all',
31364 valueField : 'value',
31365 store : new Roo.data.SimpleStore({
31366 data : (function() {
31368 _this.fireEvent('days', _this, days);
31371 fields : [ 'value' ]
31374 select : function (_self, record, index)
31376 _this.setValue(_this.getValue());
31381 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31383 this.monthField = new Roo.bootstrap.MonthField({
31384 after : '<i class=\"fa fa-calendar\"></i>',
31385 allowBlank : this.monthAllowBlank,
31386 placeholder : this.monthPlaceholder,
31389 render : function (_self)
31391 this.el.select('span.input-group-addon', true).first().on('click', function(e){
31392 e.preventDefault();
31396 select : function (_self, oldvalue, newvalue)
31398 _this.setValue(_this.getValue());
31403 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31405 this.yearField = new Roo.bootstrap.ComboBox({
31406 allowBlank : this.yearAllowBlank,
31407 alwaysQuery : true,
31408 displayField : 'value',
31411 forceSelection : true,
31413 placeholder : this.yearPlaceholder,
31414 selectOnFocus : true,
31415 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31416 triggerAction : 'all',
31418 valueField : 'value',
31419 store : new Roo.data.SimpleStore({
31420 data : (function() {
31422 _this.fireEvent('years', _this, years);
31425 fields : [ 'value' ]
31428 select : function (_self, record, index)
31430 _this.setValue(_this.getValue());
31435 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31438 setValue : function(v, format)
31440 this.inputEl.dom.value = v;
31442 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31444 var d = Date.parseDate(v, f);
31451 this.setDay(d.format(this.dayFormat));
31452 this.setMonth(d.format(this.monthFormat));
31453 this.setYear(d.format(this.yearFormat));
31460 setDay : function(v)
31462 this.dayField.setValue(v);
31463 this.inputEl.dom.value = this.getValue();
31468 setMonth : function(v)
31470 this.monthField.setValue(v, true);
31471 this.inputEl.dom.value = this.getValue();
31476 setYear : function(v)
31478 this.yearField.setValue(v);
31479 this.inputEl.dom.value = this.getValue();
31484 getDay : function()
31486 return this.dayField.getValue();
31489 getMonth : function()
31491 return this.monthField.getValue();
31494 getYear : function()
31496 return this.yearField.getValue();
31499 getValue : function()
31501 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31503 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31513 this.inputEl.dom.value = '';
31518 validate : function()
31520 var d = this.dayField.validate();
31521 var m = this.monthField.validate();
31522 var y = this.yearField.validate();
31527 (!this.dayAllowBlank && !d) ||
31528 (!this.monthAllowBlank && !m) ||
31529 (!this.yearAllowBlank && !y)
31534 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31543 this.markInvalid();
31548 markValid : function()
31551 var label = this.el.select('label', true).first();
31552 var icon = this.el.select('i.fa-star', true).first();
31558 this.fireEvent('valid', this);
31562 * Mark this field as invalid
31563 * @param {String} msg The validation message
31565 markInvalid : function(msg)
31568 var label = this.el.select('label', true).first();
31569 var icon = this.el.select('i.fa-star', true).first();
31571 if(label && !icon){
31572 this.el.select('.roo-date-split-field-label', true).createChild({
31574 cls : 'text-danger fa fa-lg fa-star',
31575 tooltip : 'This field is required',
31576 style : 'margin-right:5px;'
31580 this.fireEvent('invalid', this, msg);
31583 clearInvalid : function()
31585 var label = this.el.select('label', true).first();
31586 var icon = this.el.select('i.fa-star', true).first();
31592 this.fireEvent('valid', this);
31595 getName: function()
31605 * http://masonry.desandro.com
31607 * The idea is to render all the bricks based on vertical width...
31609 * The original code extends 'outlayer' - we might need to use that....
31615 * @class Roo.bootstrap.LayoutMasonry
31616 * @extends Roo.bootstrap.Component
31617 * Bootstrap Layout Masonry class
31620 * Create a new Element
31621 * @param {Object} config The config object
31624 Roo.bootstrap.LayoutMasonry = function(config){
31626 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31630 Roo.bootstrap.LayoutMasonry.register(this);
31636 * Fire after layout the items
31637 * @param {Roo.bootstrap.LayoutMasonry} this
31638 * @param {Roo.EventObject} e
31645 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
31648 * @cfg {Boolean} isLayoutInstant = no animation?
31650 isLayoutInstant : false, // needed?
31653 * @cfg {Number} boxWidth width of the columns
31658 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
31663 * @cfg {Number} padWidth padding below box..
31668 * @cfg {Number} gutter gutter width..
31673 * @cfg {Number} maxCols maximum number of columns
31679 * @cfg {Boolean} isAutoInitial defalut true
31681 isAutoInitial : true,
31686 * @cfg {Boolean} isHorizontal defalut false
31688 isHorizontal : false,
31690 currentSize : null,
31696 bricks: null, //CompositeElement
31700 _isLayoutInited : false,
31702 // isAlternative : false, // only use for vertical layout...
31705 * @cfg {Number} alternativePadWidth padding below box..
31707 alternativePadWidth : 50,
31709 selectedBrick : [],
31711 getAutoCreate : function(){
31713 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31717 cls: 'blog-masonary-wrapper ' + this.cls,
31719 cls : 'mas-boxes masonary'
31726 getChildContainer: function( )
31728 if (this.boxesEl) {
31729 return this.boxesEl;
31732 this.boxesEl = this.el.select('.mas-boxes').first();
31734 return this.boxesEl;
31738 initEvents : function()
31742 if(this.isAutoInitial){
31743 Roo.log('hook children rendered');
31744 this.on('childrenrendered', function() {
31745 Roo.log('children rendered');
31751 initial : function()
31753 this.selectedBrick = [];
31755 this.currentSize = this.el.getBox(true);
31757 Roo.EventManager.onWindowResize(this.resize, this);
31759 if(!this.isAutoInitial){
31767 //this.layout.defer(500,this);
31771 resize : function()
31773 var cs = this.el.getBox(true);
31776 this.currentSize.width == cs.width &&
31777 this.currentSize.x == cs.x &&
31778 this.currentSize.height == cs.height &&
31779 this.currentSize.y == cs.y
31781 Roo.log("no change in with or X or Y");
31785 this.currentSize = cs;
31791 layout : function()
31793 this._resetLayout();
31795 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31797 this.layoutItems( isInstant );
31799 this._isLayoutInited = true;
31801 this.fireEvent('layout', this);
31805 _resetLayout : function()
31807 if(this.isHorizontal){
31808 this.horizontalMeasureColumns();
31812 this.verticalMeasureColumns();
31816 verticalMeasureColumns : function()
31818 this.getContainerWidth();
31820 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31821 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31825 var boxWidth = this.boxWidth + this.padWidth;
31827 if(this.containerWidth < this.boxWidth){
31828 boxWidth = this.containerWidth
31831 var containerWidth = this.containerWidth;
31833 var cols = Math.floor(containerWidth / boxWidth);
31835 this.cols = Math.max( cols, 1 );
31837 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31839 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31841 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31843 this.colWidth = boxWidth + avail - this.padWidth;
31845 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31846 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31849 horizontalMeasureColumns : function()
31851 this.getContainerWidth();
31853 var boxWidth = this.boxWidth;
31855 if(this.containerWidth < boxWidth){
31856 boxWidth = this.containerWidth;
31859 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31861 this.el.setHeight(boxWidth);
31865 getContainerWidth : function()
31867 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31870 layoutItems : function( isInstant )
31872 Roo.log(this.bricks);
31874 var items = Roo.apply([], this.bricks);
31876 if(this.isHorizontal){
31877 this._horizontalLayoutItems( items , isInstant );
31881 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31882 // this._verticalAlternativeLayoutItems( items , isInstant );
31886 this._verticalLayoutItems( items , isInstant );
31890 _verticalLayoutItems : function ( items , isInstant)
31892 if ( !items || !items.length ) {
31897 ['xs', 'xs', 'xs', 'tall'],
31898 ['xs', 'xs', 'tall'],
31899 ['xs', 'xs', 'sm'],
31900 ['xs', 'xs', 'xs'],
31906 ['sm', 'xs', 'xs'],
31910 ['tall', 'xs', 'xs', 'xs'],
31911 ['tall', 'xs', 'xs'],
31923 Roo.each(items, function(item, k){
31925 switch (item.size) {
31926 // these layouts take up a full box,
31937 boxes.push([item]);
31960 var filterPattern = function(box, length)
31968 var pattern = box.slice(0, length);
31972 Roo.each(pattern, function(i){
31973 format.push(i.size);
31976 Roo.each(standard, function(s){
31978 if(String(s) != String(format)){
31987 if(!match && length == 1){
31992 filterPattern(box, length - 1);
31996 queue.push(pattern);
31998 box = box.slice(length, box.length);
32000 filterPattern(box, 4);
32006 Roo.each(boxes, function(box, k){
32012 if(box.length == 1){
32017 filterPattern(box, 4);
32021 this._processVerticalLayoutQueue( queue, isInstant );
32025 // _verticalAlternativeLayoutItems : function( items , isInstant )
32027 // if ( !items || !items.length ) {
32031 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
32035 _horizontalLayoutItems : function ( items , isInstant)
32037 if ( !items || !items.length || items.length < 3) {
32043 var eItems = items.slice(0, 3);
32045 items = items.slice(3, items.length);
32048 ['xs', 'xs', 'xs', 'wide'],
32049 ['xs', 'xs', 'wide'],
32050 ['xs', 'xs', 'sm'],
32051 ['xs', 'xs', 'xs'],
32057 ['sm', 'xs', 'xs'],
32061 ['wide', 'xs', 'xs', 'xs'],
32062 ['wide', 'xs', 'xs'],
32075 Roo.each(items, function(item, k){
32077 switch (item.size) {
32088 boxes.push([item]);
32112 var filterPattern = function(box, length)
32120 var pattern = box.slice(0, length);
32124 Roo.each(pattern, function(i){
32125 format.push(i.size);
32128 Roo.each(standard, function(s){
32130 if(String(s) != String(format)){
32139 if(!match && length == 1){
32144 filterPattern(box, length - 1);
32148 queue.push(pattern);
32150 box = box.slice(length, box.length);
32152 filterPattern(box, 4);
32158 Roo.each(boxes, function(box, k){
32164 if(box.length == 1){
32169 filterPattern(box, 4);
32176 var pos = this.el.getBox(true);
32180 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32182 var hit_end = false;
32184 Roo.each(queue, function(box){
32188 Roo.each(box, function(b){
32190 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32200 Roo.each(box, function(b){
32202 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32205 mx = Math.max(mx, b.x);
32209 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
32213 Roo.each(box, function(b){
32215 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32229 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
32232 /** Sets position of item in DOM
32233 * @param {Element} item
32234 * @param {Number} x - horizontal position
32235 * @param {Number} y - vertical position
32236 * @param {Boolean} isInstant - disables transitions
32238 _processVerticalLayoutQueue : function( queue, isInstant )
32240 var pos = this.el.getBox(true);
32245 for (var i = 0; i < this.cols; i++){
32249 Roo.each(queue, function(box, k){
32251 var col = k % this.cols;
32253 Roo.each(box, function(b,kk){
32255 b.el.position('absolute');
32257 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32258 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32260 if(b.size == 'md-left' || b.size == 'md-right'){
32261 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32262 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32265 b.el.setWidth(width);
32266 b.el.setHeight(height);
32268 b.el.select('iframe',true).setSize(width,height);
32272 for (var i = 0; i < this.cols; i++){
32274 if(maxY[i] < maxY[col]){
32279 col = Math.min(col, i);
32283 x = pos.x + col * (this.colWidth + this.padWidth);
32287 var positions = [];
32289 switch (box.length){
32291 positions = this.getVerticalOneBoxColPositions(x, y, box);
32294 positions = this.getVerticalTwoBoxColPositions(x, y, box);
32297 positions = this.getVerticalThreeBoxColPositions(x, y, box);
32300 positions = this.getVerticalFourBoxColPositions(x, y, box);
32306 Roo.each(box, function(b,kk){
32308 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32310 var sz = b.el.getSize();
32312 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
32320 for (var i = 0; i < this.cols; i++){
32321 mY = Math.max(mY, maxY[i]);
32324 this.el.setHeight(mY - pos.y);
32328 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
32330 // var pos = this.el.getBox(true);
32333 // var maxX = pos.right;
32335 // var maxHeight = 0;
32337 // Roo.each(items, function(item, k){
32341 // item.el.position('absolute');
32343 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
32345 // item.el.setWidth(width);
32347 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
32349 // item.el.setHeight(height);
32352 // item.el.setXY([x, y], isInstant ? false : true);
32354 // item.el.setXY([maxX - width, y], isInstant ? false : true);
32357 // y = y + height + this.alternativePadWidth;
32359 // maxHeight = maxHeight + height + this.alternativePadWidth;
32363 // this.el.setHeight(maxHeight);
32367 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32369 var pos = this.el.getBox(true);
32374 var maxX = pos.right;
32376 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32378 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32380 Roo.each(queue, function(box, k){
32382 Roo.each(box, function(b, kk){
32384 b.el.position('absolute');
32386 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32387 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32389 if(b.size == 'md-left' || b.size == 'md-right'){
32390 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32391 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32394 b.el.setWidth(width);
32395 b.el.setHeight(height);
32403 var positions = [];
32405 switch (box.length){
32407 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32410 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32413 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32416 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32422 Roo.each(box, function(b,kk){
32424 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32426 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32434 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32436 Roo.each(eItems, function(b,k){
32438 b.size = (k == 0) ? 'sm' : 'xs';
32439 b.x = (k == 0) ? 2 : 1;
32440 b.y = (k == 0) ? 2 : 1;
32442 b.el.position('absolute');
32444 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32446 b.el.setWidth(width);
32448 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32450 b.el.setHeight(height);
32454 var positions = [];
32457 x : maxX - this.unitWidth * 2 - this.gutter,
32462 x : maxX - this.unitWidth,
32463 y : minY + (this.unitWidth + this.gutter) * 2
32467 x : maxX - this.unitWidth * 3 - this.gutter * 2,
32471 Roo.each(eItems, function(b,k){
32473 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32479 getVerticalOneBoxColPositions : function(x, y, box)
32483 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32485 if(box[0].size == 'md-left'){
32489 if(box[0].size == 'md-right'){
32494 x : x + (this.unitWidth + this.gutter) * rand,
32501 getVerticalTwoBoxColPositions : function(x, y, box)
32505 if(box[0].size == 'xs'){
32509 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32513 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32527 x : x + (this.unitWidth + this.gutter) * 2,
32528 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32535 getVerticalThreeBoxColPositions : function(x, y, box)
32539 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32547 x : x + (this.unitWidth + this.gutter) * 1,
32552 x : x + (this.unitWidth + this.gutter) * 2,
32560 if(box[0].size == 'xs' && box[1].size == 'xs'){
32569 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32573 x : x + (this.unitWidth + this.gutter) * 1,
32587 x : x + (this.unitWidth + this.gutter) * 2,
32592 x : x + (this.unitWidth + this.gutter) * 2,
32593 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32600 getVerticalFourBoxColPositions : function(x, y, box)
32604 if(box[0].size == 'xs'){
32613 y : y + (this.unitHeight + this.gutter) * 1
32618 y : y + (this.unitHeight + this.gutter) * 2
32622 x : x + (this.unitWidth + this.gutter) * 1,
32636 x : x + (this.unitWidth + this.gutter) * 2,
32641 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32642 y : y + (this.unitHeight + this.gutter) * 1
32646 x : x + (this.unitWidth + this.gutter) * 2,
32647 y : y + (this.unitWidth + this.gutter) * 2
32654 getHorizontalOneBoxColPositions : function(maxX, minY, box)
32658 if(box[0].size == 'md-left'){
32660 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32667 if(box[0].size == 'md-right'){
32669 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32670 y : minY + (this.unitWidth + this.gutter) * 1
32676 var rand = Math.floor(Math.random() * (4 - box[0].y));
32679 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32680 y : minY + (this.unitWidth + this.gutter) * rand
32687 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32691 if(box[0].size == 'xs'){
32694 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32699 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32700 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32708 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32713 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32714 y : minY + (this.unitWidth + this.gutter) * 2
32721 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32725 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32728 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32733 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32734 y : minY + (this.unitWidth + this.gutter) * 1
32738 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32739 y : minY + (this.unitWidth + this.gutter) * 2
32746 if(box[0].size == 'xs' && box[1].size == 'xs'){
32749 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32754 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32759 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32760 y : minY + (this.unitWidth + this.gutter) * 1
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) * 2
32778 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32779 y : minY + (this.unitWidth + this.gutter) * 2
32786 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32790 if(box[0].size == 'xs'){
32793 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32798 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32803 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),
32808 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32809 y : minY + (this.unitWidth + this.gutter) * 1
32817 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32822 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32823 y : minY + (this.unitWidth + this.gutter) * 2
32827 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32828 y : minY + (this.unitWidth + this.gutter) * 2
32832 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),
32833 y : minY + (this.unitWidth + this.gutter) * 2
32841 * remove a Masonry Brick
32842 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32844 removeBrick : function(brick_id)
32850 for (var i = 0; i<this.bricks.length; i++) {
32851 if (this.bricks[i].id == brick_id) {
32852 this.bricks.splice(i,1);
32853 this.el.dom.removeChild(Roo.get(brick_id).dom);
32860 * adds a Masonry Brick
32861 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32863 addBrick : function(cfg)
32865 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32866 //this.register(cn);
32867 cn.parentId = this.id;
32868 cn.render(this.el);
32873 * register a Masonry Brick
32874 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32877 register : function(brick)
32879 this.bricks.push(brick);
32880 brick.masonryId = this.id;
32884 * clear all the Masonry Brick
32886 clearAll : function()
32889 //this.getChildContainer().dom.innerHTML = "";
32890 this.el.dom.innerHTML = '';
32893 getSelected : function()
32895 if (!this.selectedBrick) {
32899 return this.selectedBrick;
32903 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32907 * register a Masonry Layout
32908 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32911 register : function(layout)
32913 this.groups[layout.id] = layout;
32916 * fetch a Masonry Layout based on the masonry layout ID
32917 * @param {string} the masonry layout to add
32918 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32921 get: function(layout_id) {
32922 if (typeof(this.groups[layout_id]) == 'undefined') {
32925 return this.groups[layout_id] ;
32937 * http://masonry.desandro.com
32939 * The idea is to render all the bricks based on vertical width...
32941 * The original code extends 'outlayer' - we might need to use that....
32947 * @class Roo.bootstrap.LayoutMasonryAuto
32948 * @extends Roo.bootstrap.Component
32949 * Bootstrap Layout Masonry class
32952 * Create a new Element
32953 * @param {Object} config The config object
32956 Roo.bootstrap.LayoutMasonryAuto = function(config){
32957 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32960 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32963 * @cfg {Boolean} isFitWidth - resize the width..
32965 isFitWidth : false, // options..
32967 * @cfg {Boolean} isOriginLeft = left align?
32969 isOriginLeft : true,
32971 * @cfg {Boolean} isOriginTop = top align?
32973 isOriginTop : false,
32975 * @cfg {Boolean} isLayoutInstant = no animation?
32977 isLayoutInstant : false, // needed?
32979 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32981 isResizingContainer : true,
32983 * @cfg {Number} columnWidth width of the columns
32989 * @cfg {Number} maxCols maximum number of columns
32994 * @cfg {Number} padHeight padding below box..
33000 * @cfg {Boolean} isAutoInitial defalut true
33003 isAutoInitial : true,
33009 initialColumnWidth : 0,
33010 currentSize : null,
33012 colYs : null, // array.
33019 bricks: null, //CompositeElement
33020 cols : 0, // array?
33021 // element : null, // wrapped now this.el
33022 _isLayoutInited : null,
33025 getAutoCreate : function(){
33029 cls: 'blog-masonary-wrapper ' + this.cls,
33031 cls : 'mas-boxes masonary'
33038 getChildContainer: function( )
33040 if (this.boxesEl) {
33041 return this.boxesEl;
33044 this.boxesEl = this.el.select('.mas-boxes').first();
33046 return this.boxesEl;
33050 initEvents : function()
33054 if(this.isAutoInitial){
33055 Roo.log('hook children rendered');
33056 this.on('childrenrendered', function() {
33057 Roo.log('children rendered');
33064 initial : function()
33066 this.reloadItems();
33068 this.currentSize = this.el.getBox(true);
33070 /// was window resize... - let's see if this works..
33071 Roo.EventManager.onWindowResize(this.resize, this);
33073 if(!this.isAutoInitial){
33078 this.layout.defer(500,this);
33081 reloadItems: function()
33083 this.bricks = this.el.select('.masonry-brick', true);
33085 this.bricks.each(function(b) {
33086 //Roo.log(b.getSize());
33087 if (!b.attr('originalwidth')) {
33088 b.attr('originalwidth', b.getSize().width);
33093 Roo.log(this.bricks.elements.length);
33096 resize : function()
33099 var cs = this.el.getBox(true);
33101 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
33102 Roo.log("no change in with or X");
33105 this.currentSize = cs;
33109 layout : function()
33112 this._resetLayout();
33113 //this._manageStamps();
33115 // don't animate first layout
33116 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33117 this.layoutItems( isInstant );
33119 // flag for initalized
33120 this._isLayoutInited = true;
33123 layoutItems : function( isInstant )
33125 //var items = this._getItemsForLayout( this.items );
33126 // original code supports filtering layout items.. we just ignore it..
33128 this._layoutItems( this.bricks , isInstant );
33130 this._postLayout();
33132 _layoutItems : function ( items , isInstant)
33134 //this.fireEvent( 'layout', this, items );
33137 if ( !items || !items.elements.length ) {
33138 // no items, emit event with empty array
33143 items.each(function(item) {
33144 Roo.log("layout item");
33146 // get x/y object from method
33147 var position = this._getItemLayoutPosition( item );
33149 position.item = item;
33150 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
33151 queue.push( position );
33154 this._processLayoutQueue( queue );
33156 /** Sets position of item in DOM
33157 * @param {Element} item
33158 * @param {Number} x - horizontal position
33159 * @param {Number} y - vertical position
33160 * @param {Boolean} isInstant - disables transitions
33162 _processLayoutQueue : function( queue )
33164 for ( var i=0, len = queue.length; i < len; i++ ) {
33165 var obj = queue[i];
33166 obj.item.position('absolute');
33167 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
33173 * Any logic you want to do after each layout,
33174 * i.e. size the container
33176 _postLayout : function()
33178 this.resizeContainer();
33181 resizeContainer : function()
33183 if ( !this.isResizingContainer ) {
33186 var size = this._getContainerSize();
33188 this.el.setSize(size.width,size.height);
33189 this.boxesEl.setSize(size.width,size.height);
33195 _resetLayout : function()
33197 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
33198 this.colWidth = this.el.getWidth();
33199 //this.gutter = this.el.getWidth();
33201 this.measureColumns();
33207 this.colYs.push( 0 );
33213 measureColumns : function()
33215 this.getContainerWidth();
33216 // if columnWidth is 0, default to outerWidth of first item
33217 if ( !this.columnWidth ) {
33218 var firstItem = this.bricks.first();
33219 Roo.log(firstItem);
33220 this.columnWidth = this.containerWidth;
33221 if (firstItem && firstItem.attr('originalwidth') ) {
33222 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
33224 // columnWidth fall back to item of first element
33225 Roo.log("set column width?");
33226 this.initialColumnWidth = this.columnWidth ;
33228 // if first elem has no width, default to size of container
33233 if (this.initialColumnWidth) {
33234 this.columnWidth = this.initialColumnWidth;
33239 // column width is fixed at the top - however if container width get's smaller we should
33242 // this bit calcs how man columns..
33244 var columnWidth = this.columnWidth += this.gutter;
33246 // calculate columns
33247 var containerWidth = this.containerWidth + this.gutter;
33249 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
33250 // fix rounding errors, typically with gutters
33251 var excess = columnWidth - containerWidth % columnWidth;
33254 // if overshoot is less than a pixel, round up, otherwise floor it
33255 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
33256 cols = Math[ mathMethod ]( cols );
33257 this.cols = Math.max( cols, 1 );
33258 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33260 // padding positioning..
33261 var totalColWidth = this.cols * this.columnWidth;
33262 var padavail = this.containerWidth - totalColWidth;
33263 // so for 2 columns - we need 3 'pads'
33265 var padNeeded = (1+this.cols) * this.padWidth;
33267 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
33269 this.columnWidth += padExtra
33270 //this.padWidth = Math.floor(padavail / ( this.cols));
33272 // adjust colum width so that padding is fixed??
33274 // we have 3 columns ... total = width * 3
33275 // we have X left over... that should be used by
33277 //if (this.expandC) {
33285 getContainerWidth : function()
33287 /* // container is parent if fit width
33288 var container = this.isFitWidth ? this.element.parentNode : this.element;
33289 // check that this.size and size are there
33290 // IE8 triggers resize on body size change, so they might not be
33292 var size = getSize( container ); //FIXME
33293 this.containerWidth = size && size.innerWidth; //FIXME
33296 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33300 _getItemLayoutPosition : function( item ) // what is item?
33302 // we resize the item to our columnWidth..
33304 item.setWidth(this.columnWidth);
33305 item.autoBoxAdjust = false;
33307 var sz = item.getSize();
33309 // how many columns does this brick span
33310 var remainder = this.containerWidth % this.columnWidth;
33312 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
33313 // round if off by 1 pixel, otherwise use ceil
33314 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
33315 colSpan = Math.min( colSpan, this.cols );
33317 // normally this should be '1' as we dont' currently allow multi width columns..
33319 var colGroup = this._getColGroup( colSpan );
33320 // get the minimum Y value from the columns
33321 var minimumY = Math.min.apply( Math, colGroup );
33322 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
33324 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
33326 // position the brick
33328 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
33329 y: this.currentSize.y + minimumY + this.padHeight
33333 // apply setHeight to necessary columns
33334 var setHeight = minimumY + sz.height + this.padHeight;
33335 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
33337 var setSpan = this.cols + 1 - colGroup.length;
33338 for ( var i = 0; i < setSpan; i++ ) {
33339 this.colYs[ shortColIndex + i ] = setHeight ;
33346 * @param {Number} colSpan - number of columns the element spans
33347 * @returns {Array} colGroup
33349 _getColGroup : function( colSpan )
33351 if ( colSpan < 2 ) {
33352 // if brick spans only one column, use all the column Ys
33357 // how many different places could this brick fit horizontally
33358 var groupCount = this.cols + 1 - colSpan;
33359 // for each group potential horizontal position
33360 for ( var i = 0; i < groupCount; i++ ) {
33361 // make an array of colY values for that one group
33362 var groupColYs = this.colYs.slice( i, i + colSpan );
33363 // and get the max value of the array
33364 colGroup[i] = Math.max.apply( Math, groupColYs );
33369 _manageStamp : function( stamp )
33371 var stampSize = stamp.getSize();
33372 var offset = stamp.getBox();
33373 // get the columns that this stamp affects
33374 var firstX = this.isOriginLeft ? offset.x : offset.right;
33375 var lastX = firstX + stampSize.width;
33376 var firstCol = Math.floor( firstX / this.columnWidth );
33377 firstCol = Math.max( 0, firstCol );
33379 var lastCol = Math.floor( lastX / this.columnWidth );
33380 // lastCol should not go over if multiple of columnWidth #425
33381 lastCol -= lastX % this.columnWidth ? 0 : 1;
33382 lastCol = Math.min( this.cols - 1, lastCol );
33384 // set colYs to bottom of the stamp
33385 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33388 for ( var i = firstCol; i <= lastCol; i++ ) {
33389 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33394 _getContainerSize : function()
33396 this.maxY = Math.max.apply( Math, this.colYs );
33401 if ( this.isFitWidth ) {
33402 size.width = this._getContainerFitWidth();
33408 _getContainerFitWidth : function()
33410 var unusedCols = 0;
33411 // count unused columns
33414 if ( this.colYs[i] !== 0 ) {
33419 // fit container to columns that have been used
33420 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33423 needsResizeLayout : function()
33425 var previousWidth = this.containerWidth;
33426 this.getContainerWidth();
33427 return previousWidth !== this.containerWidth;
33442 * @class Roo.bootstrap.MasonryBrick
33443 * @extends Roo.bootstrap.Component
33444 * Bootstrap MasonryBrick class
33447 * Create a new MasonryBrick
33448 * @param {Object} config The config object
33451 Roo.bootstrap.MasonryBrick = function(config){
33453 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33455 Roo.bootstrap.MasonryBrick.register(this);
33461 * When a MasonryBrick is clcik
33462 * @param {Roo.bootstrap.MasonryBrick} this
33463 * @param {Roo.EventObject} e
33469 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
33472 * @cfg {String} title
33476 * @cfg {String} html
33480 * @cfg {String} bgimage
33484 * @cfg {String} videourl
33488 * @cfg {String} cls
33492 * @cfg {String} href
33496 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33501 * @cfg {String} placetitle (center|bottom)
33506 * @cfg {Boolean} isFitContainer defalut true
33508 isFitContainer : true,
33511 * @cfg {Boolean} preventDefault defalut false
33513 preventDefault : false,
33516 * @cfg {Boolean} inverse defalut false
33518 maskInverse : false,
33520 getAutoCreate : function()
33522 if(!this.isFitContainer){
33523 return this.getSplitAutoCreate();
33526 var cls = 'masonry-brick masonry-brick-full';
33528 if(this.href.length){
33529 cls += ' masonry-brick-link';
33532 if(this.bgimage.length){
33533 cls += ' masonry-brick-image';
33536 if(this.maskInverse){
33537 cls += ' mask-inverse';
33540 if(!this.html.length && !this.maskInverse && !this.videourl.length){
33541 cls += ' enable-mask';
33545 cls += ' masonry-' + this.size + '-brick';
33548 if(this.placetitle.length){
33550 switch (this.placetitle) {
33552 cls += ' masonry-center-title';
33555 cls += ' masonry-bottom-title';
33562 if(!this.html.length && !this.bgimage.length){
33563 cls += ' masonry-center-title';
33566 if(!this.html.length && this.bgimage.length){
33567 cls += ' masonry-bottom-title';
33572 cls += ' ' + this.cls;
33576 tag: (this.href.length) ? 'a' : 'div',
33581 cls: 'masonry-brick-mask'
33585 cls: 'masonry-brick-paragraph',
33591 if(this.href.length){
33592 cfg.href = this.href;
33595 var cn = cfg.cn[1].cn;
33597 if(this.title.length){
33600 cls: 'masonry-brick-title',
33605 if(this.html.length){
33608 cls: 'masonry-brick-text',
33613 if (!this.title.length && !this.html.length) {
33614 cfg.cn[1].cls += ' hide';
33617 if(this.bgimage.length){
33620 cls: 'masonry-brick-image-view',
33625 if(this.videourl.length){
33626 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33627 // youtube support only?
33630 cls: 'masonry-brick-image-view',
33633 allowfullscreen : true
33641 getSplitAutoCreate : function()
33643 var cls = 'masonry-brick masonry-brick-split';
33645 if(this.href.length){
33646 cls += ' masonry-brick-link';
33649 if(this.bgimage.length){
33650 cls += ' masonry-brick-image';
33654 cls += ' masonry-' + this.size + '-brick';
33657 switch (this.placetitle) {
33659 cls += ' masonry-center-title';
33662 cls += ' masonry-bottom-title';
33665 if(!this.bgimage.length){
33666 cls += ' masonry-center-title';
33669 if(this.bgimage.length){
33670 cls += ' masonry-bottom-title';
33676 cls += ' ' + this.cls;
33680 tag: (this.href.length) ? 'a' : 'div',
33685 cls: 'masonry-brick-split-head',
33689 cls: 'masonry-brick-paragraph',
33696 cls: 'masonry-brick-split-body',
33702 if(this.href.length){
33703 cfg.href = this.href;
33706 if(this.title.length){
33707 cfg.cn[0].cn[0].cn.push({
33709 cls: 'masonry-brick-title',
33714 if(this.html.length){
33715 cfg.cn[1].cn.push({
33717 cls: 'masonry-brick-text',
33722 if(this.bgimage.length){
33723 cfg.cn[0].cn.push({
33725 cls: 'masonry-brick-image-view',
33730 if(this.videourl.length){
33731 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33732 // youtube support only?
33733 cfg.cn[0].cn.cn.push({
33735 cls: 'masonry-brick-image-view',
33738 allowfullscreen : true
33745 initEvents: function()
33747 switch (this.size) {
33780 this.el.on('touchstart', this.onTouchStart, this);
33781 this.el.on('touchmove', this.onTouchMove, this);
33782 this.el.on('touchend', this.onTouchEnd, this);
33783 this.el.on('contextmenu', this.onContextMenu, this);
33785 this.el.on('mouseenter' ,this.enter, this);
33786 this.el.on('mouseleave', this.leave, this);
33787 this.el.on('click', this.onClick, this);
33790 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33791 this.parent().bricks.push(this);
33796 onClick: function(e, el)
33798 var time = this.endTimer - this.startTimer;
33799 // Roo.log(e.preventDefault());
33802 e.preventDefault();
33807 if(!this.preventDefault){
33811 e.preventDefault();
33813 if (this.activeClass != '') {
33814 this.selectBrick();
33817 this.fireEvent('click', this, e);
33820 enter: function(e, el)
33822 e.preventDefault();
33824 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33828 if(this.bgimage.length && this.html.length){
33829 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33833 leave: function(e, el)
33835 e.preventDefault();
33837 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33841 if(this.bgimage.length && this.html.length){
33842 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33846 onTouchStart: function(e, el)
33848 // e.preventDefault();
33850 this.touchmoved = false;
33852 if(!this.isFitContainer){
33856 if(!this.bgimage.length || !this.html.length){
33860 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33862 this.timer = new Date().getTime();
33866 onTouchMove: function(e, el)
33868 this.touchmoved = true;
33871 onContextMenu : function(e,el)
33873 e.preventDefault();
33874 e.stopPropagation();
33878 onTouchEnd: function(e, el)
33880 // e.preventDefault();
33882 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33889 if(!this.bgimage.length || !this.html.length){
33891 if(this.href.length){
33892 window.location.href = this.href;
33898 if(!this.isFitContainer){
33902 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33904 window.location.href = this.href;
33907 //selection on single brick only
33908 selectBrick : function() {
33910 if (!this.parentId) {
33914 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33915 var index = m.selectedBrick.indexOf(this.id);
33918 m.selectedBrick.splice(index,1);
33919 this.el.removeClass(this.activeClass);
33923 for(var i = 0; i < m.selectedBrick.length; i++) {
33924 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33925 b.el.removeClass(b.activeClass);
33928 m.selectedBrick = [];
33930 m.selectedBrick.push(this.id);
33931 this.el.addClass(this.activeClass);
33935 isSelected : function(){
33936 return this.el.hasClass(this.activeClass);
33941 Roo.apply(Roo.bootstrap.MasonryBrick, {
33944 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33946 * register a Masonry Brick
33947 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33950 register : function(brick)
33952 //this.groups[brick.id] = brick;
33953 this.groups.add(brick.id, brick);
33956 * fetch a masonry brick based on the masonry brick ID
33957 * @param {string} the masonry brick to add
33958 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33961 get: function(brick_id)
33963 // if (typeof(this.groups[brick_id]) == 'undefined') {
33966 // return this.groups[brick_id] ;
33968 if(this.groups.key(brick_id)) {
33969 return this.groups.key(brick_id);
33987 * @class Roo.bootstrap.Brick
33988 * @extends Roo.bootstrap.Component
33989 * Bootstrap Brick class
33992 * Create a new Brick
33993 * @param {Object} config The config object
33996 Roo.bootstrap.Brick = function(config){
33997 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
34003 * When a Brick is click
34004 * @param {Roo.bootstrap.Brick} this
34005 * @param {Roo.EventObject} e
34011 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
34014 * @cfg {String} title
34018 * @cfg {String} html
34022 * @cfg {String} bgimage
34026 * @cfg {String} cls
34030 * @cfg {String} href
34034 * @cfg {String} video
34038 * @cfg {Boolean} square
34042 getAutoCreate : function()
34044 var cls = 'roo-brick';
34046 if(this.href.length){
34047 cls += ' roo-brick-link';
34050 if(this.bgimage.length){
34051 cls += ' roo-brick-image';
34054 if(!this.html.length && !this.bgimage.length){
34055 cls += ' roo-brick-center-title';
34058 if(!this.html.length && this.bgimage.length){
34059 cls += ' roo-brick-bottom-title';
34063 cls += ' ' + this.cls;
34067 tag: (this.href.length) ? 'a' : 'div',
34072 cls: 'roo-brick-paragraph',
34078 if(this.href.length){
34079 cfg.href = this.href;
34082 var cn = cfg.cn[0].cn;
34084 if(this.title.length){
34087 cls: 'roo-brick-title',
34092 if(this.html.length){
34095 cls: 'roo-brick-text',
34102 if(this.bgimage.length){
34105 cls: 'roo-brick-image-view',
34113 initEvents: function()
34115 if(this.title.length || this.html.length){
34116 this.el.on('mouseenter' ,this.enter, this);
34117 this.el.on('mouseleave', this.leave, this);
34120 Roo.EventManager.onWindowResize(this.resize, this);
34122 if(this.bgimage.length){
34123 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
34124 this.imageEl.on('load', this.onImageLoad, this);
34131 onImageLoad : function()
34136 resize : function()
34138 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
34140 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
34142 if(this.bgimage.length){
34143 var image = this.el.select('.roo-brick-image-view', true).first();
34145 image.setWidth(paragraph.getWidth());
34148 image.setHeight(paragraph.getWidth());
34151 this.el.setHeight(image.getHeight());
34152 paragraph.setHeight(image.getHeight());
34158 enter: function(e, el)
34160 e.preventDefault();
34162 if(this.bgimage.length){
34163 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
34164 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
34168 leave: function(e, el)
34170 e.preventDefault();
34172 if(this.bgimage.length){
34173 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
34174 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
34189 * @class Roo.bootstrap.NumberField
34190 * @extends Roo.bootstrap.Input
34191 * Bootstrap NumberField class
34197 * Create a new NumberField
34198 * @param {Object} config The config object
34201 Roo.bootstrap.NumberField = function(config){
34202 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
34205 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
34208 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
34210 allowDecimals : true,
34212 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
34214 decimalSeparator : ".",
34216 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
34218 decimalPrecision : 2,
34220 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
34222 allowNegative : true,
34225 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
34229 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
34231 minValue : Number.NEGATIVE_INFINITY,
34233 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
34235 maxValue : Number.MAX_VALUE,
34237 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
34239 minText : "The minimum value for this field is {0}",
34241 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
34243 maxText : "The maximum value for this field is {0}",
34245 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
34246 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
34248 nanText : "{0} is not a valid number",
34250 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
34252 thousandsDelimiter : false,
34254 * @cfg {String} valueAlign alignment of value
34256 valueAlign : "left",
34258 getAutoCreate : function()
34260 var hiddenInput = {
34264 cls: 'hidden-number-input'
34268 hiddenInput.name = this.name;
34273 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
34275 this.name = hiddenInput.name;
34277 if(cfg.cn.length > 0) {
34278 cfg.cn.push(hiddenInput);
34285 initEvents : function()
34287 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
34289 var allowed = "0123456789";
34291 if(this.allowDecimals){
34292 allowed += this.decimalSeparator;
34295 if(this.allowNegative){
34299 if(this.thousandsDelimiter) {
34303 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
34305 var keyPress = function(e){
34307 var k = e.getKey();
34309 var c = e.getCharCode();
34312 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
34313 allowed.indexOf(String.fromCharCode(c)) === -1
34319 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
34323 if(allowed.indexOf(String.fromCharCode(c)) === -1){
34328 this.el.on("keypress", keyPress, this);
34331 validateValue : function(value)
34334 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
34338 var num = this.parseValue(value);
34341 this.markInvalid(String.format(this.nanText, value));
34345 if(num < this.minValue){
34346 this.markInvalid(String.format(this.minText, this.minValue));
34350 if(num > this.maxValue){
34351 this.markInvalid(String.format(this.maxText, this.maxValue));
34358 getValue : function()
34360 var v = this.hiddenEl().getValue();
34362 return this.fixPrecision(this.parseValue(v));
34365 parseValue : function(value)
34367 if(this.thousandsDelimiter) {
34369 r = new RegExp(",", "g");
34370 value = value.replace(r, "");
34373 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34374 return isNaN(value) ? '' : value;
34377 fixPrecision : function(value)
34379 if(this.thousandsDelimiter) {
34381 r = new RegExp(",", "g");
34382 value = value.replace(r, "");
34385 var nan = isNaN(value);
34387 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34388 return nan ? '' : value;
34390 return parseFloat(value).toFixed(this.decimalPrecision);
34393 setValue : function(v)
34395 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34401 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34403 this.inputEl().dom.value = (v == '') ? '' :
34404 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34406 if(!this.allowZero && v === '0') {
34407 this.hiddenEl().dom.value = '';
34408 this.inputEl().dom.value = '';
34415 decimalPrecisionFcn : function(v)
34417 return Math.floor(v);
34420 beforeBlur : function()
34422 var v = this.parseValue(this.getRawValue());
34424 if(v || v === 0 || v === ''){
34429 hiddenEl : function()
34431 return this.el.select('input.hidden-number-input',true).first();
34443 * @class Roo.bootstrap.DocumentSlider
34444 * @extends Roo.bootstrap.Component
34445 * Bootstrap DocumentSlider class
34448 * Create a new DocumentViewer
34449 * @param {Object} config The config object
34452 Roo.bootstrap.DocumentSlider = function(config){
34453 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34460 * Fire after initEvent
34461 * @param {Roo.bootstrap.DocumentSlider} this
34466 * Fire after update
34467 * @param {Roo.bootstrap.DocumentSlider} this
34473 * @param {Roo.bootstrap.DocumentSlider} this
34479 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
34485 getAutoCreate : function()
34489 cls : 'roo-document-slider',
34493 cls : 'roo-document-slider-header',
34497 cls : 'roo-document-slider-header-title'
34503 cls : 'roo-document-slider-body',
34507 cls : 'roo-document-slider-prev',
34511 cls : 'fa fa-chevron-left'
34517 cls : 'roo-document-slider-thumb',
34521 cls : 'roo-document-slider-image'
34527 cls : 'roo-document-slider-next',
34531 cls : 'fa fa-chevron-right'
34543 initEvents : function()
34545 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34546 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34548 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34549 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34551 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34552 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34554 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34555 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34557 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34558 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34560 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34561 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34563 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34564 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34566 this.thumbEl.on('click', this.onClick, this);
34568 this.prevIndicator.on('click', this.prev, this);
34570 this.nextIndicator.on('click', this.next, this);
34574 initial : function()
34576 if(this.files.length){
34577 this.indicator = 1;
34581 this.fireEvent('initial', this);
34584 update : function()
34586 this.imageEl.attr('src', this.files[this.indicator - 1]);
34588 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34590 this.prevIndicator.show();
34592 if(this.indicator == 1){
34593 this.prevIndicator.hide();
34596 this.nextIndicator.show();
34598 if(this.indicator == this.files.length){
34599 this.nextIndicator.hide();
34602 this.thumbEl.scrollTo('top');
34604 this.fireEvent('update', this);
34607 onClick : function(e)
34609 e.preventDefault();
34611 this.fireEvent('click', this);
34616 e.preventDefault();
34618 this.indicator = Math.max(1, this.indicator - 1);
34625 e.preventDefault();
34627 this.indicator = Math.min(this.files.length, this.indicator + 1);
34641 * @class Roo.bootstrap.RadioSet
34642 * @extends Roo.bootstrap.Input
34643 * Bootstrap RadioSet class
34644 * @cfg {String} indicatorpos (left|right) default left
34645 * @cfg {Boolean} inline (true|false) inline the element (default true)
34646 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34648 * Create a new RadioSet
34649 * @param {Object} config The config object
34652 Roo.bootstrap.RadioSet = function(config){
34654 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34658 Roo.bootstrap.RadioSet.register(this);
34663 * Fires when the element is checked or unchecked.
34664 * @param {Roo.bootstrap.RadioSet} this This radio
34665 * @param {Roo.bootstrap.Radio} item The checked item
34670 * Fires when the element is click.
34671 * @param {Roo.bootstrap.RadioSet} this This radio set
34672 * @param {Roo.bootstrap.Radio} item The checked item
34673 * @param {Roo.EventObject} e The event object
34680 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
34688 indicatorpos : 'left',
34690 getAutoCreate : function()
34694 cls : 'roo-radio-set-label',
34698 html : this.fieldLabel
34702 if (Roo.bootstrap.version == 3) {
34705 if(this.indicatorpos == 'left'){
34708 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34709 tooltip : 'This field is required'
34714 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34715 tooltip : 'This field is required'
34721 cls : 'roo-radio-set-items'
34724 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34726 if (align === 'left' && this.fieldLabel.length) {
34729 cls : "roo-radio-set-right",
34735 if(this.labelWidth > 12){
34736 label.style = "width: " + this.labelWidth + 'px';
34739 if(this.labelWidth < 13 && this.labelmd == 0){
34740 this.labelmd = this.labelWidth;
34743 if(this.labellg > 0){
34744 label.cls += ' col-lg-' + this.labellg;
34745 items.cls += ' col-lg-' + (12 - this.labellg);
34748 if(this.labelmd > 0){
34749 label.cls += ' col-md-' + this.labelmd;
34750 items.cls += ' col-md-' + (12 - this.labelmd);
34753 if(this.labelsm > 0){
34754 label.cls += ' col-sm-' + this.labelsm;
34755 items.cls += ' col-sm-' + (12 - this.labelsm);
34758 if(this.labelxs > 0){
34759 label.cls += ' col-xs-' + this.labelxs;
34760 items.cls += ' col-xs-' + (12 - this.labelxs);
34766 cls : 'roo-radio-set',
34770 cls : 'roo-radio-set-input',
34773 value : this.value ? this.value : ''
34780 if(this.weight.length){
34781 cfg.cls += ' roo-radio-' + this.weight;
34785 cfg.cls += ' roo-radio-set-inline';
34789 ['xs','sm','md','lg'].map(function(size){
34790 if (settings[size]) {
34791 cfg.cls += ' col-' + size + '-' + settings[size];
34799 initEvents : function()
34801 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34802 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34804 if(!this.fieldLabel.length){
34805 this.labelEl.hide();
34808 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34809 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34811 this.indicator = this.indicatorEl();
34813 if(this.indicator){
34814 this.indicator.addClass('invisible');
34817 this.originalValue = this.getValue();
34821 inputEl: function ()
34823 return this.el.select('.roo-radio-set-input', true).first();
34826 getChildContainer : function()
34828 return this.itemsEl;
34831 register : function(item)
34833 this.radioes.push(item);
34837 validate : function()
34839 if(this.getVisibilityEl().hasClass('hidden')){
34845 Roo.each(this.radioes, function(i){
34854 if(this.allowBlank) {
34858 if(this.disabled || valid){
34863 this.markInvalid();
34868 markValid : function()
34870 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34871 this.indicatorEl().removeClass('visible');
34872 this.indicatorEl().addClass('invisible');
34876 if (Roo.bootstrap.version == 3) {
34877 this.el.removeClass([this.invalidClass, this.validClass]);
34878 this.el.addClass(this.validClass);
34880 this.el.removeClass(['is-invalid','is-valid']);
34881 this.el.addClass(['is-valid']);
34883 this.fireEvent('valid', this);
34886 markInvalid : function(msg)
34888 if(this.allowBlank || this.disabled){
34892 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34893 this.indicatorEl().removeClass('invisible');
34894 this.indicatorEl().addClass('visible');
34896 if (Roo.bootstrap.version == 3) {
34897 this.el.removeClass([this.invalidClass, this.validClass]);
34898 this.el.addClass(this.invalidClass);
34900 this.el.removeClass(['is-invalid','is-valid']);
34901 this.el.addClass(['is-invalid']);
34904 this.fireEvent('invalid', this, msg);
34908 setValue : function(v, suppressEvent)
34910 if(this.value === v){
34917 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34920 Roo.each(this.radioes, function(i){
34922 i.el.removeClass('checked');
34925 Roo.each(this.radioes, function(i){
34927 if(i.value === v || i.value.toString() === v.toString()){
34929 i.el.addClass('checked');
34931 if(suppressEvent !== true){
34932 this.fireEvent('check', this, i);
34943 clearInvalid : function(){
34945 if(!this.el || this.preventMark){
34949 this.el.removeClass([this.invalidClass]);
34951 this.fireEvent('valid', this);
34956 Roo.apply(Roo.bootstrap.RadioSet, {
34960 register : function(set)
34962 this.groups[set.name] = set;
34965 get: function(name)
34967 if (typeof(this.groups[name]) == 'undefined') {
34971 return this.groups[name] ;
34977 * Ext JS Library 1.1.1
34978 * Copyright(c) 2006-2007, Ext JS, LLC.
34980 * Originally Released Under LGPL - original licence link has changed is not relivant.
34983 * <script type="text/javascript">
34988 * @class Roo.bootstrap.SplitBar
34989 * @extends Roo.util.Observable
34990 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34994 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34995 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34996 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34997 split.minSize = 100;
34998 split.maxSize = 600;
34999 split.animate = true;
35000 split.on('moved', splitterMoved);
35003 * Create a new SplitBar
35004 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
35005 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
35006 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
35007 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
35008 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
35009 position of the SplitBar).
35011 Roo.bootstrap.SplitBar = function(cfg){
35016 // dragElement : elm
35017 // resizingElement: el,
35019 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
35020 // placement : Roo.bootstrap.SplitBar.LEFT ,
35021 // existingProxy ???
35024 this.el = Roo.get(cfg.dragElement, true);
35025 this.el.dom.unselectable = "on";
35027 this.resizingEl = Roo.get(cfg.resizingElement, true);
35031 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
35032 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
35035 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
35038 * The minimum size of the resizing element. (Defaults to 0)
35044 * The maximum size of the resizing element. (Defaults to 2000)
35047 this.maxSize = 2000;
35050 * Whether to animate the transition to the new size
35053 this.animate = false;
35056 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
35059 this.useShim = false;
35064 if(!cfg.existingProxy){
35066 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
35068 this.proxy = Roo.get(cfg.existingProxy).dom;
35071 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
35074 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
35077 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
35080 this.dragSpecs = {};
35083 * @private The adapter to use to positon and resize elements
35085 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35086 this.adapter.init(this);
35088 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35090 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
35091 this.el.addClass("roo-splitbar-h");
35094 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
35095 this.el.addClass("roo-splitbar-v");
35101 * Fires when the splitter is moved (alias for {@link #event-moved})
35102 * @param {Roo.bootstrap.SplitBar} this
35103 * @param {Number} newSize the new width or height
35108 * Fires when the splitter is moved
35109 * @param {Roo.bootstrap.SplitBar} this
35110 * @param {Number} newSize the new width or height
35114 * @event beforeresize
35115 * Fires before the splitter is dragged
35116 * @param {Roo.bootstrap.SplitBar} this
35118 "beforeresize" : true,
35120 "beforeapply" : true
35123 Roo.util.Observable.call(this);
35126 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
35127 onStartProxyDrag : function(x, y){
35128 this.fireEvent("beforeresize", this);
35130 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
35132 o.enableDisplayMode("block");
35133 // all splitbars share the same overlay
35134 Roo.bootstrap.SplitBar.prototype.overlay = o;
35136 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
35137 this.overlay.show();
35138 Roo.get(this.proxy).setDisplayed("block");
35139 var size = this.adapter.getElementSize(this);
35140 this.activeMinSize = this.getMinimumSize();;
35141 this.activeMaxSize = this.getMaximumSize();;
35142 var c1 = size - this.activeMinSize;
35143 var c2 = Math.max(this.activeMaxSize - size, 0);
35144 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35145 this.dd.resetConstraints();
35146 this.dd.setXConstraint(
35147 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
35148 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
35150 this.dd.setYConstraint(0, 0);
35152 this.dd.resetConstraints();
35153 this.dd.setXConstraint(0, 0);
35154 this.dd.setYConstraint(
35155 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
35156 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
35159 this.dragSpecs.startSize = size;
35160 this.dragSpecs.startPoint = [x, y];
35161 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
35165 * @private Called after the drag operation by the DDProxy
35167 onEndProxyDrag : function(e){
35168 Roo.get(this.proxy).setDisplayed(false);
35169 var endPoint = Roo.lib.Event.getXY(e);
35171 this.overlay.hide();
35174 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35175 newSize = this.dragSpecs.startSize +
35176 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
35177 endPoint[0] - this.dragSpecs.startPoint[0] :
35178 this.dragSpecs.startPoint[0] - endPoint[0]
35181 newSize = this.dragSpecs.startSize +
35182 (this.placement == Roo.bootstrap.SplitBar.TOP ?
35183 endPoint[1] - this.dragSpecs.startPoint[1] :
35184 this.dragSpecs.startPoint[1] - endPoint[1]
35187 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
35188 if(newSize != this.dragSpecs.startSize){
35189 if(this.fireEvent('beforeapply', this, newSize) !== false){
35190 this.adapter.setElementSize(this, newSize);
35191 this.fireEvent("moved", this, newSize);
35192 this.fireEvent("resize", this, newSize);
35198 * Get the adapter this SplitBar uses
35199 * @return The adapter object
35201 getAdapter : function(){
35202 return this.adapter;
35206 * Set the adapter this SplitBar uses
35207 * @param {Object} adapter A SplitBar adapter object
35209 setAdapter : function(adapter){
35210 this.adapter = adapter;
35211 this.adapter.init(this);
35215 * Gets the minimum size for the resizing element
35216 * @return {Number} The minimum size
35218 getMinimumSize : function(){
35219 return this.minSize;
35223 * Sets the minimum size for the resizing element
35224 * @param {Number} minSize The minimum size
35226 setMinimumSize : function(minSize){
35227 this.minSize = minSize;
35231 * Gets the maximum size for the resizing element
35232 * @return {Number} The maximum size
35234 getMaximumSize : function(){
35235 return this.maxSize;
35239 * Sets the maximum size for the resizing element
35240 * @param {Number} maxSize The maximum size
35242 setMaximumSize : function(maxSize){
35243 this.maxSize = maxSize;
35247 * Sets the initialize size for the resizing element
35248 * @param {Number} size The initial size
35250 setCurrentSize : function(size){
35251 var oldAnimate = this.animate;
35252 this.animate = false;
35253 this.adapter.setElementSize(this, size);
35254 this.animate = oldAnimate;
35258 * Destroy this splitbar.
35259 * @param {Boolean} removeEl True to remove the element
35261 destroy : function(removeEl){
35263 this.shim.remove();
35266 this.proxy.parentNode.removeChild(this.proxy);
35274 * @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.
35276 Roo.bootstrap.SplitBar.createProxy = function(dir){
35277 var proxy = new Roo.Element(document.createElement("div"));
35278 proxy.unselectable();
35279 var cls = 'roo-splitbar-proxy';
35280 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
35281 document.body.appendChild(proxy.dom);
35286 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
35287 * Default Adapter. It assumes the splitter and resizing element are not positioned
35288 * elements and only gets/sets the width of the element. Generally used for table based layouts.
35290 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
35293 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
35294 // do nothing for now
35295 init : function(s){
35299 * Called before drag operations to get the current size of the resizing element.
35300 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35302 getElementSize : function(s){
35303 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35304 return s.resizingEl.getWidth();
35306 return s.resizingEl.getHeight();
35311 * Called after drag operations to set the size of the resizing element.
35312 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35313 * @param {Number} newSize The new size to set
35314 * @param {Function} onComplete A function to be invoked when resizing is complete
35316 setElementSize : function(s, newSize, onComplete){
35317 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35319 s.resizingEl.setWidth(newSize);
35321 onComplete(s, newSize);
35324 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
35329 s.resizingEl.setHeight(newSize);
35331 onComplete(s, newSize);
35334 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
35341 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
35342 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
35343 * Adapter that moves the splitter element to align with the resized sizing element.
35344 * Used with an absolute positioned SplitBar.
35345 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
35346 * document.body, make sure you assign an id to the body element.
35348 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
35349 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35350 this.container = Roo.get(container);
35353 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
35354 init : function(s){
35355 this.basic.init(s);
35358 getElementSize : function(s){
35359 return this.basic.getElementSize(s);
35362 setElementSize : function(s, newSize, onComplete){
35363 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
35366 moveSplitter : function(s){
35367 var yes = Roo.bootstrap.SplitBar;
35368 switch(s.placement){
35370 s.el.setX(s.resizingEl.getRight());
35373 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35376 s.el.setY(s.resizingEl.getBottom());
35379 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35386 * Orientation constant - Create a vertical SplitBar
35390 Roo.bootstrap.SplitBar.VERTICAL = 1;
35393 * Orientation constant - Create a horizontal SplitBar
35397 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35400 * Placement constant - The resizing element is to the left of the splitter element
35404 Roo.bootstrap.SplitBar.LEFT = 1;
35407 * Placement constant - The resizing element is to the right of the splitter element
35411 Roo.bootstrap.SplitBar.RIGHT = 2;
35414 * Placement constant - The resizing element is positioned above the splitter element
35418 Roo.bootstrap.SplitBar.TOP = 3;
35421 * Placement constant - The resizing element is positioned under splitter element
35425 Roo.bootstrap.SplitBar.BOTTOM = 4;
35426 Roo.namespace("Roo.bootstrap.layout");/*
35428 * Ext JS Library 1.1.1
35429 * Copyright(c) 2006-2007, Ext JS, LLC.
35431 * Originally Released Under LGPL - original licence link has changed is not relivant.
35434 * <script type="text/javascript">
35438 * @class Roo.bootstrap.layout.Manager
35439 * @extends Roo.bootstrap.Component
35440 * Base class for layout managers.
35442 Roo.bootstrap.layout.Manager = function(config)
35444 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35450 /** false to disable window resize monitoring @type Boolean */
35451 this.monitorWindowResize = true;
35456 * Fires when a layout is performed.
35457 * @param {Roo.LayoutManager} this
35461 * @event regionresized
35462 * Fires when the user resizes a region.
35463 * @param {Roo.LayoutRegion} region The resized region
35464 * @param {Number} newSize The new size (width for east/west, height for north/south)
35466 "regionresized" : true,
35468 * @event regioncollapsed
35469 * Fires when a region is collapsed.
35470 * @param {Roo.LayoutRegion} region The collapsed region
35472 "regioncollapsed" : true,
35474 * @event regionexpanded
35475 * Fires when a region is expanded.
35476 * @param {Roo.LayoutRegion} region The expanded region
35478 "regionexpanded" : true
35480 this.updating = false;
35483 this.el = Roo.get(config.el);
35489 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35494 monitorWindowResize : true,
35500 onRender : function(ct, position)
35503 this.el = Roo.get(ct);
35506 //this.fireEvent('render',this);
35510 initEvents: function()
35514 // ie scrollbar fix
35515 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35516 document.body.scroll = "no";
35517 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35518 this.el.position('relative');
35520 this.id = this.el.id;
35521 this.el.addClass("roo-layout-container");
35522 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35523 if(this.el.dom != document.body ) {
35524 this.el.on('resize', this.layout,this);
35525 this.el.on('show', this.layout,this);
35531 * Returns true if this layout is currently being updated
35532 * @return {Boolean}
35534 isUpdating : function(){
35535 return this.updating;
35539 * Suspend the LayoutManager from doing auto-layouts while
35540 * making multiple add or remove calls
35542 beginUpdate : function(){
35543 this.updating = true;
35547 * Restore auto-layouts and optionally disable the manager from performing a layout
35548 * @param {Boolean} noLayout true to disable a layout update
35550 endUpdate : function(noLayout){
35551 this.updating = false;
35557 layout: function(){
35561 onRegionResized : function(region, newSize){
35562 this.fireEvent("regionresized", region, newSize);
35566 onRegionCollapsed : function(region){
35567 this.fireEvent("regioncollapsed", region);
35570 onRegionExpanded : function(region){
35571 this.fireEvent("regionexpanded", region);
35575 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35576 * performs box-model adjustments.
35577 * @return {Object} The size as an object {width: (the width), height: (the height)}
35579 getViewSize : function()
35582 if(this.el.dom != document.body){
35583 size = this.el.getSize();
35585 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35587 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35588 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35593 * Returns the Element this layout is bound to.
35594 * @return {Roo.Element}
35596 getEl : function(){
35601 * Returns the specified region.
35602 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35603 * @return {Roo.LayoutRegion}
35605 getRegion : function(target){
35606 return this.regions[target.toLowerCase()];
35609 onWindowResize : function(){
35610 if(this.monitorWindowResize){
35617 * Ext JS Library 1.1.1
35618 * Copyright(c) 2006-2007, Ext JS, LLC.
35620 * Originally Released Under LGPL - original licence link has changed is not relivant.
35623 * <script type="text/javascript">
35626 * @class Roo.bootstrap.layout.Border
35627 * @extends Roo.bootstrap.layout.Manager
35628 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35629 * please see: examples/bootstrap/nested.html<br><br>
35631 <b>The container the layout is rendered into can be either the body element or any other element.
35632 If it is not the body element, the container needs to either be an absolute positioned element,
35633 or you will need to add "position:relative" to the css of the container. You will also need to specify
35634 the container size if it is not the body element.</b>
35637 * Create a new Border
35638 * @param {Object} config Configuration options
35640 Roo.bootstrap.layout.Border = function(config){
35641 config = config || {};
35642 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35646 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35647 if(config[region]){
35648 config[region].region = region;
35649 this.addRegion(config[region]);
35655 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
35657 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35659 parent : false, // this might point to a 'nest' or a ???
35662 * Creates and adds a new region if it doesn't already exist.
35663 * @param {String} target The target region key (north, south, east, west or center).
35664 * @param {Object} config The regions config object
35665 * @return {BorderLayoutRegion} The new region
35667 addRegion : function(config)
35669 if(!this.regions[config.region]){
35670 var r = this.factory(config);
35671 this.bindRegion(r);
35673 return this.regions[config.region];
35677 bindRegion : function(r){
35678 this.regions[r.config.region] = r;
35680 r.on("visibilitychange", this.layout, this);
35681 r.on("paneladded", this.layout, this);
35682 r.on("panelremoved", this.layout, this);
35683 r.on("invalidated", this.layout, this);
35684 r.on("resized", this.onRegionResized, this);
35685 r.on("collapsed", this.onRegionCollapsed, this);
35686 r.on("expanded", this.onRegionExpanded, this);
35690 * Performs a layout update.
35692 layout : function()
35694 if(this.updating) {
35698 // render all the rebions if they have not been done alreayd?
35699 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35700 if(this.regions[region] && !this.regions[region].bodyEl){
35701 this.regions[region].onRender(this.el)
35705 var size = this.getViewSize();
35706 var w = size.width;
35707 var h = size.height;
35712 //var x = 0, y = 0;
35714 var rs = this.regions;
35715 var north = rs["north"];
35716 var south = rs["south"];
35717 var west = rs["west"];
35718 var east = rs["east"];
35719 var center = rs["center"];
35720 //if(this.hideOnLayout){ // not supported anymore
35721 //c.el.setStyle("display", "none");
35723 if(north && north.isVisible()){
35724 var b = north.getBox();
35725 var m = north.getMargins();
35726 b.width = w - (m.left+m.right);
35729 centerY = b.height + b.y + m.bottom;
35730 centerH -= centerY;
35731 north.updateBox(this.safeBox(b));
35733 if(south && south.isVisible()){
35734 var b = south.getBox();
35735 var m = south.getMargins();
35736 b.width = w - (m.left+m.right);
35738 var totalHeight = (b.height + m.top + m.bottom);
35739 b.y = h - totalHeight + m.top;
35740 centerH -= totalHeight;
35741 south.updateBox(this.safeBox(b));
35743 if(west && west.isVisible()){
35744 var b = west.getBox();
35745 var m = west.getMargins();
35746 b.height = centerH - (m.top+m.bottom);
35748 b.y = centerY + m.top;
35749 var totalWidth = (b.width + m.left + m.right);
35750 centerX += totalWidth;
35751 centerW -= totalWidth;
35752 west.updateBox(this.safeBox(b));
35754 if(east && east.isVisible()){
35755 var b = east.getBox();
35756 var m = east.getMargins();
35757 b.height = centerH - (m.top+m.bottom);
35758 var totalWidth = (b.width + m.left + m.right);
35759 b.x = w - totalWidth + m.left;
35760 b.y = centerY + m.top;
35761 centerW -= totalWidth;
35762 east.updateBox(this.safeBox(b));
35765 var m = center.getMargins();
35767 x: centerX + m.left,
35768 y: centerY + m.top,
35769 width: centerW - (m.left+m.right),
35770 height: centerH - (m.top+m.bottom)
35772 //if(this.hideOnLayout){
35773 //center.el.setStyle("display", "block");
35775 center.updateBox(this.safeBox(centerBox));
35778 this.fireEvent("layout", this);
35782 safeBox : function(box){
35783 box.width = Math.max(0, box.width);
35784 box.height = Math.max(0, box.height);
35789 * Adds a ContentPanel (or subclass) to this layout.
35790 * @param {String} target The target region key (north, south, east, west or center).
35791 * @param {Roo.ContentPanel} panel The panel to add
35792 * @return {Roo.ContentPanel} The added panel
35794 add : function(target, panel){
35796 target = target.toLowerCase();
35797 return this.regions[target].add(panel);
35801 * Remove a ContentPanel (or subclass) to this layout.
35802 * @param {String} target The target region key (north, south, east, west or center).
35803 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35804 * @return {Roo.ContentPanel} The removed panel
35806 remove : function(target, panel){
35807 target = target.toLowerCase();
35808 return this.regions[target].remove(panel);
35812 * Searches all regions for a panel with the specified id
35813 * @param {String} panelId
35814 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35816 findPanel : function(panelId){
35817 var rs = this.regions;
35818 for(var target in rs){
35819 if(typeof rs[target] != "function"){
35820 var p = rs[target].getPanel(panelId);
35830 * Searches all regions for a panel with the specified id and activates (shows) it.
35831 * @param {String/ContentPanel} panelId The panels id or the panel itself
35832 * @return {Roo.ContentPanel} The shown panel or null
35834 showPanel : function(panelId) {
35835 var rs = this.regions;
35836 for(var target in rs){
35837 var r = rs[target];
35838 if(typeof r != "function"){
35839 if(r.hasPanel(panelId)){
35840 return r.showPanel(panelId);
35848 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35849 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35852 restoreState : function(provider){
35854 provider = Roo.state.Manager;
35856 var sm = new Roo.LayoutStateManager();
35857 sm.init(this, provider);
35863 * Adds a xtype elements to the layout.
35867 xtype : 'ContentPanel',
35874 xtype : 'NestedLayoutPanel',
35880 items : [ ... list of content panels or nested layout panels.. ]
35884 * @param {Object} cfg Xtype definition of item to add.
35886 addxtype : function(cfg)
35888 // basically accepts a pannel...
35889 // can accept a layout region..!?!?
35890 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35893 // theory? children can only be panels??
35895 //if (!cfg.xtype.match(/Panel$/)) {
35900 if (typeof(cfg.region) == 'undefined') {
35901 Roo.log("Failed to add Panel, region was not set");
35905 var region = cfg.region;
35911 xitems = cfg.items;
35916 if ( region == 'center') {
35917 Roo.log("Center: " + cfg.title);
35923 case 'Content': // ContentPanel (el, cfg)
35924 case 'Scroll': // ContentPanel (el, cfg)
35926 cfg.autoCreate = cfg.autoCreate || true;
35927 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35929 // var el = this.el.createChild();
35930 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35933 this.add(region, ret);
35937 case 'TreePanel': // our new panel!
35938 cfg.el = this.el.createChild();
35939 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35940 this.add(region, ret);
35945 // create a new Layout (which is a Border Layout...
35947 var clayout = cfg.layout;
35948 clayout.el = this.el.createChild();
35949 clayout.items = clayout.items || [];
35953 // replace this exitems with the clayout ones..
35954 xitems = clayout.items;
35956 // force background off if it's in center...
35957 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35958 cfg.background = false;
35960 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35963 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35964 //console.log('adding nested layout panel ' + cfg.toSource());
35965 this.add(region, ret);
35966 nb = {}; /// find first...
35971 // needs grid and region
35973 //var el = this.getRegion(region).el.createChild();
35975 *var el = this.el.createChild();
35976 // create the grid first...
35977 cfg.grid.container = el;
35978 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35981 if (region == 'center' && this.active ) {
35982 cfg.background = false;
35985 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35987 this.add(region, ret);
35989 if (cfg.background) {
35990 // render grid on panel activation (if panel background)
35991 ret.on('activate', function(gp) {
35992 if (!gp.grid.rendered) {
35993 // gp.grid.render(el);
35997 // cfg.grid.render(el);
36003 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
36004 // it was the old xcomponent building that caused this before.
36005 // espeically if border is the top element in the tree.
36015 if (typeof(Roo[cfg.xtype]) != 'undefined') {
36017 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
36018 this.add(region, ret);
36022 throw "Can not add '" + cfg.xtype + "' to Border";
36028 this.beginUpdate();
36032 Roo.each(xitems, function(i) {
36033 region = nb && i.region ? i.region : false;
36035 var add = ret.addxtype(i);
36038 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
36039 if (!i.background) {
36040 abn[region] = nb[region] ;
36047 // make the last non-background panel active..
36048 //if (nb) { Roo.log(abn); }
36051 for(var r in abn) {
36052 region = this.getRegion(r);
36054 // tried using nb[r], but it does not work..
36056 region.showPanel(abn[r]);
36067 factory : function(cfg)
36070 var validRegions = Roo.bootstrap.layout.Border.regions;
36072 var target = cfg.region;
36075 var r = Roo.bootstrap.layout;
36079 return new r.North(cfg);
36081 return new r.South(cfg);
36083 return new r.East(cfg);
36085 return new r.West(cfg);
36087 return new r.Center(cfg);
36089 throw 'Layout region "'+target+'" not supported.';
36096 * Ext JS Library 1.1.1
36097 * Copyright(c) 2006-2007, Ext JS, LLC.
36099 * Originally Released Under LGPL - original licence link has changed is not relivant.
36102 * <script type="text/javascript">
36106 * @class Roo.bootstrap.layout.Basic
36107 * @extends Roo.util.Observable
36108 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
36109 * and does not have a titlebar, tabs or any other features. All it does is size and position
36110 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
36111 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
36112 * @cfg {string} region the region that it inhabits..
36113 * @cfg {bool} skipConfig skip config?
36117 Roo.bootstrap.layout.Basic = function(config){
36119 this.mgr = config.mgr;
36121 this.position = config.region;
36123 var skipConfig = config.skipConfig;
36127 * @scope Roo.BasicLayoutRegion
36131 * @event beforeremove
36132 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
36133 * @param {Roo.LayoutRegion} this
36134 * @param {Roo.ContentPanel} panel The panel
36135 * @param {Object} e The cancel event object
36137 "beforeremove" : true,
36139 * @event invalidated
36140 * Fires when the layout for this region is changed.
36141 * @param {Roo.LayoutRegion} this
36143 "invalidated" : true,
36145 * @event visibilitychange
36146 * Fires when this region is shown or hidden
36147 * @param {Roo.LayoutRegion} this
36148 * @param {Boolean} visibility true or false
36150 "visibilitychange" : true,
36152 * @event paneladded
36153 * Fires when a panel is added.
36154 * @param {Roo.LayoutRegion} this
36155 * @param {Roo.ContentPanel} panel The panel
36157 "paneladded" : true,
36159 * @event panelremoved
36160 * Fires when a panel is removed.
36161 * @param {Roo.LayoutRegion} this
36162 * @param {Roo.ContentPanel} panel The panel
36164 "panelremoved" : true,
36166 * @event beforecollapse
36167 * Fires when this region before collapse.
36168 * @param {Roo.LayoutRegion} this
36170 "beforecollapse" : true,
36173 * Fires when this region is collapsed.
36174 * @param {Roo.LayoutRegion} this
36176 "collapsed" : true,
36179 * Fires when this region is expanded.
36180 * @param {Roo.LayoutRegion} this
36185 * Fires when this region is slid into view.
36186 * @param {Roo.LayoutRegion} this
36188 "slideshow" : true,
36191 * Fires when this region slides out of view.
36192 * @param {Roo.LayoutRegion} this
36194 "slidehide" : true,
36196 * @event panelactivated
36197 * Fires when a panel is activated.
36198 * @param {Roo.LayoutRegion} this
36199 * @param {Roo.ContentPanel} panel The activated panel
36201 "panelactivated" : true,
36204 * Fires when the user resizes this region.
36205 * @param {Roo.LayoutRegion} this
36206 * @param {Number} newSize The new size (width for east/west, height for north/south)
36210 /** A collection of panels in this region. @type Roo.util.MixedCollection */
36211 this.panels = new Roo.util.MixedCollection();
36212 this.panels.getKey = this.getPanelId.createDelegate(this);
36214 this.activePanel = null;
36215 // ensure listeners are added...
36217 if (config.listeners || config.events) {
36218 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
36219 listeners : config.listeners || {},
36220 events : config.events || {}
36224 if(skipConfig !== true){
36225 this.applyConfig(config);
36229 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
36231 getPanelId : function(p){
36235 applyConfig : function(config){
36236 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36237 this.config = config;
36242 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
36243 * the width, for horizontal (north, south) the height.
36244 * @param {Number} newSize The new width or height
36246 resizeTo : function(newSize){
36247 var el = this.el ? this.el :
36248 (this.activePanel ? this.activePanel.getEl() : null);
36250 switch(this.position){
36253 el.setWidth(newSize);
36254 this.fireEvent("resized", this, newSize);
36258 el.setHeight(newSize);
36259 this.fireEvent("resized", this, newSize);
36265 getBox : function(){
36266 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
36269 getMargins : function(){
36270 return this.margins;
36273 updateBox : function(box){
36275 var el = this.activePanel.getEl();
36276 el.dom.style.left = box.x + "px";
36277 el.dom.style.top = box.y + "px";
36278 this.activePanel.setSize(box.width, box.height);
36282 * Returns the container element for this region.
36283 * @return {Roo.Element}
36285 getEl : function(){
36286 return this.activePanel;
36290 * Returns true if this region is currently visible.
36291 * @return {Boolean}
36293 isVisible : function(){
36294 return this.activePanel ? true : false;
36297 setActivePanel : function(panel){
36298 panel = this.getPanel(panel);
36299 if(this.activePanel && this.activePanel != panel){
36300 this.activePanel.setActiveState(false);
36301 this.activePanel.getEl().setLeftTop(-10000,-10000);
36303 this.activePanel = panel;
36304 panel.setActiveState(true);
36306 panel.setSize(this.box.width, this.box.height);
36308 this.fireEvent("panelactivated", this, panel);
36309 this.fireEvent("invalidated");
36313 * Show the specified panel.
36314 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
36315 * @return {Roo.ContentPanel} The shown panel or null
36317 showPanel : function(panel){
36318 panel = this.getPanel(panel);
36320 this.setActivePanel(panel);
36326 * Get the active panel for this region.
36327 * @return {Roo.ContentPanel} The active panel or null
36329 getActivePanel : function(){
36330 return this.activePanel;
36334 * Add the passed ContentPanel(s)
36335 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36336 * @return {Roo.ContentPanel} The panel added (if only one was added)
36338 add : function(panel){
36339 if(arguments.length > 1){
36340 for(var i = 0, len = arguments.length; i < len; i++) {
36341 this.add(arguments[i]);
36345 if(this.hasPanel(panel)){
36346 this.showPanel(panel);
36349 var el = panel.getEl();
36350 if(el.dom.parentNode != this.mgr.el.dom){
36351 this.mgr.el.dom.appendChild(el.dom);
36353 if(panel.setRegion){
36354 panel.setRegion(this);
36356 this.panels.add(panel);
36357 el.setStyle("position", "absolute");
36358 if(!panel.background){
36359 this.setActivePanel(panel);
36360 if(this.config.initialSize && this.panels.getCount()==1){
36361 this.resizeTo(this.config.initialSize);
36364 this.fireEvent("paneladded", this, panel);
36369 * Returns true if the panel is in this region.
36370 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36371 * @return {Boolean}
36373 hasPanel : function(panel){
36374 if(typeof panel == "object"){ // must be panel obj
36375 panel = panel.getId();
36377 return this.getPanel(panel) ? true : false;
36381 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36382 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36383 * @param {Boolean} preservePanel Overrides the config preservePanel option
36384 * @return {Roo.ContentPanel} The panel that was removed
36386 remove : function(panel, preservePanel){
36387 panel = this.getPanel(panel);
36392 this.fireEvent("beforeremove", this, panel, e);
36393 if(e.cancel === true){
36396 var panelId = panel.getId();
36397 this.panels.removeKey(panelId);
36402 * Returns the panel specified or null if it's not in this region.
36403 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36404 * @return {Roo.ContentPanel}
36406 getPanel : function(id){
36407 if(typeof id == "object"){ // must be panel obj
36410 return this.panels.get(id);
36414 * Returns this regions position (north/south/east/west/center).
36417 getPosition: function(){
36418 return this.position;
36422 * Ext JS Library 1.1.1
36423 * Copyright(c) 2006-2007, Ext JS, LLC.
36425 * Originally Released Under LGPL - original licence link has changed is not relivant.
36428 * <script type="text/javascript">
36432 * @class Roo.bootstrap.layout.Region
36433 * @extends Roo.bootstrap.layout.Basic
36434 * This class represents a region in a layout manager.
36436 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36437 * @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})
36438 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
36439 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
36440 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
36441 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
36442 * @cfg {String} title The title for the region (overrides panel titles)
36443 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
36444 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36445 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
36446 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36447 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
36448 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36449 * the space available, similar to FireFox 1.5 tabs (defaults to false)
36450 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
36451 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
36452 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
36454 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
36455 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
36456 * @cfg {Boolean} disableTabTips True to disable tab tooltips
36457 * @cfg {Number} width For East/West panels
36458 * @cfg {Number} height For North/South panels
36459 * @cfg {Boolean} split To show the splitter
36460 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
36462 * @cfg {string} cls Extra CSS classes to add to region
36464 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
36465 * @cfg {string} region the region that it inhabits..
36468 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
36469 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
36471 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
36472 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
36473 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
36475 Roo.bootstrap.layout.Region = function(config)
36477 this.applyConfig(config);
36479 var mgr = config.mgr;
36480 var pos = config.region;
36481 config.skipConfig = true;
36482 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36485 this.onRender(mgr.el);
36488 this.visible = true;
36489 this.collapsed = false;
36490 this.unrendered_panels = [];
36493 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36495 position: '', // set by wrapper (eg. north/south etc..)
36496 unrendered_panels : null, // unrendered panels.
36498 tabPosition : false,
36500 mgr: false, // points to 'Border'
36503 createBody : function(){
36504 /** This region's body element
36505 * @type Roo.Element */
36506 this.bodyEl = this.el.createChild({
36508 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36512 onRender: function(ctr, pos)
36514 var dh = Roo.DomHelper;
36515 /** This region's container element
36516 * @type Roo.Element */
36517 this.el = dh.append(ctr.dom, {
36519 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36521 /** This region's title element
36522 * @type Roo.Element */
36524 this.titleEl = dh.append(this.el.dom, {
36526 unselectable: "on",
36527 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36529 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
36530 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36534 this.titleEl.enableDisplayMode();
36535 /** This region's title text element
36536 * @type HTMLElement */
36537 this.titleTextEl = this.titleEl.dom.firstChild;
36538 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36540 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36541 this.closeBtn.enableDisplayMode();
36542 this.closeBtn.on("click", this.closeClicked, this);
36543 this.closeBtn.hide();
36545 this.createBody(this.config);
36546 if(this.config.hideWhenEmpty){
36548 this.on("paneladded", this.validateVisibility, this);
36549 this.on("panelremoved", this.validateVisibility, this);
36551 if(this.autoScroll){
36552 this.bodyEl.setStyle("overflow", "auto");
36554 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36556 //if(c.titlebar !== false){
36557 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36558 this.titleEl.hide();
36560 this.titleEl.show();
36561 if(this.config.title){
36562 this.titleTextEl.innerHTML = this.config.title;
36566 if(this.config.collapsed){
36567 this.collapse(true);
36569 if(this.config.hidden){
36573 if (this.unrendered_panels && this.unrendered_panels.length) {
36574 for (var i =0;i< this.unrendered_panels.length; i++) {
36575 this.add(this.unrendered_panels[i]);
36577 this.unrendered_panels = null;
36583 applyConfig : function(c)
36586 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36587 var dh = Roo.DomHelper;
36588 if(c.titlebar !== false){
36589 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36590 this.collapseBtn.on("click", this.collapse, this);
36591 this.collapseBtn.enableDisplayMode();
36593 if(c.showPin === true || this.showPin){
36594 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36595 this.stickBtn.enableDisplayMode();
36596 this.stickBtn.on("click", this.expand, this);
36597 this.stickBtn.hide();
36602 /** This region's collapsed element
36603 * @type Roo.Element */
36606 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36607 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36610 if(c.floatable !== false){
36611 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36612 this.collapsedEl.on("click", this.collapseClick, this);
36615 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36616 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36617 id: "message", unselectable: "on", style:{"float":"left"}});
36618 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36620 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36621 this.expandBtn.on("click", this.expand, this);
36625 if(this.collapseBtn){
36626 this.collapseBtn.setVisible(c.collapsible == true);
36629 this.cmargins = c.cmargins || this.cmargins ||
36630 (this.position == "west" || this.position == "east" ?
36631 {top: 0, left: 2, right:2, bottom: 0} :
36632 {top: 2, left: 0, right:0, bottom: 2});
36634 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36637 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36639 this.autoScroll = c.autoScroll || false;
36644 this.duration = c.duration || .30;
36645 this.slideDuration = c.slideDuration || .45;
36650 * Returns true if this region is currently visible.
36651 * @return {Boolean}
36653 isVisible : function(){
36654 return this.visible;
36658 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36659 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
36661 //setCollapsedTitle : function(title){
36662 // title = title || " ";
36663 // if(this.collapsedTitleTextEl){
36664 // this.collapsedTitleTextEl.innerHTML = title;
36668 getBox : function(){
36670 // if(!this.collapsed){
36671 b = this.el.getBox(false, true);
36673 // b = this.collapsedEl.getBox(false, true);
36678 getMargins : function(){
36679 return this.margins;
36680 //return this.collapsed ? this.cmargins : this.margins;
36683 highlight : function(){
36684 this.el.addClass("x-layout-panel-dragover");
36687 unhighlight : function(){
36688 this.el.removeClass("x-layout-panel-dragover");
36691 updateBox : function(box)
36693 if (!this.bodyEl) {
36694 return; // not rendered yet..
36698 if(!this.collapsed){
36699 this.el.dom.style.left = box.x + "px";
36700 this.el.dom.style.top = box.y + "px";
36701 this.updateBody(box.width, box.height);
36703 this.collapsedEl.dom.style.left = box.x + "px";
36704 this.collapsedEl.dom.style.top = box.y + "px";
36705 this.collapsedEl.setSize(box.width, box.height);
36708 this.tabs.autoSizeTabs();
36712 updateBody : function(w, h)
36715 this.el.setWidth(w);
36716 w -= this.el.getBorderWidth("rl");
36717 if(this.config.adjustments){
36718 w += this.config.adjustments[0];
36721 if(h !== null && h > 0){
36722 this.el.setHeight(h);
36723 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36724 h -= this.el.getBorderWidth("tb");
36725 if(this.config.adjustments){
36726 h += this.config.adjustments[1];
36728 this.bodyEl.setHeight(h);
36730 h = this.tabs.syncHeight(h);
36733 if(this.panelSize){
36734 w = w !== null ? w : this.panelSize.width;
36735 h = h !== null ? h : this.panelSize.height;
36737 if(this.activePanel){
36738 var el = this.activePanel.getEl();
36739 w = w !== null ? w : el.getWidth();
36740 h = h !== null ? h : el.getHeight();
36741 this.panelSize = {width: w, height: h};
36742 this.activePanel.setSize(w, h);
36744 if(Roo.isIE && this.tabs){
36745 this.tabs.el.repaint();
36750 * Returns the container element for this region.
36751 * @return {Roo.Element}
36753 getEl : function(){
36758 * Hides this region.
36761 //if(!this.collapsed){
36762 this.el.dom.style.left = "-2000px";
36765 // this.collapsedEl.dom.style.left = "-2000px";
36766 // this.collapsedEl.hide();
36768 this.visible = false;
36769 this.fireEvent("visibilitychange", this, false);
36773 * Shows this region if it was previously hidden.
36776 //if(!this.collapsed){
36779 // this.collapsedEl.show();
36781 this.visible = true;
36782 this.fireEvent("visibilitychange", this, true);
36785 closeClicked : function(){
36786 if(this.activePanel){
36787 this.remove(this.activePanel);
36791 collapseClick : function(e){
36793 e.stopPropagation();
36796 e.stopPropagation();
36802 * Collapses this region.
36803 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36806 collapse : function(skipAnim, skipCheck = false){
36807 if(this.collapsed) {
36811 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36813 this.collapsed = true;
36815 this.split.el.hide();
36817 if(this.config.animate && skipAnim !== true){
36818 this.fireEvent("invalidated", this);
36819 this.animateCollapse();
36821 this.el.setLocation(-20000,-20000);
36823 this.collapsedEl.show();
36824 this.fireEvent("collapsed", this);
36825 this.fireEvent("invalidated", this);
36831 animateCollapse : function(){
36836 * Expands this region if it was previously collapsed.
36837 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36838 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36841 expand : function(e, skipAnim){
36843 e.stopPropagation();
36845 if(!this.collapsed || this.el.hasActiveFx()) {
36849 this.afterSlideIn();
36852 this.collapsed = false;
36853 if(this.config.animate && skipAnim !== true){
36854 this.animateExpand();
36858 this.split.el.show();
36860 this.collapsedEl.setLocation(-2000,-2000);
36861 this.collapsedEl.hide();
36862 this.fireEvent("invalidated", this);
36863 this.fireEvent("expanded", this);
36867 animateExpand : function(){
36871 initTabs : function()
36873 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36875 var ts = new Roo.bootstrap.panel.Tabs({
36876 el: this.bodyEl.dom,
36878 tabPosition: this.tabPosition ? this.tabPosition : 'top',
36879 disableTooltips: this.config.disableTabTips,
36880 toolbar : this.config.toolbar
36883 if(this.config.hideTabs){
36884 ts.stripWrap.setDisplayed(false);
36887 ts.resizeTabs = this.config.resizeTabs === true;
36888 ts.minTabWidth = this.config.minTabWidth || 40;
36889 ts.maxTabWidth = this.config.maxTabWidth || 250;
36890 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36891 ts.monitorResize = false;
36892 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36893 ts.bodyEl.addClass('roo-layout-tabs-body');
36894 this.panels.each(this.initPanelAsTab, this);
36897 initPanelAsTab : function(panel){
36898 var ti = this.tabs.addTab(
36902 this.config.closeOnTab && panel.isClosable(),
36905 if(panel.tabTip !== undefined){
36906 ti.setTooltip(panel.tabTip);
36908 ti.on("activate", function(){
36909 this.setActivePanel(panel);
36912 if(this.config.closeOnTab){
36913 ti.on("beforeclose", function(t, e){
36915 this.remove(panel);
36919 panel.tabItem = ti;
36924 updatePanelTitle : function(panel, title)
36926 if(this.activePanel == panel){
36927 this.updateTitle(title);
36930 var ti = this.tabs.getTab(panel.getEl().id);
36932 if(panel.tabTip !== undefined){
36933 ti.setTooltip(panel.tabTip);
36938 updateTitle : function(title){
36939 if(this.titleTextEl && !this.config.title){
36940 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36944 setActivePanel : function(panel)
36946 panel = this.getPanel(panel);
36947 if(this.activePanel && this.activePanel != panel){
36948 if(this.activePanel.setActiveState(false) === false){
36952 this.activePanel = panel;
36953 panel.setActiveState(true);
36954 if(this.panelSize){
36955 panel.setSize(this.panelSize.width, this.panelSize.height);
36958 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36960 this.updateTitle(panel.getTitle());
36962 this.fireEvent("invalidated", this);
36964 this.fireEvent("panelactivated", this, panel);
36968 * Shows the specified panel.
36969 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36970 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36972 showPanel : function(panel)
36974 panel = this.getPanel(panel);
36977 var tab = this.tabs.getTab(panel.getEl().id);
36978 if(tab.isHidden()){
36979 this.tabs.unhideTab(tab.id);
36983 this.setActivePanel(panel);
36990 * Get the active panel for this region.
36991 * @return {Roo.ContentPanel} The active panel or null
36993 getActivePanel : function(){
36994 return this.activePanel;
36997 validateVisibility : function(){
36998 if(this.panels.getCount() < 1){
36999 this.updateTitle(" ");
37000 this.closeBtn.hide();
37003 if(!this.isVisible()){
37010 * Adds the passed ContentPanel(s) to this region.
37011 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37012 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
37014 add : function(panel)
37016 if(arguments.length > 1){
37017 for(var i = 0, len = arguments.length; i < len; i++) {
37018 this.add(arguments[i]);
37023 // if we have not been rendered yet, then we can not really do much of this..
37024 if (!this.bodyEl) {
37025 this.unrendered_panels.push(panel);
37032 if(this.hasPanel(panel)){
37033 this.showPanel(panel);
37036 panel.setRegion(this);
37037 this.panels.add(panel);
37038 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
37039 // sinle panel - no tab...?? would it not be better to render it with the tabs,
37040 // and hide them... ???
37041 this.bodyEl.dom.appendChild(panel.getEl().dom);
37042 if(panel.background !== true){
37043 this.setActivePanel(panel);
37045 this.fireEvent("paneladded", this, panel);
37052 this.initPanelAsTab(panel);
37056 if(panel.background !== true){
37057 this.tabs.activate(panel.getEl().id);
37059 this.fireEvent("paneladded", this, panel);
37064 * Hides the tab for the specified panel.
37065 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37067 hidePanel : function(panel){
37068 if(this.tabs && (panel = this.getPanel(panel))){
37069 this.tabs.hideTab(panel.getEl().id);
37074 * Unhides the tab for a previously hidden panel.
37075 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37077 unhidePanel : function(panel){
37078 if(this.tabs && (panel = this.getPanel(panel))){
37079 this.tabs.unhideTab(panel.getEl().id);
37083 clearPanels : function(){
37084 while(this.panels.getCount() > 0){
37085 this.remove(this.panels.first());
37090 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37091 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37092 * @param {Boolean} preservePanel Overrides the config preservePanel option
37093 * @return {Roo.ContentPanel} The panel that was removed
37095 remove : function(panel, preservePanel)
37097 panel = this.getPanel(panel);
37102 this.fireEvent("beforeremove", this, panel, e);
37103 if(e.cancel === true){
37106 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
37107 var panelId = panel.getId();
37108 this.panels.removeKey(panelId);
37110 document.body.appendChild(panel.getEl().dom);
37113 this.tabs.removeTab(panel.getEl().id);
37114 }else if (!preservePanel){
37115 this.bodyEl.dom.removeChild(panel.getEl().dom);
37117 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
37118 var p = this.panels.first();
37119 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
37120 tempEl.appendChild(p.getEl().dom);
37121 this.bodyEl.update("");
37122 this.bodyEl.dom.appendChild(p.getEl().dom);
37124 this.updateTitle(p.getTitle());
37126 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
37127 this.setActivePanel(p);
37129 panel.setRegion(null);
37130 if(this.activePanel == panel){
37131 this.activePanel = null;
37133 if(this.config.autoDestroy !== false && preservePanel !== true){
37134 try{panel.destroy();}catch(e){}
37136 this.fireEvent("panelremoved", this, panel);
37141 * Returns the TabPanel component used by this region
37142 * @return {Roo.TabPanel}
37144 getTabs : function(){
37148 createTool : function(parentEl, className){
37149 var btn = Roo.DomHelper.append(parentEl, {
37151 cls: "x-layout-tools-button",
37154 cls: "roo-layout-tools-button-inner " + className,
37158 btn.addClassOnOver("roo-layout-tools-button-over");
37163 * Ext JS Library 1.1.1
37164 * Copyright(c) 2006-2007, Ext JS, LLC.
37166 * Originally Released Under LGPL - original licence link has changed is not relivant.
37169 * <script type="text/javascript">
37175 * @class Roo.SplitLayoutRegion
37176 * @extends Roo.LayoutRegion
37177 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
37179 Roo.bootstrap.layout.Split = function(config){
37180 this.cursor = config.cursor;
37181 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
37184 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
37186 splitTip : "Drag to resize.",
37187 collapsibleSplitTip : "Drag to resize. Double click to hide.",
37188 useSplitTips : false,
37190 applyConfig : function(config){
37191 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
37194 onRender : function(ctr,pos) {
37196 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
37197 if(!this.config.split){
37202 var splitEl = Roo.DomHelper.append(ctr.dom, {
37204 id: this.el.id + "-split",
37205 cls: "roo-layout-split roo-layout-split-"+this.position,
37208 /** The SplitBar for this region
37209 * @type Roo.SplitBar */
37210 // does not exist yet...
37211 Roo.log([this.position, this.orientation]);
37213 this.split = new Roo.bootstrap.SplitBar({
37214 dragElement : splitEl,
37215 resizingElement: this.el,
37216 orientation : this.orientation
37219 this.split.on("moved", this.onSplitMove, this);
37220 this.split.useShim = this.config.useShim === true;
37221 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
37222 if(this.useSplitTips){
37223 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
37225 //if(config.collapsible){
37226 // this.split.el.on("dblclick", this.collapse, this);
37229 if(typeof this.config.minSize != "undefined"){
37230 this.split.minSize = this.config.minSize;
37232 if(typeof this.config.maxSize != "undefined"){
37233 this.split.maxSize = this.config.maxSize;
37235 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
37236 this.hideSplitter();
37241 getHMaxSize : function(){
37242 var cmax = this.config.maxSize || 10000;
37243 var center = this.mgr.getRegion("center");
37244 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
37247 getVMaxSize : function(){
37248 var cmax = this.config.maxSize || 10000;
37249 var center = this.mgr.getRegion("center");
37250 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
37253 onSplitMove : function(split, newSize){
37254 this.fireEvent("resized", this, newSize);
37258 * Returns the {@link Roo.SplitBar} for this region.
37259 * @return {Roo.SplitBar}
37261 getSplitBar : function(){
37266 this.hideSplitter();
37267 Roo.bootstrap.layout.Split.superclass.hide.call(this);
37270 hideSplitter : function(){
37272 this.split.el.setLocation(-2000,-2000);
37273 this.split.el.hide();
37279 this.split.el.show();
37281 Roo.bootstrap.layout.Split.superclass.show.call(this);
37284 beforeSlide: function(){
37285 if(Roo.isGecko){// firefox overflow auto bug workaround
37286 this.bodyEl.clip();
37288 this.tabs.bodyEl.clip();
37290 if(this.activePanel){
37291 this.activePanel.getEl().clip();
37293 if(this.activePanel.beforeSlide){
37294 this.activePanel.beforeSlide();
37300 afterSlide : function(){
37301 if(Roo.isGecko){// firefox overflow auto bug workaround
37302 this.bodyEl.unclip();
37304 this.tabs.bodyEl.unclip();
37306 if(this.activePanel){
37307 this.activePanel.getEl().unclip();
37308 if(this.activePanel.afterSlide){
37309 this.activePanel.afterSlide();
37315 initAutoHide : function(){
37316 if(this.autoHide !== false){
37317 if(!this.autoHideHd){
37318 var st = new Roo.util.DelayedTask(this.slideIn, this);
37319 this.autoHideHd = {
37320 "mouseout": function(e){
37321 if(!e.within(this.el, true)){
37325 "mouseover" : function(e){
37331 this.el.on(this.autoHideHd);
37335 clearAutoHide : function(){
37336 if(this.autoHide !== false){
37337 this.el.un("mouseout", this.autoHideHd.mouseout);
37338 this.el.un("mouseover", this.autoHideHd.mouseover);
37342 clearMonitor : function(){
37343 Roo.get(document).un("click", this.slideInIf, this);
37346 // these names are backwards but not changed for compat
37347 slideOut : function(){
37348 if(this.isSlid || this.el.hasActiveFx()){
37351 this.isSlid = true;
37352 if(this.collapseBtn){
37353 this.collapseBtn.hide();
37355 this.closeBtnState = this.closeBtn.getStyle('display');
37356 this.closeBtn.hide();
37358 this.stickBtn.show();
37361 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37362 this.beforeSlide();
37363 this.el.setStyle("z-index", 10001);
37364 this.el.slideIn(this.getSlideAnchor(), {
37365 callback: function(){
37367 this.initAutoHide();
37368 Roo.get(document).on("click", this.slideInIf, this);
37369 this.fireEvent("slideshow", this);
37376 afterSlideIn : function(){
37377 this.clearAutoHide();
37378 this.isSlid = false;
37379 this.clearMonitor();
37380 this.el.setStyle("z-index", "");
37381 if(this.collapseBtn){
37382 this.collapseBtn.show();
37384 this.closeBtn.setStyle('display', this.closeBtnState);
37386 this.stickBtn.hide();
37388 this.fireEvent("slidehide", this);
37391 slideIn : function(cb){
37392 if(!this.isSlid || this.el.hasActiveFx()){
37396 this.isSlid = false;
37397 this.beforeSlide();
37398 this.el.slideOut(this.getSlideAnchor(), {
37399 callback: function(){
37400 this.el.setLeftTop(-10000, -10000);
37402 this.afterSlideIn();
37410 slideInIf : function(e){
37411 if(!e.within(this.el)){
37416 animateCollapse : function(){
37417 this.beforeSlide();
37418 this.el.setStyle("z-index", 20000);
37419 var anchor = this.getSlideAnchor();
37420 this.el.slideOut(anchor, {
37421 callback : function(){
37422 this.el.setStyle("z-index", "");
37423 this.collapsedEl.slideIn(anchor, {duration:.3});
37425 this.el.setLocation(-10000,-10000);
37427 this.fireEvent("collapsed", this);
37434 animateExpand : function(){
37435 this.beforeSlide();
37436 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37437 this.el.setStyle("z-index", 20000);
37438 this.collapsedEl.hide({
37441 this.el.slideIn(this.getSlideAnchor(), {
37442 callback : function(){
37443 this.el.setStyle("z-index", "");
37446 this.split.el.show();
37448 this.fireEvent("invalidated", this);
37449 this.fireEvent("expanded", this);
37477 getAnchor : function(){
37478 return this.anchors[this.position];
37481 getCollapseAnchor : function(){
37482 return this.canchors[this.position];
37485 getSlideAnchor : function(){
37486 return this.sanchors[this.position];
37489 getAlignAdj : function(){
37490 var cm = this.cmargins;
37491 switch(this.position){
37507 getExpandAdj : function(){
37508 var c = this.collapsedEl, cm = this.cmargins;
37509 switch(this.position){
37511 return [-(cm.right+c.getWidth()+cm.left), 0];
37514 return [cm.right+c.getWidth()+cm.left, 0];
37517 return [0, -(cm.top+cm.bottom+c.getHeight())];
37520 return [0, cm.top+cm.bottom+c.getHeight()];
37526 * Ext JS Library 1.1.1
37527 * Copyright(c) 2006-2007, Ext JS, LLC.
37529 * Originally Released Under LGPL - original licence link has changed is not relivant.
37532 * <script type="text/javascript">
37535 * These classes are private internal classes
37537 Roo.bootstrap.layout.Center = function(config){
37538 config.region = "center";
37539 Roo.bootstrap.layout.Region.call(this, config);
37540 this.visible = true;
37541 this.minWidth = config.minWidth || 20;
37542 this.minHeight = config.minHeight || 20;
37545 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37547 // center panel can't be hidden
37551 // center panel can't be hidden
37554 getMinWidth: function(){
37555 return this.minWidth;
37558 getMinHeight: function(){
37559 return this.minHeight;
37573 Roo.bootstrap.layout.North = function(config)
37575 config.region = 'north';
37576 config.cursor = 'n-resize';
37578 Roo.bootstrap.layout.Split.call(this, config);
37582 this.split.placement = Roo.bootstrap.SplitBar.TOP;
37583 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37584 this.split.el.addClass("roo-layout-split-v");
37586 var size = config.initialSize || config.height;
37587 if(typeof size != "undefined"){
37588 this.el.setHeight(size);
37591 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37593 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37597 getBox : function(){
37598 if(this.collapsed){
37599 return this.collapsedEl.getBox();
37601 var box = this.el.getBox();
37603 box.height += this.split.el.getHeight();
37608 updateBox : function(box){
37609 if(this.split && !this.collapsed){
37610 box.height -= this.split.el.getHeight();
37611 this.split.el.setLeft(box.x);
37612 this.split.el.setTop(box.y+box.height);
37613 this.split.el.setWidth(box.width);
37615 if(this.collapsed){
37616 this.updateBody(box.width, null);
37618 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37626 Roo.bootstrap.layout.South = function(config){
37627 config.region = 'south';
37628 config.cursor = 's-resize';
37629 Roo.bootstrap.layout.Split.call(this, config);
37631 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37632 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37633 this.split.el.addClass("roo-layout-split-v");
37635 var size = config.initialSize || config.height;
37636 if(typeof size != "undefined"){
37637 this.el.setHeight(size);
37641 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37642 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37643 getBox : function(){
37644 if(this.collapsed){
37645 return this.collapsedEl.getBox();
37647 var box = this.el.getBox();
37649 var sh = this.split.el.getHeight();
37656 updateBox : function(box){
37657 if(this.split && !this.collapsed){
37658 var sh = this.split.el.getHeight();
37661 this.split.el.setLeft(box.x);
37662 this.split.el.setTop(box.y-sh);
37663 this.split.el.setWidth(box.width);
37665 if(this.collapsed){
37666 this.updateBody(box.width, null);
37668 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37672 Roo.bootstrap.layout.East = function(config){
37673 config.region = "east";
37674 config.cursor = "e-resize";
37675 Roo.bootstrap.layout.Split.call(this, config);
37677 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37678 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37679 this.split.el.addClass("roo-layout-split-h");
37681 var size = config.initialSize || config.width;
37682 if(typeof size != "undefined"){
37683 this.el.setWidth(size);
37686 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37687 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37688 getBox : function(){
37689 if(this.collapsed){
37690 return this.collapsedEl.getBox();
37692 var box = this.el.getBox();
37694 var sw = this.split.el.getWidth();
37701 updateBox : function(box){
37702 if(this.split && !this.collapsed){
37703 var sw = this.split.el.getWidth();
37705 this.split.el.setLeft(box.x);
37706 this.split.el.setTop(box.y);
37707 this.split.el.setHeight(box.height);
37710 if(this.collapsed){
37711 this.updateBody(null, box.height);
37713 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37717 Roo.bootstrap.layout.West = function(config){
37718 config.region = "west";
37719 config.cursor = "w-resize";
37721 Roo.bootstrap.layout.Split.call(this, config);
37723 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37724 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37725 this.split.el.addClass("roo-layout-split-h");
37729 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37730 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37732 onRender: function(ctr, pos)
37734 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37735 var size = this.config.initialSize || this.config.width;
37736 if(typeof size != "undefined"){
37737 this.el.setWidth(size);
37741 getBox : function(){
37742 if(this.collapsed){
37743 return this.collapsedEl.getBox();
37745 var box = this.el.getBox();
37747 box.width += this.split.el.getWidth();
37752 updateBox : function(box){
37753 if(this.split && !this.collapsed){
37754 var sw = this.split.el.getWidth();
37756 this.split.el.setLeft(box.x+box.width);
37757 this.split.el.setTop(box.y);
37758 this.split.el.setHeight(box.height);
37760 if(this.collapsed){
37761 this.updateBody(null, box.height);
37763 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37765 });Roo.namespace("Roo.bootstrap.panel");/*
37767 * Ext JS Library 1.1.1
37768 * Copyright(c) 2006-2007, Ext JS, LLC.
37770 * Originally Released Under LGPL - original licence link has changed is not relivant.
37773 * <script type="text/javascript">
37776 * @class Roo.ContentPanel
37777 * @extends Roo.util.Observable
37778 * A basic ContentPanel element.
37779 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37780 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37781 * @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
37782 * @cfg {Boolean} closable True if the panel can be closed/removed
37783 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37784 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37785 * @cfg {Toolbar} toolbar A toolbar for this panel
37786 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37787 * @cfg {String} title The title for this panel
37788 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37789 * @cfg {String} url Calls {@link #setUrl} with this value
37790 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37791 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37792 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37793 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37794 * @cfg {Boolean} badges render the badges
37797 * Create a new ContentPanel.
37798 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37799 * @param {String/Object} config A string to set only the title or a config object
37800 * @param {String} content (optional) Set the HTML content for this panel
37801 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37803 Roo.bootstrap.panel.Content = function( config){
37805 this.tpl = config.tpl || false;
37807 var el = config.el;
37808 var content = config.content;
37810 if(config.autoCreate){ // xtype is available if this is called from factory
37813 this.el = Roo.get(el);
37814 if(!this.el && config && config.autoCreate){
37815 if(typeof config.autoCreate == "object"){
37816 if(!config.autoCreate.id){
37817 config.autoCreate.id = config.id||el;
37819 this.el = Roo.DomHelper.append(document.body,
37820 config.autoCreate, true);
37822 var elcfg = { tag: "div",
37823 cls: "roo-layout-inactive-content",
37827 elcfg.html = config.html;
37831 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37834 this.closable = false;
37835 this.loaded = false;
37836 this.active = false;
37839 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37841 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37843 this.wrapEl = this.el; //this.el.wrap();
37845 if (config.toolbar.items) {
37846 ti = config.toolbar.items ;
37847 delete config.toolbar.items ;
37851 this.toolbar.render(this.wrapEl, 'before');
37852 for(var i =0;i < ti.length;i++) {
37853 // Roo.log(['add child', items[i]]);
37854 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37856 this.toolbar.items = nitems;
37857 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37858 delete config.toolbar;
37862 // xtype created footer. - not sure if will work as we normally have to render first..
37863 if (this.footer && !this.footer.el && this.footer.xtype) {
37864 if (!this.wrapEl) {
37865 this.wrapEl = this.el.wrap();
37868 this.footer.container = this.wrapEl.createChild();
37870 this.footer = Roo.factory(this.footer, Roo);
37875 if(typeof config == "string"){
37876 this.title = config;
37878 Roo.apply(this, config);
37882 this.resizeEl = Roo.get(this.resizeEl, true);
37884 this.resizeEl = this.el;
37886 // handle view.xtype
37894 * Fires when this panel is activated.
37895 * @param {Roo.ContentPanel} this
37899 * @event deactivate
37900 * Fires when this panel is activated.
37901 * @param {Roo.ContentPanel} this
37903 "deactivate" : true,
37907 * Fires when this panel is resized if fitToFrame is true.
37908 * @param {Roo.ContentPanel} this
37909 * @param {Number} width The width after any component adjustments
37910 * @param {Number} height The height after any component adjustments
37916 * Fires when this tab is created
37917 * @param {Roo.ContentPanel} this
37928 if(this.autoScroll){
37929 this.resizeEl.setStyle("overflow", "auto");
37931 // fix randome scrolling
37932 //this.el.on('scroll', function() {
37933 // Roo.log('fix random scolling');
37934 // this.scrollTo('top',0);
37937 content = content || this.content;
37939 this.setContent(content);
37941 if(config && config.url){
37942 this.setUrl(this.url, this.params, this.loadOnce);
37947 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37949 if (this.view && typeof(this.view.xtype) != 'undefined') {
37950 this.view.el = this.el.appendChild(document.createElement("div"));
37951 this.view = Roo.factory(this.view);
37952 this.view.render && this.view.render(false, '');
37956 this.fireEvent('render', this);
37959 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37963 setRegion : function(region){
37964 this.region = region;
37965 this.setActiveClass(region && !this.background);
37969 setActiveClass: function(state)
37972 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37973 this.el.setStyle('position','relative');
37975 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37976 this.el.setStyle('position', 'absolute');
37981 * Returns the toolbar for this Panel if one was configured.
37982 * @return {Roo.Toolbar}
37984 getToolbar : function(){
37985 return this.toolbar;
37988 setActiveState : function(active)
37990 this.active = active;
37991 this.setActiveClass(active);
37993 if(this.fireEvent("deactivate", this) === false){
37998 this.fireEvent("activate", this);
38002 * Updates this panel's element
38003 * @param {String} content The new content
38004 * @param {Boolean} loadScripts (optional) true to look for and process scripts
38006 setContent : function(content, loadScripts){
38007 this.el.update(content, loadScripts);
38010 ignoreResize : function(w, h){
38011 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
38014 this.lastSize = {width: w, height: h};
38019 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
38020 * @return {Roo.UpdateManager} The UpdateManager
38022 getUpdateManager : function(){
38023 return this.el.getUpdateManager();
38026 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
38027 * @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:
38030 url: "your-url.php",
38031 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
38032 callback: yourFunction,
38033 scope: yourObject, //(optional scope)
38036 text: "Loading...",
38041 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
38042 * 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.
38043 * @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}
38044 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
38045 * @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.
38046 * @return {Roo.ContentPanel} this
38049 var um = this.el.getUpdateManager();
38050 um.update.apply(um, arguments);
38056 * 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.
38057 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
38058 * @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)
38059 * @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)
38060 * @return {Roo.UpdateManager} The UpdateManager
38062 setUrl : function(url, params, loadOnce){
38063 if(this.refreshDelegate){
38064 this.removeListener("activate", this.refreshDelegate);
38066 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38067 this.on("activate", this.refreshDelegate);
38068 return this.el.getUpdateManager();
38071 _handleRefresh : function(url, params, loadOnce){
38072 if(!loadOnce || !this.loaded){
38073 var updater = this.el.getUpdateManager();
38074 updater.update(url, params, this._setLoaded.createDelegate(this));
38078 _setLoaded : function(){
38079 this.loaded = true;
38083 * Returns this panel's id
38086 getId : function(){
38091 * Returns this panel's element - used by regiosn to add.
38092 * @return {Roo.Element}
38094 getEl : function(){
38095 return this.wrapEl || this.el;
38100 adjustForComponents : function(width, height)
38102 //Roo.log('adjustForComponents ');
38103 if(this.resizeEl != this.el){
38104 width -= this.el.getFrameWidth('lr');
38105 height -= this.el.getFrameWidth('tb');
38108 var te = this.toolbar.getEl();
38109 te.setWidth(width);
38110 height -= te.getHeight();
38113 var te = this.footer.getEl();
38114 te.setWidth(width);
38115 height -= te.getHeight();
38119 if(this.adjustments){
38120 width += this.adjustments[0];
38121 height += this.adjustments[1];
38123 return {"width": width, "height": height};
38126 setSize : function(width, height){
38127 if(this.fitToFrame && !this.ignoreResize(width, height)){
38128 if(this.fitContainer && this.resizeEl != this.el){
38129 this.el.setSize(width, height);
38131 var size = this.adjustForComponents(width, height);
38132 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
38133 this.fireEvent('resize', this, size.width, size.height);
38138 * Returns this panel's title
38141 getTitle : function(){
38143 if (typeof(this.title) != 'object') {
38148 for (var k in this.title) {
38149 if (!this.title.hasOwnProperty(k)) {
38153 if (k.indexOf('-') >= 0) {
38154 var s = k.split('-');
38155 for (var i = 0; i<s.length; i++) {
38156 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
38159 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
38166 * Set this panel's title
38167 * @param {String} title
38169 setTitle : function(title){
38170 this.title = title;
38172 this.region.updatePanelTitle(this, title);
38177 * Returns true is this panel was configured to be closable
38178 * @return {Boolean}
38180 isClosable : function(){
38181 return this.closable;
38184 beforeSlide : function(){
38186 this.resizeEl.clip();
38189 afterSlide : function(){
38191 this.resizeEl.unclip();
38195 * Force a content refresh from the URL specified in the {@link #setUrl} method.
38196 * Will fail silently if the {@link #setUrl} method has not been called.
38197 * This does not activate the panel, just updates its content.
38199 refresh : function(){
38200 if(this.refreshDelegate){
38201 this.loaded = false;
38202 this.refreshDelegate();
38207 * Destroys this panel
38209 destroy : function(){
38210 this.el.removeAllListeners();
38211 var tempEl = document.createElement("span");
38212 tempEl.appendChild(this.el.dom);
38213 tempEl.innerHTML = "";
38219 * form - if the content panel contains a form - this is a reference to it.
38220 * @type {Roo.form.Form}
38224 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
38225 * This contains a reference to it.
38231 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
38241 * @param {Object} cfg Xtype definition of item to add.
38245 getChildContainer: function () {
38246 return this.getEl();
38251 var ret = new Roo.factory(cfg);
38256 if (cfg.xtype.match(/^Form$/)) {
38259 //if (this.footer) {
38260 // el = this.footer.container.insertSibling(false, 'before');
38262 el = this.el.createChild();
38265 this.form = new Roo.form.Form(cfg);
38268 if ( this.form.allItems.length) {
38269 this.form.render(el.dom);
38273 // should only have one of theses..
38274 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
38275 // views.. should not be just added - used named prop 'view''
38277 cfg.el = this.el.appendChild(document.createElement("div"));
38280 var ret = new Roo.factory(cfg);
38282 ret.render && ret.render(false, ''); // render blank..
38292 * @class Roo.bootstrap.panel.Grid
38293 * @extends Roo.bootstrap.panel.Content
38295 * Create a new GridPanel.
38296 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
38297 * @param {Object} config A the config object
38303 Roo.bootstrap.panel.Grid = function(config)
38307 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
38308 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
38310 config.el = this.wrapper;
38311 //this.el = this.wrapper;
38313 if (config.container) {
38314 // ctor'ed from a Border/panel.grid
38317 this.wrapper.setStyle("overflow", "hidden");
38318 this.wrapper.addClass('roo-grid-container');
38323 if(config.toolbar){
38324 var tool_el = this.wrapper.createChild();
38325 this.toolbar = Roo.factory(config.toolbar);
38327 if (config.toolbar.items) {
38328 ti = config.toolbar.items ;
38329 delete config.toolbar.items ;
38333 this.toolbar.render(tool_el);
38334 for(var i =0;i < ti.length;i++) {
38335 // Roo.log(['add child', items[i]]);
38336 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38338 this.toolbar.items = nitems;
38340 delete config.toolbar;
38343 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
38344 config.grid.scrollBody = true;;
38345 config.grid.monitorWindowResize = false; // turn off autosizing
38346 config.grid.autoHeight = false;
38347 config.grid.autoWidth = false;
38349 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
38351 if (config.background) {
38352 // render grid on panel activation (if panel background)
38353 this.on('activate', function(gp) {
38354 if (!gp.grid.rendered) {
38355 gp.grid.render(this.wrapper);
38356 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
38361 this.grid.render(this.wrapper);
38362 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
38365 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
38366 // ??? needed ??? config.el = this.wrapper;
38371 // xtype created footer. - not sure if will work as we normally have to render first..
38372 if (this.footer && !this.footer.el && this.footer.xtype) {
38374 var ctr = this.grid.getView().getFooterPanel(true);
38375 this.footer.dataSource = this.grid.dataSource;
38376 this.footer = Roo.factory(this.footer, Roo);
38377 this.footer.render(ctr);
38387 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38388 getId : function(){
38389 return this.grid.id;
38393 * Returns the grid for this panel
38394 * @return {Roo.bootstrap.Table}
38396 getGrid : function(){
38400 setSize : function(width, height){
38401 if(!this.ignoreResize(width, height)){
38402 var grid = this.grid;
38403 var size = this.adjustForComponents(width, height);
38404 var gridel = grid.getGridEl();
38405 gridel.setSize(size.width, size.height);
38407 var thd = grid.getGridEl().select('thead',true).first();
38408 var tbd = grid.getGridEl().select('tbody', true).first();
38410 tbd.setSize(width, height - thd.getHeight());
38419 beforeSlide : function(){
38420 this.grid.getView().scroller.clip();
38423 afterSlide : function(){
38424 this.grid.getView().scroller.unclip();
38427 destroy : function(){
38428 this.grid.destroy();
38430 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
38435 * @class Roo.bootstrap.panel.Nest
38436 * @extends Roo.bootstrap.panel.Content
38438 * Create a new Panel, that can contain a layout.Border.
38441 * @param {Roo.BorderLayout} layout The layout for this panel
38442 * @param {String/Object} config A string to set only the title or a config object
38444 Roo.bootstrap.panel.Nest = function(config)
38446 // construct with only one argument..
38447 /* FIXME - implement nicer consturctors
38448 if (layout.layout) {
38450 layout = config.layout;
38451 delete config.layout;
38453 if (layout.xtype && !layout.getEl) {
38454 // then layout needs constructing..
38455 layout = Roo.factory(layout, Roo);
38459 config.el = config.layout.getEl();
38461 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38463 config.layout.monitorWindowResize = false; // turn off autosizing
38464 this.layout = config.layout;
38465 this.layout.getEl().addClass("roo-layout-nested-layout");
38466 this.layout.parent = this;
38473 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38475 setSize : function(width, height){
38476 if(!this.ignoreResize(width, height)){
38477 var size = this.adjustForComponents(width, height);
38478 var el = this.layout.getEl();
38479 if (size.height < 1) {
38480 el.setWidth(size.width);
38482 el.setSize(size.width, size.height);
38484 var touch = el.dom.offsetWidth;
38485 this.layout.layout();
38486 // ie requires a double layout on the first pass
38487 if(Roo.isIE && !this.initialized){
38488 this.initialized = true;
38489 this.layout.layout();
38494 // activate all subpanels if not currently active..
38496 setActiveState : function(active){
38497 this.active = active;
38498 this.setActiveClass(active);
38501 this.fireEvent("deactivate", this);
38505 this.fireEvent("activate", this);
38506 // not sure if this should happen before or after..
38507 if (!this.layout) {
38508 return; // should not happen..
38511 for (var r in this.layout.regions) {
38512 reg = this.layout.getRegion(r);
38513 if (reg.getActivePanel()) {
38514 //reg.showPanel(reg.getActivePanel()); // force it to activate..
38515 reg.setActivePanel(reg.getActivePanel());
38518 if (!reg.panels.length) {
38521 reg.showPanel(reg.getPanel(0));
38530 * Returns the nested BorderLayout for this panel
38531 * @return {Roo.BorderLayout}
38533 getLayout : function(){
38534 return this.layout;
38538 * Adds a xtype elements to the layout of the nested panel
38542 xtype : 'ContentPanel',
38549 xtype : 'NestedLayoutPanel',
38555 items : [ ... list of content panels or nested layout panels.. ]
38559 * @param {Object} cfg Xtype definition of item to add.
38561 addxtype : function(cfg) {
38562 return this.layout.addxtype(cfg);
38567 * Ext JS Library 1.1.1
38568 * Copyright(c) 2006-2007, Ext JS, LLC.
38570 * Originally Released Under LGPL - original licence link has changed is not relivant.
38573 * <script type="text/javascript">
38576 * @class Roo.TabPanel
38577 * @extends Roo.util.Observable
38578 * A lightweight tab container.
38582 // basic tabs 1, built from existing content
38583 var tabs = new Roo.TabPanel("tabs1");
38584 tabs.addTab("script", "View Script");
38585 tabs.addTab("markup", "View Markup");
38586 tabs.activate("script");
38588 // more advanced tabs, built from javascript
38589 var jtabs = new Roo.TabPanel("jtabs");
38590 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38592 // set up the UpdateManager
38593 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38594 var updater = tab2.getUpdateManager();
38595 updater.setDefaultUrl("ajax1.htm");
38596 tab2.on('activate', updater.refresh, updater, true);
38598 // Use setUrl for Ajax loading
38599 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38600 tab3.setUrl("ajax2.htm", null, true);
38603 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38606 jtabs.activate("jtabs-1");
38609 * Create a new TabPanel.
38610 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38611 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38613 Roo.bootstrap.panel.Tabs = function(config){
38615 * The container element for this TabPanel.
38616 * @type Roo.Element
38618 this.el = Roo.get(config.el);
38621 if(typeof config == "boolean"){
38622 this.tabPosition = config ? "bottom" : "top";
38624 Roo.apply(this, config);
38628 if(this.tabPosition == "bottom"){
38629 // if tabs are at the bottom = create the body first.
38630 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38631 this.el.addClass("roo-tabs-bottom");
38633 // next create the tabs holders
38635 if (this.tabPosition == "west"){
38637 var reg = this.region; // fake it..
38639 if (!reg.mgr.parent) {
38642 reg = reg.mgr.parent.region;
38644 Roo.log("got nest?");
38646 if (reg.mgr.getRegion('west')) {
38647 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38648 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38649 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38650 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38651 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38659 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38660 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38661 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38662 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38667 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38670 // finally - if tabs are at the top, then create the body last..
38671 if(this.tabPosition != "bottom"){
38672 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38673 * @type Roo.Element
38675 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38676 this.el.addClass("roo-tabs-top");
38680 this.bodyEl.setStyle("position", "relative");
38682 this.active = null;
38683 this.activateDelegate = this.activate.createDelegate(this);
38688 * Fires when the active tab changes
38689 * @param {Roo.TabPanel} this
38690 * @param {Roo.TabPanelItem} activePanel The new active tab
38694 * @event beforetabchange
38695 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38696 * @param {Roo.TabPanel} this
38697 * @param {Object} e Set cancel to true on this object to cancel the tab change
38698 * @param {Roo.TabPanelItem} tab The tab being changed to
38700 "beforetabchange" : true
38703 Roo.EventManager.onWindowResize(this.onResize, this);
38704 this.cpad = this.el.getPadding("lr");
38705 this.hiddenCount = 0;
38708 // toolbar on the tabbar support...
38709 if (this.toolbar) {
38710 alert("no toolbar support yet");
38711 this.toolbar = false;
38713 var tcfg = this.toolbar;
38714 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
38715 this.toolbar = new Roo.Toolbar(tcfg);
38716 if (Roo.isSafari) {
38717 var tbl = tcfg.container.child('table', true);
38718 tbl.setAttribute('width', '100%');
38726 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38729 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38731 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38733 tabPosition : "top",
38735 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38737 currentTabWidth : 0,
38739 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38743 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38747 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38749 preferredTabWidth : 175,
38751 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38753 resizeTabs : false,
38755 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38757 monitorResize : true,
38759 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
38761 toolbar : false, // set by caller..
38763 region : false, /// set by caller
38765 disableTooltips : true, // not used yet...
38768 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38769 * @param {String} id The id of the div to use <b>or create</b>
38770 * @param {String} text The text for the tab
38771 * @param {String} content (optional) Content to put in the TabPanelItem body
38772 * @param {Boolean} closable (optional) True to create a close icon on the tab
38773 * @return {Roo.TabPanelItem} The created TabPanelItem
38775 addTab : function(id, text, content, closable, tpl)
38777 var item = new Roo.bootstrap.panel.TabItem({
38781 closable : closable,
38784 this.addTabItem(item);
38786 item.setContent(content);
38792 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38793 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38794 * @return {Roo.TabPanelItem}
38796 getTab : function(id){
38797 return this.items[id];
38801 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38802 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38804 hideTab : function(id){
38805 var t = this.items[id];
38808 this.hiddenCount++;
38809 this.autoSizeTabs();
38814 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38815 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38817 unhideTab : function(id){
38818 var t = this.items[id];
38820 t.setHidden(false);
38821 this.hiddenCount--;
38822 this.autoSizeTabs();
38827 * Adds an existing {@link Roo.TabPanelItem}.
38828 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38830 addTabItem : function(item)
38832 this.items[item.id] = item;
38833 this.items.push(item);
38834 this.autoSizeTabs();
38835 // if(this.resizeTabs){
38836 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38837 // this.autoSizeTabs();
38839 // item.autoSize();
38844 * Removes a {@link Roo.TabPanelItem}.
38845 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38847 removeTab : function(id){
38848 var items = this.items;
38849 var tab = items[id];
38850 if(!tab) { return; }
38851 var index = items.indexOf(tab);
38852 if(this.active == tab && items.length > 1){
38853 var newTab = this.getNextAvailable(index);
38858 this.stripEl.dom.removeChild(tab.pnode.dom);
38859 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38860 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38862 items.splice(index, 1);
38863 delete this.items[tab.id];
38864 tab.fireEvent("close", tab);
38865 tab.purgeListeners();
38866 this.autoSizeTabs();
38869 getNextAvailable : function(start){
38870 var items = this.items;
38872 // look for a next tab that will slide over to
38873 // replace the one being removed
38874 while(index < items.length){
38875 var item = items[++index];
38876 if(item && !item.isHidden()){
38880 // if one isn't found select the previous tab (on the left)
38883 var item = items[--index];
38884 if(item && !item.isHidden()){
38892 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38893 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38895 disableTab : function(id){
38896 var tab = this.items[id];
38897 if(tab && this.active != tab){
38903 * Enables a {@link Roo.TabPanelItem} that is disabled.
38904 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38906 enableTab : function(id){
38907 var tab = this.items[id];
38912 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38913 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38914 * @return {Roo.TabPanelItem} The TabPanelItem.
38916 activate : function(id)
38918 //Roo.log('activite:' + id);
38920 var tab = this.items[id];
38924 if(tab == this.active || tab.disabled){
38928 this.fireEvent("beforetabchange", this, e, tab);
38929 if(e.cancel !== true && !tab.disabled){
38931 this.active.hide();
38933 this.active = this.items[id];
38934 this.active.show();
38935 this.fireEvent("tabchange", this, this.active);
38941 * Gets the active {@link Roo.TabPanelItem}.
38942 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38944 getActiveTab : function(){
38945 return this.active;
38949 * Updates the tab body element to fit the height of the container element
38950 * for overflow scrolling
38951 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38953 syncHeight : function(targetHeight){
38954 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38955 var bm = this.bodyEl.getMargins();
38956 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38957 this.bodyEl.setHeight(newHeight);
38961 onResize : function(){
38962 if(this.monitorResize){
38963 this.autoSizeTabs();
38968 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38970 beginUpdate : function(){
38971 this.updating = true;
38975 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38977 endUpdate : function(){
38978 this.updating = false;
38979 this.autoSizeTabs();
38983 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38985 autoSizeTabs : function()
38987 var count = this.items.length;
38988 var vcount = count - this.hiddenCount;
38991 this.stripEl.hide();
38993 this.stripEl.show();
38996 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
39001 var w = Math.max(this.el.getWidth() - this.cpad, 10);
39002 var availWidth = Math.floor(w / vcount);
39003 var b = this.stripBody;
39004 if(b.getWidth() > w){
39005 var tabs = this.items;
39006 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
39007 if(availWidth < this.minTabWidth){
39008 /*if(!this.sleft){ // incomplete scrolling code
39009 this.createScrollButtons();
39012 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
39015 if(this.currentTabWidth < this.preferredTabWidth){
39016 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
39022 * Returns the number of tabs in this TabPanel.
39025 getCount : function(){
39026 return this.items.length;
39030 * Resizes all the tabs to the passed width
39031 * @param {Number} The new width
39033 setTabWidth : function(width){
39034 this.currentTabWidth = width;
39035 for(var i = 0, len = this.items.length; i < len; i++) {
39036 if(!this.items[i].isHidden()) {
39037 this.items[i].setWidth(width);
39043 * Destroys this TabPanel
39044 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
39046 destroy : function(removeEl){
39047 Roo.EventManager.removeResizeListener(this.onResize, this);
39048 for(var i = 0, len = this.items.length; i < len; i++){
39049 this.items[i].purgeListeners();
39051 if(removeEl === true){
39052 this.el.update("");
39057 createStrip : function(container)
39059 var strip = document.createElement("nav");
39060 strip.className = Roo.bootstrap.version == 4 ?
39061 "navbar-light bg-light" :
39062 "navbar navbar-default"; //"x-tabs-wrap";
39063 container.appendChild(strip);
39067 createStripList : function(strip)
39069 // div wrapper for retard IE
39070 // returns the "tr" element.
39071 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
39072 //'<div class="x-tabs-strip-wrap">'+
39073 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
39074 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
39075 return strip.firstChild; //.firstChild.firstChild.firstChild;
39077 createBody : function(container)
39079 var body = document.createElement("div");
39080 Roo.id(body, "tab-body");
39081 //Roo.fly(body).addClass("x-tabs-body");
39082 Roo.fly(body).addClass("tab-content");
39083 container.appendChild(body);
39086 createItemBody :function(bodyEl, id){
39087 var body = Roo.getDom(id);
39089 body = document.createElement("div");
39092 //Roo.fly(body).addClass("x-tabs-item-body");
39093 Roo.fly(body).addClass("tab-pane");
39094 bodyEl.insertBefore(body, bodyEl.firstChild);
39098 createStripElements : function(stripEl, text, closable, tpl)
39100 var td = document.createElement("li"); // was td..
39101 td.className = 'nav-item';
39103 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
39106 stripEl.appendChild(td);
39108 td.className = "x-tabs-closable";
39109 if(!this.closeTpl){
39110 this.closeTpl = new Roo.Template(
39111 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39112 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
39113 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
39116 var el = this.closeTpl.overwrite(td, {"text": text});
39117 var close = el.getElementsByTagName("div")[0];
39118 var inner = el.getElementsByTagName("em")[0];
39119 return {"el": el, "close": close, "inner": inner};
39122 // not sure what this is..
39123 // if(!this.tabTpl){
39124 //this.tabTpl = new Roo.Template(
39125 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39126 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
39128 // this.tabTpl = new Roo.Template(
39129 // '<a href="#">' +
39130 // '<span unselectable="on"' +
39131 // (this.disableTooltips ? '' : ' title="{text}"') +
39132 // ' >{text}</span></a>'
39138 var template = tpl || this.tabTpl || false;
39141 template = new Roo.Template(
39142 Roo.bootstrap.version == 4 ?
39144 '<a class="nav-link" href="#" unselectable="on"' +
39145 (this.disableTooltips ? '' : ' title="{text}"') +
39148 '<a class="nav-link" href="#">' +
39149 '<span unselectable="on"' +
39150 (this.disableTooltips ? '' : ' title="{text}"') +
39151 ' >{text}</span></a>'
39156 switch (typeof(template)) {
39160 template = new Roo.Template(template);
39166 var el = template.overwrite(td, {"text": text});
39168 var inner = el.getElementsByTagName("span")[0];
39170 return {"el": el, "inner": inner};
39178 * @class Roo.TabPanelItem
39179 * @extends Roo.util.Observable
39180 * Represents an individual item (tab plus body) in a TabPanel.
39181 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
39182 * @param {String} id The id of this TabPanelItem
39183 * @param {String} text The text for the tab of this TabPanelItem
39184 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
39186 Roo.bootstrap.panel.TabItem = function(config){
39188 * The {@link Roo.TabPanel} this TabPanelItem belongs to
39189 * @type Roo.TabPanel
39191 this.tabPanel = config.panel;
39193 * The id for this TabPanelItem
39196 this.id = config.id;
39198 this.disabled = false;
39200 this.text = config.text;
39202 this.loaded = false;
39203 this.closable = config.closable;
39206 * The body element for this TabPanelItem.
39207 * @type Roo.Element
39209 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
39210 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
39211 this.bodyEl.setStyle("display", "block");
39212 this.bodyEl.setStyle("zoom", "1");
39213 //this.hideAction();
39215 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
39217 this.el = Roo.get(els.el);
39218 this.inner = Roo.get(els.inner, true);
39219 this.textEl = Roo.bootstrap.version == 4 ?
39220 this.el : Roo.get(this.el.dom.firstChild, true);
39222 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
39223 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
39226 // this.el.on("mousedown", this.onTabMouseDown, this);
39227 this.el.on("click", this.onTabClick, this);
39229 if(config.closable){
39230 var c = Roo.get(els.close, true);
39231 c.dom.title = this.closeText;
39232 c.addClassOnOver("close-over");
39233 c.on("click", this.closeClick, this);
39239 * Fires when this tab becomes the active tab.
39240 * @param {Roo.TabPanel} tabPanel The parent TabPanel
39241 * @param {Roo.TabPanelItem} this
39245 * @event beforeclose
39246 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
39247 * @param {Roo.TabPanelItem} this
39248 * @param {Object} e Set cancel to true on this object to cancel the close.
39250 "beforeclose": true,
39253 * Fires when this tab is closed.
39254 * @param {Roo.TabPanelItem} this
39258 * @event deactivate
39259 * Fires when this tab is no longer the active tab.
39260 * @param {Roo.TabPanel} tabPanel The parent TabPanel
39261 * @param {Roo.TabPanelItem} this
39263 "deactivate" : true
39265 this.hidden = false;
39267 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
39270 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
39272 purgeListeners : function(){
39273 Roo.util.Observable.prototype.purgeListeners.call(this);
39274 this.el.removeAllListeners();
39277 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
39280 this.status_node.addClass("active");
39283 this.tabPanel.stripWrap.repaint();
39285 this.fireEvent("activate", this.tabPanel, this);
39289 * Returns true if this tab is the active tab.
39290 * @return {Boolean}
39292 isActive : function(){
39293 return this.tabPanel.getActiveTab() == this;
39297 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
39300 this.status_node.removeClass("active");
39302 this.fireEvent("deactivate", this.tabPanel, this);
39305 hideAction : function(){
39306 this.bodyEl.hide();
39307 this.bodyEl.setStyle("position", "absolute");
39308 this.bodyEl.setLeft("-20000px");
39309 this.bodyEl.setTop("-20000px");
39312 showAction : function(){
39313 this.bodyEl.setStyle("position", "relative");
39314 this.bodyEl.setTop("");
39315 this.bodyEl.setLeft("");
39316 this.bodyEl.show();
39320 * Set the tooltip for the tab.
39321 * @param {String} tooltip The tab's tooltip
39323 setTooltip : function(text){
39324 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
39325 this.textEl.dom.qtip = text;
39326 this.textEl.dom.removeAttribute('title');
39328 this.textEl.dom.title = text;
39332 onTabClick : function(e){
39333 e.preventDefault();
39334 this.tabPanel.activate(this.id);
39337 onTabMouseDown : function(e){
39338 e.preventDefault();
39339 this.tabPanel.activate(this.id);
39342 getWidth : function(){
39343 return this.inner.getWidth();
39346 setWidth : function(width){
39347 var iwidth = width - this.linode.getPadding("lr");
39348 this.inner.setWidth(iwidth);
39349 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
39350 this.linode.setWidth(width);
39354 * Show or hide the tab
39355 * @param {Boolean} hidden True to hide or false to show.
39357 setHidden : function(hidden){
39358 this.hidden = hidden;
39359 this.linode.setStyle("display", hidden ? "none" : "");
39363 * Returns true if this tab is "hidden"
39364 * @return {Boolean}
39366 isHidden : function(){
39367 return this.hidden;
39371 * Returns the text for this tab
39374 getText : function(){
39378 autoSize : function(){
39379 //this.el.beginMeasure();
39380 this.textEl.setWidth(1);
39382 * #2804 [new] Tabs in Roojs
39383 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39385 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39386 //this.el.endMeasure();
39390 * Sets the text for the tab (Note: this also sets the tooltip text)
39391 * @param {String} text The tab's text and tooltip
39393 setText : function(text){
39395 this.textEl.update(text);
39396 this.setTooltip(text);
39397 //if(!this.tabPanel.resizeTabs){
39398 // this.autoSize();
39402 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39404 activate : function(){
39405 this.tabPanel.activate(this.id);
39409 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39411 disable : function(){
39412 if(this.tabPanel.active != this){
39413 this.disabled = true;
39414 this.status_node.addClass("disabled");
39419 * Enables this TabPanelItem if it was previously disabled.
39421 enable : function(){
39422 this.disabled = false;
39423 this.status_node.removeClass("disabled");
39427 * Sets the content for this TabPanelItem.
39428 * @param {String} content The content
39429 * @param {Boolean} loadScripts true to look for and load scripts
39431 setContent : function(content, loadScripts){
39432 this.bodyEl.update(content, loadScripts);
39436 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39437 * @return {Roo.UpdateManager} The UpdateManager
39439 getUpdateManager : function(){
39440 return this.bodyEl.getUpdateManager();
39444 * Set a URL to be used to load the content for this TabPanelItem.
39445 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39446 * @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)
39447 * @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)
39448 * @return {Roo.UpdateManager} The UpdateManager
39450 setUrl : function(url, params, loadOnce){
39451 if(this.refreshDelegate){
39452 this.un('activate', this.refreshDelegate);
39454 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39455 this.on("activate", this.refreshDelegate);
39456 return this.bodyEl.getUpdateManager();
39460 _handleRefresh : function(url, params, loadOnce){
39461 if(!loadOnce || !this.loaded){
39462 var updater = this.bodyEl.getUpdateManager();
39463 updater.update(url, params, this._setLoaded.createDelegate(this));
39468 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
39469 * Will fail silently if the setUrl method has not been called.
39470 * This does not activate the panel, just updates its content.
39472 refresh : function(){
39473 if(this.refreshDelegate){
39474 this.loaded = false;
39475 this.refreshDelegate();
39480 _setLoaded : function(){
39481 this.loaded = true;
39485 closeClick : function(e){
39488 this.fireEvent("beforeclose", this, o);
39489 if(o.cancel !== true){
39490 this.tabPanel.removeTab(this.id);
39494 * The text displayed in the tooltip for the close icon.
39497 closeText : "Close this tab"
39500 * This script refer to:
39501 * Title: International Telephone Input
39502 * Author: Jack O'Connor
39503 * Code version: v12.1.12
39504 * Availability: https://github.com/jackocnr/intl-tel-input.git
39507 Roo.bootstrap.PhoneInputData = function() {
39510 "Afghanistan (افغانستان)",
39515 "Albania (Shqipëri)",
39520 "Algeria (الجزائر)",
39545 "Antigua and Barbuda",
39555 "Armenia (Հայաստան)",
39571 "Austria (Österreich)",
39576 "Azerbaijan (Azərbaycan)",
39586 "Bahrain (البحرين)",
39591 "Bangladesh (বাংলাদেশ)",
39601 "Belarus (Беларусь)",
39606 "Belgium (België)",
39636 "Bosnia and Herzegovina (Босна и Херцеговина)",
39651 "British Indian Ocean Territory",
39656 "British Virgin Islands",
39666 "Bulgaria (България)",
39676 "Burundi (Uburundi)",
39681 "Cambodia (កម្ពុជា)",
39686 "Cameroon (Cameroun)",
39695 ["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"]
39698 "Cape Verde (Kabu Verdi)",
39703 "Caribbean Netherlands",
39714 "Central African Republic (République centrafricaine)",
39734 "Christmas Island",
39740 "Cocos (Keeling) Islands",
39751 "Comoros (جزر القمر)",
39756 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39761 "Congo (Republic) (Congo-Brazzaville)",
39781 "Croatia (Hrvatska)",
39802 "Czech Republic (Česká republika)",
39807 "Denmark (Danmark)",
39822 "Dominican Republic (República Dominicana)",
39826 ["809", "829", "849"]
39844 "Equatorial Guinea (Guinea Ecuatorial)",
39864 "Falkland Islands (Islas Malvinas)",
39869 "Faroe Islands (Føroyar)",
39890 "French Guiana (Guyane française)",
39895 "French Polynesia (Polynésie française)",
39910 "Georgia (საქართველო)",
39915 "Germany (Deutschland)",
39935 "Greenland (Kalaallit Nunaat)",
39972 "Guinea-Bissau (Guiné Bissau)",
39997 "Hungary (Magyarország)",
40002 "Iceland (Ísland)",
40022 "Iraq (العراق)",
40038 "Israel (ישראל)",
40065 "Jordan (الأردن)",
40070 "Kazakhstan (Казахстан)",
40091 "Kuwait (الكويت)",
40096 "Kyrgyzstan (Кыргызстан)",
40106 "Latvia (Latvija)",
40111 "Lebanon (لبنان)",
40126 "Libya (ليبيا)",
40136 "Lithuania (Lietuva)",
40151 "Macedonia (FYROM) (Македонија)",
40156 "Madagascar (Madagasikara)",
40186 "Marshall Islands",
40196 "Mauritania (موريتانيا)",
40201 "Mauritius (Moris)",
40222 "Moldova (Republica Moldova)",
40232 "Mongolia (Монгол)",
40237 "Montenegro (Crna Gora)",
40247 "Morocco (المغرب)",
40253 "Mozambique (Moçambique)",
40258 "Myanmar (Burma) (မြန်မာ)",
40263 "Namibia (Namibië)",
40278 "Netherlands (Nederland)",
40283 "New Caledonia (Nouvelle-Calédonie)",
40318 "North Korea (조선 민주주의 인민 공화국)",
40323 "Northern Mariana Islands",
40339 "Pakistan (پاکستان)",
40349 "Palestine (فلسطين)",
40359 "Papua New Guinea",
40401 "Réunion (La Réunion)",
40407 "Romania (România)",
40423 "Saint Barthélemy",
40434 "Saint Kitts and Nevis",
40444 "Saint Martin (Saint-Martin (partie française))",
40450 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40455 "Saint Vincent and the Grenadines",
40470 "São Tomé and Príncipe (São Tomé e Príncipe)",
40475 "Saudi Arabia (المملكة العربية السعودية)",
40480 "Senegal (Sénégal)",
40510 "Slovakia (Slovensko)",
40515 "Slovenia (Slovenija)",
40525 "Somalia (Soomaaliya)",
40535 "South Korea (대한민국)",
40540 "South Sudan (جنوب السودان)",
40550 "Sri Lanka (ශ්රී ලංකාව)",
40555 "Sudan (السودان)",
40565 "Svalbard and Jan Mayen",
40576 "Sweden (Sverige)",
40581 "Switzerland (Schweiz)",
40586 "Syria (سوريا)",
40631 "Trinidad and Tobago",
40636 "Tunisia (تونس)",
40641 "Turkey (Türkiye)",
40651 "Turks and Caicos Islands",
40661 "U.S. Virgin Islands",
40671 "Ukraine (Україна)",
40676 "United Arab Emirates (الإمارات العربية المتحدة)",
40698 "Uzbekistan (Oʻzbekiston)",
40708 "Vatican City (Città del Vaticano)",
40719 "Vietnam (Việt Nam)",
40724 "Wallis and Futuna (Wallis-et-Futuna)",
40729 "Western Sahara (الصحراء الغربية)",
40735 "Yemen (اليمن)",
40759 * This script refer to:
40760 * Title: International Telephone Input
40761 * Author: Jack O'Connor
40762 * Code version: v12.1.12
40763 * Availability: https://github.com/jackocnr/intl-tel-input.git
40767 * @class Roo.bootstrap.PhoneInput
40768 * @extends Roo.bootstrap.TriggerField
40769 * An input with International dial-code selection
40771 * @cfg {String} defaultDialCode default '+852'
40772 * @cfg {Array} preferedCountries default []
40775 * Create a new PhoneInput.
40776 * @param {Object} config Configuration options
40779 Roo.bootstrap.PhoneInput = function(config) {
40780 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40783 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40785 listWidth: undefined,
40787 selectedClass: 'active',
40789 invalidClass : "has-warning",
40791 validClass: 'has-success',
40793 allowed: '0123456789',
40798 * @cfg {String} defaultDialCode The default dial code when initializing the input
40800 defaultDialCode: '+852',
40803 * @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
40805 preferedCountries: false,
40807 getAutoCreate : function()
40809 var data = Roo.bootstrap.PhoneInputData();
40810 var align = this.labelAlign || this.parentLabelAlign();
40813 this.allCountries = [];
40814 this.dialCodeMapping = [];
40816 for (var i = 0; i < data.length; i++) {
40818 this.allCountries[i] = {
40822 priority: c[3] || 0,
40823 areaCodes: c[4] || null
40825 this.dialCodeMapping[c[2]] = {
40828 priority: c[3] || 0,
40829 areaCodes: c[4] || null
40841 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40842 maxlength: this.max_length,
40843 cls : 'form-control tel-input',
40844 autocomplete: 'new-password'
40847 var hiddenInput = {
40850 cls: 'hidden-tel-input'
40854 hiddenInput.name = this.name;
40857 if (this.disabled) {
40858 input.disabled = true;
40861 var flag_container = {
40878 cls: this.hasFeedback ? 'has-feedback' : '',
40884 cls: 'dial-code-holder',
40891 cls: 'roo-select2-container input-group',
40898 if (this.fieldLabel.length) {
40901 tooltip: 'This field is required'
40907 cls: 'control-label',
40913 html: this.fieldLabel
40916 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40922 if(this.indicatorpos == 'right') {
40923 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40930 if(align == 'left') {
40938 if(this.labelWidth > 12){
40939 label.style = "width: " + this.labelWidth + 'px';
40941 if(this.labelWidth < 13 && this.labelmd == 0){
40942 this.labelmd = this.labelWidth;
40944 if(this.labellg > 0){
40945 label.cls += ' col-lg-' + this.labellg;
40946 input.cls += ' col-lg-' + (12 - this.labellg);
40948 if(this.labelmd > 0){
40949 label.cls += ' col-md-' + this.labelmd;
40950 container.cls += ' col-md-' + (12 - this.labelmd);
40952 if(this.labelsm > 0){
40953 label.cls += ' col-sm-' + this.labelsm;
40954 container.cls += ' col-sm-' + (12 - this.labelsm);
40956 if(this.labelxs > 0){
40957 label.cls += ' col-xs-' + this.labelxs;
40958 container.cls += ' col-xs-' + (12 - this.labelxs);
40968 var settings = this;
40970 ['xs','sm','md','lg'].map(function(size){
40971 if (settings[size]) {
40972 cfg.cls += ' col-' + size + '-' + settings[size];
40976 this.store = new Roo.data.Store({
40977 proxy : new Roo.data.MemoryProxy({}),
40978 reader : new Roo.data.JsonReader({
40989 'name' : 'dialCode',
40993 'name' : 'priority',
40997 'name' : 'areaCodes',
41004 if(!this.preferedCountries) {
41005 this.preferedCountries = [
41012 var p = this.preferedCountries.reverse();
41015 for (var i = 0; i < p.length; i++) {
41016 for (var j = 0; j < this.allCountries.length; j++) {
41017 if(this.allCountries[j].iso2 == p[i]) {
41018 var t = this.allCountries[j];
41019 this.allCountries.splice(j,1);
41020 this.allCountries.unshift(t);
41026 this.store.proxy.data = {
41028 data: this.allCountries
41034 initEvents : function()
41037 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
41039 this.indicator = this.indicatorEl();
41040 this.flag = this.flagEl();
41041 this.dialCodeHolder = this.dialCodeHolderEl();
41043 this.trigger = this.el.select('div.flag-box',true).first();
41044 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41049 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41050 _this.list.setWidth(lw);
41053 this.list.on('mouseover', this.onViewOver, this);
41054 this.list.on('mousemove', this.onViewMove, this);
41055 this.inputEl().on("keyup", this.onKeyUp, this);
41056 this.inputEl().on("keypress", this.onKeyPress, this);
41058 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
41060 this.view = new Roo.View(this.list, this.tpl, {
41061 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41064 this.view.on('click', this.onViewClick, this);
41065 this.setValue(this.defaultDialCode);
41068 onTriggerClick : function(e)
41070 Roo.log('trigger click');
41075 if(this.isExpanded()){
41077 this.hasFocus = false;
41079 this.store.load({});
41080 this.hasFocus = true;
41085 isExpanded : function()
41087 return this.list.isVisible();
41090 collapse : function()
41092 if(!this.isExpanded()){
41096 Roo.get(document).un('mousedown', this.collapseIf, this);
41097 Roo.get(document).un('mousewheel', this.collapseIf, this);
41098 this.fireEvent('collapse', this);
41102 expand : function()
41106 if(this.isExpanded() || !this.hasFocus){
41110 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
41111 this.list.setWidth(lw);
41114 this.restrictHeight();
41116 Roo.get(document).on('mousedown', this.collapseIf, this);
41117 Roo.get(document).on('mousewheel', this.collapseIf, this);
41119 this.fireEvent('expand', this);
41122 restrictHeight : function()
41124 this.list.alignTo(this.inputEl(), this.listAlign);
41125 this.list.alignTo(this.inputEl(), this.listAlign);
41128 onViewOver : function(e, t)
41130 if(this.inKeyMode){
41133 var item = this.view.findItemFromChild(t);
41136 var index = this.view.indexOf(item);
41137 this.select(index, false);
41142 onViewClick : function(view, doFocus, el, e)
41144 var index = this.view.getSelectedIndexes()[0];
41146 var r = this.store.getAt(index);
41149 this.onSelect(r, index);
41151 if(doFocus !== false && !this.blockFocus){
41152 this.inputEl().focus();
41156 onViewMove : function(e, t)
41158 this.inKeyMode = false;
41161 select : function(index, scrollIntoView)
41163 this.selectedIndex = index;
41164 this.view.select(index);
41165 if(scrollIntoView !== false){
41166 var el = this.view.getNode(index);
41168 this.list.scrollChildIntoView(el, false);
41173 createList : function()
41175 this.list = Roo.get(document.body).createChild({
41177 cls: 'typeahead typeahead-long dropdown-menu tel-list',
41178 style: 'display:none'
41181 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
41184 collapseIf : function(e)
41186 var in_combo = e.within(this.el);
41187 var in_list = e.within(this.list);
41188 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
41190 if (in_combo || in_list || is_list) {
41196 onSelect : function(record, index)
41198 if(this.fireEvent('beforeselect', this, record, index) !== false){
41200 this.setFlagClass(record.data.iso2);
41201 this.setDialCode(record.data.dialCode);
41202 this.hasFocus = false;
41204 this.fireEvent('select', this, record, index);
41208 flagEl : function()
41210 var flag = this.el.select('div.flag',true).first();
41217 dialCodeHolderEl : function()
41219 var d = this.el.select('input.dial-code-holder',true).first();
41226 setDialCode : function(v)
41228 this.dialCodeHolder.dom.value = '+'+v;
41231 setFlagClass : function(n)
41233 this.flag.dom.className = 'flag '+n;
41236 getValue : function()
41238 var v = this.inputEl().getValue();
41239 if(this.dialCodeHolder) {
41240 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
41245 setValue : function(v)
41247 var d = this.getDialCode(v);
41249 //invalid dial code
41250 if(v.length == 0 || !d || d.length == 0) {
41252 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41253 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41259 this.setFlagClass(this.dialCodeMapping[d].iso2);
41260 this.setDialCode(d);
41261 this.inputEl().dom.value = v.replace('+'+d,'');
41262 this.hiddenEl().dom.value = this.getValue();
41267 getDialCode : function(v)
41271 if (v.length == 0) {
41272 return this.dialCodeHolder.dom.value;
41276 if (v.charAt(0) != "+") {
41279 var numericChars = "";
41280 for (var i = 1; i < v.length; i++) {
41281 var c = v.charAt(i);
41284 if (this.dialCodeMapping[numericChars]) {
41285 dialCode = v.substr(1, i);
41287 if (numericChars.length == 4) {
41297 this.setValue(this.defaultDialCode);
41301 hiddenEl : function()
41303 return this.el.select('input.hidden-tel-input',true).first();
41306 // after setting val
41307 onKeyUp : function(e){
41308 this.setValue(this.getValue());
41311 onKeyPress : function(e){
41312 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
41319 * @class Roo.bootstrap.MoneyField
41320 * @extends Roo.bootstrap.ComboBox
41321 * Bootstrap MoneyField class
41324 * Create a new MoneyField.
41325 * @param {Object} config Configuration options
41328 Roo.bootstrap.MoneyField = function(config) {
41330 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
41334 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
41337 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41339 allowDecimals : true,
41341 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41343 decimalSeparator : ".",
41345 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41347 decimalPrecision : 0,
41349 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41351 allowNegative : true,
41353 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41357 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41359 minValue : Number.NEGATIVE_INFINITY,
41361 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41363 maxValue : Number.MAX_VALUE,
41365 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41367 minText : "The minimum value for this field is {0}",
41369 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41371 maxText : "The maximum value for this field is {0}",
41373 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
41374 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41376 nanText : "{0} is not a valid number",
41378 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41382 * @cfg {String} defaults currency of the MoneyField
41383 * value should be in lkey
41385 defaultCurrency : false,
41387 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41389 thousandsDelimiter : false,
41391 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41402 getAutoCreate : function()
41404 var align = this.labelAlign || this.parentLabelAlign();
41416 cls : 'form-control roo-money-amount-input',
41417 autocomplete: 'new-password'
41420 var hiddenInput = {
41424 cls: 'hidden-number-input'
41427 if(this.max_length) {
41428 input.maxlength = this.max_length;
41432 hiddenInput.name = this.name;
41435 if (this.disabled) {
41436 input.disabled = true;
41439 var clg = 12 - this.inputlg;
41440 var cmd = 12 - this.inputmd;
41441 var csm = 12 - this.inputsm;
41442 var cxs = 12 - this.inputxs;
41446 cls : 'row roo-money-field',
41450 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41454 cls: 'roo-select2-container input-group',
41458 cls : 'form-control roo-money-currency-input',
41459 autocomplete: 'new-password',
41461 name : this.currencyName
41465 cls : 'input-group-addon',
41479 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41483 cls: this.hasFeedback ? 'has-feedback' : '',
41494 if (this.fieldLabel.length) {
41497 tooltip: 'This field is required'
41503 cls: 'control-label',
41509 html: this.fieldLabel
41512 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41518 if(this.indicatorpos == 'right') {
41519 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41526 if(align == 'left') {
41534 if(this.labelWidth > 12){
41535 label.style = "width: " + this.labelWidth + 'px';
41537 if(this.labelWidth < 13 && this.labelmd == 0){
41538 this.labelmd = this.labelWidth;
41540 if(this.labellg > 0){
41541 label.cls += ' col-lg-' + this.labellg;
41542 input.cls += ' col-lg-' + (12 - this.labellg);
41544 if(this.labelmd > 0){
41545 label.cls += ' col-md-' + this.labelmd;
41546 container.cls += ' col-md-' + (12 - this.labelmd);
41548 if(this.labelsm > 0){
41549 label.cls += ' col-sm-' + this.labelsm;
41550 container.cls += ' col-sm-' + (12 - this.labelsm);
41552 if(this.labelxs > 0){
41553 label.cls += ' col-xs-' + this.labelxs;
41554 container.cls += ' col-xs-' + (12 - this.labelxs);
41565 var settings = this;
41567 ['xs','sm','md','lg'].map(function(size){
41568 if (settings[size]) {
41569 cfg.cls += ' col-' + size + '-' + settings[size];
41576 initEvents : function()
41578 this.indicator = this.indicatorEl();
41580 this.initCurrencyEvent();
41582 this.initNumberEvent();
41585 initCurrencyEvent : function()
41588 throw "can not find store for combo";
41591 this.store = Roo.factory(this.store, Roo.data);
41592 this.store.parent = this;
41596 this.triggerEl = this.el.select('.input-group-addon', true).first();
41598 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41603 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41604 _this.list.setWidth(lw);
41607 this.list.on('mouseover', this.onViewOver, this);
41608 this.list.on('mousemove', this.onViewMove, this);
41609 this.list.on('scroll', this.onViewScroll, this);
41612 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41615 this.view = new Roo.View(this.list, this.tpl, {
41616 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41619 this.view.on('click', this.onViewClick, this);
41621 this.store.on('beforeload', this.onBeforeLoad, this);
41622 this.store.on('load', this.onLoad, this);
41623 this.store.on('loadexception', this.onLoadException, this);
41625 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41626 "up" : function(e){
41627 this.inKeyMode = true;
41631 "down" : function(e){
41632 if(!this.isExpanded()){
41633 this.onTriggerClick();
41635 this.inKeyMode = true;
41640 "enter" : function(e){
41643 if(this.fireEvent("specialkey", this, e)){
41644 this.onViewClick(false);
41650 "esc" : function(e){
41654 "tab" : function(e){
41657 if(this.fireEvent("specialkey", this, e)){
41658 this.onViewClick(false);
41666 doRelay : function(foo, bar, hname){
41667 if(hname == 'down' || this.scope.isExpanded()){
41668 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41676 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41680 initNumberEvent : function(e)
41682 this.inputEl().on("keydown" , this.fireKey, this);
41683 this.inputEl().on("focus", this.onFocus, this);
41684 this.inputEl().on("blur", this.onBlur, this);
41686 this.inputEl().relayEvent('keyup', this);
41688 if(this.indicator){
41689 this.indicator.addClass('invisible');
41692 this.originalValue = this.getValue();
41694 if(this.validationEvent == 'keyup'){
41695 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41696 this.inputEl().on('keyup', this.filterValidation, this);
41698 else if(this.validationEvent !== false){
41699 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41702 if(this.selectOnFocus){
41703 this.on("focus", this.preFocus, this);
41706 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41707 this.inputEl().on("keypress", this.filterKeys, this);
41709 this.inputEl().relayEvent('keypress', this);
41712 var allowed = "0123456789";
41714 if(this.allowDecimals){
41715 allowed += this.decimalSeparator;
41718 if(this.allowNegative){
41722 if(this.thousandsDelimiter) {
41726 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41728 var keyPress = function(e){
41730 var k = e.getKey();
41732 var c = e.getCharCode();
41735 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41736 allowed.indexOf(String.fromCharCode(c)) === -1
41742 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41746 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41751 this.inputEl().on("keypress", keyPress, this);
41755 onTriggerClick : function(e)
41762 this.loadNext = false;
41764 if(this.isExpanded()){
41769 this.hasFocus = true;
41771 if(this.triggerAction == 'all') {
41772 this.doQuery(this.allQuery, true);
41776 this.doQuery(this.getRawValue());
41779 getCurrency : function()
41781 var v = this.currencyEl().getValue();
41786 restrictHeight : function()
41788 this.list.alignTo(this.currencyEl(), this.listAlign);
41789 this.list.alignTo(this.currencyEl(), this.listAlign);
41792 onViewClick : function(view, doFocus, el, e)
41794 var index = this.view.getSelectedIndexes()[0];
41796 var r = this.store.getAt(index);
41799 this.onSelect(r, index);
41803 onSelect : function(record, index){
41805 if(this.fireEvent('beforeselect', this, record, index) !== false){
41807 this.setFromCurrencyData(index > -1 ? record.data : false);
41811 this.fireEvent('select', this, record, index);
41815 setFromCurrencyData : function(o)
41819 this.lastCurrency = o;
41821 if (this.currencyField) {
41822 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41824 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41827 this.lastSelectionText = currency;
41829 //setting default currency
41830 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41831 this.setCurrency(this.defaultCurrency);
41835 this.setCurrency(currency);
41838 setFromData : function(o)
41842 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41844 this.setFromCurrencyData(c);
41849 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41851 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41854 this.setValue(value);
41858 setCurrency : function(v)
41860 this.currencyValue = v;
41863 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41868 setValue : function(v)
41870 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41876 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41878 this.inputEl().dom.value = (v == '') ? '' :
41879 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41881 if(!this.allowZero && v === '0') {
41882 this.hiddenEl().dom.value = '';
41883 this.inputEl().dom.value = '';
41890 getRawValue : function()
41892 var v = this.inputEl().getValue();
41897 getValue : function()
41899 return this.fixPrecision(this.parseValue(this.getRawValue()));
41902 parseValue : function(value)
41904 if(this.thousandsDelimiter) {
41906 r = new RegExp(",", "g");
41907 value = value.replace(r, "");
41910 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41911 return isNaN(value) ? '' : value;
41915 fixPrecision : function(value)
41917 if(this.thousandsDelimiter) {
41919 r = new RegExp(",", "g");
41920 value = value.replace(r, "");
41923 var nan = isNaN(value);
41925 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41926 return nan ? '' : value;
41928 return parseFloat(value).toFixed(this.decimalPrecision);
41931 decimalPrecisionFcn : function(v)
41933 return Math.floor(v);
41936 validateValue : function(value)
41938 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41942 var num = this.parseValue(value);
41945 this.markInvalid(String.format(this.nanText, value));
41949 if(num < this.minValue){
41950 this.markInvalid(String.format(this.minText, this.minValue));
41954 if(num > this.maxValue){
41955 this.markInvalid(String.format(this.maxText, this.maxValue));
41962 validate : function()
41964 if(this.disabled || this.allowBlank){
41969 var currency = this.getCurrency();
41971 if(this.validateValue(this.getRawValue()) && currency.length){
41976 this.markInvalid();
41980 getName: function()
41985 beforeBlur : function()
41991 var v = this.parseValue(this.getRawValue());
41998 onBlur : function()
42002 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
42003 //this.el.removeClass(this.focusClass);
42006 this.hasFocus = false;
42008 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
42012 var v = this.getValue();
42014 if(String(v) !== String(this.startValue)){
42015 this.fireEvent('change', this, v, this.startValue);
42018 this.fireEvent("blur", this);
42021 inputEl : function()
42023 return this.el.select('.roo-money-amount-input', true).first();
42026 currencyEl : function()
42028 return this.el.select('.roo-money-currency-input', true).first();
42031 hiddenEl : function()
42033 return this.el.select('input.hidden-number-input',true).first();
42037 * @class Roo.bootstrap.BezierSignature
42038 * @extends Roo.bootstrap.Component
42039 * Bootstrap BezierSignature class
42040 * This script refer to:
42041 * Title: Signature Pad
42043 * Availability: https://github.com/szimek/signature_pad
42046 * Create a new BezierSignature
42047 * @param {Object} config The config object
42050 Roo.bootstrap.BezierSignature = function(config){
42051 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
42057 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
42064 mouse_btn_down: true,
42067 * @cfg {int} canvas height
42069 canvas_height: '200px',
42072 * @cfg {float|function} Radius of a single dot.
42077 * @cfg {float} Minimum width of a line. Defaults to 0.5.
42082 * @cfg {float} Maximum width of a line. Defaults to 2.5.
42087 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
42092 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
42097 * @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.
42099 bg_color: 'rgba(0, 0, 0, 0)',
42102 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
42104 dot_color: 'black',
42107 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
42109 velocity_filter_weight: 0.7,
42112 * @cfg {function} Callback when stroke begin.
42117 * @cfg {function} Callback when stroke end.
42121 getAutoCreate : function()
42123 var cls = 'roo-signature column';
42126 cls += ' ' + this.cls;
42136 for(var i = 0; i < col_sizes.length; i++) {
42137 if(this[col_sizes[i]]) {
42138 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
42148 cls: 'roo-signature-body',
42152 cls: 'roo-signature-body-canvas',
42153 height: this.canvas_height,
42154 width: this.canvas_width
42161 style: 'display: none'
42169 initEvents: function()
42171 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
42173 var canvas = this.canvasEl();
42175 // mouse && touch event swapping...
42176 canvas.dom.style.touchAction = 'none';
42177 canvas.dom.style.msTouchAction = 'none';
42179 this.mouse_btn_down = false;
42180 canvas.on('mousedown', this._handleMouseDown, this);
42181 canvas.on('mousemove', this._handleMouseMove, this);
42182 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
42184 if (window.PointerEvent) {
42185 canvas.on('pointerdown', this._handleMouseDown, this);
42186 canvas.on('pointermove', this._handleMouseMove, this);
42187 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
42190 if ('ontouchstart' in window) {
42191 canvas.on('touchstart', this._handleTouchStart, this);
42192 canvas.on('touchmove', this._handleTouchMove, this);
42193 canvas.on('touchend', this._handleTouchEnd, this);
42196 Roo.EventManager.onWindowResize(this.resize, this, true);
42198 // file input event
42199 this.fileEl().on('change', this.uploadImage, this);
42206 resize: function(){
42208 var canvas = this.canvasEl().dom;
42209 var ctx = this.canvasElCtx();
42210 var img_data = false;
42212 if(canvas.width > 0) {
42213 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
42215 // setting canvas width will clean img data
42218 var style = window.getComputedStyle ?
42219 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
42221 var padding_left = parseInt(style.paddingLeft) || 0;
42222 var padding_right = parseInt(style.paddingRight) || 0;
42224 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
42227 ctx.putImageData(img_data, 0, 0);
42231 _handleMouseDown: function(e)
42233 if (e.browserEvent.which === 1) {
42234 this.mouse_btn_down = true;
42235 this.strokeBegin(e);
42239 _handleMouseMove: function (e)
42241 if (this.mouse_btn_down) {
42242 this.strokeMoveUpdate(e);
42246 _handleMouseUp: function (e)
42248 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
42249 this.mouse_btn_down = false;
42254 _handleTouchStart: function (e) {
42256 e.preventDefault();
42257 if (e.browserEvent.targetTouches.length === 1) {
42258 // var touch = e.browserEvent.changedTouches[0];
42259 // this.strokeBegin(touch);
42261 this.strokeBegin(e); // assume e catching the correct xy...
42265 _handleTouchMove: function (e) {
42266 e.preventDefault();
42267 // var touch = event.targetTouches[0];
42268 // _this._strokeMoveUpdate(touch);
42269 this.strokeMoveUpdate(e);
42272 _handleTouchEnd: function (e) {
42273 var wasCanvasTouched = e.target === this.canvasEl().dom;
42274 if (wasCanvasTouched) {
42275 e.preventDefault();
42276 // var touch = event.changedTouches[0];
42277 // _this._strokeEnd(touch);
42282 reset: function () {
42283 this._lastPoints = [];
42284 this._lastVelocity = 0;
42285 this._lastWidth = (this.min_width + this.max_width) / 2;
42286 this.canvasElCtx().fillStyle = this.dot_color;
42289 strokeMoveUpdate: function(e)
42291 this.strokeUpdate(e);
42293 if (this.throttle) {
42294 this.throttleStroke(this.strokeUpdate, this.throttle);
42297 this.strokeUpdate(e);
42301 strokeBegin: function(e)
42303 var newPointGroup = {
42304 color: this.dot_color,
42308 if (typeof this.onBegin === 'function') {
42312 this.curve_data.push(newPointGroup);
42314 this.strokeUpdate(e);
42317 strokeUpdate: function(e)
42319 var rect = this.canvasEl().dom.getBoundingClientRect();
42320 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
42321 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
42322 var lastPoints = lastPointGroup.points;
42323 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
42324 var isLastPointTooClose = lastPoint
42325 ? point.distanceTo(lastPoint) <= this.min_distance
42327 var color = lastPointGroup.color;
42328 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
42329 var curve = this.addPoint(point);
42331 this.drawDot({color: color, point: point});
42334 this.drawCurve({color: color, curve: curve});
42344 strokeEnd: function(e)
42346 this.strokeUpdate(e);
42347 if (typeof this.onEnd === 'function') {
42352 addPoint: function (point) {
42353 var _lastPoints = this._lastPoints;
42354 _lastPoints.push(point);
42355 if (_lastPoints.length > 2) {
42356 if (_lastPoints.length === 3) {
42357 _lastPoints.unshift(_lastPoints[0]);
42359 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
42360 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
42361 _lastPoints.shift();
42367 calculateCurveWidths: function (startPoint, endPoint) {
42368 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42369 (1 - this.velocity_filter_weight) * this._lastVelocity;
42371 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42374 start: this._lastWidth
42377 this._lastVelocity = velocity;
42378 this._lastWidth = newWidth;
42382 drawDot: function (_a) {
42383 var color = _a.color, point = _a.point;
42384 var ctx = this.canvasElCtx();
42385 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42387 this.drawCurveSegment(point.x, point.y, width);
42389 ctx.fillStyle = color;
42393 drawCurve: function (_a) {
42394 var color = _a.color, curve = _a.curve;
42395 var ctx = this.canvasElCtx();
42396 var widthDelta = curve.endWidth - curve.startWidth;
42397 var drawSteps = Math.floor(curve.length()) * 2;
42399 ctx.fillStyle = color;
42400 for (var i = 0; i < drawSteps; i += 1) {
42401 var t = i / drawSteps;
42407 var x = uuu * curve.startPoint.x;
42408 x += 3 * uu * t * curve.control1.x;
42409 x += 3 * u * tt * curve.control2.x;
42410 x += ttt * curve.endPoint.x;
42411 var y = uuu * curve.startPoint.y;
42412 y += 3 * uu * t * curve.control1.y;
42413 y += 3 * u * tt * curve.control2.y;
42414 y += ttt * curve.endPoint.y;
42415 var width = curve.startWidth + ttt * widthDelta;
42416 this.drawCurveSegment(x, y, width);
42422 drawCurveSegment: function (x, y, width) {
42423 var ctx = this.canvasElCtx();
42425 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42426 this.is_empty = false;
42431 var ctx = this.canvasElCtx();
42432 var canvas = this.canvasEl().dom;
42433 ctx.fillStyle = this.bg_color;
42434 ctx.clearRect(0, 0, canvas.width, canvas.height);
42435 ctx.fillRect(0, 0, canvas.width, canvas.height);
42436 this.curve_data = [];
42438 this.is_empty = true;
42443 return this.el.select('input',true).first();
42446 canvasEl: function()
42448 return this.el.select('canvas',true).first();
42451 canvasElCtx: function()
42453 return this.el.select('canvas',true).first().dom.getContext('2d');
42456 getImage: function(type)
42458 if(this.is_empty) {
42463 return this.canvasEl().dom.toDataURL('image/'+type, 1);
42466 drawFromImage: function(img_src)
42468 var img = new Image();
42470 img.onload = function(){
42471 this.canvasElCtx().drawImage(img, 0, 0);
42476 this.is_empty = false;
42479 selectImage: function()
42481 this.fileEl().dom.click();
42484 uploadImage: function(e)
42486 var reader = new FileReader();
42488 reader.onload = function(e){
42489 var img = new Image();
42490 img.onload = function(){
42492 this.canvasElCtx().drawImage(img, 0, 0);
42494 img.src = e.target.result;
42497 reader.readAsDataURL(e.target.files[0]);
42500 // Bezier Point Constructor
42501 Point: (function () {
42502 function Point(x, y, time) {
42505 this.time = time || Date.now();
42507 Point.prototype.distanceTo = function (start) {
42508 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42510 Point.prototype.equals = function (other) {
42511 return this.x === other.x && this.y === other.y && this.time === other.time;
42513 Point.prototype.velocityFrom = function (start) {
42514 return this.time !== start.time
42515 ? this.distanceTo(start) / (this.time - start.time)
42522 // Bezier Constructor
42523 Bezier: (function () {
42524 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42525 this.startPoint = startPoint;
42526 this.control2 = control2;
42527 this.control1 = control1;
42528 this.endPoint = endPoint;
42529 this.startWidth = startWidth;
42530 this.endWidth = endWidth;
42532 Bezier.fromPoints = function (points, widths, scope) {
42533 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42534 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42535 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42537 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42538 var dx1 = s1.x - s2.x;
42539 var dy1 = s1.y - s2.y;
42540 var dx2 = s2.x - s3.x;
42541 var dy2 = s2.y - s3.y;
42542 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42543 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42544 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42545 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42546 var dxm = m1.x - m2.x;
42547 var dym = m1.y - m2.y;
42548 var k = l2 / (l1 + l2);
42549 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42550 var tx = s2.x - cm.x;
42551 var ty = s2.y - cm.y;
42553 c1: new scope.Point(m1.x + tx, m1.y + ty),
42554 c2: new scope.Point(m2.x + tx, m2.y + ty)
42557 Bezier.prototype.length = function () {
42562 for (var i = 0; i <= steps; i += 1) {
42564 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42565 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42567 var xdiff = cx - px;
42568 var ydiff = cy - py;
42569 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42576 Bezier.prototype.point = function (t, start, c1, c2, end) {
42577 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42578 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42579 + (3.0 * c2 * (1.0 - t) * t * t)
42580 + (end * t * t * t);
42585 throttleStroke: function(fn, wait) {
42586 if (wait === void 0) { wait = 250; }
42588 var timeout = null;
42592 var later = function () {
42593 previous = Date.now();
42595 result = fn.apply(storedContext, storedArgs);
42597 storedContext = null;
42601 return function wrapper() {
42603 for (var _i = 0; _i < arguments.length; _i++) {
42604 args[_i] = arguments[_i];
42606 var now = Date.now();
42607 var remaining = wait - (now - previous);
42608 storedContext = this;
42610 if (remaining <= 0 || remaining > wait) {
42612 clearTimeout(timeout);
42616 result = fn.apply(storedContext, storedArgs);
42618 storedContext = null;
42622 else if (!timeout) {
42623 timeout = window.setTimeout(later, remaining);