2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = (
9 Roo.each(document.styleSheets[0], function(s) {
10 if (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');
418 this.fireEvent('show', this);
423 * Hide a component - adds 'hidden' class
427 if(!this.getVisibilityEl()){
431 this.getVisibilityEl().addClass('hidden');
433 this.fireEvent('hide', this);
446 * @class Roo.bootstrap.Body
447 * @extends Roo.bootstrap.Component
448 * Bootstrap Body class
452 * @param {Object} config The config object
455 Roo.bootstrap.Body = function(config){
457 config = config || {};
459 Roo.bootstrap.Body.superclass.constructor.call(this, config);
460 this.el = Roo.get(config.el ? config.el : document.body );
461 if (this.cls && this.cls.length) {
462 Roo.get(document.body).addClass(this.cls);
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
468 is_body : true,// just to make sure it's constructed?
473 onRender : function(ct, position)
475 /* Roo.log("Roo.bootstrap.Body - onRender");
476 if (this.cls && this.cls.length) {
477 Roo.get(document.body).addClass(this.cls);
496 * @class Roo.bootstrap.ButtonGroup
497 * @extends Roo.bootstrap.Component
498 * Bootstrap ButtonGroup class
499 * @cfg {String} size lg | sm | xs (default empty normal)
500 * @cfg {String} align vertical | justified (default none)
501 * @cfg {String} direction up | down (default down)
502 * @cfg {Boolean} toolbar false | true
503 * @cfg {Boolean} btn true | false
508 * @param {Object} config The config object
511 Roo.bootstrap.ButtonGroup = function(config){
512 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
523 getAutoCreate : function(){
529 cfg.html = this.html || cfg.html;
540 if (['vertical','justified'].indexOf(this.align)!==-1) {
541 cfg.cls = 'btn-group-' + this.align;
543 if (this.align == 'justified') {
544 console.log(this.items);
548 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549 cfg.cls += ' btn-group-' + this.size;
552 if (this.direction == 'up') {
553 cfg.cls += ' dropup' ;
559 * Add a button to the group (similar to NavItem API.)
561 addItem : function(cfg)
563 var cn = new Roo.bootstrap.Button(cfg);
565 cn.parentId = this.id;
566 cn.onRender(this.el, null);
580 * @class Roo.bootstrap.Button
581 * @extends Roo.bootstrap.Component
582 * Bootstrap Button class
583 * @cfg {String} html The button content
584 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
585 * @cfg {String} size ( lg | sm | xs)
586 * @cfg {String} tag ( a | input | submit)
587 * @cfg {String} href empty or href
588 * @cfg {Boolean} disabled default false;
589 * @cfg {Boolean} isClose default false;
590 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
591 * @cfg {String} badge text for badge
592 * @cfg {String} theme (default|glow)
593 * @cfg {Boolean} inverse dark themed version
594 * @cfg {Boolean} toggle is it a slidy toggle button
595 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
596 * @cfg {String} ontext text for on slidy toggle state
597 * @cfg {String} offtext text for off slidy toggle state
598 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
599 * @cfg {Boolean} removeClass remove the standard class..
600 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
603 * Create a new button
604 * @param {Object} config The config object
608 Roo.bootstrap.Button = function(config){
609 Roo.bootstrap.Button.superclass.constructor.call(this, config);
610 this.weightClass = ["btn-default",
622 * When a butotn is pressed
623 * @param {Roo.bootstrap.Button} btn
624 * @param {Roo.EventObject} e
629 * After the button has been toggles
630 * @param {Roo.bootstrap.Button} btn
631 * @param {Roo.EventObject} e
632 * @param {boolean} pressed (also available as button.pressed)
638 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
656 preventDefault: true,
664 getAutoCreate : function(){
672 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
673 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
678 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
680 if (this.toggle == true) {
683 cls: 'slider-frame roo-button',
688 'data-off-text':'OFF',
689 cls: 'slider-button',
695 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
696 cfg.cls += ' '+this.weight;
705 cfg["aria-hidden"] = true;
707 cfg.html = "×";
713 if (this.theme==='default') {
714 cfg.cls = 'btn roo-button';
716 //if (this.parentType != 'Navbar') {
717 this.weight = this.weight.length ? this.weight : 'default';
719 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
721 cfg.cls += ' btn-' + this.weight;
723 } else if (this.theme==='glow') {
726 cfg.cls = 'btn-glow roo-button';
728 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
730 cfg.cls += ' ' + this.weight;
736 this.cls += ' inverse';
740 if (this.active || this.pressed === true) {
741 cfg.cls += ' active';
745 cfg.disabled = 'disabled';
749 Roo.log('changing to ul' );
751 this.glyphicon = 'caret';
754 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
756 //gsRoo.log(this.parentType);
757 if (this.parentType === 'Navbar' && !this.parent().bar) {
758 Roo.log('changing to li?');
767 href : this.href || '#'
770 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
771 cfg.cls += ' dropdown';
778 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
780 if (this.glyphicon) {
781 cfg.html = ' ' + cfg.html;
786 cls: 'glyphicon glyphicon-' + this.glyphicon
796 // cfg.cls='btn roo-button';
800 var value = cfg.html;
805 cls: 'glyphicon glyphicon-' + this.glyphicon,
824 cfg.cls += ' dropdown';
825 cfg.html = typeof(cfg.html) != 'undefined' ?
826 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
829 if (cfg.tag !== 'a' && this.href !== '') {
830 throw "Tag must be a to set href.";
831 } else if (this.href.length > 0) {
832 cfg.href = this.href;
835 if(this.removeClass){
840 cfg.target = this.target;
845 initEvents: function() {
846 // Roo.log('init events?');
847 // Roo.log(this.el.dom);
850 if (typeof (this.menu) != 'undefined') {
851 this.menu.parentType = this.xtype;
852 this.menu.triggerEl = this.el;
853 this.addxtype(Roo.apply({}, this.menu));
857 if (this.el.hasClass('roo-button')) {
858 this.el.on('click', this.onClick, this);
860 this.el.select('.roo-button').on('click', this.onClick, this);
863 if(this.removeClass){
864 this.el.on('click', this.onClick, this);
867 this.el.enableDisplayMode();
870 onClick : function(e)
876 Roo.log('button on click ');
877 if(this.preventDefault){
881 if (this.pressed === true || this.pressed === false) {
882 this.toggleActive(e);
886 this.fireEvent('click', this, e);
890 * Enables this button
894 this.disabled = false;
895 this.el.removeClass('disabled');
899 * Disable this button
903 this.disabled = true;
904 this.el.addClass('disabled');
907 * sets the active state on/off,
908 * @param {Boolean} state (optional) Force a particular state
910 setActive : function(v) {
912 this.el[v ? 'addClass' : 'removeClass']('active');
916 * toggles the current active state
918 toggleActive : function(e)
920 this.setActive(!this.pressed);
921 this.fireEvent('toggle', this, e, !this.pressed);
924 * get the current active state
925 * @return {boolean} true if it's active
927 isActive : function()
929 return this.el.hasClass('active');
932 * set the text of the first selected button
934 setText : function(str)
936 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
939 * get the text of the first selected button
943 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
946 setWeight : function(str)
948 this.el.removeClass(this.weightClass);
949 this.el.addClass('btn-' + str);
963 * @class Roo.bootstrap.Column
964 * @extends Roo.bootstrap.Component
965 * Bootstrap Column class
966 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
967 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
968 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
969 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
970 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
971 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
972 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
973 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
976 * @cfg {Boolean} hidden (true|false) hide the element
977 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
978 * @cfg {String} fa (ban|check|...) font awesome icon
979 * @cfg {Number} fasize (1|2|....) font awsome size
981 * @cfg {String} icon (info-sign|check|...) glyphicon name
983 * @cfg {String} html content of column.
986 * Create a new Column
987 * @param {Object} config The config object
990 Roo.bootstrap.Column = function(config){
991 Roo.bootstrap.Column.superclass.constructor.call(this, config);
994 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1012 getAutoCreate : function(){
1013 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1021 ['xs','sm','md','lg'].map(function(size){
1022 //Roo.log( size + ':' + settings[size]);
1024 if (settings[size+'off'] !== false) {
1025 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1028 if (settings[size] === false) {
1032 if (!settings[size]) { // 0 = hidden
1033 cfg.cls += ' hidden-' + size;
1036 cfg.cls += ' col-' + size + '-' + settings[size];
1041 cfg.cls += ' hidden';
1044 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1045 cfg.cls +=' alert alert-' + this.alert;
1049 if (this.html.length) {
1050 cfg.html = this.html;
1054 if (this.fasize > 1) {
1055 fasize = ' fa-' + this.fasize + 'x';
1057 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1062 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1081 * @class Roo.bootstrap.Container
1082 * @extends Roo.bootstrap.Component
1083 * Bootstrap Container class
1084 * @cfg {Boolean} jumbotron is it a jumbotron element
1085 * @cfg {String} html content of element
1086 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1087 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1088 * @cfg {String} header content of header (for panel)
1089 * @cfg {String} footer content of footer (for panel)
1090 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1091 * @cfg {String} tag (header|aside|section) type of HTML tag.
1092 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1093 * @cfg {String} fa font awesome icon
1094 * @cfg {String} icon (info-sign|check|...) glyphicon name
1095 * @cfg {Boolean} hidden (true|false) hide the element
1096 * @cfg {Boolean} expandable (true|false) default false
1097 * @cfg {Boolean} expanded (true|false) default true
1098 * @cfg {String} rheader contet on the right of header
1099 * @cfg {Boolean} clickable (true|false) default false
1103 * Create a new Container
1104 * @param {Object} config The config object
1107 Roo.bootstrap.Container = function(config){
1108 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1114 * After the panel has been expand
1116 * @param {Roo.bootstrap.Container} this
1121 * After the panel has been collapsed
1123 * @param {Roo.bootstrap.Container} this
1128 * When a element is chick
1129 * @param {Roo.bootstrap.Container} this
1130 * @param {Roo.EventObject} e
1136 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1154 getChildContainer : function() {
1160 if (this.panel.length) {
1161 return this.el.select('.panel-body',true).first();
1168 getAutoCreate : function(){
1171 tag : this.tag || 'div',
1175 if (this.jumbotron) {
1176 cfg.cls = 'jumbotron';
1181 // - this is applied by the parent..
1183 // cfg.cls = this.cls + '';
1186 if (this.sticky.length) {
1188 var bd = Roo.get(document.body);
1189 if (!bd.hasClass('bootstrap-sticky')) {
1190 bd.addClass('bootstrap-sticky');
1191 Roo.select('html',true).setStyle('height', '100%');
1194 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1198 if (this.well.length) {
1199 switch (this.well) {
1202 cfg.cls +=' well well-' +this.well;
1211 cfg.cls += ' hidden';
1215 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1216 cfg.cls +=' alert alert-' + this.alert;
1221 if (this.panel.length) {
1222 cfg.cls += ' panel panel-' + this.panel;
1224 if (this.header.length) {
1228 if(this.expandable){
1230 cfg.cls = cfg.cls + ' expandable';
1234 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1242 cls : 'panel-title',
1243 html : (this.expandable ? ' ' : '') + this.header
1247 cls: 'panel-header-right',
1253 cls : 'panel-heading',
1254 style : this.expandable ? 'cursor: pointer' : '',
1262 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1267 if (this.footer.length) {
1269 cls : 'panel-footer',
1278 body.html = this.html || cfg.html;
1279 // prefix with the icons..
1281 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1284 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1289 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1290 cfg.cls = 'container';
1296 initEvents: function()
1298 if(this.expandable){
1299 var headerEl = this.headerEl();
1302 headerEl.on('click', this.onToggleClick, this);
1307 this.el.on('click', this.onClick, this);
1312 onToggleClick : function()
1314 var headerEl = this.headerEl();
1330 if(this.fireEvent('expand', this)) {
1332 this.expanded = true;
1334 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1336 this.el.select('.panel-body',true).first().removeClass('hide');
1338 var toggleEl = this.toggleEl();
1344 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1349 collapse : function()
1351 if(this.fireEvent('collapse', this)) {
1353 this.expanded = false;
1355 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1356 this.el.select('.panel-body',true).first().addClass('hide');
1358 var toggleEl = this.toggleEl();
1364 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1368 toggleEl : function()
1370 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1374 return this.el.select('.panel-heading .fa',true).first();
1377 headerEl : function()
1379 if(!this.el || !this.panel.length || !this.header.length){
1383 return this.el.select('.panel-heading',true).first()
1388 if(!this.el || !this.panel.length){
1392 return this.el.select('.panel-body',true).first()
1395 titleEl : function()
1397 if(!this.el || !this.panel.length || !this.header.length){
1401 return this.el.select('.panel-title',true).first();
1404 setTitle : function(v)
1406 var titleEl = this.titleEl();
1412 titleEl.dom.innerHTML = v;
1415 getTitle : function()
1418 var titleEl = this.titleEl();
1424 return titleEl.dom.innerHTML;
1427 setRightTitle : function(v)
1429 var t = this.el.select('.panel-header-right',true).first();
1435 t.dom.innerHTML = v;
1438 onClick : function(e)
1442 this.fireEvent('click', this, e);
1455 * @class Roo.bootstrap.Img
1456 * @extends Roo.bootstrap.Component
1457 * Bootstrap Img class
1458 * @cfg {Boolean} imgResponsive false | true
1459 * @cfg {String} border rounded | circle | thumbnail
1460 * @cfg {String} src image source
1461 * @cfg {String} alt image alternative text
1462 * @cfg {String} href a tag href
1463 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1464 * @cfg {String} xsUrl xs image source
1465 * @cfg {String} smUrl sm image source
1466 * @cfg {String} mdUrl md image source
1467 * @cfg {String} lgUrl lg image source
1470 * Create a new Input
1471 * @param {Object} config The config object
1474 Roo.bootstrap.Img = function(config){
1475 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1481 * The img click event for the img.
1482 * @param {Roo.EventObject} e
1488 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1490 imgResponsive: true,
1500 getAutoCreate : function()
1502 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1503 return this.createSingleImg();
1508 cls: 'roo-image-responsive-group',
1513 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1515 if(!_this[size + 'Url']){
1521 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1522 html: _this.html || cfg.html,
1523 src: _this[size + 'Url']
1526 img.cls += ' roo-image-responsive-' + size;
1528 var s = ['xs', 'sm', 'md', 'lg'];
1530 s.splice(s.indexOf(size), 1);
1532 Roo.each(s, function(ss){
1533 img.cls += ' hidden-' + ss;
1536 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1537 cfg.cls += ' img-' + _this.border;
1541 cfg.alt = _this.alt;
1554 a.target = _this.target;
1558 cfg.cn.push((_this.href) ? a : img);
1565 createSingleImg : function()
1569 cls: (this.imgResponsive) ? 'img-responsive' : '',
1571 src : 'about:blank' // just incase src get's set to undefined?!?
1574 cfg.html = this.html || cfg.html;
1576 cfg.src = this.src || cfg.src;
1578 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1579 cfg.cls += ' img-' + this.border;
1596 a.target = this.target;
1601 return (this.href) ? a : cfg;
1604 initEvents: function()
1607 this.el.on('click', this.onClick, this);
1612 onClick : function(e)
1614 Roo.log('img onclick');
1615 this.fireEvent('click', this, e);
1618 * Sets the url of the image - used to update it
1619 * @param {String} url the url of the image
1622 setSrc : function(url)
1626 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1627 this.el.dom.src = url;
1631 this.el.select('img', true).first().dom.src = url;
1647 * @class Roo.bootstrap.Link
1648 * @extends Roo.bootstrap.Component
1649 * Bootstrap Link Class
1650 * @cfg {String} alt image alternative text
1651 * @cfg {String} href a tag href
1652 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1653 * @cfg {String} html the content of the link.
1654 * @cfg {String} anchor name for the anchor link
1655 * @cfg {String} fa - favicon
1657 * @cfg {Boolean} preventDefault (true | false) default false
1661 * Create a new Input
1662 * @param {Object} config The config object
1665 Roo.bootstrap.Link = function(config){
1666 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1672 * The img click event for the img.
1673 * @param {Roo.EventObject} e
1679 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1683 preventDefault: false,
1689 getAutoCreate : function()
1691 var html = this.html || '';
1693 if (this.fa !== false) {
1694 html = '<i class="fa fa-' + this.fa + '"></i>';
1699 // anchor's do not require html/href...
1700 if (this.anchor === false) {
1702 cfg.href = this.href || '#';
1704 cfg.name = this.anchor;
1705 if (this.html !== false || this.fa !== false) {
1708 if (this.href !== false) {
1709 cfg.href = this.href;
1713 if(this.alt !== false){
1718 if(this.target !== false) {
1719 cfg.target = this.target;
1725 initEvents: function() {
1727 if(!this.href || this.preventDefault){
1728 this.el.on('click', this.onClick, this);
1732 onClick : function(e)
1734 if(this.preventDefault){
1737 //Roo.log('img onclick');
1738 this.fireEvent('click', this, e);
1751 * @class Roo.bootstrap.Header
1752 * @extends Roo.bootstrap.Component
1753 * Bootstrap Header class
1754 * @cfg {String} html content of header
1755 * @cfg {Number} level (1|2|3|4|5|6) default 1
1758 * Create a new Header
1759 * @param {Object} config The config object
1763 Roo.bootstrap.Header = function(config){
1764 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1767 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1775 getAutoCreate : function(){
1780 tag: 'h' + (1 *this.level),
1781 html: this.html || ''
1793 * Ext JS Library 1.1.1
1794 * Copyright(c) 2006-2007, Ext JS, LLC.
1796 * Originally Released Under LGPL - original licence link has changed is not relivant.
1799 * <script type="text/javascript">
1803 * @class Roo.bootstrap.MenuMgr
1804 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1807 Roo.bootstrap.MenuMgr = function(){
1808 var menus, active, groups = {}, attached = false, lastShow = new Date();
1810 // private - called when first menu is created
1813 active = new Roo.util.MixedCollection();
1814 Roo.get(document).addKeyListener(27, function(){
1815 if(active.length > 0){
1823 if(active && active.length > 0){
1824 var c = active.clone();
1834 if(active.length < 1){
1835 Roo.get(document).un("mouseup", onMouseDown);
1843 var last = active.last();
1844 lastShow = new Date();
1847 Roo.get(document).on("mouseup", onMouseDown);
1852 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1853 m.parentMenu.activeChild = m;
1854 }else if(last && last.isVisible()){
1855 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1860 function onBeforeHide(m){
1862 m.activeChild.hide();
1864 if(m.autoHideTimer){
1865 clearTimeout(m.autoHideTimer);
1866 delete m.autoHideTimer;
1871 function onBeforeShow(m){
1872 var pm = m.parentMenu;
1873 if(!pm && !m.allowOtherMenus){
1875 }else if(pm && pm.activeChild && active != m){
1876 pm.activeChild.hide();
1880 // private this should really trigger on mouseup..
1881 function onMouseDown(e){
1882 Roo.log("on Mouse Up");
1884 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1885 Roo.log("MenuManager hideAll");
1894 function onBeforeCheck(mi, state){
1896 var g = groups[mi.group];
1897 for(var i = 0, l = g.length; i < l; i++){
1899 g[i].setChecked(false);
1908 * Hides all menus that are currently visible
1910 hideAll : function(){
1915 register : function(menu){
1919 menus[menu.id] = menu;
1920 menu.on("beforehide", onBeforeHide);
1921 menu.on("hide", onHide);
1922 menu.on("beforeshow", onBeforeShow);
1923 menu.on("show", onShow);
1925 if(g && menu.events["checkchange"]){
1929 groups[g].push(menu);
1930 menu.on("checkchange", onCheck);
1935 * Returns a {@link Roo.menu.Menu} object
1936 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1937 * be used to generate and return a new Menu instance.
1939 get : function(menu){
1940 if(typeof menu == "string"){ // menu id
1942 }else if(menu.events){ // menu instance
1945 /*else if(typeof menu.length == 'number'){ // array of menu items?
1946 return new Roo.bootstrap.Menu({items:menu});
1947 }else{ // otherwise, must be a config
1948 return new Roo.bootstrap.Menu(menu);
1955 unregister : function(menu){
1956 delete menus[menu.id];
1957 menu.un("beforehide", onBeforeHide);
1958 menu.un("hide", onHide);
1959 menu.un("beforeshow", onBeforeShow);
1960 menu.un("show", onShow);
1962 if(g && menu.events["checkchange"]){
1963 groups[g].remove(menu);
1964 menu.un("checkchange", onCheck);
1969 registerCheckable : function(menuItem){
1970 var g = menuItem.group;
1975 groups[g].push(menuItem);
1976 menuItem.on("beforecheckchange", onBeforeCheck);
1981 unregisterCheckable : function(menuItem){
1982 var g = menuItem.group;
1984 groups[g].remove(menuItem);
1985 menuItem.un("beforecheckchange", onBeforeCheck);
1997 * @class Roo.bootstrap.Menu
1998 * @extends Roo.bootstrap.Component
1999 * Bootstrap Menu class - container for MenuItems
2000 * @cfg {String} type (dropdown|treeview|submenu) type of menu
2001 * @cfg {bool} hidden if the menu should be hidden when rendered.
2002 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
2003 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
2007 * @param {Object} config The config object
2011 Roo.bootstrap.Menu = function(config){
2012 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2013 if (this.registerMenu && this.type != 'treeview') {
2014 Roo.bootstrap.MenuMgr.register(this);
2021 * Fires before this menu is displayed
2022 * @param {Roo.menu.Menu} this
2027 * Fires before this menu is hidden
2028 * @param {Roo.menu.Menu} this
2033 * Fires after this menu is displayed
2034 * @param {Roo.menu.Menu} this
2039 * Fires after this menu is hidden
2040 * @param {Roo.menu.Menu} this
2045 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2046 * @param {Roo.menu.Menu} this
2047 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2048 * @param {Roo.EventObject} e
2053 * Fires when the mouse is hovering over this menu
2054 * @param {Roo.menu.Menu} this
2055 * @param {Roo.EventObject} e
2056 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2061 * Fires when the mouse exits this menu
2062 * @param {Roo.menu.Menu} this
2063 * @param {Roo.EventObject} e
2064 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2069 * Fires when a menu item contained in this menu is clicked
2070 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2071 * @param {Roo.EventObject} e
2075 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2078 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2082 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2085 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2087 registerMenu : true,
2089 menuItems :false, // stores the menu items..
2099 getChildContainer : function() {
2103 getAutoCreate : function(){
2105 //if (['right'].indexOf(this.align)!==-1) {
2106 // cfg.cn[1].cls += ' pull-right'
2112 cls : 'dropdown-menu' ,
2113 style : 'z-index:1000'
2117 if (this.type === 'submenu') {
2118 cfg.cls = 'submenu active';
2120 if (this.type === 'treeview') {
2121 cfg.cls = 'treeview-menu';
2126 initEvents : function() {
2128 // Roo.log("ADD event");
2129 // Roo.log(this.triggerEl.dom);
2131 this.triggerEl.on('click', this.onTriggerClick, this);
2133 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2136 if (this.triggerEl.hasClass('nav-item')) {
2137 // dropdown toggle on the 'a' in BS4?
2138 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2140 this.triggerEl.addClass('dropdown-toggle');
2143 this.el.on('touchstart' , this.onTouch, this);
2145 this.el.on('click' , this.onClick, this);
2147 this.el.on("mouseover", this.onMouseOver, this);
2148 this.el.on("mouseout", this.onMouseOut, this);
2152 findTargetItem : function(e)
2154 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2158 //Roo.log(t); Roo.log(t.id);
2160 //Roo.log(this.menuitems);
2161 return this.menuitems.get(t.id);
2163 //return this.items.get(t.menuItemId);
2169 onTouch : function(e)
2171 Roo.log("menu.onTouch");
2172 //e.stopEvent(); this make the user popdown broken
2176 onClick : function(e)
2178 Roo.log("menu.onClick");
2180 var t = this.findTargetItem(e);
2181 if(!t || t.isContainer){
2186 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2187 if(t == this.activeItem && t.shouldDeactivate(e)){
2188 this.activeItem.deactivate();
2189 delete this.activeItem;
2193 this.setActiveItem(t, true);
2201 Roo.log('pass click event');
2205 this.fireEvent("click", this, t, e);
2209 if(!t.href.length || t.href == '#'){
2210 (function() { _this.hide(); }).defer(100);
2215 onMouseOver : function(e){
2216 var t = this.findTargetItem(e);
2219 // if(t.canActivate && !t.disabled){
2220 // this.setActiveItem(t, true);
2224 this.fireEvent("mouseover", this, e, t);
2226 isVisible : function(){
2227 return !this.hidden;
2229 onMouseOut : function(e){
2230 var t = this.findTargetItem(e);
2233 // if(t == this.activeItem && t.shouldDeactivate(e)){
2234 // this.activeItem.deactivate();
2235 // delete this.activeItem;
2238 this.fireEvent("mouseout", this, e, t);
2243 * Displays this menu relative to another element
2244 * @param {String/HTMLElement/Roo.Element} element The element to align to
2245 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2246 * the element (defaults to this.defaultAlign)
2247 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2249 show : function(el, pos, parentMenu){
2250 this.parentMenu = parentMenu;
2254 this.fireEvent("beforeshow", this);
2255 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2258 * Displays this menu at a specific xy position
2259 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2260 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2262 showAt : function(xy, parentMenu, /* private: */_e){
2263 this.parentMenu = parentMenu;
2268 this.fireEvent("beforeshow", this);
2269 //xy = this.el.adjustForConstraints(xy);
2273 this.hideMenuItems();
2274 this.hidden = false;
2275 this.triggerEl.addClass('open');
2276 this.el.addClass('show');
2278 // reassign x when hitting right
2279 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2280 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2283 // reassign y when hitting bottom
2284 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2285 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2288 // but the list may align on trigger left or trigger top... should it be a properity?
2290 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2295 this.fireEvent("show", this);
2301 this.doFocus.defer(50, this);
2305 doFocus : function(){
2307 this.focusEl.focus();
2312 * Hides this menu and optionally all parent menus
2313 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2315 hide : function(deep)
2318 this.hideMenuItems();
2319 if(this.el && this.isVisible()){
2320 this.fireEvent("beforehide", this);
2321 if(this.activeItem){
2322 this.activeItem.deactivate();
2323 this.activeItem = null;
2325 this.triggerEl.removeClass('open');;
2326 this.el.removeClass('show');
2328 this.fireEvent("hide", this);
2330 if(deep === true && this.parentMenu){
2331 this.parentMenu.hide(true);
2335 onTriggerClick : function(e)
2337 Roo.log('trigger click');
2339 var target = e.getTarget();
2341 Roo.log(target.nodeName.toLowerCase());
2343 if(target.nodeName.toLowerCase() === 'i'){
2349 onTriggerPress : function(e)
2351 Roo.log('trigger press');
2352 //Roo.log(e.getTarget());
2353 // Roo.log(this.triggerEl.dom);
2355 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2356 var pel = Roo.get(e.getTarget());
2357 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2358 Roo.log('is treeview or dropdown?');
2362 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2366 if (this.isVisible()) {
2371 this.show(this.triggerEl, false, false);
2374 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2381 hideMenuItems : function()
2383 Roo.log("hide Menu Items");
2387 //$(backdrop).remove()
2388 this.el.select('.open',true).each(function(aa) {
2390 aa.removeClass('open');
2391 //var parent = getParent($(this))
2392 //var relatedTarget = { relatedTarget: this }
2394 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2395 //if (e.isDefaultPrevented()) return
2396 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2399 addxtypeChild : function (tree, cntr) {
2400 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2402 this.menuitems.add(comp);
2414 this.getEl().dom.innerHTML = '';
2415 this.menuitems.clear();
2429 * @class Roo.bootstrap.MenuItem
2430 * @extends Roo.bootstrap.Component
2431 * Bootstrap MenuItem class
2432 * @cfg {String} html the menu label
2433 * @cfg {String} href the link
2434 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2435 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2436 * @cfg {Boolean} active used on sidebars to highlight active itesm
2437 * @cfg {String} fa favicon to show on left of menu item.
2438 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2442 * Create a new MenuItem
2443 * @param {Object} config The config object
2447 Roo.bootstrap.MenuItem = function(config){
2448 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2453 * The raw click event for the entire grid.
2454 * @param {Roo.bootstrap.MenuItem} this
2455 * @param {Roo.EventObject} e
2461 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2465 preventDefault: false,
2466 isContainer : false,
2470 getAutoCreate : function(){
2472 if(this.isContainer){
2475 cls: 'dropdown-menu-item dropdown-item'
2489 if (this.fa !== false) {
2492 cls : 'fa fa-' + this.fa
2501 cls: 'dropdown-menu-item dropdown-item',
2504 if (this.parent().type == 'treeview') {
2505 cfg.cls = 'treeview-menu';
2508 cfg.cls += ' active';
2513 anc.href = this.href || cfg.cn[0].href ;
2514 ctag.html = this.html || cfg.cn[0].html ;
2518 initEvents: function()
2520 if (this.parent().type == 'treeview') {
2521 this.el.select('a').on('click', this.onClick, this);
2525 this.menu.parentType = this.xtype;
2526 this.menu.triggerEl = this.el;
2527 this.menu = this.addxtype(Roo.apply({}, this.menu));
2531 onClick : function(e)
2533 Roo.log('item on click ');
2535 if(this.preventDefault){
2538 //this.parent().hideMenuItems();
2540 this.fireEvent('click', this, e);
2559 * @class Roo.bootstrap.MenuSeparator
2560 * @extends Roo.bootstrap.Component
2561 * Bootstrap MenuSeparator class
2564 * Create a new MenuItem
2565 * @param {Object} config The config object
2569 Roo.bootstrap.MenuSeparator = function(config){
2570 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2573 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2575 getAutoCreate : function(){
2594 * @class Roo.bootstrap.Modal
2595 * @extends Roo.bootstrap.Component
2596 * Bootstrap Modal class
2597 * @cfg {String} title Title of dialog
2598 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2599 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2600 * @cfg {Boolean} specificTitle default false
2601 * @cfg {Array} buttons Array of buttons or standard button set..
2602 * @cfg {String} buttonPosition (left|right|center) default right
2603 * @cfg {Boolean} animate default true
2604 * @cfg {Boolean} allow_close default true
2605 * @cfg {Boolean} fitwindow default false
2606 * @cfg {String} size (sm|lg) default empty
2607 * @cfg {Number} max_width set the max width of modal
2611 * Create a new Modal Dialog
2612 * @param {Object} config The config object
2615 Roo.bootstrap.Modal = function(config){
2616 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2621 * The raw btnclick event for the button
2622 * @param {Roo.EventObject} e
2627 * Fire when dialog resize
2628 * @param {Roo.bootstrap.Modal} this
2629 * @param {Roo.EventObject} e
2633 this.buttons = this.buttons || [];
2636 this.tmpl = Roo.factory(this.tmpl);
2641 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2643 title : 'test dialog',
2653 specificTitle: false,
2655 buttonPosition: 'right',
2678 onRender : function(ct, position)
2680 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2683 var cfg = Roo.apply({}, this.getAutoCreate());
2686 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2688 //if (!cfg.name.length) {
2692 cfg.cls += ' ' + this.cls;
2695 cfg.style = this.style;
2697 this.el = Roo.get(document.body).createChild(cfg, position);
2699 //var type = this.el.dom.type;
2702 if(this.tabIndex !== undefined){
2703 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2706 this.dialogEl = this.el.select('.modal-dialog',true).first();
2707 this.bodyEl = this.el.select('.modal-body',true).first();
2708 this.closeEl = this.el.select('.modal-header .close', true).first();
2709 this.headerEl = this.el.select('.modal-header',true).first();
2710 this.titleEl = this.el.select('.modal-title',true).first();
2711 this.footerEl = this.el.select('.modal-footer',true).first();
2713 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2715 //this.el.addClass("x-dlg-modal");
2717 if (this.buttons.length) {
2718 Roo.each(this.buttons, function(bb) {
2719 var b = Roo.apply({}, bb);
2720 b.xns = b.xns || Roo.bootstrap;
2721 b.xtype = b.xtype || 'Button';
2722 if (typeof(b.listeners) == 'undefined') {
2723 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2726 var btn = Roo.factory(b);
2728 btn.render(this.el.select('.modal-footer div').first());
2732 // render the children.
2735 if(typeof(this.items) != 'undefined'){
2736 var items = this.items;
2739 for(var i =0;i < items.length;i++) {
2740 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2744 this.items = nitems;
2746 // where are these used - they used to be body/close/footer
2750 //this.el.addClass([this.fieldClass, this.cls]);
2754 getAutoCreate : function()
2758 html : this.html || ''
2763 cls : 'modal-title',
2767 if(this.specificTitle){
2773 if (this.allow_close) {
2785 if(this.size.length){
2786 size = 'modal-' + this.size;
2793 cls: "modal-dialog " + size,
2796 cls : "modal-content",
2799 cls : 'modal-header',
2804 cls : 'modal-footer',
2808 cls: 'btn-' + this.buttonPosition
2825 modal.cls += ' fade';
2831 getChildContainer : function() {
2836 getButtonContainer : function() {
2837 return this.el.select('.modal-footer div',true).first();
2840 initEvents : function()
2842 if (this.allow_close) {
2843 this.closeEl.on('click', this.hide, this);
2845 Roo.EventManager.onWindowResize(this.resize, this, true);
2852 this.maskEl.setSize(
2853 Roo.lib.Dom.getViewWidth(true),
2854 Roo.lib.Dom.getViewHeight(true)
2857 if (this.fitwindow) {
2859 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2860 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2865 if(this.max_width !== 0) {
2867 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2870 this.setSize(w, this.height);
2874 if(this.max_height) {
2875 this.setSize(w,Math.min(
2877 Roo.lib.Dom.getViewportHeight(true) - 60
2883 if(!this.fit_content) {
2884 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2888 this.setSize(w, Math.min(
2890 this.headerEl.getHeight() +
2891 this.footerEl.getHeight() +
2892 this.getChildHeight(this.bodyEl.dom.childNodes),
2893 Roo.lib.Dom.getViewportHeight(true) - 60)
2899 setSize : function(w,h)
2910 if (!this.rendered) {
2914 //this.el.setStyle('display', 'block');
2915 this.el.removeClass('hideing');
2916 this.el.addClass('show');
2918 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2921 this.el.addClass('in');
2924 this.el.addClass('in');
2927 // not sure how we can show data in here..
2929 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2932 Roo.get(document.body).addClass("x-body-masked");
2934 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2935 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2936 this.maskEl.addClass('show');
2940 this.fireEvent('show', this);
2942 // set zindex here - otherwise it appears to be ignored...
2943 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2946 this.items.forEach( function(e) {
2947 e.layout ? e.layout() : false;
2955 if(this.fireEvent("beforehide", this) !== false){
2956 this.maskEl.removeClass('show');
2957 Roo.get(document.body).removeClass("x-body-masked");
2958 this.el.removeClass('in');
2959 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2961 if(this.animate){ // why
2962 this.el.addClass('hideing');
2964 if (!this.el.hasClass('hideing')) {
2965 return; // it's been shown again...
2967 this.el.removeClass('show');
2968 this.el.removeClass('hideing');
2972 this.el.removeClass('show');
2974 this.fireEvent('hide', this);
2977 isVisible : function()
2980 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2984 addButton : function(str, cb)
2988 var b = Roo.apply({}, { html : str } );
2989 b.xns = b.xns || Roo.bootstrap;
2990 b.xtype = b.xtype || 'Button';
2991 if (typeof(b.listeners) == 'undefined') {
2992 b.listeners = { click : cb.createDelegate(this) };
2995 var btn = Roo.factory(b);
2997 btn.render(this.el.select('.modal-footer div').first());
3003 setDefaultButton : function(btn)
3005 //this.el.select('.modal-footer').()
3009 resizeTo: function(w,h)
3013 this.dialogEl.setWidth(w);
3014 if (this.diff === false) {
3015 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3018 this.bodyEl.setHeight(h - this.diff);
3020 this.fireEvent('resize', this);
3023 setContentSize : function(w, h)
3027 onButtonClick: function(btn,e)
3030 this.fireEvent('btnclick', btn.name, e);
3033 * Set the title of the Dialog
3034 * @param {String} str new Title
3036 setTitle: function(str) {
3037 this.titleEl.dom.innerHTML = str;
3040 * Set the body of the Dialog
3041 * @param {String} str new Title
3043 setBody: function(str) {
3044 this.bodyEl.dom.innerHTML = str;
3047 * Set the body of the Dialog using the template
3048 * @param {Obj} data - apply this data to the template and replace the body contents.
3050 applyBody: function(obj)
3053 Roo.log("Error - using apply Body without a template");
3056 this.tmpl.overwrite(this.bodyEl, obj);
3059 getChildHeight : function(child_nodes)
3063 child_nodes.length == 0
3068 var child_height = 0;
3070 for(var i = 0; i < child_nodes.length; i++) {
3073 * for modal with tabs...
3074 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3076 var layout_childs = child_nodes[i].childNodes;
3078 for(var j = 0; j < layout_childs.length; j++) {
3080 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3082 var layout_body_childs = layout_childs[j].childNodes;
3084 for(var k = 0; k < layout_body_childs.length; k++) {
3086 if(layout_body_childs[k].classList.contains('navbar')) {
3087 child_height += layout_body_childs[k].offsetHeight;
3091 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3093 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3095 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3097 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3098 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3113 child_height += child_nodes[i].offsetHeight;
3114 // Roo.log(child_nodes[i].offsetHeight);
3117 return child_height;
3123 Roo.apply(Roo.bootstrap.Modal, {
3125 * Button config that displays a single OK button
3134 * Button config that displays Yes and No buttons
3150 * Button config that displays OK and Cancel buttons
3165 * Button config that displays Yes, No and Cancel buttons
3189 * messagebox - can be used as a replace
3193 * @class Roo.MessageBox
3194 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3198 Roo.Msg.alert('Status', 'Changes saved successfully.');
3200 // Prompt for user data:
3201 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3203 // process text value...
3207 // Show a dialog using config options:
3209 title:'Save Changes?',
3210 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3211 buttons: Roo.Msg.YESNOCANCEL,
3218 Roo.bootstrap.MessageBox = function(){
3219 var dlg, opt, mask, waitTimer;
3220 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3221 var buttons, activeTextEl, bwidth;
3225 var handleButton = function(button){
3227 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3231 var handleHide = function(){
3233 dlg.el.removeClass(opt.cls);
3236 // Roo.TaskMgr.stop(waitTimer);
3237 // waitTimer = null;
3242 var updateButtons = function(b){
3245 buttons["ok"].hide();
3246 buttons["cancel"].hide();
3247 buttons["yes"].hide();
3248 buttons["no"].hide();
3249 //dlg.footer.dom.style.display = 'none';
3252 dlg.footerEl.dom.style.display = '';
3253 for(var k in buttons){
3254 if(typeof buttons[k] != "function"){
3257 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3258 width += buttons[k].el.getWidth()+15;
3268 var handleEsc = function(d, k, e){
3269 if(opt && opt.closable !== false){
3279 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3280 * @return {Roo.BasicDialog} The BasicDialog element
3282 getDialog : function(){
3284 dlg = new Roo.bootstrap.Modal( {
3287 //constraintoviewport:false,
3289 //collapsible : false,
3294 //buttonAlign:"center",
3295 closeClick : function(){
3296 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3299 handleButton("cancel");
3304 dlg.on("hide", handleHide);
3306 //dlg.addKeyListener(27, handleEsc);
3308 this.buttons = buttons;
3309 var bt = this.buttonText;
3310 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3311 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3312 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3313 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3315 bodyEl = dlg.bodyEl.createChild({
3317 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3318 '<textarea class="roo-mb-textarea"></textarea>' +
3319 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3321 msgEl = bodyEl.dom.firstChild;
3322 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3323 textboxEl.enableDisplayMode();
3324 textboxEl.addKeyListener([10,13], function(){
3325 if(dlg.isVisible() && opt && opt.buttons){
3328 }else if(opt.buttons.yes){
3329 handleButton("yes");
3333 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3334 textareaEl.enableDisplayMode();
3335 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3336 progressEl.enableDisplayMode();
3338 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3339 var pf = progressEl.dom.firstChild;
3341 pp = Roo.get(pf.firstChild);
3342 pp.setHeight(pf.offsetHeight);
3350 * Updates the message box body text
3351 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3352 * the XHTML-compliant non-breaking space character '&#160;')
3353 * @return {Roo.MessageBox} This message box
3355 updateText : function(text)
3357 if(!dlg.isVisible() && !opt.width){
3358 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3359 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3361 msgEl.innerHTML = text || ' ';
3363 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3364 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3366 Math.min(opt.width || cw , this.maxWidth),
3367 Math.max(opt.minWidth || this.minWidth, bwidth)
3370 activeTextEl.setWidth(w);
3372 if(dlg.isVisible()){
3373 dlg.fixedcenter = false;
3375 // to big, make it scroll. = But as usual stupid IE does not support
3378 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3379 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3380 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3382 bodyEl.dom.style.height = '';
3383 bodyEl.dom.style.overflowY = '';
3386 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3388 bodyEl.dom.style.overflowX = '';
3391 dlg.setContentSize(w, bodyEl.getHeight());
3392 if(dlg.isVisible()){
3393 dlg.fixedcenter = true;
3399 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3400 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3401 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3402 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3403 * @return {Roo.MessageBox} This message box
3405 updateProgress : function(value, text){
3407 this.updateText(text);
3410 if (pp) { // weird bug on my firefox - for some reason this is not defined
3411 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3412 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3418 * Returns true if the message box is currently displayed
3419 * @return {Boolean} True if the message box is visible, else false
3421 isVisible : function(){
3422 return dlg && dlg.isVisible();
3426 * Hides the message box if it is displayed
3429 if(this.isVisible()){
3435 * Displays a new message box, or reinitializes an existing message box, based on the config options
3436 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3437 * The following config object properties are supported:
3439 Property Type Description
3440 ---------- --------------- ------------------------------------------------------------------------------------
3441 animEl String/Element An id or Element from which the message box should animate as it opens and
3442 closes (defaults to undefined)
3443 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3444 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3445 closable Boolean False to hide the top-right close button (defaults to true). Note that
3446 progress and wait dialogs will ignore this property and always hide the
3447 close button as they can only be closed programmatically.
3448 cls String A custom CSS class to apply to the message box element
3449 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3450 displayed (defaults to 75)
3451 fn Function A callback function to execute after closing the dialog. The arguments to the
3452 function will be btn (the name of the button that was clicked, if applicable,
3453 e.g. "ok"), and text (the value of the active text field, if applicable).
3454 Progress and wait dialogs will ignore this option since they do not respond to
3455 user actions and can only be closed programmatically, so any required function
3456 should be called by the same code after it closes the dialog.
3457 icon String A CSS class that provides a background image to be used as an icon for
3458 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3459 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3460 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3461 modal Boolean False to allow user interaction with the page while the message box is
3462 displayed (defaults to true)
3463 msg String A string that will replace the existing message box body text (defaults
3464 to the XHTML-compliant non-breaking space character ' ')
3465 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3466 progress Boolean True to display a progress bar (defaults to false)
3467 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3468 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3469 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3470 title String The title text
3471 value String The string value to set into the active textbox element if displayed
3472 wait Boolean True to display a progress bar (defaults to false)
3473 width Number The width of the dialog in pixels
3480 msg: 'Please enter your address:',
3482 buttons: Roo.MessageBox.OKCANCEL,
3485 animEl: 'addAddressBtn'
3488 * @param {Object} config Configuration options
3489 * @return {Roo.MessageBox} This message box
3491 show : function(options)
3494 // this causes nightmares if you show one dialog after another
3495 // especially on callbacks..
3497 if(this.isVisible()){
3500 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3501 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3502 Roo.log("New Dialog Message:" + options.msg )
3503 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3504 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3507 var d = this.getDialog();
3509 d.setTitle(opt.title || " ");
3510 d.closeEl.setDisplayed(opt.closable !== false);
3511 activeTextEl = textboxEl;
3512 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3517 textareaEl.setHeight(typeof opt.multiline == "number" ?
3518 opt.multiline : this.defaultTextHeight);
3519 activeTextEl = textareaEl;
3528 progressEl.setDisplayed(opt.progress === true);
3529 this.updateProgress(0);
3530 activeTextEl.dom.value = opt.value || "";
3532 dlg.setDefaultButton(activeTextEl);
3534 var bs = opt.buttons;
3538 }else if(bs && bs.yes){
3539 db = buttons["yes"];
3541 dlg.setDefaultButton(db);
3543 bwidth = updateButtons(opt.buttons);
3544 this.updateText(opt.msg);
3546 d.el.addClass(opt.cls);
3548 d.proxyDrag = opt.proxyDrag === true;
3549 d.modal = opt.modal !== false;
3550 d.mask = opt.modal !== false ? mask : false;
3552 // force it to the end of the z-index stack so it gets a cursor in FF
3553 document.body.appendChild(dlg.el.dom);
3554 d.animateTarget = null;
3555 d.show(options.animEl);
3561 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3562 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3563 * and closing the message box when the process is complete.
3564 * @param {String} title The title bar text
3565 * @param {String} msg The message box body text
3566 * @return {Roo.MessageBox} This message box
3568 progress : function(title, msg){
3575 minWidth: this.minProgressWidth,
3582 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3583 * If a callback function is passed it will be called after the user clicks the button, and the
3584 * id of the button that was clicked will be passed as the only parameter to the callback
3585 * (could also be the top-right close button).
3586 * @param {String} title The title bar text
3587 * @param {String} msg The message box body text
3588 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3589 * @param {Object} scope (optional) The scope of the callback function
3590 * @return {Roo.MessageBox} This message box
3592 alert : function(title, msg, fn, scope)
3607 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3608 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3609 * You are responsible for closing the message box when the process is complete.
3610 * @param {String} msg The message box body text
3611 * @param {String} title (optional) The title bar text
3612 * @return {Roo.MessageBox} This message box
3614 wait : function(msg, title){
3625 waitTimer = Roo.TaskMgr.start({
3627 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3635 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3636 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3637 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3638 * @param {String} title The title bar text
3639 * @param {String} msg The message box body text
3640 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3641 * @param {Object} scope (optional) The scope of the callback function
3642 * @return {Roo.MessageBox} This message box
3644 confirm : function(title, msg, fn, scope){
3648 buttons: this.YESNO,
3657 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3658 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3659 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3660 * (could also be the top-right close button) and the text that was entered will be passed as the two
3661 * parameters to the callback.
3662 * @param {String} title The title bar text
3663 * @param {String} msg The message box body text
3664 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3665 * @param {Object} scope (optional) The scope of the callback function
3666 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3667 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3668 * @return {Roo.MessageBox} This message box
3670 prompt : function(title, msg, fn, scope, multiline){
3674 buttons: this.OKCANCEL,
3679 multiline: multiline,
3686 * Button config that displays a single OK button
3691 * Button config that displays Yes and No buttons
3694 YESNO : {yes:true, no:true},
3696 * Button config that displays OK and Cancel buttons
3699 OKCANCEL : {ok:true, cancel:true},
3701 * Button config that displays Yes, No and Cancel buttons
3704 YESNOCANCEL : {yes:true, no:true, cancel:true},
3707 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3710 defaultTextHeight : 75,
3712 * The maximum width in pixels of the message box (defaults to 600)
3717 * The minimum width in pixels of the message box (defaults to 100)
3722 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3723 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3726 minProgressWidth : 250,
3728 * An object containing the default button text strings that can be overriden for localized language support.
3729 * Supported properties are: ok, cancel, yes and no.
3730 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3743 * Shorthand for {@link Roo.MessageBox}
3745 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3746 Roo.Msg = Roo.Msg || Roo.MessageBox;
3755 * @class Roo.bootstrap.Navbar
3756 * @extends Roo.bootstrap.Component
3757 * Bootstrap Navbar class
3760 * Create a new Navbar
3761 * @param {Object} config The config object
3765 Roo.bootstrap.Navbar = function(config){
3766 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3770 * @event beforetoggle
3771 * Fire before toggle the menu
3772 * @param {Roo.EventObject} e
3774 "beforetoggle" : true
3778 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3787 getAutoCreate : function(){
3790 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3794 initEvents :function ()
3796 //Roo.log(this.el.select('.navbar-toggle',true));
3797 this.el.select('.navbar-toggle',true).on('click', function() {
3798 if(this.fireEvent('beforetoggle', this) !== false){
3799 this.el.select('.navbar-collapse',true).toggleClass('in');
3809 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3811 var size = this.el.getSize();
3812 this.maskEl.setSize(size.width, size.height);
3813 this.maskEl.enableDisplayMode("block");
3822 getChildContainer : function()
3824 if (this.el.select('.collapse').getCount()) {
3825 return this.el.select('.collapse',true).first();
3858 * @class Roo.bootstrap.NavSimplebar
3859 * @extends Roo.bootstrap.Navbar
3860 * Bootstrap Sidebar class
3862 * @cfg {Boolean} inverse is inverted color
3864 * @cfg {String} type (nav | pills | tabs)
3865 * @cfg {Boolean} arrangement stacked | justified
3866 * @cfg {String} align (left | right) alignment
3868 * @cfg {Boolean} main (true|false) main nav bar? default false
3869 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3871 * @cfg {String} tag (header|footer|nav|div) default is nav
3877 * Create a new Sidebar
3878 * @param {Object} config The config object
3882 Roo.bootstrap.NavSimplebar = function(config){
3883 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3886 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3902 getAutoCreate : function(){
3906 tag : this.tag || 'div',
3919 this.type = this.type || 'nav';
3920 if (['tabs','pills'].indexOf(this.type)!==-1) {
3921 cfg.cn[0].cls += ' nav-' + this.type
3925 if (this.type!=='nav') {
3926 Roo.log('nav type must be nav/tabs/pills')
3928 cfg.cn[0].cls += ' navbar-nav'
3934 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3935 cfg.cn[0].cls += ' nav-' + this.arrangement;
3939 if (this.align === 'right') {
3940 cfg.cn[0].cls += ' navbar-right';
3944 cfg.cls += ' navbar-inverse';
3968 * navbar-expand-md fixed-top
3972 * @class Roo.bootstrap.NavHeaderbar
3973 * @extends Roo.bootstrap.NavSimplebar
3974 * Bootstrap Sidebar class
3976 * @cfg {String} brand what is brand
3977 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3978 * @cfg {String} brand_href href of the brand
3979 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3980 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3981 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3982 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3985 * Create a new Sidebar
3986 * @param {Object} config The config object
3990 Roo.bootstrap.NavHeaderbar = function(config){
3991 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3995 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4002 desktopCenter : false,
4005 getAutoCreate : function(){
4008 tag: this.nav || 'nav',
4009 cls: 'navbar navbar-expand-md',
4015 if (this.desktopCenter) {
4016 cn.push({cls : 'container', cn : []});
4023 cls: 'navbar-header',
4028 cls: 'navbar-toggle navbar-toggler',
4029 'data-toggle': 'collapse',
4034 html: 'Toggle navigation'
4038 cls: 'icon-bar navbar-toggler-icon'
4056 cls: 'collapse navbar-collapse',
4060 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4062 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4063 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4065 // tag can override this..
4067 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4070 if (this.brand !== '') {
4073 href: this.brand_href ? this.brand_href : '#',
4074 cls: 'navbar-brand',
4082 cfg.cls += ' main-nav';
4090 getHeaderChildContainer : function()
4092 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4093 return this.el.select('.navbar-header',true).first();
4096 return this.getChildContainer();
4100 initEvents : function()
4102 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4104 if (this.autohide) {
4109 Roo.get(document).on('scroll',function(e) {
4110 var ns = Roo.get(document).getScroll().top;
4111 var os = prevScroll;
4115 ft.removeClass('slideDown');
4116 ft.addClass('slideUp');
4119 ft.removeClass('slideUp');
4120 ft.addClass('slideDown');
4141 * @class Roo.bootstrap.NavSidebar
4142 * @extends Roo.bootstrap.Navbar
4143 * Bootstrap Sidebar class
4146 * Create a new Sidebar
4147 * @param {Object} config The config object
4151 Roo.bootstrap.NavSidebar = function(config){
4152 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4155 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4157 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4159 getAutoCreate : function(){
4164 cls: 'sidebar sidebar-nav'
4186 * @class Roo.bootstrap.NavGroup
4187 * @extends Roo.bootstrap.Component
4188 * Bootstrap NavGroup class
4189 * @cfg {String} align (left|right)
4190 * @cfg {Boolean} inverse
4191 * @cfg {String} type (nav|pills|tab) default nav
4192 * @cfg {String} navId - reference Id for navbar.
4196 * Create a new nav group
4197 * @param {Object} config The config object
4200 Roo.bootstrap.NavGroup = function(config){
4201 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4204 Roo.bootstrap.NavGroup.register(this);
4208 * Fires when the active item changes
4209 * @param {Roo.bootstrap.NavGroup} this
4210 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4211 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4218 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4229 getAutoCreate : function()
4231 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4238 if (['tabs','pills'].indexOf(this.type)!==-1) {
4239 cfg.cls += ' nav-' + this.type
4241 if (this.type!=='nav') {
4242 Roo.log('nav type must be nav/tabs/pills')
4244 cfg.cls += ' navbar-nav mr-auto'
4247 if (this.parent() && this.parent().sidebar) {
4250 cls: 'dashboard-menu sidebar-menu'
4256 if (this.form === true) {
4262 if (this.align === 'right') {
4263 cfg.cls += ' navbar-right';
4265 cfg.cls += ' navbar-left';
4269 if (this.align === 'right') {
4270 cfg.cls += ' navbar-right';
4274 cfg.cls += ' navbar-inverse';
4282 * sets the active Navigation item
4283 * @param {Roo.bootstrap.NavItem} the new current navitem
4285 setActiveItem : function(item)
4288 Roo.each(this.navItems, function(v){
4293 v.setActive(false, true);
4300 item.setActive(true, true);
4301 this.fireEvent('changed', this, item, prev);
4306 * gets the active Navigation item
4307 * @return {Roo.bootstrap.NavItem} the current navitem
4309 getActive : function()
4313 Roo.each(this.navItems, function(v){
4324 indexOfNav : function()
4328 Roo.each(this.navItems, function(v,i){
4339 * adds a Navigation item
4340 * @param {Roo.bootstrap.NavItem} the navitem to add
4342 addItem : function(cfg)
4344 var cn = new Roo.bootstrap.NavItem(cfg);
4346 cn.parentId = this.id;
4347 cn.onRender(this.el, null);
4351 * register a Navigation item
4352 * @param {Roo.bootstrap.NavItem} the navitem to add
4354 register : function(item)
4356 this.navItems.push( item);
4357 item.navId = this.navId;
4362 * clear all the Navigation item
4365 clearAll : function()
4368 this.el.dom.innerHTML = '';
4371 getNavItem: function(tabId)
4374 Roo.each(this.navItems, function(e) {
4375 if (e.tabId == tabId) {
4385 setActiveNext : function()
4387 var i = this.indexOfNav(this.getActive());
4388 if (i > this.navItems.length) {
4391 this.setActiveItem(this.navItems[i+1]);
4393 setActivePrev : function()
4395 var i = this.indexOfNav(this.getActive());
4399 this.setActiveItem(this.navItems[i-1]);
4401 clearWasActive : function(except) {
4402 Roo.each(this.navItems, function(e) {
4403 if (e.tabId != except.tabId && e.was_active) {
4404 e.was_active = false;
4411 getWasActive : function ()
4414 Roo.each(this.navItems, function(e) {
4429 Roo.apply(Roo.bootstrap.NavGroup, {
4433 * register a Navigation Group
4434 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4436 register : function(navgrp)
4438 this.groups[navgrp.navId] = navgrp;
4442 * fetch a Navigation Group based on the navigation ID
4443 * @param {string} the navgroup to add
4444 * @returns {Roo.bootstrap.NavGroup} the navgroup
4446 get: function(navId) {
4447 if (typeof(this.groups[navId]) == 'undefined') {
4449 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4451 return this.groups[navId] ;
4466 * @class Roo.bootstrap.NavItem
4467 * @extends Roo.bootstrap.Component
4468 * Bootstrap Navbar.NavItem class
4469 * @cfg {String} href link to
4470 * @cfg {String} html content of button
4471 * @cfg {String} badge text inside badge
4472 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4473 * @cfg {String} glyphicon name of glyphicon
4474 * @cfg {String} icon name of font awesome icon
4475 * @cfg {Boolean} active Is item active
4476 * @cfg {Boolean} disabled Is item disabled
4478 * @cfg {Boolean} preventDefault (true | false) default false
4479 * @cfg {String} tabId the tab that this item activates.
4480 * @cfg {String} tagtype (a|span) render as a href or span?
4481 * @cfg {Boolean} animateRef (true|false) link to element default false
4484 * Create a new Navbar Item
4485 * @param {Object} config The config object
4487 Roo.bootstrap.NavItem = function(config){
4488 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4493 * The raw click event for the entire grid.
4494 * @param {Roo.EventObject} e
4499 * Fires when the active item active state changes
4500 * @param {Roo.bootstrap.NavItem} this
4501 * @param {boolean} state the new state
4507 * Fires when scroll to element
4508 * @param {Roo.bootstrap.NavItem} this
4509 * @param {Object} options
4510 * @param {Roo.EventObject} e
4518 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4526 preventDefault : false,
4533 getAutoCreate : function(){
4542 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4544 if (this.disabled) {
4545 cfg.cls += ' disabled';
4548 if (this.href || this.html || this.glyphicon || this.icon) {
4552 href : this.href || "#",
4553 html: this.html || ''
4556 if (this.tagtype == 'a') {
4557 cfg.cn[0].cls = 'nav-link';
4560 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4563 if(this.glyphicon) {
4564 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4569 cfg.cn[0].html += " <span class='caret'></span>";
4573 if (this.badge !== '') {
4575 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4583 initEvents: function()
4585 if (typeof (this.menu) != 'undefined') {
4586 this.menu.parentType = this.xtype;
4587 this.menu.triggerEl = this.el;
4588 this.menu = this.addxtype(Roo.apply({}, this.menu));
4591 this.el.select('a',true).on('click', this.onClick, this);
4593 if(this.tagtype == 'span'){
4594 this.el.select('span',true).on('click', this.onClick, this);
4597 // at this point parent should be available..
4598 this.parent().register(this);
4601 onClick : function(e)
4603 if (e.getTarget('.dropdown-menu-item')) {
4604 // did you click on a menu itemm.... - then don't trigger onclick..
4609 this.preventDefault ||
4612 Roo.log("NavItem - prevent Default?");
4616 if (this.disabled) {
4620 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4621 if (tg && tg.transition) {
4622 Roo.log("waiting for the transitionend");
4628 //Roo.log("fire event clicked");
4629 if(this.fireEvent('click', this, e) === false){
4633 if(this.tagtype == 'span'){
4637 //Roo.log(this.href);
4638 var ael = this.el.select('a',true).first();
4641 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4642 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4643 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4644 return; // ignore... - it's a 'hash' to another page.
4646 Roo.log("NavItem - prevent Default?");
4648 this.scrollToElement(e);
4652 var p = this.parent();
4654 if (['tabs','pills'].indexOf(p.type)!==-1) {
4655 if (typeof(p.setActiveItem) !== 'undefined') {
4656 p.setActiveItem(this);
4660 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4661 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4662 // remove the collapsed menu expand...
4663 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4667 isActive: function () {
4670 setActive : function(state, fire, is_was_active)
4672 if (this.active && !state && this.navId) {
4673 this.was_active = true;
4674 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4676 nv.clearWasActive(this);
4680 this.active = state;
4683 this.el.removeClass('active');
4684 } else if (!this.el.hasClass('active')) {
4685 this.el.addClass('active');
4688 this.fireEvent('changed', this, state);
4691 // show a panel if it's registered and related..
4693 if (!this.navId || !this.tabId || !state || is_was_active) {
4697 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4701 var pan = tg.getPanelByName(this.tabId);
4705 // if we can not flip to new panel - go back to old nav highlight..
4706 if (false == tg.showPanel(pan)) {
4707 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4709 var onav = nv.getWasActive();
4711 onav.setActive(true, false, true);
4720 // this should not be here...
4721 setDisabled : function(state)
4723 this.disabled = state;
4725 this.el.removeClass('disabled');
4726 } else if (!this.el.hasClass('disabled')) {
4727 this.el.addClass('disabled');
4733 * Fetch the element to display the tooltip on.
4734 * @return {Roo.Element} defaults to this.el
4736 tooltipEl : function()
4738 return this.el.select('' + this.tagtype + '', true).first();
4741 scrollToElement : function(e)
4743 var c = document.body;
4746 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4748 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4749 c = document.documentElement;
4752 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4758 var o = target.calcOffsetsTo(c);
4765 this.fireEvent('scrollto', this, options, e);
4767 Roo.get(c).scrollTo('top', options.value, true);
4780 * <span> icon </span>
4781 * <span> text </span>
4782 * <span>badge </span>
4786 * @class Roo.bootstrap.NavSidebarItem
4787 * @extends Roo.bootstrap.NavItem
4788 * Bootstrap Navbar.NavSidebarItem class
4789 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4790 * {Boolean} open is the menu open
4791 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4792 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4793 * {String} buttonSize (sm|md|lg)the extra classes for the button
4794 * {Boolean} showArrow show arrow next to the text (default true)
4796 * Create a new Navbar Button
4797 * @param {Object} config The config object
4799 Roo.bootstrap.NavSidebarItem = function(config){
4800 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4805 * The raw click event for the entire grid.
4806 * @param {Roo.EventObject} e
4811 * Fires when the active item active state changes
4812 * @param {Roo.bootstrap.NavSidebarItem} this
4813 * @param {boolean} state the new state
4821 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4823 badgeWeight : 'default',
4829 buttonWeight : 'default',
4835 getAutoCreate : function(){
4840 href : this.href || '#',
4846 if(this.buttonView){
4849 href : this.href || '#',
4850 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4863 cfg.cls += ' active';
4866 if (this.disabled) {
4867 cfg.cls += ' disabled';
4870 cfg.cls += ' open x-open';
4873 if (this.glyphicon || this.icon) {
4874 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4875 a.cn.push({ tag : 'i', cls : c }) ;
4878 if(!this.buttonView){
4881 html : this.html || ''
4888 if (this.badge !== '') {
4889 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4895 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4898 a.cls += ' dropdown-toggle treeview' ;
4904 initEvents : function()
4906 if (typeof (this.menu) != 'undefined') {
4907 this.menu.parentType = this.xtype;
4908 this.menu.triggerEl = this.el;
4909 this.menu = this.addxtype(Roo.apply({}, this.menu));
4912 this.el.on('click', this.onClick, this);
4914 if(this.badge !== ''){
4915 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4920 onClick : function(e)
4927 if(this.preventDefault){
4931 this.fireEvent('click', this);
4934 disable : function()
4936 this.setDisabled(true);
4941 this.setDisabled(false);
4944 setDisabled : function(state)
4946 if(this.disabled == state){
4950 this.disabled = state;
4953 this.el.addClass('disabled');
4957 this.el.removeClass('disabled');
4962 setActive : function(state)
4964 if(this.active == state){
4968 this.active = state;
4971 this.el.addClass('active');
4975 this.el.removeClass('active');
4980 isActive: function ()
4985 setBadge : function(str)
4991 this.badgeEl.dom.innerHTML = str;
5008 * @class Roo.bootstrap.Row
5009 * @extends Roo.bootstrap.Component
5010 * Bootstrap Row class (contains columns...)
5014 * @param {Object} config The config object
5017 Roo.bootstrap.Row = function(config){
5018 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5021 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5023 getAutoCreate : function(){
5042 * @class Roo.bootstrap.Element
5043 * @extends Roo.bootstrap.Component
5044 * Bootstrap Element class
5045 * @cfg {String} html contents of the element
5046 * @cfg {String} tag tag of the element
5047 * @cfg {String} cls class of the element
5048 * @cfg {Boolean} preventDefault (true|false) default false
5049 * @cfg {Boolean} clickable (true|false) default false
5052 * Create a new Element
5053 * @param {Object} config The config object
5056 Roo.bootstrap.Element = function(config){
5057 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5063 * When a element is chick
5064 * @param {Roo.bootstrap.Element} this
5065 * @param {Roo.EventObject} e
5071 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5076 preventDefault: false,
5079 getAutoCreate : function(){
5083 // cls: this.cls, double assign in parent class Component.js :: onRender
5090 initEvents: function()
5092 Roo.bootstrap.Element.superclass.initEvents.call(this);
5095 this.el.on('click', this.onClick, this);
5100 onClick : function(e)
5102 if(this.preventDefault){
5106 this.fireEvent('click', this, e);
5109 getValue : function()
5111 return this.el.dom.innerHTML;
5114 setValue : function(value)
5116 this.el.dom.innerHTML = value;
5131 * @class Roo.bootstrap.Pagination
5132 * @extends Roo.bootstrap.Component
5133 * Bootstrap Pagination class
5134 * @cfg {String} size xs | sm | md | lg
5135 * @cfg {Boolean} inverse false | true
5138 * Create a new Pagination
5139 * @param {Object} config The config object
5142 Roo.bootstrap.Pagination = function(config){
5143 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5146 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5152 getAutoCreate : function(){
5158 cfg.cls += ' inverse';
5164 cfg.cls += " " + this.cls;
5182 * @class Roo.bootstrap.PaginationItem
5183 * @extends Roo.bootstrap.Component
5184 * Bootstrap PaginationItem class
5185 * @cfg {String} html text
5186 * @cfg {String} href the link
5187 * @cfg {Boolean} preventDefault (true | false) default true
5188 * @cfg {Boolean} active (true | false) default false
5189 * @cfg {Boolean} disabled default false
5193 * Create a new PaginationItem
5194 * @param {Object} config The config object
5198 Roo.bootstrap.PaginationItem = function(config){
5199 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5204 * The raw click event for the entire grid.
5205 * @param {Roo.EventObject} e
5211 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5215 preventDefault: true,
5220 getAutoCreate : function(){
5226 href : this.href ? this.href : '#',
5227 html : this.html ? this.html : ''
5237 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5241 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5247 initEvents: function() {
5249 this.el.on('click', this.onClick, this);
5252 onClick : function(e)
5254 Roo.log('PaginationItem on click ');
5255 if(this.preventDefault){
5263 this.fireEvent('click', this, e);
5279 * @class Roo.bootstrap.Slider
5280 * @extends Roo.bootstrap.Component
5281 * Bootstrap Slider class
5284 * Create a new Slider
5285 * @param {Object} config The config object
5288 Roo.bootstrap.Slider = function(config){
5289 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5292 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5294 getAutoCreate : function(){
5298 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5302 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5314 * Ext JS Library 1.1.1
5315 * Copyright(c) 2006-2007, Ext JS, LLC.
5317 * Originally Released Under LGPL - original licence link has changed is not relivant.
5320 * <script type="text/javascript">
5325 * @class Roo.grid.ColumnModel
5326 * @extends Roo.util.Observable
5327 * This is the default implementation of a ColumnModel used by the Grid. It defines
5328 * the columns in the grid.
5331 var colModel = new Roo.grid.ColumnModel([
5332 {header: "Ticker", width: 60, sortable: true, locked: true},
5333 {header: "Company Name", width: 150, sortable: true},
5334 {header: "Market Cap.", width: 100, sortable: true},
5335 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5336 {header: "Employees", width: 100, sortable: true, resizable: false}
5341 * The config options listed for this class are options which may appear in each
5342 * individual column definition.
5343 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5345 * @param {Object} config An Array of column config objects. See this class's
5346 * config objects for details.
5348 Roo.grid.ColumnModel = function(config){
5350 * The config passed into the constructor
5352 this.config = config;
5355 // if no id, create one
5356 // if the column does not have a dataIndex mapping,
5357 // map it to the order it is in the config
5358 for(var i = 0, len = config.length; i < len; i++){
5360 if(typeof c.dataIndex == "undefined"){
5363 if(typeof c.renderer == "string"){
5364 c.renderer = Roo.util.Format[c.renderer];
5366 if(typeof c.id == "undefined"){
5369 if(c.editor && c.editor.xtype){
5370 c.editor = Roo.factory(c.editor, Roo.grid);
5372 if(c.editor && c.editor.isFormField){
5373 c.editor = new Roo.grid.GridEditor(c.editor);
5375 this.lookup[c.id] = c;
5379 * The width of columns which have no width specified (defaults to 100)
5382 this.defaultWidth = 100;
5385 * Default sortable of columns which have no sortable specified (defaults to false)
5388 this.defaultSortable = false;
5392 * @event widthchange
5393 * Fires when the width of a column changes.
5394 * @param {ColumnModel} this
5395 * @param {Number} columnIndex The column index
5396 * @param {Number} newWidth The new width
5398 "widthchange": true,
5400 * @event headerchange
5401 * Fires when the text of a header changes.
5402 * @param {ColumnModel} this
5403 * @param {Number} columnIndex The column index
5404 * @param {Number} newText The new header text
5406 "headerchange": true,
5408 * @event hiddenchange
5409 * Fires when a column is hidden or "unhidden".
5410 * @param {ColumnModel} this
5411 * @param {Number} columnIndex The column index
5412 * @param {Boolean} hidden true if hidden, false otherwise
5414 "hiddenchange": true,
5416 * @event columnmoved
5417 * Fires when a column is moved.
5418 * @param {ColumnModel} this
5419 * @param {Number} oldIndex
5420 * @param {Number} newIndex
5422 "columnmoved" : true,
5424 * @event columlockchange
5425 * Fires when a column's locked state is changed
5426 * @param {ColumnModel} this
5427 * @param {Number} colIndex
5428 * @param {Boolean} locked true if locked
5430 "columnlockchange" : true
5432 Roo.grid.ColumnModel.superclass.constructor.call(this);
5434 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5436 * @cfg {String} header The header text to display in the Grid view.
5439 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5440 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5441 * specified, the column's index is used as an index into the Record's data Array.
5444 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5445 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5448 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5449 * Defaults to the value of the {@link #defaultSortable} property.
5450 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5453 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5456 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5459 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5462 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5465 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5466 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5467 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5468 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5471 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5474 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5477 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5480 * @cfg {String} cursor (Optional)
5483 * @cfg {String} tooltip (Optional)
5486 * @cfg {Number} xs (Optional)
5489 * @cfg {Number} sm (Optional)
5492 * @cfg {Number} md (Optional)
5495 * @cfg {Number} lg (Optional)
5498 * Returns the id of the column at the specified index.
5499 * @param {Number} index The column index
5500 * @return {String} the id
5502 getColumnId : function(index){
5503 return this.config[index].id;
5507 * Returns the column for a specified id.
5508 * @param {String} id The column id
5509 * @return {Object} the column
5511 getColumnById : function(id){
5512 return this.lookup[id];
5517 * Returns the column for a specified dataIndex.
5518 * @param {String} dataIndex The column dataIndex
5519 * @return {Object|Boolean} the column or false if not found
5521 getColumnByDataIndex: function(dataIndex){
5522 var index = this.findColumnIndex(dataIndex);
5523 return index > -1 ? this.config[index] : false;
5527 * Returns the index for a specified column id.
5528 * @param {String} id The column id
5529 * @return {Number} the index, or -1 if not found
5531 getIndexById : function(id){
5532 for(var i = 0, len = this.config.length; i < len; i++){
5533 if(this.config[i].id == id){
5541 * Returns the index for a specified column dataIndex.
5542 * @param {String} dataIndex The column dataIndex
5543 * @return {Number} the index, or -1 if not found
5546 findColumnIndex : function(dataIndex){
5547 for(var i = 0, len = this.config.length; i < len; i++){
5548 if(this.config[i].dataIndex == dataIndex){
5556 moveColumn : function(oldIndex, newIndex){
5557 var c = this.config[oldIndex];
5558 this.config.splice(oldIndex, 1);
5559 this.config.splice(newIndex, 0, c);
5560 this.dataMap = null;
5561 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5564 isLocked : function(colIndex){
5565 return this.config[colIndex].locked === true;
5568 setLocked : function(colIndex, value, suppressEvent){
5569 if(this.isLocked(colIndex) == value){
5572 this.config[colIndex].locked = value;
5574 this.fireEvent("columnlockchange", this, colIndex, value);
5578 getTotalLockedWidth : function(){
5580 for(var i = 0; i < this.config.length; i++){
5581 if(this.isLocked(i) && !this.isHidden(i)){
5582 this.totalWidth += this.getColumnWidth(i);
5588 getLockedCount : function(){
5589 for(var i = 0, len = this.config.length; i < len; i++){
5590 if(!this.isLocked(i)){
5595 return this.config.length;
5599 * Returns the number of columns.
5602 getColumnCount : function(visibleOnly){
5603 if(visibleOnly === true){
5605 for(var i = 0, len = this.config.length; i < len; i++){
5606 if(!this.isHidden(i)){
5612 return this.config.length;
5616 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5617 * @param {Function} fn
5618 * @param {Object} scope (optional)
5619 * @return {Array} result
5621 getColumnsBy : function(fn, scope){
5623 for(var i = 0, len = this.config.length; i < len; i++){
5624 var c = this.config[i];
5625 if(fn.call(scope||this, c, i) === true){
5633 * Returns true if the specified column is sortable.
5634 * @param {Number} col The column index
5637 isSortable : function(col){
5638 if(typeof this.config[col].sortable == "undefined"){
5639 return this.defaultSortable;
5641 return this.config[col].sortable;
5645 * Returns the rendering (formatting) function defined for the column.
5646 * @param {Number} col The column index.
5647 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5649 getRenderer : function(col){
5650 if(!this.config[col].renderer){
5651 return Roo.grid.ColumnModel.defaultRenderer;
5653 return this.config[col].renderer;
5657 * Sets the rendering (formatting) function for a column.
5658 * @param {Number} col The column index
5659 * @param {Function} fn The function to use to process the cell's raw data
5660 * to return HTML markup for the grid view. The render function is called with
5661 * the following parameters:<ul>
5662 * <li>Data value.</li>
5663 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5664 * <li>css A CSS style string to apply to the table cell.</li>
5665 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5666 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5667 * <li>Row index</li>
5668 * <li>Column index</li>
5669 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5671 setRenderer : function(col, fn){
5672 this.config[col].renderer = fn;
5676 * Returns the width for the specified column.
5677 * @param {Number} col The column index
5680 getColumnWidth : function(col){
5681 return this.config[col].width * 1 || this.defaultWidth;
5685 * Sets the width for a column.
5686 * @param {Number} col The column index
5687 * @param {Number} width The new width
5689 setColumnWidth : function(col, width, suppressEvent){
5690 this.config[col].width = width;
5691 this.totalWidth = null;
5693 this.fireEvent("widthchange", this, col, width);
5698 * Returns the total width of all columns.
5699 * @param {Boolean} includeHidden True to include hidden column widths
5702 getTotalWidth : function(includeHidden){
5703 if(!this.totalWidth){
5704 this.totalWidth = 0;
5705 for(var i = 0, len = this.config.length; i < len; i++){
5706 if(includeHidden || !this.isHidden(i)){
5707 this.totalWidth += this.getColumnWidth(i);
5711 return this.totalWidth;
5715 * Returns the header for the specified column.
5716 * @param {Number} col The column index
5719 getColumnHeader : function(col){
5720 return this.config[col].header;
5724 * Sets the header for a column.
5725 * @param {Number} col The column index
5726 * @param {String} header The new header
5728 setColumnHeader : function(col, header){
5729 this.config[col].header = header;
5730 this.fireEvent("headerchange", this, col, header);
5734 * Returns the tooltip for the specified column.
5735 * @param {Number} col The column index
5738 getColumnTooltip : function(col){
5739 return this.config[col].tooltip;
5742 * Sets the tooltip for a column.
5743 * @param {Number} col The column index
5744 * @param {String} tooltip The new tooltip
5746 setColumnTooltip : function(col, tooltip){
5747 this.config[col].tooltip = tooltip;
5751 * Returns the dataIndex for the specified column.
5752 * @param {Number} col The column index
5755 getDataIndex : function(col){
5756 return this.config[col].dataIndex;
5760 * Sets the dataIndex for a column.
5761 * @param {Number} col The column index
5762 * @param {Number} dataIndex The new dataIndex
5764 setDataIndex : function(col, dataIndex){
5765 this.config[col].dataIndex = dataIndex;
5771 * Returns true if the cell is editable.
5772 * @param {Number} colIndex The column index
5773 * @param {Number} rowIndex The row index - this is nto actually used..?
5776 isCellEditable : function(colIndex, rowIndex){
5777 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5781 * Returns the editor defined for the cell/column.
5782 * return false or null to disable editing.
5783 * @param {Number} colIndex The column index
5784 * @param {Number} rowIndex The row index
5787 getCellEditor : function(colIndex, rowIndex){
5788 return this.config[colIndex].editor;
5792 * Sets if a column is editable.
5793 * @param {Number} col The column index
5794 * @param {Boolean} editable True if the column is editable
5796 setEditable : function(col, editable){
5797 this.config[col].editable = editable;
5802 * Returns true if the column is hidden.
5803 * @param {Number} colIndex The column index
5806 isHidden : function(colIndex){
5807 return this.config[colIndex].hidden;
5812 * Returns true if the column width cannot be changed
5814 isFixed : function(colIndex){
5815 return this.config[colIndex].fixed;
5819 * Returns true if the column can be resized
5822 isResizable : function(colIndex){
5823 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5826 * Sets if a column is hidden.
5827 * @param {Number} colIndex The column index
5828 * @param {Boolean} hidden True if the column is hidden
5830 setHidden : function(colIndex, hidden){
5831 this.config[colIndex].hidden = hidden;
5832 this.totalWidth = null;
5833 this.fireEvent("hiddenchange", this, colIndex, hidden);
5837 * Sets the editor for a column.
5838 * @param {Number} col The column index
5839 * @param {Object} editor The editor object
5841 setEditor : function(col, editor){
5842 this.config[col].editor = editor;
5846 Roo.grid.ColumnModel.defaultRenderer = function(value)
5848 if(typeof value == "object") {
5851 if(typeof value == "string" && value.length < 1){
5855 return String.format("{0}", value);
5858 // Alias for backwards compatibility
5859 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5862 * Ext JS Library 1.1.1
5863 * Copyright(c) 2006-2007, Ext JS, LLC.
5865 * Originally Released Under LGPL - original licence link has changed is not relivant.
5868 * <script type="text/javascript">
5872 * @class Roo.LoadMask
5873 * A simple utility class for generically masking elements while loading data. If the element being masked has
5874 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5875 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5876 * element's UpdateManager load indicator and will be destroyed after the initial load.
5878 * Create a new LoadMask
5879 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5880 * @param {Object} config The config object
5882 Roo.LoadMask = function(el, config){
5883 this.el = Roo.get(el);
5884 Roo.apply(this, config);
5886 this.store.on('beforeload', this.onBeforeLoad, this);
5887 this.store.on('load', this.onLoad, this);
5888 this.store.on('loadexception', this.onLoadException, this);
5889 this.removeMask = false;
5891 var um = this.el.getUpdateManager();
5892 um.showLoadIndicator = false; // disable the default indicator
5893 um.on('beforeupdate', this.onBeforeLoad, this);
5894 um.on('update', this.onLoad, this);
5895 um.on('failure', this.onLoad, this);
5896 this.removeMask = true;
5900 Roo.LoadMask.prototype = {
5902 * @cfg {Boolean} removeMask
5903 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5904 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5908 * The text to display in a centered loading message box (defaults to 'Loading...')
5912 * @cfg {String} msgCls
5913 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5915 msgCls : 'x-mask-loading',
5918 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5924 * Disables the mask to prevent it from being displayed
5926 disable : function(){
5927 this.disabled = true;
5931 * Enables the mask so that it can be displayed
5933 enable : function(){
5934 this.disabled = false;
5937 onLoadException : function()
5941 if (typeof(arguments[3]) != 'undefined') {
5942 Roo.MessageBox.alert("Error loading",arguments[3]);
5946 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5947 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5954 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5959 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5963 onBeforeLoad : function(){
5965 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5970 destroy : function(){
5972 this.store.un('beforeload', this.onBeforeLoad, this);
5973 this.store.un('load', this.onLoad, this);
5974 this.store.un('loadexception', this.onLoadException, this);
5976 var um = this.el.getUpdateManager();
5977 um.un('beforeupdate', this.onBeforeLoad, this);
5978 um.un('update', this.onLoad, this);
5979 um.un('failure', this.onLoad, this);
5990 * @class Roo.bootstrap.Table
5991 * @extends Roo.bootstrap.Component
5992 * Bootstrap Table class
5993 * @cfg {String} cls table class
5994 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5995 * @cfg {String} bgcolor Specifies the background color for a table
5996 * @cfg {Number} border Specifies whether the table cells should have borders or not
5997 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5998 * @cfg {Number} cellspacing Specifies the space between cells
5999 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6000 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6001 * @cfg {String} sortable Specifies that the table should be sortable
6002 * @cfg {String} summary Specifies a summary of the content of a table
6003 * @cfg {Number} width Specifies the width of a table
6004 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6006 * @cfg {boolean} striped Should the rows be alternative striped
6007 * @cfg {boolean} bordered Add borders to the table
6008 * @cfg {boolean} hover Add hover highlighting
6009 * @cfg {boolean} condensed Format condensed
6010 * @cfg {boolean} responsive Format condensed
6011 * @cfg {Boolean} loadMask (true|false) default false
6012 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6013 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6014 * @cfg {Boolean} rowSelection (true|false) default false
6015 * @cfg {Boolean} cellSelection (true|false) default false
6016 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6017 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6018 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6019 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6023 * Create a new Table
6024 * @param {Object} config The config object
6027 Roo.bootstrap.Table = function(config){
6028 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6033 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6034 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6035 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6036 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6038 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6040 this.sm.grid = this;
6041 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6042 this.sm = this.selModel;
6043 this.sm.xmodule = this.xmodule || false;
6046 if (this.cm && typeof(this.cm.config) == 'undefined') {
6047 this.colModel = new Roo.grid.ColumnModel(this.cm);
6048 this.cm = this.colModel;
6049 this.cm.xmodule = this.xmodule || false;
6052 this.store= Roo.factory(this.store, Roo.data);
6053 this.ds = this.store;
6054 this.ds.xmodule = this.xmodule || false;
6057 if (this.footer && this.store) {
6058 this.footer.dataSource = this.ds;
6059 this.footer = Roo.factory(this.footer);
6066 * Fires when a cell is clicked
6067 * @param {Roo.bootstrap.Table} this
6068 * @param {Roo.Element} el
6069 * @param {Number} rowIndex
6070 * @param {Number} columnIndex
6071 * @param {Roo.EventObject} e
6075 * @event celldblclick
6076 * Fires when a cell is double clicked
6077 * @param {Roo.bootstrap.Table} this
6078 * @param {Roo.Element} el
6079 * @param {Number} rowIndex
6080 * @param {Number} columnIndex
6081 * @param {Roo.EventObject} e
6083 "celldblclick" : true,
6086 * Fires when a row is clicked
6087 * @param {Roo.bootstrap.Table} this
6088 * @param {Roo.Element} el
6089 * @param {Number} rowIndex
6090 * @param {Roo.EventObject} e
6094 * @event rowdblclick
6095 * Fires when a row is double clicked
6096 * @param {Roo.bootstrap.Table} this
6097 * @param {Roo.Element} el
6098 * @param {Number} rowIndex
6099 * @param {Roo.EventObject} e
6101 "rowdblclick" : true,
6104 * Fires when a mouseover occur
6105 * @param {Roo.bootstrap.Table} this
6106 * @param {Roo.Element} el
6107 * @param {Number} rowIndex
6108 * @param {Number} columnIndex
6109 * @param {Roo.EventObject} e
6114 * Fires when a mouseout occur
6115 * @param {Roo.bootstrap.Table} this
6116 * @param {Roo.Element} el
6117 * @param {Number} rowIndex
6118 * @param {Number} columnIndex
6119 * @param {Roo.EventObject} e
6124 * Fires when a row is rendered, so you can change add a style to it.
6125 * @param {Roo.bootstrap.Table} this
6126 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6130 * @event rowsrendered
6131 * Fires when all the rows have been rendered
6132 * @param {Roo.bootstrap.Table} this
6134 'rowsrendered' : true,
6136 * @event contextmenu
6137 * The raw contextmenu event for the entire grid.
6138 * @param {Roo.EventObject} e
6140 "contextmenu" : true,
6142 * @event rowcontextmenu
6143 * Fires when a row is right clicked
6144 * @param {Roo.bootstrap.Table} this
6145 * @param {Number} rowIndex
6146 * @param {Roo.EventObject} e
6148 "rowcontextmenu" : true,
6150 * @event cellcontextmenu
6151 * Fires when a cell is right clicked
6152 * @param {Roo.bootstrap.Table} this
6153 * @param {Number} rowIndex
6154 * @param {Number} cellIndex
6155 * @param {Roo.EventObject} e
6157 "cellcontextmenu" : true,
6159 * @event headercontextmenu
6160 * Fires when a header is right clicked
6161 * @param {Roo.bootstrap.Table} this
6162 * @param {Number} columnIndex
6163 * @param {Roo.EventObject} e
6165 "headercontextmenu" : true
6169 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6195 rowSelection : false,
6196 cellSelection : false,
6199 // Roo.Element - the tbody
6201 // Roo.Element - thead element
6204 container: false, // used by gridpanel...
6210 auto_hide_footer : false,
6212 getAutoCreate : function()
6214 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6221 if (this.scrollBody) {
6222 cfg.cls += ' table-body-fixed';
6225 cfg.cls += ' table-striped';
6229 cfg.cls += ' table-hover';
6231 if (this.bordered) {
6232 cfg.cls += ' table-bordered';
6234 if (this.condensed) {
6235 cfg.cls += ' table-condensed';
6237 if (this.responsive) {
6238 cfg.cls += ' table-responsive';
6242 cfg.cls+= ' ' +this.cls;
6245 // this lot should be simplifed...
6258 ].forEach(function(k) {
6266 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6269 if(this.store || this.cm){
6270 if(this.headerShow){
6271 cfg.cn.push(this.renderHeader());
6274 cfg.cn.push(this.renderBody());
6276 if(this.footerShow){
6277 cfg.cn.push(this.renderFooter());
6279 // where does this come from?
6280 //cfg.cls+= ' TableGrid';
6283 return { cn : [ cfg ] };
6286 initEvents : function()
6288 if(!this.store || !this.cm){
6291 if (this.selModel) {
6292 this.selModel.initEvents();
6296 //Roo.log('initEvents with ds!!!!');
6298 this.mainBody = this.el.select('tbody', true).first();
6299 this.mainHead = this.el.select('thead', true).first();
6300 this.mainFoot = this.el.select('tfoot', true).first();
6306 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6307 e.on('click', _this.sort, _this);
6310 this.mainBody.on("click", this.onClick, this);
6311 this.mainBody.on("dblclick", this.onDblClick, this);
6313 // why is this done????? = it breaks dialogs??
6314 //this.parent().el.setStyle('position', 'relative');
6318 this.footer.parentId = this.id;
6319 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6322 this.el.select('tfoot tr td').first().addClass('hide');
6327 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6330 this.store.on('load', this.onLoad, this);
6331 this.store.on('beforeload', this.onBeforeLoad, this);
6332 this.store.on('update', this.onUpdate, this);
6333 this.store.on('add', this.onAdd, this);
6334 this.store.on("clear", this.clear, this);
6336 this.el.on("contextmenu", this.onContextMenu, this);
6338 this.mainBody.on('scroll', this.onBodyScroll, this);
6340 this.cm.on("headerchange", this.onHeaderChange, this);
6342 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6346 onContextMenu : function(e, t)
6348 this.processEvent("contextmenu", e);
6351 processEvent : function(name, e)
6353 if (name != 'touchstart' ) {
6354 this.fireEvent(name, e);
6357 var t = e.getTarget();
6359 var cell = Roo.get(t);
6365 if(cell.findParent('tfoot', false, true)){
6369 if(cell.findParent('thead', false, true)){
6371 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6372 cell = Roo.get(t).findParent('th', false, true);
6374 Roo.log("failed to find th in thead?");
6375 Roo.log(e.getTarget());
6380 var cellIndex = cell.dom.cellIndex;
6382 var ename = name == 'touchstart' ? 'click' : name;
6383 this.fireEvent("header" + ename, this, cellIndex, e);
6388 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6389 cell = Roo.get(t).findParent('td', false, true);
6391 Roo.log("failed to find th in tbody?");
6392 Roo.log(e.getTarget());
6397 var row = cell.findParent('tr', false, true);
6398 var cellIndex = cell.dom.cellIndex;
6399 var rowIndex = row.dom.rowIndex - 1;
6403 this.fireEvent("row" + name, this, rowIndex, e);
6407 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6413 onMouseover : function(e, el)
6415 var cell = Roo.get(el);
6421 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6422 cell = cell.findParent('td', false, true);
6425 var row = cell.findParent('tr', false, true);
6426 var cellIndex = cell.dom.cellIndex;
6427 var rowIndex = row.dom.rowIndex - 1; // start from 0
6429 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6433 onMouseout : function(e, el)
6435 var cell = Roo.get(el);
6441 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6442 cell = cell.findParent('td', false, true);
6445 var row = cell.findParent('tr', false, true);
6446 var cellIndex = cell.dom.cellIndex;
6447 var rowIndex = row.dom.rowIndex - 1; // start from 0
6449 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6453 onClick : function(e, el)
6455 var cell = Roo.get(el);
6457 if(!cell || (!this.cellSelection && !this.rowSelection)){
6461 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6462 cell = cell.findParent('td', false, true);
6465 if(!cell || typeof(cell) == 'undefined'){
6469 var row = cell.findParent('tr', false, true);
6471 if(!row || typeof(row) == 'undefined'){
6475 var cellIndex = cell.dom.cellIndex;
6476 var rowIndex = this.getRowIndex(row);
6478 // why??? - should these not be based on SelectionModel?
6479 if(this.cellSelection){
6480 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6483 if(this.rowSelection){
6484 this.fireEvent('rowclick', this, row, rowIndex, e);
6490 onDblClick : function(e,el)
6492 var cell = Roo.get(el);
6494 if(!cell || (!this.cellSelection && !this.rowSelection)){
6498 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6499 cell = cell.findParent('td', false, true);
6502 if(!cell || typeof(cell) == 'undefined'){
6506 var row = cell.findParent('tr', false, true);
6508 if(!row || typeof(row) == 'undefined'){
6512 var cellIndex = cell.dom.cellIndex;
6513 var rowIndex = this.getRowIndex(row);
6515 if(this.cellSelection){
6516 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6519 if(this.rowSelection){
6520 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6524 sort : function(e,el)
6526 var col = Roo.get(el);
6528 if(!col.hasClass('sortable')){
6532 var sort = col.attr('sort');
6535 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6539 this.store.sortInfo = {field : sort, direction : dir};
6542 Roo.log("calling footer first");
6543 this.footer.onClick('first');
6546 this.store.load({ params : { start : 0 } });
6550 renderHeader : function()
6558 this.totalWidth = 0;
6560 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6562 var config = cm.config[i];
6566 cls : 'x-hcol-' + i,
6568 html: cm.getColumnHeader(i)
6573 if(typeof(config.sortable) != 'undefined' && config.sortable){
6575 c.html = '<i class="glyphicon"></i>' + c.html;
6578 if(typeof(config.lgHeader) != 'undefined'){
6579 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6582 if(typeof(config.mdHeader) != 'undefined'){
6583 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6586 if(typeof(config.smHeader) != 'undefined'){
6587 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6590 if(typeof(config.xsHeader) != 'undefined'){
6591 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6598 if(typeof(config.tooltip) != 'undefined'){
6599 c.tooltip = config.tooltip;
6602 if(typeof(config.colspan) != 'undefined'){
6603 c.colspan = config.colspan;
6606 if(typeof(config.hidden) != 'undefined' && config.hidden){
6607 c.style += ' display:none;';
6610 if(typeof(config.dataIndex) != 'undefined'){
6611 c.sort = config.dataIndex;
6616 if(typeof(config.align) != 'undefined' && config.align.length){
6617 c.style += ' text-align:' + config.align + ';';
6620 if(typeof(config.width) != 'undefined'){
6621 c.style += ' width:' + config.width + 'px;';
6622 this.totalWidth += config.width;
6624 this.totalWidth += 100; // assume minimum of 100 per column?
6627 if(typeof(config.cls) != 'undefined'){
6628 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6631 ['xs','sm','md','lg'].map(function(size){
6633 if(typeof(config[size]) == 'undefined'){
6637 if (!config[size]) { // 0 = hidden
6638 c.cls += ' hidden-' + size;
6642 c.cls += ' col-' + size + '-' + config[size];
6652 renderBody : function()
6662 colspan : this.cm.getColumnCount()
6672 renderFooter : function()
6682 colspan : this.cm.getColumnCount()
6696 // Roo.log('ds onload');
6701 var ds = this.store;
6703 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6704 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6705 if (_this.store.sortInfo) {
6707 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6708 e.select('i', true).addClass(['glyphicon-arrow-up']);
6711 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6712 e.select('i', true).addClass(['glyphicon-arrow-down']);
6717 var tbody = this.mainBody;
6719 if(ds.getCount() > 0){
6720 ds.data.each(function(d,rowIndex){
6721 var row = this.renderRow(cm, ds, rowIndex);
6723 tbody.createChild(row);
6727 if(row.cellObjects.length){
6728 Roo.each(row.cellObjects, function(r){
6729 _this.renderCellObject(r);
6736 var tfoot = this.el.select('tfoot', true).first();
6738 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6740 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6742 var total = this.ds.getTotalCount();
6744 if(this.footer.pageSize < total){
6745 this.mainFoot.show();
6749 Roo.each(this.el.select('tbody td', true).elements, function(e){
6750 e.on('mouseover', _this.onMouseover, _this);
6753 Roo.each(this.el.select('tbody td', true).elements, function(e){
6754 e.on('mouseout', _this.onMouseout, _this);
6756 this.fireEvent('rowsrendered', this);
6762 onUpdate : function(ds,record)
6764 this.refreshRow(record);
6768 onRemove : function(ds, record, index, isUpdate){
6769 if(isUpdate !== true){
6770 this.fireEvent("beforerowremoved", this, index, record);
6772 var bt = this.mainBody.dom;
6774 var rows = this.el.select('tbody > tr', true).elements;
6776 if(typeof(rows[index]) != 'undefined'){
6777 bt.removeChild(rows[index].dom);
6780 // if(bt.rows[index]){
6781 // bt.removeChild(bt.rows[index]);
6784 if(isUpdate !== true){
6785 //this.stripeRows(index);
6786 //this.syncRowHeights(index, index);
6788 this.fireEvent("rowremoved", this, index, record);
6792 onAdd : function(ds, records, rowIndex)
6794 //Roo.log('on Add called');
6795 // - note this does not handle multiple adding very well..
6796 var bt = this.mainBody.dom;
6797 for (var i =0 ; i < records.length;i++) {
6798 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6799 //Roo.log(records[i]);
6800 //Roo.log(this.store.getAt(rowIndex+i));
6801 this.insertRow(this.store, rowIndex + i, false);
6808 refreshRow : function(record){
6809 var ds = this.store, index;
6810 if(typeof record == 'number'){
6812 record = ds.getAt(index);
6814 index = ds.indexOf(record);
6816 this.insertRow(ds, index, true);
6818 this.onRemove(ds, record, index+1, true);
6820 //this.syncRowHeights(index, index);
6822 this.fireEvent("rowupdated", this, index, record);
6825 insertRow : function(dm, rowIndex, isUpdate){
6828 this.fireEvent("beforerowsinserted", this, rowIndex);
6830 //var s = this.getScrollState();
6831 var row = this.renderRow(this.cm, this.store, rowIndex);
6832 // insert before rowIndex..
6833 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6837 if(row.cellObjects.length){
6838 Roo.each(row.cellObjects, function(r){
6839 _this.renderCellObject(r);
6844 this.fireEvent("rowsinserted", this, rowIndex);
6845 //this.syncRowHeights(firstRow, lastRow);
6846 //this.stripeRows(firstRow);
6853 getRowDom : function(rowIndex)
6855 var rows = this.el.select('tbody > tr', true).elements;
6857 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6860 // returns the object tree for a tr..
6863 renderRow : function(cm, ds, rowIndex)
6865 var d = ds.getAt(rowIndex);
6869 cls : 'x-row-' + rowIndex,
6873 var cellObjects = [];
6875 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6876 var config = cm.config[i];
6878 var renderer = cm.getRenderer(i);
6882 if(typeof(renderer) !== 'undefined'){
6883 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6885 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6886 // and are rendered into the cells after the row is rendered - using the id for the element.
6888 if(typeof(value) === 'object'){
6898 rowIndex : rowIndex,
6903 this.fireEvent('rowclass', this, rowcfg);
6907 cls : rowcfg.rowClass + ' x-col-' + i,
6909 html: (typeof(value) === 'object') ? '' : value
6916 if(typeof(config.colspan) != 'undefined'){
6917 td.colspan = config.colspan;
6920 if(typeof(config.hidden) != 'undefined' && config.hidden){
6921 td.style += ' display:none;';
6924 if(typeof(config.align) != 'undefined' && config.align.length){
6925 td.style += ' text-align:' + config.align + ';';
6927 if(typeof(config.valign) != 'undefined' && config.valign.length){
6928 td.style += ' vertical-align:' + config.valign + ';';
6931 if(typeof(config.width) != 'undefined'){
6932 td.style += ' width:' + config.width + 'px;';
6935 if(typeof(config.cursor) != 'undefined'){
6936 td.style += ' cursor:' + config.cursor + ';';
6939 if(typeof(config.cls) != 'undefined'){
6940 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6943 ['xs','sm','md','lg'].map(function(size){
6945 if(typeof(config[size]) == 'undefined'){
6949 if (!config[size]) { // 0 = hidden
6950 td.cls += ' hidden-' + size;
6954 td.cls += ' col-' + size + '-' + config[size];
6962 row.cellObjects = cellObjects;
6970 onBeforeLoad : function()
6979 this.el.select('tbody', true).first().dom.innerHTML = '';
6982 * Show or hide a row.
6983 * @param {Number} rowIndex to show or hide
6984 * @param {Boolean} state hide
6986 setRowVisibility : function(rowIndex, state)
6988 var bt = this.mainBody.dom;
6990 var rows = this.el.select('tbody > tr', true).elements;
6992 if(typeof(rows[rowIndex]) == 'undefined'){
6995 rows[rowIndex].dom.style.display = state ? '' : 'none';
6999 getSelectionModel : function(){
7001 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7003 return this.selModel;
7006 * Render the Roo.bootstrap object from renderder
7008 renderCellObject : function(r)
7012 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7014 var t = r.cfg.render(r.container);
7017 Roo.each(r.cfg.cn, function(c){
7019 container: t.getChildContainer(),
7022 _this.renderCellObject(child);
7027 getRowIndex : function(row)
7031 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7042 * Returns the grid's underlying element = used by panel.Grid
7043 * @return {Element} The element
7045 getGridEl : function(){
7049 * Forces a resize - used by panel.Grid
7050 * @return {Element} The element
7052 autoSize : function()
7054 //var ctr = Roo.get(this.container.dom.parentElement);
7055 var ctr = Roo.get(this.el.dom);
7057 var thd = this.getGridEl().select('thead',true).first();
7058 var tbd = this.getGridEl().select('tbody', true).first();
7059 var tfd = this.getGridEl().select('tfoot', true).first();
7061 var cw = ctr.getWidth();
7065 tbd.setSize(ctr.getWidth(),
7066 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7068 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7071 cw = Math.max(cw, this.totalWidth);
7072 this.getGridEl().select('tr',true).setWidth(cw);
7073 // resize 'expandable coloumn?
7075 return; // we doe not have a view in this design..
7078 onBodyScroll: function()
7080 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7082 this.mainHead.setStyle({
7083 'position' : 'relative',
7084 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7090 var scrollHeight = this.mainBody.dom.scrollHeight;
7092 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7094 var height = this.mainBody.getHeight();
7096 if(scrollHeight - height == scrollTop) {
7098 var total = this.ds.getTotalCount();
7100 if(this.footer.cursor + this.footer.pageSize < total){
7102 this.footer.ds.load({
7104 start : this.footer.cursor + this.footer.pageSize,
7105 limit : this.footer.pageSize
7115 onHeaderChange : function()
7117 var header = this.renderHeader();
7118 var table = this.el.select('table', true).first();
7120 this.mainHead.remove();
7121 this.mainHead = table.createChild(header, this.mainBody, false);
7124 onHiddenChange : function(colModel, colIndex, hidden)
7126 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7127 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7129 this.CSS.updateRule(thSelector, "display", "");
7130 this.CSS.updateRule(tdSelector, "display", "");
7133 this.CSS.updateRule(thSelector, "display", "none");
7134 this.CSS.updateRule(tdSelector, "display", "none");
7137 this.onHeaderChange();
7141 setColumnWidth: function(col_index, width)
7143 // width = "md-2 xs-2..."
7144 if(!this.colModel.config[col_index]) {
7148 var w = width.split(" ");
7150 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7152 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7155 for(var j = 0; j < w.length; j++) {
7161 var size_cls = w[j].split("-");
7163 if(!Number.isInteger(size_cls[1] * 1)) {
7167 if(!this.colModel.config[col_index][size_cls[0]]) {
7171 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7175 h_row[0].classList.replace(
7176 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7177 "col-"+size_cls[0]+"-"+size_cls[1]
7180 for(var i = 0; i < rows.length; i++) {
7182 var size_cls = w[j].split("-");
7184 if(!Number.isInteger(size_cls[1] * 1)) {
7188 if(!this.colModel.config[col_index][size_cls[0]]) {
7192 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7196 rows[i].classList.replace(
7197 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7198 "col-"+size_cls[0]+"-"+size_cls[1]
7202 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7217 * @class Roo.bootstrap.TableCell
7218 * @extends Roo.bootstrap.Component
7219 * Bootstrap TableCell class
7220 * @cfg {String} html cell contain text
7221 * @cfg {String} cls cell class
7222 * @cfg {String} tag cell tag (td|th) default td
7223 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7224 * @cfg {String} align Aligns the content in a cell
7225 * @cfg {String} axis Categorizes cells
7226 * @cfg {String} bgcolor Specifies the background color of a cell
7227 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7228 * @cfg {Number} colspan Specifies the number of columns a cell should span
7229 * @cfg {String} headers Specifies one or more header cells a cell is related to
7230 * @cfg {Number} height Sets the height of a cell
7231 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7232 * @cfg {Number} rowspan Sets the number of rows a cell should span
7233 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7234 * @cfg {String} valign Vertical aligns the content in a cell
7235 * @cfg {Number} width Specifies the width of a cell
7238 * Create a new TableCell
7239 * @param {Object} config The config object
7242 Roo.bootstrap.TableCell = function(config){
7243 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7246 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7266 getAutoCreate : function(){
7267 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7287 cfg.align=this.align
7293 cfg.bgcolor=this.bgcolor
7296 cfg.charoff=this.charoff
7299 cfg.colspan=this.colspan
7302 cfg.headers=this.headers
7305 cfg.height=this.height
7308 cfg.nowrap=this.nowrap
7311 cfg.rowspan=this.rowspan
7314 cfg.scope=this.scope
7317 cfg.valign=this.valign
7320 cfg.width=this.width
7339 * @class Roo.bootstrap.TableRow
7340 * @extends Roo.bootstrap.Component
7341 * Bootstrap TableRow class
7342 * @cfg {String} cls row class
7343 * @cfg {String} align Aligns the content in a table row
7344 * @cfg {String} bgcolor Specifies a background color for a table row
7345 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7346 * @cfg {String} valign Vertical aligns the content in a table row
7349 * Create a new TableRow
7350 * @param {Object} config The config object
7353 Roo.bootstrap.TableRow = function(config){
7354 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7357 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7365 getAutoCreate : function(){
7366 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7376 cfg.align = this.align;
7379 cfg.bgcolor = this.bgcolor;
7382 cfg.charoff = this.charoff;
7385 cfg.valign = this.valign;
7403 * @class Roo.bootstrap.TableBody
7404 * @extends Roo.bootstrap.Component
7405 * Bootstrap TableBody class
7406 * @cfg {String} cls element class
7407 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7408 * @cfg {String} align Aligns the content inside the element
7409 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7410 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7413 * Create a new TableBody
7414 * @param {Object} config The config object
7417 Roo.bootstrap.TableBody = function(config){
7418 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7421 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7429 getAutoCreate : function(){
7430 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7444 cfg.align = this.align;
7447 cfg.charoff = this.charoff;
7450 cfg.valign = this.valign;
7457 // initEvents : function()
7464 // this.store = Roo.factory(this.store, Roo.data);
7465 // this.store.on('load', this.onLoad, this);
7467 // this.store.load();
7471 // onLoad: function ()
7473 // this.fireEvent('load', this);
7483 * Ext JS Library 1.1.1
7484 * Copyright(c) 2006-2007, Ext JS, LLC.
7486 * Originally Released Under LGPL - original licence link has changed is not relivant.
7489 * <script type="text/javascript">
7492 // as we use this in bootstrap.
7493 Roo.namespace('Roo.form');
7495 * @class Roo.form.Action
7496 * Internal Class used to handle form actions
7498 * @param {Roo.form.BasicForm} el The form element or its id
7499 * @param {Object} config Configuration options
7504 // define the action interface
7505 Roo.form.Action = function(form, options){
7507 this.options = options || {};
7510 * Client Validation Failed
7513 Roo.form.Action.CLIENT_INVALID = 'client';
7515 * Server Validation Failed
7518 Roo.form.Action.SERVER_INVALID = 'server';
7520 * Connect to Server Failed
7523 Roo.form.Action.CONNECT_FAILURE = 'connect';
7525 * Reading Data from Server Failed
7528 Roo.form.Action.LOAD_FAILURE = 'load';
7530 Roo.form.Action.prototype = {
7532 failureType : undefined,
7533 response : undefined,
7537 run : function(options){
7542 success : function(response){
7547 handleResponse : function(response){
7551 // default connection failure
7552 failure : function(response){
7554 this.response = response;
7555 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7556 this.form.afterAction(this, false);
7559 processResponse : function(response){
7560 this.response = response;
7561 if(!response.responseText){
7564 this.result = this.handleResponse(response);
7568 // utility functions used internally
7569 getUrl : function(appendParams){
7570 var url = this.options.url || this.form.url || this.form.el.dom.action;
7572 var p = this.getParams();
7574 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7580 getMethod : function(){
7581 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7584 getParams : function(){
7585 var bp = this.form.baseParams;
7586 var p = this.options.params;
7588 if(typeof p == "object"){
7589 p = Roo.urlEncode(Roo.applyIf(p, bp));
7590 }else if(typeof p == 'string' && bp){
7591 p += '&' + Roo.urlEncode(bp);
7594 p = Roo.urlEncode(bp);
7599 createCallback : function(){
7601 success: this.success,
7602 failure: this.failure,
7604 timeout: (this.form.timeout*1000),
7605 upload: this.form.fileUpload ? this.success : undefined
7610 Roo.form.Action.Submit = function(form, options){
7611 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7614 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7617 haveProgress : false,
7618 uploadComplete : false,
7620 // uploadProgress indicator.
7621 uploadProgress : function()
7623 if (!this.form.progressUrl) {
7627 if (!this.haveProgress) {
7628 Roo.MessageBox.progress("Uploading", "Uploading");
7630 if (this.uploadComplete) {
7631 Roo.MessageBox.hide();
7635 this.haveProgress = true;
7637 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7639 var c = new Roo.data.Connection();
7641 url : this.form.progressUrl,
7646 success : function(req){
7647 //console.log(data);
7651 rdata = Roo.decode(req.responseText)
7653 Roo.log("Invalid data from server..");
7657 if (!rdata || !rdata.success) {
7659 Roo.MessageBox.alert(Roo.encode(rdata));
7662 var data = rdata.data;
7664 if (this.uploadComplete) {
7665 Roo.MessageBox.hide();
7670 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7671 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7674 this.uploadProgress.defer(2000,this);
7677 failure: function(data) {
7678 Roo.log('progress url failed ');
7689 // run get Values on the form, so it syncs any secondary forms.
7690 this.form.getValues();
7692 var o = this.options;
7693 var method = this.getMethod();
7694 var isPost = method == 'POST';
7695 if(o.clientValidation === false || this.form.isValid()){
7697 if (this.form.progressUrl) {
7698 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7699 (new Date() * 1) + '' + Math.random());
7704 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7705 form:this.form.el.dom,
7706 url:this.getUrl(!isPost),
7708 params:isPost ? this.getParams() : null,
7709 isUpload: this.form.fileUpload
7712 this.uploadProgress();
7714 }else if (o.clientValidation !== false){ // client validation failed
7715 this.failureType = Roo.form.Action.CLIENT_INVALID;
7716 this.form.afterAction(this, false);
7720 success : function(response)
7722 this.uploadComplete= true;
7723 if (this.haveProgress) {
7724 Roo.MessageBox.hide();
7728 var result = this.processResponse(response);
7729 if(result === true || result.success){
7730 this.form.afterAction(this, true);
7734 this.form.markInvalid(result.errors);
7735 this.failureType = Roo.form.Action.SERVER_INVALID;
7737 this.form.afterAction(this, false);
7739 failure : function(response)
7741 this.uploadComplete= true;
7742 if (this.haveProgress) {
7743 Roo.MessageBox.hide();
7746 this.response = response;
7747 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7748 this.form.afterAction(this, false);
7751 handleResponse : function(response){
7752 if(this.form.errorReader){
7753 var rs = this.form.errorReader.read(response);
7756 for(var i = 0, len = rs.records.length; i < len; i++) {
7757 var r = rs.records[i];
7761 if(errors.length < 1){
7765 success : rs.success,
7771 ret = Roo.decode(response.responseText);
7775 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7785 Roo.form.Action.Load = function(form, options){
7786 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7787 this.reader = this.form.reader;
7790 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7795 Roo.Ajax.request(Roo.apply(
7796 this.createCallback(), {
7797 method:this.getMethod(),
7798 url:this.getUrl(false),
7799 params:this.getParams()
7803 success : function(response){
7805 var result = this.processResponse(response);
7806 if(result === true || !result.success || !result.data){
7807 this.failureType = Roo.form.Action.LOAD_FAILURE;
7808 this.form.afterAction(this, false);
7811 this.form.clearInvalid();
7812 this.form.setValues(result.data);
7813 this.form.afterAction(this, true);
7816 handleResponse : function(response){
7817 if(this.form.reader){
7818 var rs = this.form.reader.read(response);
7819 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7821 success : rs.success,
7825 return Roo.decode(response.responseText);
7829 Roo.form.Action.ACTION_TYPES = {
7830 'load' : Roo.form.Action.Load,
7831 'submit' : Roo.form.Action.Submit
7840 * @class Roo.bootstrap.Form
7841 * @extends Roo.bootstrap.Component
7842 * Bootstrap Form class
7843 * @cfg {String} method GET | POST (default POST)
7844 * @cfg {String} labelAlign top | left (default top)
7845 * @cfg {String} align left | right - for navbars
7846 * @cfg {Boolean} loadMask load mask when submit (default true)
7851 * @param {Object} config The config object
7855 Roo.bootstrap.Form = function(config){
7857 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7859 Roo.bootstrap.Form.popover.apply();
7863 * @event clientvalidation
7864 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7865 * @param {Form} this
7866 * @param {Boolean} valid true if the form has passed client-side validation
7868 clientvalidation: true,
7870 * @event beforeaction
7871 * Fires before any action is performed. Return false to cancel the action.
7872 * @param {Form} this
7873 * @param {Action} action The action to be performed
7877 * @event actionfailed
7878 * Fires when an action fails.
7879 * @param {Form} this
7880 * @param {Action} action The action that failed
7882 actionfailed : true,
7884 * @event actioncomplete
7885 * Fires when an action is completed.
7886 * @param {Form} this
7887 * @param {Action} action The action that completed
7889 actioncomplete : true
7893 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7896 * @cfg {String} method
7897 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7902 * The URL to use for form actions if one isn't supplied in the action options.
7905 * @cfg {Boolean} fileUpload
7906 * Set to true if this form is a file upload.
7910 * @cfg {Object} baseParams
7911 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7915 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7919 * @cfg {Sting} align (left|right) for navbar forms
7924 activeAction : null,
7927 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7928 * element by passing it or its id or mask the form itself by passing in true.
7931 waitMsgTarget : false,
7936 * @cfg {Boolean} errorMask (true|false) default false
7941 * @cfg {Number} maskOffset Default 100
7946 * @cfg {Boolean} maskBody
7950 getAutoCreate : function(){
7954 method : this.method || 'POST',
7955 id : this.id || Roo.id(),
7958 if (this.parent().xtype.match(/^Nav/)) {
7959 cfg.cls = 'navbar-form navbar-' + this.align;
7963 if (this.labelAlign == 'left' ) {
7964 cfg.cls += ' form-horizontal';
7970 initEvents : function()
7972 this.el.on('submit', this.onSubmit, this);
7973 // this was added as random key presses on the form where triggering form submit.
7974 this.el.on('keypress', function(e) {
7975 if (e.getCharCode() != 13) {
7978 // we might need to allow it for textareas.. and some other items.
7979 // check e.getTarget().
7981 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7985 Roo.log("keypress blocked");
7993 onSubmit : function(e){
7998 * Returns true if client-side validation on the form is successful.
8001 isValid : function(){
8002 var items = this.getItems();
8006 items.each(function(f){
8012 Roo.log('invalid field: ' + f.name);
8016 if(!target && f.el.isVisible(true)){
8022 if(this.errorMask && !valid){
8023 Roo.bootstrap.Form.popover.mask(this, target);
8030 * Returns true if any fields in this form have changed since their original load.
8033 isDirty : function(){
8035 var items = this.getItems();
8036 items.each(function(f){
8046 * Performs a predefined action (submit or load) or custom actions you define on this form.
8047 * @param {String} actionName The name of the action type
8048 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8049 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8050 * accept other config options):
8052 Property Type Description
8053 ---------------- --------------- ----------------------------------------------------------------------------------
8054 url String The url for the action (defaults to the form's url)
8055 method String The form method to use (defaults to the form's method, or POST if not defined)
8056 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8057 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8058 validate the form on the client (defaults to false)
8060 * @return {BasicForm} this
8062 doAction : function(action, options){
8063 if(typeof action == 'string'){
8064 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8066 if(this.fireEvent('beforeaction', this, action) !== false){
8067 this.beforeAction(action);
8068 action.run.defer(100, action);
8074 beforeAction : function(action){
8075 var o = action.options;
8080 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8082 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8085 // not really supported yet.. ??
8087 //if(this.waitMsgTarget === true){
8088 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8089 //}else if(this.waitMsgTarget){
8090 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8091 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8093 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8099 afterAction : function(action, success){
8100 this.activeAction = null;
8101 var o = action.options;
8106 Roo.get(document.body).unmask();
8112 //if(this.waitMsgTarget === true){
8113 // this.el.unmask();
8114 //}else if(this.waitMsgTarget){
8115 // this.waitMsgTarget.unmask();
8117 // Roo.MessageBox.updateProgress(1);
8118 // Roo.MessageBox.hide();
8125 Roo.callback(o.success, o.scope, [this, action]);
8126 this.fireEvent('actioncomplete', this, action);
8130 // failure condition..
8131 // we have a scenario where updates need confirming.
8132 // eg. if a locking scenario exists..
8133 // we look for { errors : { needs_confirm : true }} in the response.
8135 (typeof(action.result) != 'undefined') &&
8136 (typeof(action.result.errors) != 'undefined') &&
8137 (typeof(action.result.errors.needs_confirm) != 'undefined')
8140 Roo.log("not supported yet");
8143 Roo.MessageBox.confirm(
8144 "Change requires confirmation",
8145 action.result.errorMsg,
8150 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8160 Roo.callback(o.failure, o.scope, [this, action]);
8161 // show an error message if no failed handler is set..
8162 if (!this.hasListener('actionfailed')) {
8163 Roo.log("need to add dialog support");
8165 Roo.MessageBox.alert("Error",
8166 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8167 action.result.errorMsg :
8168 "Saving Failed, please check your entries or try again"
8173 this.fireEvent('actionfailed', this, action);
8178 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8179 * @param {String} id The value to search for
8182 findField : function(id){
8183 var items = this.getItems();
8184 var field = items.get(id);
8186 items.each(function(f){
8187 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8194 return field || null;
8197 * Mark fields in this form invalid in bulk.
8198 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8199 * @return {BasicForm} this
8201 markInvalid : function(errors){
8202 if(errors instanceof Array){
8203 for(var i = 0, len = errors.length; i < len; i++){
8204 var fieldError = errors[i];
8205 var f = this.findField(fieldError.id);
8207 f.markInvalid(fieldError.msg);
8213 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8214 field.markInvalid(errors[id]);
8218 //Roo.each(this.childForms || [], function (f) {
8219 // f.markInvalid(errors);
8226 * Set values for fields in this form in bulk.
8227 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8228 * @return {BasicForm} this
8230 setValues : function(values){
8231 if(values instanceof Array){ // array of objects
8232 for(var i = 0, len = values.length; i < len; i++){
8234 var f = this.findField(v.id);
8236 f.setValue(v.value);
8237 if(this.trackResetOnLoad){
8238 f.originalValue = f.getValue();
8242 }else{ // object hash
8245 if(typeof values[id] != 'function' && (field = this.findField(id))){
8247 if (field.setFromData &&
8249 field.displayField &&
8250 // combos' with local stores can
8251 // be queried via setValue()
8252 // to set their value..
8253 (field.store && !field.store.isLocal)
8257 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8258 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8259 field.setFromData(sd);
8261 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8263 field.setFromData(values);
8266 field.setValue(values[id]);
8270 if(this.trackResetOnLoad){
8271 field.originalValue = field.getValue();
8277 //Roo.each(this.childForms || [], function (f) {
8278 // f.setValues(values);
8285 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8286 * they are returned as an array.
8287 * @param {Boolean} asString
8290 getValues : function(asString){
8291 //if (this.childForms) {
8292 // copy values from the child forms
8293 // Roo.each(this.childForms, function (f) {
8294 // this.setValues(f.getValues());
8300 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8301 if(asString === true){
8304 return Roo.urlDecode(fs);
8308 * Returns the fields in this form as an object with key/value pairs.
8309 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8312 getFieldValues : function(with_hidden)
8314 var items = this.getItems();
8316 items.each(function(f){
8322 var v = f.getValue();
8324 if (f.inputType =='radio') {
8325 if (typeof(ret[f.getName()]) == 'undefined') {
8326 ret[f.getName()] = ''; // empty..
8329 if (!f.el.dom.checked) {
8337 if(f.xtype == 'MoneyField'){
8338 ret[f.currencyName] = f.getCurrency();
8341 // not sure if this supported any more..
8342 if ((typeof(v) == 'object') && f.getRawValue) {
8343 v = f.getRawValue() ; // dates..
8345 // combo boxes where name != hiddenName...
8346 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8347 ret[f.name] = f.getRawValue();
8349 ret[f.getName()] = v;
8356 * Clears all invalid messages in this form.
8357 * @return {BasicForm} this
8359 clearInvalid : function(){
8360 var items = this.getItems();
8362 items.each(function(f){
8371 * @return {BasicForm} this
8374 var items = this.getItems();
8375 items.each(function(f){
8379 Roo.each(this.childForms || [], function (f) {
8387 getItems : function()
8389 var r=new Roo.util.MixedCollection(false, function(o){
8390 return o.id || (o.id = Roo.id());
8392 var iter = function(el) {
8399 Roo.each(el.items,function(e) {
8408 hideFields : function(items)
8410 Roo.each(items, function(i){
8412 var f = this.findField(i);
8423 showFields : function(items)
8425 Roo.each(items, function(i){
8427 var f = this.findField(i);
8440 Roo.apply(Roo.bootstrap.Form, {
8467 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8468 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8469 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8470 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8473 this.maskEl.top.enableDisplayMode("block");
8474 this.maskEl.left.enableDisplayMode("block");
8475 this.maskEl.bottom.enableDisplayMode("block");
8476 this.maskEl.right.enableDisplayMode("block");
8478 this.toolTip = new Roo.bootstrap.Tooltip({
8479 cls : 'roo-form-error-popover',
8481 'left' : ['r-l', [-2,0], 'right'],
8482 'right' : ['l-r', [2,0], 'left'],
8483 'bottom' : ['tl-bl', [0,2], 'top'],
8484 'top' : [ 'bl-tl', [0,-2], 'bottom']
8488 this.toolTip.render(Roo.get(document.body));
8490 this.toolTip.el.enableDisplayMode("block");
8492 Roo.get(document.body).on('click', function(){
8496 Roo.get(document.body).on('touchstart', function(){
8500 this.isApplied = true
8503 mask : function(form, target)
8507 this.target = target;
8509 if(!this.form.errorMask || !target.el){
8513 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8515 Roo.log(scrollable);
8517 var ot = this.target.el.calcOffsetsTo(scrollable);
8519 var scrollTo = ot[1] - this.form.maskOffset;
8521 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8523 scrollable.scrollTo('top', scrollTo);
8525 var box = this.target.el.getBox();
8527 var zIndex = Roo.bootstrap.Modal.zIndex++;
8530 this.maskEl.top.setStyle('position', 'absolute');
8531 this.maskEl.top.setStyle('z-index', zIndex);
8532 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8533 this.maskEl.top.setLeft(0);
8534 this.maskEl.top.setTop(0);
8535 this.maskEl.top.show();
8537 this.maskEl.left.setStyle('position', 'absolute');
8538 this.maskEl.left.setStyle('z-index', zIndex);
8539 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8540 this.maskEl.left.setLeft(0);
8541 this.maskEl.left.setTop(box.y - this.padding);
8542 this.maskEl.left.show();
8544 this.maskEl.bottom.setStyle('position', 'absolute');
8545 this.maskEl.bottom.setStyle('z-index', zIndex);
8546 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8547 this.maskEl.bottom.setLeft(0);
8548 this.maskEl.bottom.setTop(box.bottom + this.padding);
8549 this.maskEl.bottom.show();
8551 this.maskEl.right.setStyle('position', 'absolute');
8552 this.maskEl.right.setStyle('z-index', zIndex);
8553 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8554 this.maskEl.right.setLeft(box.right + this.padding);
8555 this.maskEl.right.setTop(box.y - this.padding);
8556 this.maskEl.right.show();
8558 this.toolTip.bindEl = this.target.el;
8560 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8562 var tip = this.target.blankText;
8564 if(this.target.getValue() !== '' ) {
8566 if (this.target.invalidText.length) {
8567 tip = this.target.invalidText;
8568 } else if (this.target.regexText.length){
8569 tip = this.target.regexText;
8573 this.toolTip.show(tip);
8575 this.intervalID = window.setInterval(function() {
8576 Roo.bootstrap.Form.popover.unmask();
8579 window.onwheel = function(){ return false;};
8581 (function(){ this.isMasked = true; }).defer(500, this);
8587 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8591 this.maskEl.top.setStyle('position', 'absolute');
8592 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8593 this.maskEl.top.hide();
8595 this.maskEl.left.setStyle('position', 'absolute');
8596 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8597 this.maskEl.left.hide();
8599 this.maskEl.bottom.setStyle('position', 'absolute');
8600 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8601 this.maskEl.bottom.hide();
8603 this.maskEl.right.setStyle('position', 'absolute');
8604 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8605 this.maskEl.right.hide();
8607 this.toolTip.hide();
8609 this.toolTip.el.hide();
8611 window.onwheel = function(){ return true;};
8613 if(this.intervalID){
8614 window.clearInterval(this.intervalID);
8615 this.intervalID = false;
8618 this.isMasked = false;
8628 * Ext JS Library 1.1.1
8629 * Copyright(c) 2006-2007, Ext JS, LLC.
8631 * Originally Released Under LGPL - original licence link has changed is not relivant.
8634 * <script type="text/javascript">
8637 * @class Roo.form.VTypes
8638 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8641 Roo.form.VTypes = function(){
8642 // closure these in so they are only created once.
8643 var alpha = /^[a-zA-Z_]+$/;
8644 var alphanum = /^[a-zA-Z0-9_]+$/;
8645 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8646 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8648 // All these messages and functions are configurable
8651 * The function used to validate email addresses
8652 * @param {String} value The email address
8654 'email' : function(v){
8655 return email.test(v);
8658 * The error text to display when the email validation function returns false
8661 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8663 * The keystroke filter mask to be applied on email input
8666 'emailMask' : /[a-z0-9_\.\-@]/i,
8669 * The function used to validate URLs
8670 * @param {String} value The URL
8672 'url' : function(v){
8676 * The error text to display when the url validation function returns false
8679 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8682 * The function used to validate alpha values
8683 * @param {String} value The value
8685 'alpha' : function(v){
8686 return alpha.test(v);
8689 * The error text to display when the alpha validation function returns false
8692 'alphaText' : 'This field should only contain letters and _',
8694 * The keystroke filter mask to be applied on alpha input
8697 'alphaMask' : /[a-z_]/i,
8700 * The function used to validate alphanumeric values
8701 * @param {String} value The value
8703 'alphanum' : function(v){
8704 return alphanum.test(v);
8707 * The error text to display when the alphanumeric validation function returns false
8710 'alphanumText' : 'This field should only contain letters, numbers and _',
8712 * The keystroke filter mask to be applied on alphanumeric input
8715 'alphanumMask' : /[a-z0-9_]/i
8725 * @class Roo.bootstrap.Input
8726 * @extends Roo.bootstrap.Component
8727 * Bootstrap Input class
8728 * @cfg {Boolean} disabled is it disabled
8729 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8730 * @cfg {String} name name of the input
8731 * @cfg {string} fieldLabel - the label associated
8732 * @cfg {string} placeholder - placeholder to put in text.
8733 * @cfg {string} before - input group add on before
8734 * @cfg {string} after - input group add on after
8735 * @cfg {string} size - (lg|sm) or leave empty..
8736 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8737 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8738 * @cfg {Number} md colspan out of 12 for computer-sized screens
8739 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8740 * @cfg {string} value default value of the input
8741 * @cfg {Number} labelWidth set the width of label
8742 * @cfg {Number} labellg set the width of label (1-12)
8743 * @cfg {Number} labelmd set the width of label (1-12)
8744 * @cfg {Number} labelsm set the width of label (1-12)
8745 * @cfg {Number} labelxs set the width of label (1-12)
8746 * @cfg {String} labelAlign (top|left)
8747 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8748 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8749 * @cfg {String} indicatorpos (left|right) default left
8750 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8751 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8753 * @cfg {String} align (left|center|right) Default left
8754 * @cfg {Boolean} forceFeedback (true|false) Default false
8757 * Create a new Input
8758 * @param {Object} config The config object
8761 Roo.bootstrap.Input = function(config){
8763 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8768 * Fires when this field receives input focus.
8769 * @param {Roo.form.Field} this
8774 * Fires when this field loses input focus.
8775 * @param {Roo.form.Field} this
8780 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8781 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8782 * @param {Roo.form.Field} this
8783 * @param {Roo.EventObject} e The event object
8788 * Fires just before the field blurs if the field value has changed.
8789 * @param {Roo.form.Field} this
8790 * @param {Mixed} newValue The new value
8791 * @param {Mixed} oldValue The original value
8796 * Fires after the field has been marked as invalid.
8797 * @param {Roo.form.Field} this
8798 * @param {String} msg The validation message
8803 * Fires after the field has been validated with no errors.
8804 * @param {Roo.form.Field} this
8809 * Fires after the key up
8810 * @param {Roo.form.Field} this
8811 * @param {Roo.EventObject} e The event Object
8817 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8819 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8820 automatic validation (defaults to "keyup").
8822 validationEvent : "keyup",
8824 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8826 validateOnBlur : true,
8828 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8830 validationDelay : 250,
8832 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8834 focusClass : "x-form-focus", // not needed???
8838 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8840 invalidClass : "has-warning",
8843 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8845 validClass : "has-success",
8848 * @cfg {Boolean} hasFeedback (true|false) default true
8853 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8855 invalidFeedbackClass : "glyphicon-warning-sign",
8858 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8860 validFeedbackClass : "glyphicon-ok",
8863 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8865 selectOnFocus : false,
8868 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8872 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8877 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8879 disableKeyFilter : false,
8882 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8886 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8890 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8892 blankText : "Please complete this mandatory field",
8895 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8899 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8901 maxLength : Number.MAX_VALUE,
8903 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8905 minLengthText : "The minimum length for this field is {0}",
8907 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8909 maxLengthText : "The maximum length for this field is {0}",
8913 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8914 * If available, this function will be called only after the basic validators all return true, and will be passed the
8915 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8919 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8920 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8921 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8925 * @cfg {String} regexText -- Depricated - use Invalid Text
8930 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8936 autocomplete: false,
8955 formatedValue : false,
8956 forceFeedback : false,
8958 indicatorpos : 'left',
8968 parentLabelAlign : function()
8971 while (parent.parent()) {
8972 parent = parent.parent();
8973 if (typeof(parent.labelAlign) !='undefined') {
8974 return parent.labelAlign;
8981 getAutoCreate : function()
8983 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8989 if(this.inputType != 'hidden'){
8990 cfg.cls = 'form-group' //input-group
8996 type : this.inputType,
8998 cls : 'form-control',
8999 placeholder : this.placeholder || '',
9000 autocomplete : this.autocomplete || 'new-password'
9003 if(this.capture.length){
9004 input.capture = this.capture;
9007 if(this.accept.length){
9008 input.accept = this.accept + "/*";
9012 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9015 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9016 input.maxLength = this.maxLength;
9019 if (this.disabled) {
9020 input.disabled=true;
9023 if (this.readOnly) {
9024 input.readonly=true;
9028 input.name = this.name;
9032 input.cls += ' input-' + this.size;
9036 ['xs','sm','md','lg'].map(function(size){
9037 if (settings[size]) {
9038 cfg.cls += ' col-' + size + '-' + settings[size];
9042 var inputblock = input;
9046 cls: 'glyphicon form-control-feedback'
9049 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9052 cls : 'has-feedback',
9060 if (this.before || this.after) {
9063 cls : 'input-group',
9067 if (this.before && typeof(this.before) == 'string') {
9069 inputblock.cn.push({
9071 cls : 'roo-input-before input-group-addon',
9075 if (this.before && typeof(this.before) == 'object') {
9076 this.before = Roo.factory(this.before);
9078 inputblock.cn.push({
9080 cls : 'roo-input-before input-group-' +
9081 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9085 inputblock.cn.push(input);
9087 if (this.after && typeof(this.after) == 'string') {
9088 inputblock.cn.push({
9090 cls : 'roo-input-after input-group-addon',
9094 if (this.after && typeof(this.after) == 'object') {
9095 this.after = Roo.factory(this.after);
9097 inputblock.cn.push({
9099 cls : 'roo-input-after input-group-' +
9100 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9104 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9105 inputblock.cls += ' has-feedback';
9106 inputblock.cn.push(feedback);
9110 if (align ==='left' && this.fieldLabel.length) {
9112 cfg.cls += ' roo-form-group-label-left';
9117 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9118 tooltip : 'This field is required'
9123 cls : 'control-label',
9124 html : this.fieldLabel
9135 var labelCfg = cfg.cn[1];
9136 var contentCfg = cfg.cn[2];
9138 if(this.indicatorpos == 'right'){
9143 cls : 'control-label',
9147 html : this.fieldLabel
9151 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9152 tooltip : 'This field is required'
9165 labelCfg = cfg.cn[0];
9166 contentCfg = cfg.cn[1];
9170 if(this.labelWidth > 12){
9171 labelCfg.style = "width: " + this.labelWidth + 'px';
9174 if(this.labelWidth < 13 && this.labelmd == 0){
9175 this.labelmd = this.labelWidth;
9178 if(this.labellg > 0){
9179 labelCfg.cls += ' col-lg-' + this.labellg;
9180 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9183 if(this.labelmd > 0){
9184 labelCfg.cls += ' col-md-' + this.labelmd;
9185 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9188 if(this.labelsm > 0){
9189 labelCfg.cls += ' col-sm-' + this.labelsm;
9190 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9193 if(this.labelxs > 0){
9194 labelCfg.cls += ' col-xs-' + this.labelxs;
9195 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9199 } else if ( this.fieldLabel.length) {
9204 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9205 tooltip : 'This field is required'
9209 //cls : 'input-group-addon',
9210 html : this.fieldLabel
9218 if(this.indicatorpos == 'right'){
9223 //cls : 'input-group-addon',
9224 html : this.fieldLabel
9229 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9230 tooltip : 'This field is required'
9250 if (this.parentType === 'Navbar' && this.parent().bar) {
9251 cfg.cls += ' navbar-form';
9254 if (this.parentType === 'NavGroup') {
9255 cfg.cls += ' navbar-form';
9263 * return the real input element.
9265 inputEl: function ()
9267 return this.el.select('input.form-control',true).first();
9270 tooltipEl : function()
9272 return this.inputEl();
9275 indicatorEl : function()
9277 var indicator = this.el.select('i.roo-required-indicator',true).first();
9287 setDisabled : function(v)
9289 var i = this.inputEl().dom;
9291 i.removeAttribute('disabled');
9295 i.setAttribute('disabled','true');
9297 initEvents : function()
9300 this.inputEl().on("keydown" , this.fireKey, this);
9301 this.inputEl().on("focus", this.onFocus, this);
9302 this.inputEl().on("blur", this.onBlur, this);
9304 this.inputEl().relayEvent('keyup', this);
9306 this.indicator = this.indicatorEl();
9309 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9312 // reference to original value for reset
9313 this.originalValue = this.getValue();
9314 //Roo.form.TextField.superclass.initEvents.call(this);
9315 if(this.validationEvent == 'keyup'){
9316 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9317 this.inputEl().on('keyup', this.filterValidation, this);
9319 else if(this.validationEvent !== false){
9320 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9323 if(this.selectOnFocus){
9324 this.on("focus", this.preFocus, this);
9327 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9328 this.inputEl().on("keypress", this.filterKeys, this);
9330 this.inputEl().relayEvent('keypress', this);
9333 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9334 this.el.on("click", this.autoSize, this);
9337 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9338 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9341 if (typeof(this.before) == 'object') {
9342 this.before.render(this.el.select('.roo-input-before',true).first());
9344 if (typeof(this.after) == 'object') {
9345 this.after.render(this.el.select('.roo-input-after',true).first());
9348 this.inputEl().on('change', this.onChange, this);
9351 filterValidation : function(e){
9352 if(!e.isNavKeyPress()){
9353 this.validationTask.delay(this.validationDelay);
9357 * Validates the field value
9358 * @return {Boolean} True if the value is valid, else false
9360 validate : function(){
9361 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9362 if(this.disabled || this.validateValue(this.getRawValue())){
9373 * Validates a value according to the field's validation rules and marks the field as invalid
9374 * if the validation fails
9375 * @param {Mixed} value The value to validate
9376 * @return {Boolean} True if the value is valid, else false
9378 validateValue : function(value)
9380 if(this.getVisibilityEl().hasClass('hidden')){
9384 if(value.length < 1) { // if it's blank
9385 if(this.allowBlank){
9391 if(value.length < this.minLength){
9394 if(value.length > this.maxLength){
9398 var vt = Roo.form.VTypes;
9399 if(!vt[this.vtype](value, this)){
9403 if(typeof this.validator == "function"){
9404 var msg = this.validator(value);
9408 if (typeof(msg) == 'string') {
9409 this.invalidText = msg;
9413 if(this.regex && !this.regex.test(value)){
9421 fireKey : function(e){
9422 //Roo.log('field ' + e.getKey());
9423 if(e.isNavKeyPress()){
9424 this.fireEvent("specialkey", this, e);
9427 focus : function (selectText){
9429 this.inputEl().focus();
9430 if(selectText === true){
9431 this.inputEl().dom.select();
9437 onFocus : function(){
9438 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9439 // this.el.addClass(this.focusClass);
9442 this.hasFocus = true;
9443 this.startValue = this.getValue();
9444 this.fireEvent("focus", this);
9448 beforeBlur : Roo.emptyFn,
9452 onBlur : function(){
9454 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9455 //this.el.removeClass(this.focusClass);
9457 this.hasFocus = false;
9458 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9461 var v = this.getValue();
9462 if(String(v) !== String(this.startValue)){
9463 this.fireEvent('change', this, v, this.startValue);
9465 this.fireEvent("blur", this);
9468 onChange : function(e)
9470 var v = this.getValue();
9471 if(String(v) !== String(this.startValue)){
9472 this.fireEvent('change', this, v, this.startValue);
9478 * Resets the current field value to the originally loaded value and clears any validation messages
9481 this.setValue(this.originalValue);
9485 * Returns the name of the field
9486 * @return {Mixed} name The name field
9488 getName: function(){
9492 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9493 * @return {Mixed} value The field value
9495 getValue : function(){
9497 var v = this.inputEl().getValue();
9502 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9503 * @return {Mixed} value The field value
9505 getRawValue : function(){
9506 var v = this.inputEl().getValue();
9512 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9513 * @param {Mixed} value The value to set
9515 setRawValue : function(v){
9516 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9519 selectText : function(start, end){
9520 var v = this.getRawValue();
9522 start = start === undefined ? 0 : start;
9523 end = end === undefined ? v.length : end;
9524 var d = this.inputEl().dom;
9525 if(d.setSelectionRange){
9526 d.setSelectionRange(start, end);
9527 }else if(d.createTextRange){
9528 var range = d.createTextRange();
9529 range.moveStart("character", start);
9530 range.moveEnd("character", v.length-end);
9537 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9538 * @param {Mixed} value The value to set
9540 setValue : function(v){
9543 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9549 processValue : function(value){
9550 if(this.stripCharsRe){
9551 var newValue = value.replace(this.stripCharsRe, '');
9552 if(newValue !== value){
9553 this.setRawValue(newValue);
9560 preFocus : function(){
9562 if(this.selectOnFocus){
9563 this.inputEl().dom.select();
9566 filterKeys : function(e){
9568 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9571 var c = e.getCharCode(), cc = String.fromCharCode(c);
9572 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9575 if(!this.maskRe.test(cc)){
9580 * Clear any invalid styles/messages for this field
9582 clearInvalid : function(){
9584 if(!this.el || this.preventMark){ // not rendered
9589 this.el.removeClass(this.invalidClass);
9591 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9593 var feedback = this.el.select('.form-control-feedback', true).first();
9596 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9602 this.indicator.removeClass('visible');
9603 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9606 this.fireEvent('valid', this);
9610 * Mark this field as valid
9612 markValid : function()
9614 if(!this.el || this.preventMark){ // not rendered...
9618 this.el.removeClass([this.invalidClass, this.validClass]);
9620 var feedback = this.el.select('.form-control-feedback', true).first();
9623 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9627 this.indicator.removeClass('visible');
9628 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9635 if(this.allowBlank && !this.getRawValue().length){
9639 this.el.addClass(this.validClass);
9641 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9643 var feedback = this.el.select('.form-control-feedback', true).first();
9646 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9647 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9652 this.fireEvent('valid', this);
9656 * Mark this field as invalid
9657 * @param {String} msg The validation message
9659 markInvalid : function(msg)
9661 if(!this.el || this.preventMark){ // not rendered
9665 this.el.removeClass([this.invalidClass, this.validClass]);
9667 var feedback = this.el.select('.form-control-feedback', true).first();
9670 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9677 if(this.allowBlank && !this.getRawValue().length){
9682 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9683 this.indicator.addClass('visible');
9686 this.el.addClass(this.invalidClass);
9688 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9690 var feedback = this.el.select('.form-control-feedback', true).first();
9693 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9695 if(this.getValue().length || this.forceFeedback){
9696 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9703 this.fireEvent('invalid', this, msg);
9706 SafariOnKeyDown : function(event)
9708 // this is a workaround for a password hang bug on chrome/ webkit.
9709 if (this.inputEl().dom.type != 'password') {
9713 var isSelectAll = false;
9715 if(this.inputEl().dom.selectionEnd > 0){
9716 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9718 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9719 event.preventDefault();
9724 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9726 event.preventDefault();
9727 // this is very hacky as keydown always get's upper case.
9729 var cc = String.fromCharCode(event.getCharCode());
9730 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9734 adjustWidth : function(tag, w){
9735 tag = tag.toLowerCase();
9736 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9737 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9741 if(tag == 'textarea'){
9744 }else if(Roo.isOpera){
9748 if(tag == 'textarea'){
9756 setFieldLabel : function(v)
9763 var ar = this.el.select('label > span',true);
9765 if (ar.elements.length) {
9766 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9767 this.fieldLabel = v;
9771 var br = this.el.select('label',true);
9773 if(br.elements.length) {
9774 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9775 this.fieldLabel = v;
9779 Roo.log('Cannot Found any of label > span || label in input');
9783 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9784 this.fieldLabel = v;
9799 * @class Roo.bootstrap.TextArea
9800 * @extends Roo.bootstrap.Input
9801 * Bootstrap TextArea class
9802 * @cfg {Number} cols Specifies the visible width of a text area
9803 * @cfg {Number} rows Specifies the visible number of lines in a text area
9804 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9805 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9806 * @cfg {string} html text
9809 * Create a new TextArea
9810 * @param {Object} config The config object
9813 Roo.bootstrap.TextArea = function(config){
9814 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9818 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9828 getAutoCreate : function(){
9830 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9836 if(this.inputType != 'hidden'){
9837 cfg.cls = 'form-group' //input-group
9845 value : this.value || '',
9846 html: this.html || '',
9847 cls : 'form-control',
9848 placeholder : this.placeholder || ''
9852 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9853 input.maxLength = this.maxLength;
9857 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9861 input.cols = this.cols;
9864 if (this.readOnly) {
9865 input.readonly = true;
9869 input.name = this.name;
9873 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9877 ['xs','sm','md','lg'].map(function(size){
9878 if (settings[size]) {
9879 cfg.cls += ' col-' + size + '-' + settings[size];
9883 var inputblock = input;
9885 if(this.hasFeedback && !this.allowBlank){
9889 cls: 'glyphicon form-control-feedback'
9893 cls : 'has-feedback',
9902 if (this.before || this.after) {
9905 cls : 'input-group',
9909 inputblock.cn.push({
9911 cls : 'input-group-addon',
9916 inputblock.cn.push(input);
9918 if(this.hasFeedback && !this.allowBlank){
9919 inputblock.cls += ' has-feedback';
9920 inputblock.cn.push(feedback);
9924 inputblock.cn.push({
9926 cls : 'input-group-addon',
9933 if (align ==='left' && this.fieldLabel.length) {
9938 cls : 'control-label',
9939 html : this.fieldLabel
9950 if(this.labelWidth > 12){
9951 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9954 if(this.labelWidth < 13 && this.labelmd == 0){
9955 this.labelmd = this.labelWidth;
9958 if(this.labellg > 0){
9959 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9960 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9963 if(this.labelmd > 0){
9964 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9965 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9968 if(this.labelsm > 0){
9969 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9970 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9973 if(this.labelxs > 0){
9974 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9975 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9978 } else if ( this.fieldLabel.length) {
9983 //cls : 'input-group-addon',
9984 html : this.fieldLabel
10002 if (this.disabled) {
10003 input.disabled=true;
10010 * return the real textarea element.
10012 inputEl: function ()
10014 return this.el.select('textarea.form-control',true).first();
10018 * Clear any invalid styles/messages for this field
10020 clearInvalid : function()
10023 if(!this.el || this.preventMark){ // not rendered
10027 var label = this.el.select('label', true).first();
10028 var icon = this.el.select('i.fa-star', true).first();
10034 this.el.removeClass(this.invalidClass);
10036 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10038 var feedback = this.el.select('.form-control-feedback', true).first();
10041 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10046 this.fireEvent('valid', this);
10050 * Mark this field as valid
10052 markValid : function()
10054 if(!this.el || this.preventMark){ // not rendered
10058 this.el.removeClass([this.invalidClass, this.validClass]);
10060 var feedback = this.el.select('.form-control-feedback', true).first();
10063 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10066 if(this.disabled || this.allowBlank){
10070 var label = this.el.select('label', true).first();
10071 var icon = this.el.select('i.fa-star', true).first();
10077 this.el.addClass(this.validClass);
10079 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10081 var feedback = this.el.select('.form-control-feedback', true).first();
10084 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10085 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10090 this.fireEvent('valid', this);
10094 * Mark this field as invalid
10095 * @param {String} msg The validation message
10097 markInvalid : function(msg)
10099 if(!this.el || this.preventMark){ // not rendered
10103 this.el.removeClass([this.invalidClass, this.validClass]);
10105 var feedback = this.el.select('.form-control-feedback', true).first();
10108 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10111 if(this.disabled || this.allowBlank){
10115 var label = this.el.select('label', true).first();
10116 var icon = this.el.select('i.fa-star', true).first();
10118 if(!this.getValue().length && label && !icon){
10119 this.el.createChild({
10121 cls : 'text-danger fa fa-lg fa-star',
10122 tooltip : 'This field is required',
10123 style : 'margin-right:5px;'
10127 this.el.addClass(this.invalidClass);
10129 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10131 var feedback = this.el.select('.form-control-feedback', true).first();
10134 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10136 if(this.getValue().length || this.forceFeedback){
10137 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10144 this.fireEvent('invalid', this, msg);
10152 * trigger field - base class for combo..
10157 * @class Roo.bootstrap.TriggerField
10158 * @extends Roo.bootstrap.Input
10159 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10160 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10161 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10162 * for which you can provide a custom implementation. For example:
10164 var trigger = new Roo.bootstrap.TriggerField();
10165 trigger.onTriggerClick = myTriggerFn;
10166 trigger.applyTo('my-field');
10169 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10170 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10171 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10172 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10173 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10176 * Create a new TriggerField.
10177 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10178 * to the base TextField)
10180 Roo.bootstrap.TriggerField = function(config){
10181 this.mimicing = false;
10182 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10185 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10187 * @cfg {String} triggerClass A CSS class to apply to the trigger
10190 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10195 * @cfg {Boolean} removable (true|false) special filter default false
10199 /** @cfg {Boolean} grow @hide */
10200 /** @cfg {Number} growMin @hide */
10201 /** @cfg {Number} growMax @hide */
10207 autoSize: Roo.emptyFn,
10211 deferHeight : true,
10214 actionMode : 'wrap',
10219 getAutoCreate : function(){
10221 var align = this.labelAlign || this.parentLabelAlign();
10226 cls: 'form-group' //input-group
10233 type : this.inputType,
10234 cls : 'form-control',
10235 autocomplete: 'new-password',
10236 placeholder : this.placeholder || ''
10240 input.name = this.name;
10243 input.cls += ' input-' + this.size;
10246 if (this.disabled) {
10247 input.disabled=true;
10250 var inputblock = input;
10252 if(this.hasFeedback && !this.allowBlank){
10256 cls: 'glyphicon form-control-feedback'
10259 if(this.removable && !this.editable && !this.tickable){
10261 cls : 'has-feedback',
10267 cls : 'roo-combo-removable-btn close'
10274 cls : 'has-feedback',
10283 if(this.removable && !this.editable && !this.tickable){
10285 cls : 'roo-removable',
10291 cls : 'roo-combo-removable-btn close'
10298 if (this.before || this.after) {
10301 cls : 'input-group',
10305 inputblock.cn.push({
10307 cls : 'input-group-addon',
10312 inputblock.cn.push(input);
10314 if(this.hasFeedback && !this.allowBlank){
10315 inputblock.cls += ' has-feedback';
10316 inputblock.cn.push(feedback);
10320 inputblock.cn.push({
10322 cls : 'input-group-addon',
10335 cls: 'form-hidden-field'
10349 cls: 'form-hidden-field'
10353 cls: 'roo-select2-choices',
10357 cls: 'roo-select2-search-field',
10370 cls: 'roo-select2-container input-group',
10375 // cls: 'typeahead typeahead-long dropdown-menu',
10376 // style: 'display:none'
10381 if(!this.multiple && this.showToggleBtn){
10387 if (this.caret != false) {
10390 cls: 'fa fa-' + this.caret
10397 cls : 'input-group-addon btn dropdown-toggle',
10402 cls: 'combobox-clear',
10416 combobox.cls += ' roo-select2-container-multi';
10419 if (align ==='left' && this.fieldLabel.length) {
10421 cfg.cls += ' roo-form-group-label-left';
10426 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10427 tooltip : 'This field is required'
10432 cls : 'control-label',
10433 html : this.fieldLabel
10445 var labelCfg = cfg.cn[1];
10446 var contentCfg = cfg.cn[2];
10448 if(this.indicatorpos == 'right'){
10453 cls : 'control-label',
10457 html : this.fieldLabel
10461 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10462 tooltip : 'This field is required'
10475 labelCfg = cfg.cn[0];
10476 contentCfg = cfg.cn[1];
10479 if(this.labelWidth > 12){
10480 labelCfg.style = "width: " + this.labelWidth + 'px';
10483 if(this.labelWidth < 13 && this.labelmd == 0){
10484 this.labelmd = this.labelWidth;
10487 if(this.labellg > 0){
10488 labelCfg.cls += ' col-lg-' + this.labellg;
10489 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10492 if(this.labelmd > 0){
10493 labelCfg.cls += ' col-md-' + this.labelmd;
10494 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10497 if(this.labelsm > 0){
10498 labelCfg.cls += ' col-sm-' + this.labelsm;
10499 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10502 if(this.labelxs > 0){
10503 labelCfg.cls += ' col-xs-' + this.labelxs;
10504 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10507 } else if ( this.fieldLabel.length) {
10508 // Roo.log(" label");
10512 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10513 tooltip : 'This field is required'
10517 //cls : 'input-group-addon',
10518 html : this.fieldLabel
10526 if(this.indicatorpos == 'right'){
10534 html : this.fieldLabel
10538 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10539 tooltip : 'This field is required'
10552 // Roo.log(" no label && no align");
10559 ['xs','sm','md','lg'].map(function(size){
10560 if (settings[size]) {
10561 cfg.cls += ' col-' + size + '-' + settings[size];
10572 onResize : function(w, h){
10573 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10574 // if(typeof w == 'number'){
10575 // var x = w - this.trigger.getWidth();
10576 // this.inputEl().setWidth(this.adjustWidth('input', x));
10577 // this.trigger.setStyle('left', x+'px');
10582 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10585 getResizeEl : function(){
10586 return this.inputEl();
10590 getPositionEl : function(){
10591 return this.inputEl();
10595 alignErrorIcon : function(){
10596 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10600 initEvents : function(){
10604 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10605 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10606 if(!this.multiple && this.showToggleBtn){
10607 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10608 if(this.hideTrigger){
10609 this.trigger.setDisplayed(false);
10611 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10615 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10618 if(this.removable && !this.editable && !this.tickable){
10619 var close = this.closeTriggerEl();
10622 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10623 close.on('click', this.removeBtnClick, this, close);
10627 //this.trigger.addClassOnOver('x-form-trigger-over');
10628 //this.trigger.addClassOnClick('x-form-trigger-click');
10631 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10635 closeTriggerEl : function()
10637 var close = this.el.select('.roo-combo-removable-btn', true).first();
10638 return close ? close : false;
10641 removeBtnClick : function(e, h, el)
10643 e.preventDefault();
10645 if(this.fireEvent("remove", this) !== false){
10647 this.fireEvent("afterremove", this)
10651 createList : function()
10653 this.list = Roo.get(document.body).createChild({
10655 cls: 'typeahead typeahead-long dropdown-menu',
10656 style: 'display:none'
10659 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10664 initTrigger : function(){
10669 onDestroy : function(){
10671 this.trigger.removeAllListeners();
10672 // this.trigger.remove();
10675 // this.wrap.remove();
10677 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10681 onFocus : function(){
10682 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10684 if(!this.mimicing){
10685 this.wrap.addClass('x-trigger-wrap-focus');
10686 this.mimicing = true;
10687 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10688 if(this.monitorTab){
10689 this.el.on("keydown", this.checkTab, this);
10696 checkTab : function(e){
10697 if(e.getKey() == e.TAB){
10698 this.triggerBlur();
10703 onBlur : function(){
10708 mimicBlur : function(e, t){
10710 if(!this.wrap.contains(t) && this.validateBlur()){
10711 this.triggerBlur();
10717 triggerBlur : function(){
10718 this.mimicing = false;
10719 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10720 if(this.monitorTab){
10721 this.el.un("keydown", this.checkTab, this);
10723 //this.wrap.removeClass('x-trigger-wrap-focus');
10724 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10728 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10729 validateBlur : function(e, t){
10734 onDisable : function(){
10735 this.inputEl().dom.disabled = true;
10736 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10738 // this.wrap.addClass('x-item-disabled');
10743 onEnable : function(){
10744 this.inputEl().dom.disabled = false;
10745 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10747 // this.el.removeClass('x-item-disabled');
10752 onShow : function(){
10753 var ae = this.getActionEl();
10756 ae.dom.style.display = '';
10757 ae.dom.style.visibility = 'visible';
10763 onHide : function(){
10764 var ae = this.getActionEl();
10765 ae.dom.style.display = 'none';
10769 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10770 * by an implementing function.
10772 * @param {EventObject} e
10774 onTriggerClick : Roo.emptyFn
10778 * Ext JS Library 1.1.1
10779 * Copyright(c) 2006-2007, Ext JS, LLC.
10781 * Originally Released Under LGPL - original licence link has changed is not relivant.
10784 * <script type="text/javascript">
10789 * @class Roo.data.SortTypes
10791 * Defines the default sorting (casting?) comparison functions used when sorting data.
10793 Roo.data.SortTypes = {
10795 * Default sort that does nothing
10796 * @param {Mixed} s The value being converted
10797 * @return {Mixed} The comparison value
10799 none : function(s){
10804 * The regular expression used to strip tags
10808 stripTagsRE : /<\/?[^>]+>/gi,
10811 * Strips all HTML tags to sort on text only
10812 * @param {Mixed} s The value being converted
10813 * @return {String} The comparison value
10815 asText : function(s){
10816 return String(s).replace(this.stripTagsRE, "");
10820 * Strips all HTML tags to sort on text only - Case insensitive
10821 * @param {Mixed} s The value being converted
10822 * @return {String} The comparison value
10824 asUCText : function(s){
10825 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10829 * Case insensitive string
10830 * @param {Mixed} s The value being converted
10831 * @return {String} The comparison value
10833 asUCString : function(s) {
10834 return String(s).toUpperCase();
10839 * @param {Mixed} s The value being converted
10840 * @return {Number} The comparison value
10842 asDate : function(s) {
10846 if(s instanceof Date){
10847 return s.getTime();
10849 return Date.parse(String(s));
10854 * @param {Mixed} s The value being converted
10855 * @return {Float} The comparison value
10857 asFloat : function(s) {
10858 var val = parseFloat(String(s).replace(/,/g, ""));
10867 * @param {Mixed} s The value being converted
10868 * @return {Number} The comparison value
10870 asInt : function(s) {
10871 var val = parseInt(String(s).replace(/,/g, ""));
10879 * Ext JS Library 1.1.1
10880 * Copyright(c) 2006-2007, Ext JS, LLC.
10882 * Originally Released Under LGPL - original licence link has changed is not relivant.
10885 * <script type="text/javascript">
10889 * @class Roo.data.Record
10890 * Instances of this class encapsulate both record <em>definition</em> information, and record
10891 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10892 * to access Records cached in an {@link Roo.data.Store} object.<br>
10894 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10895 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10898 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10900 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10901 * {@link #create}. The parameters are the same.
10902 * @param {Array} data An associative Array of data values keyed by the field name.
10903 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10904 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10905 * not specified an integer id is generated.
10907 Roo.data.Record = function(data, id){
10908 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10913 * Generate a constructor for a specific record layout.
10914 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10915 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10916 * Each field definition object may contain the following properties: <ul>
10917 * <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,
10918 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10919 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10920 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10921 * is being used, then this is a string containing the javascript expression to reference the data relative to
10922 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10923 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10924 * this may be omitted.</p></li>
10925 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10926 * <ul><li>auto (Default, implies no conversion)</li>
10931 * <li>date</li></ul></p></li>
10932 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10933 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10934 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10935 * by the Reader into an object that will be stored in the Record. It is passed the
10936 * following parameters:<ul>
10937 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10939 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10941 * <br>usage:<br><pre><code>
10942 var TopicRecord = Roo.data.Record.create(
10943 {name: 'title', mapping: 'topic_title'},
10944 {name: 'author', mapping: 'username'},
10945 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10946 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10947 {name: 'lastPoster', mapping: 'user2'},
10948 {name: 'excerpt', mapping: 'post_text'}
10951 var myNewRecord = new TopicRecord({
10952 title: 'Do my job please',
10955 lastPost: new Date(),
10956 lastPoster: 'Animal',
10957 excerpt: 'No way dude!'
10959 myStore.add(myNewRecord);
10964 Roo.data.Record.create = function(o){
10965 var f = function(){
10966 f.superclass.constructor.apply(this, arguments);
10968 Roo.extend(f, Roo.data.Record);
10969 var p = f.prototype;
10970 p.fields = new Roo.util.MixedCollection(false, function(field){
10973 for(var i = 0, len = o.length; i < len; i++){
10974 p.fields.add(new Roo.data.Field(o[i]));
10976 f.getField = function(name){
10977 return p.fields.get(name);
10982 Roo.data.Record.AUTO_ID = 1000;
10983 Roo.data.Record.EDIT = 'edit';
10984 Roo.data.Record.REJECT = 'reject';
10985 Roo.data.Record.COMMIT = 'commit';
10987 Roo.data.Record.prototype = {
10989 * Readonly flag - true if this record has been modified.
10998 join : function(store){
10999 this.store = store;
11003 * Set the named field to the specified value.
11004 * @param {String} name The name of the field to set.
11005 * @param {Object} value The value to set the field to.
11007 set : function(name, value){
11008 if(this.data[name] == value){
11012 if(!this.modified){
11013 this.modified = {};
11015 if(typeof this.modified[name] == 'undefined'){
11016 this.modified[name] = this.data[name];
11018 this.data[name] = value;
11019 if(!this.editing && this.store){
11020 this.store.afterEdit(this);
11025 * Get the value of the named field.
11026 * @param {String} name The name of the field to get the value of.
11027 * @return {Object} The value of the field.
11029 get : function(name){
11030 return this.data[name];
11034 beginEdit : function(){
11035 this.editing = true;
11036 this.modified = {};
11040 cancelEdit : function(){
11041 this.editing = false;
11042 delete this.modified;
11046 endEdit : function(){
11047 this.editing = false;
11048 if(this.dirty && this.store){
11049 this.store.afterEdit(this);
11054 * Usually called by the {@link Roo.data.Store} which owns the Record.
11055 * Rejects all changes made to the Record since either creation, or the last commit operation.
11056 * Modified fields are reverted to their original values.
11058 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11059 * of reject operations.
11061 reject : function(){
11062 var m = this.modified;
11064 if(typeof m[n] != "function"){
11065 this.data[n] = m[n];
11068 this.dirty = false;
11069 delete this.modified;
11070 this.editing = false;
11072 this.store.afterReject(this);
11077 * Usually called by the {@link Roo.data.Store} which owns the Record.
11078 * Commits all changes made to the Record since either creation, or the last commit operation.
11080 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11081 * of commit operations.
11083 commit : function(){
11084 this.dirty = false;
11085 delete this.modified;
11086 this.editing = false;
11088 this.store.afterCommit(this);
11093 hasError : function(){
11094 return this.error != null;
11098 clearError : function(){
11103 * Creates a copy of this record.
11104 * @param {String} id (optional) A new record id if you don't want to use this record's id
11107 copy : function(newId) {
11108 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11112 * Ext JS Library 1.1.1
11113 * Copyright(c) 2006-2007, Ext JS, LLC.
11115 * Originally Released Under LGPL - original licence link has changed is not relivant.
11118 * <script type="text/javascript">
11124 * @class Roo.data.Store
11125 * @extends Roo.util.Observable
11126 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11127 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11129 * 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
11130 * has no knowledge of the format of the data returned by the Proxy.<br>
11132 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11133 * instances from the data object. These records are cached and made available through accessor functions.
11135 * Creates a new Store.
11136 * @param {Object} config A config object containing the objects needed for the Store to access data,
11137 * and read the data into Records.
11139 Roo.data.Store = function(config){
11140 this.data = new Roo.util.MixedCollection(false);
11141 this.data.getKey = function(o){
11144 this.baseParams = {};
11146 this.paramNames = {
11151 "multisort" : "_multisort"
11154 if(config && config.data){
11155 this.inlineData = config.data;
11156 delete config.data;
11159 Roo.apply(this, config);
11161 if(this.reader){ // reader passed
11162 this.reader = Roo.factory(this.reader, Roo.data);
11163 this.reader.xmodule = this.xmodule || false;
11164 if(!this.recordType){
11165 this.recordType = this.reader.recordType;
11167 if(this.reader.onMetaChange){
11168 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11172 if(this.recordType){
11173 this.fields = this.recordType.prototype.fields;
11175 this.modified = [];
11179 * @event datachanged
11180 * Fires when the data cache has changed, and a widget which is using this Store
11181 * as a Record cache should refresh its view.
11182 * @param {Store} this
11184 datachanged : true,
11186 * @event metachange
11187 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11188 * @param {Store} this
11189 * @param {Object} meta The JSON metadata
11194 * Fires when Records have been added to the Store
11195 * @param {Store} this
11196 * @param {Roo.data.Record[]} records The array of Records added
11197 * @param {Number} index The index at which the record(s) were added
11202 * Fires when a Record has been removed from the Store
11203 * @param {Store} this
11204 * @param {Roo.data.Record} record The Record that was removed
11205 * @param {Number} index The index at which the record was removed
11210 * Fires when a Record has been updated
11211 * @param {Store} this
11212 * @param {Roo.data.Record} record The Record that was updated
11213 * @param {String} operation The update operation being performed. Value may be one of:
11215 Roo.data.Record.EDIT
11216 Roo.data.Record.REJECT
11217 Roo.data.Record.COMMIT
11223 * Fires when the data cache has been cleared.
11224 * @param {Store} this
11228 * @event beforeload
11229 * Fires before a request is made for a new data object. If the beforeload handler returns false
11230 * the load action will be canceled.
11231 * @param {Store} this
11232 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11236 * @event beforeloadadd
11237 * Fires after a new set of Records has been loaded.
11238 * @param {Store} this
11239 * @param {Roo.data.Record[]} records The Records that were loaded
11240 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11242 beforeloadadd : true,
11245 * Fires after a new set of Records has been loaded, before they are added to the store.
11246 * @param {Store} this
11247 * @param {Roo.data.Record[]} records The Records that were loaded
11248 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11249 * @params {Object} return from reader
11253 * @event loadexception
11254 * Fires if an exception occurs in the Proxy during loading.
11255 * Called with the signature of the Proxy's "loadexception" event.
11256 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11259 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11260 * @param {Object} load options
11261 * @param {Object} jsonData from your request (normally this contains the Exception)
11263 loadexception : true
11267 this.proxy = Roo.factory(this.proxy, Roo.data);
11268 this.proxy.xmodule = this.xmodule || false;
11269 this.relayEvents(this.proxy, ["loadexception"]);
11271 this.sortToggle = {};
11272 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11274 Roo.data.Store.superclass.constructor.call(this);
11276 if(this.inlineData){
11277 this.loadData(this.inlineData);
11278 delete this.inlineData;
11282 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11284 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11285 * without a remote query - used by combo/forms at present.
11289 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11292 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11295 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11296 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11299 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11300 * on any HTTP request
11303 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11306 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11310 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11311 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11313 remoteSort : false,
11316 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11317 * loaded or when a record is removed. (defaults to false).
11319 pruneModifiedRecords : false,
11322 lastOptions : null,
11325 * Add Records to the Store and fires the add event.
11326 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11328 add : function(records){
11329 records = [].concat(records);
11330 for(var i = 0, len = records.length; i < len; i++){
11331 records[i].join(this);
11333 var index = this.data.length;
11334 this.data.addAll(records);
11335 this.fireEvent("add", this, records, index);
11339 * Remove a Record from the Store and fires the remove event.
11340 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11342 remove : function(record){
11343 var index = this.data.indexOf(record);
11344 this.data.removeAt(index);
11346 if(this.pruneModifiedRecords){
11347 this.modified.remove(record);
11349 this.fireEvent("remove", this, record, index);
11353 * Remove all Records from the Store and fires the clear event.
11355 removeAll : function(){
11357 if(this.pruneModifiedRecords){
11358 this.modified = [];
11360 this.fireEvent("clear", this);
11364 * Inserts Records to the Store at the given index and fires the add event.
11365 * @param {Number} index The start index at which to insert the passed Records.
11366 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11368 insert : function(index, records){
11369 records = [].concat(records);
11370 for(var i = 0, len = records.length; i < len; i++){
11371 this.data.insert(index, records[i]);
11372 records[i].join(this);
11374 this.fireEvent("add", this, records, index);
11378 * Get the index within the cache of the passed Record.
11379 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11380 * @return {Number} The index of the passed Record. Returns -1 if not found.
11382 indexOf : function(record){
11383 return this.data.indexOf(record);
11387 * Get the index within the cache of the Record with the passed id.
11388 * @param {String} id The id of the Record to find.
11389 * @return {Number} The index of the Record. Returns -1 if not found.
11391 indexOfId : function(id){
11392 return this.data.indexOfKey(id);
11396 * Get the Record with the specified id.
11397 * @param {String} id The id of the Record to find.
11398 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11400 getById : function(id){
11401 return this.data.key(id);
11405 * Get the Record at the specified index.
11406 * @param {Number} index The index of the Record to find.
11407 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11409 getAt : function(index){
11410 return this.data.itemAt(index);
11414 * Returns a range of Records between specified indices.
11415 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11416 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11417 * @return {Roo.data.Record[]} An array of Records
11419 getRange : function(start, end){
11420 return this.data.getRange(start, end);
11424 storeOptions : function(o){
11425 o = Roo.apply({}, o);
11428 this.lastOptions = o;
11432 * Loads the Record cache from the configured Proxy using the configured Reader.
11434 * If using remote paging, then the first load call must specify the <em>start</em>
11435 * and <em>limit</em> properties in the options.params property to establish the initial
11436 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11438 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11439 * and this call will return before the new data has been loaded. Perform any post-processing
11440 * in a callback function, or in a "load" event handler.</strong>
11442 * @param {Object} options An object containing properties which control loading options:<ul>
11443 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11444 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11445 * passed the following arguments:<ul>
11446 * <li>r : Roo.data.Record[]</li>
11447 * <li>options: Options object from the load call</li>
11448 * <li>success: Boolean success indicator</li></ul></li>
11449 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11450 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11453 load : function(options){
11454 options = options || {};
11455 if(this.fireEvent("beforeload", this, options) !== false){
11456 this.storeOptions(options);
11457 var p = Roo.apply(options.params || {}, this.baseParams);
11458 // if meta was not loaded from remote source.. try requesting it.
11459 if (!this.reader.metaFromRemote) {
11460 p._requestMeta = 1;
11462 if(this.sortInfo && this.remoteSort){
11463 var pn = this.paramNames;
11464 p[pn["sort"]] = this.sortInfo.field;
11465 p[pn["dir"]] = this.sortInfo.direction;
11467 if (this.multiSort) {
11468 var pn = this.paramNames;
11469 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11472 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11477 * Reloads the Record cache from the configured Proxy using the configured Reader and
11478 * the options from the last load operation performed.
11479 * @param {Object} options (optional) An object containing properties which may override the options
11480 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11481 * the most recently used options are reused).
11483 reload : function(options){
11484 this.load(Roo.applyIf(options||{}, this.lastOptions));
11488 // Called as a callback by the Reader during a load operation.
11489 loadRecords : function(o, options, success){
11490 if(!o || success === false){
11491 if(success !== false){
11492 this.fireEvent("load", this, [], options, o);
11494 if(options.callback){
11495 options.callback.call(options.scope || this, [], options, false);
11499 // if data returned failure - throw an exception.
11500 if (o.success === false) {
11501 // show a message if no listener is registered.
11502 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11503 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11505 // loadmask wil be hooked into this..
11506 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11509 var r = o.records, t = o.totalRecords || r.length;
11511 this.fireEvent("beforeloadadd", this, r, options, o);
11513 if(!options || options.add !== true){
11514 if(this.pruneModifiedRecords){
11515 this.modified = [];
11517 for(var i = 0, len = r.length; i < len; i++){
11521 this.data = this.snapshot;
11522 delete this.snapshot;
11525 this.data.addAll(r);
11526 this.totalLength = t;
11528 this.fireEvent("datachanged", this);
11530 this.totalLength = Math.max(t, this.data.length+r.length);
11534 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11536 var e = new Roo.data.Record({});
11538 e.set(this.parent.displayField, this.parent.emptyTitle);
11539 e.set(this.parent.valueField, '');
11544 this.fireEvent("load", this, r, options, o);
11545 if(options.callback){
11546 options.callback.call(options.scope || this, r, options, true);
11552 * Loads data from a passed data block. A Reader which understands the format of the data
11553 * must have been configured in the constructor.
11554 * @param {Object} data The data block from which to read the Records. The format of the data expected
11555 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11556 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11558 loadData : function(o, append){
11559 var r = this.reader.readRecords(o);
11560 this.loadRecords(r, {add: append}, true);
11564 * Gets the number of cached records.
11566 * <em>If using paging, this may not be the total size of the dataset. If the data object
11567 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11568 * the data set size</em>
11570 getCount : function(){
11571 return this.data.length || 0;
11575 * Gets the total number of records in the dataset as returned by the server.
11577 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11578 * the dataset size</em>
11580 getTotalCount : function(){
11581 return this.totalLength || 0;
11585 * Returns the sort state of the Store as an object with two properties:
11587 field {String} The name of the field by which the Records are sorted
11588 direction {String} The sort order, "ASC" or "DESC"
11591 getSortState : function(){
11592 return this.sortInfo;
11596 applySort : function(){
11597 if(this.sortInfo && !this.remoteSort){
11598 var s = this.sortInfo, f = s.field;
11599 var st = this.fields.get(f).sortType;
11600 var fn = function(r1, r2){
11601 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11602 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11604 this.data.sort(s.direction, fn);
11605 if(this.snapshot && this.snapshot != this.data){
11606 this.snapshot.sort(s.direction, fn);
11612 * Sets the default sort column and order to be used by the next load operation.
11613 * @param {String} fieldName The name of the field to sort by.
11614 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11616 setDefaultSort : function(field, dir){
11617 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11621 * Sort the Records.
11622 * If remote sorting is used, the sort is performed on the server, and the cache is
11623 * reloaded. If local sorting is used, the cache is sorted internally.
11624 * @param {String} fieldName The name of the field to sort by.
11625 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11627 sort : function(fieldName, dir){
11628 var f = this.fields.get(fieldName);
11630 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11632 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11633 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11638 this.sortToggle[f.name] = dir;
11639 this.sortInfo = {field: f.name, direction: dir};
11640 if(!this.remoteSort){
11642 this.fireEvent("datachanged", this);
11644 this.load(this.lastOptions);
11649 * Calls the specified function for each of the Records in the cache.
11650 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11651 * Returning <em>false</em> aborts and exits the iteration.
11652 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11654 each : function(fn, scope){
11655 this.data.each(fn, scope);
11659 * Gets all records modified since the last commit. Modified records are persisted across load operations
11660 * (e.g., during paging).
11661 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11663 getModifiedRecords : function(){
11664 return this.modified;
11668 createFilterFn : function(property, value, anyMatch){
11669 if(!value.exec){ // not a regex
11670 value = String(value);
11671 if(value.length == 0){
11674 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11676 return function(r){
11677 return value.test(r.data[property]);
11682 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11683 * @param {String} property A field on your records
11684 * @param {Number} start The record index to start at (defaults to 0)
11685 * @param {Number} end The last record index to include (defaults to length - 1)
11686 * @return {Number} The sum
11688 sum : function(property, start, end){
11689 var rs = this.data.items, v = 0;
11690 start = start || 0;
11691 end = (end || end === 0) ? end : rs.length-1;
11693 for(var i = start; i <= end; i++){
11694 v += (rs[i].data[property] || 0);
11700 * Filter the records by a specified property.
11701 * @param {String} field A field on your records
11702 * @param {String/RegExp} value Either a string that the field
11703 * should start with or a RegExp to test against the field
11704 * @param {Boolean} anyMatch True to match any part not just the beginning
11706 filter : function(property, value, anyMatch){
11707 var fn = this.createFilterFn(property, value, anyMatch);
11708 return fn ? this.filterBy(fn) : this.clearFilter();
11712 * Filter by a function. The specified function will be called with each
11713 * record in this data source. If the function returns true the record is included,
11714 * otherwise it is filtered.
11715 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11716 * @param {Object} scope (optional) The scope of the function (defaults to this)
11718 filterBy : function(fn, scope){
11719 this.snapshot = this.snapshot || this.data;
11720 this.data = this.queryBy(fn, scope||this);
11721 this.fireEvent("datachanged", this);
11725 * Query the records by a specified property.
11726 * @param {String} field A field on your records
11727 * @param {String/RegExp} value Either a string that the field
11728 * should start with or a RegExp to test against the field
11729 * @param {Boolean} anyMatch True to match any part not just the beginning
11730 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11732 query : function(property, value, anyMatch){
11733 var fn = this.createFilterFn(property, value, anyMatch);
11734 return fn ? this.queryBy(fn) : this.data.clone();
11738 * Query by a function. The specified function will be called with each
11739 * record in this data source. If the function returns true the record is included
11741 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11742 * @param {Object} scope (optional) The scope of the function (defaults to this)
11743 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11745 queryBy : function(fn, scope){
11746 var data = this.snapshot || this.data;
11747 return data.filterBy(fn, scope||this);
11751 * Collects unique values for a particular dataIndex from this store.
11752 * @param {String} dataIndex The property to collect
11753 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11754 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11755 * @return {Array} An array of the unique values
11757 collect : function(dataIndex, allowNull, bypassFilter){
11758 var d = (bypassFilter === true && this.snapshot) ?
11759 this.snapshot.items : this.data.items;
11760 var v, sv, r = [], l = {};
11761 for(var i = 0, len = d.length; i < len; i++){
11762 v = d[i].data[dataIndex];
11764 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11773 * Revert to a view of the Record cache with no filtering applied.
11774 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11776 clearFilter : function(suppressEvent){
11777 if(this.snapshot && this.snapshot != this.data){
11778 this.data = this.snapshot;
11779 delete this.snapshot;
11780 if(suppressEvent !== true){
11781 this.fireEvent("datachanged", this);
11787 afterEdit : function(record){
11788 if(this.modified.indexOf(record) == -1){
11789 this.modified.push(record);
11791 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11795 afterReject : function(record){
11796 this.modified.remove(record);
11797 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11801 afterCommit : function(record){
11802 this.modified.remove(record);
11803 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11807 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11808 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11810 commitChanges : function(){
11811 var m = this.modified.slice(0);
11812 this.modified = [];
11813 for(var i = 0, len = m.length; i < len; i++){
11819 * Cancel outstanding changes on all changed records.
11821 rejectChanges : function(){
11822 var m = this.modified.slice(0);
11823 this.modified = [];
11824 for(var i = 0, len = m.length; i < len; i++){
11829 onMetaChange : function(meta, rtype, o){
11830 this.recordType = rtype;
11831 this.fields = rtype.prototype.fields;
11832 delete this.snapshot;
11833 this.sortInfo = meta.sortInfo || this.sortInfo;
11834 this.modified = [];
11835 this.fireEvent('metachange', this, this.reader.meta);
11838 moveIndex : function(data, type)
11840 var index = this.indexOf(data);
11842 var newIndex = index + type;
11846 this.insert(newIndex, data);
11851 * Ext JS Library 1.1.1
11852 * Copyright(c) 2006-2007, Ext JS, LLC.
11854 * Originally Released Under LGPL - original licence link has changed is not relivant.
11857 * <script type="text/javascript">
11861 * @class Roo.data.SimpleStore
11862 * @extends Roo.data.Store
11863 * Small helper class to make creating Stores from Array data easier.
11864 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11865 * @cfg {Array} fields An array of field definition objects, or field name strings.
11866 * @cfg {Array} data The multi-dimensional array of data
11868 * @param {Object} config
11870 Roo.data.SimpleStore = function(config){
11871 Roo.data.SimpleStore.superclass.constructor.call(this, {
11873 reader: new Roo.data.ArrayReader({
11876 Roo.data.Record.create(config.fields)
11878 proxy : new Roo.data.MemoryProxy(config.data)
11882 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11884 * Ext JS Library 1.1.1
11885 * Copyright(c) 2006-2007, Ext JS, LLC.
11887 * Originally Released Under LGPL - original licence link has changed is not relivant.
11890 * <script type="text/javascript">
11895 * @extends Roo.data.Store
11896 * @class Roo.data.JsonStore
11897 * Small helper class to make creating Stores for JSON data easier. <br/>
11899 var store = new Roo.data.JsonStore({
11900 url: 'get-images.php',
11902 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11905 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11906 * JsonReader and HttpProxy (unless inline data is provided).</b>
11907 * @cfg {Array} fields An array of field definition objects, or field name strings.
11909 * @param {Object} config
11911 Roo.data.JsonStore = function(c){
11912 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11913 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11914 reader: new Roo.data.JsonReader(c, c.fields)
11917 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11919 * Ext JS Library 1.1.1
11920 * Copyright(c) 2006-2007, Ext JS, LLC.
11922 * Originally Released Under LGPL - original licence link has changed is not relivant.
11925 * <script type="text/javascript">
11929 Roo.data.Field = function(config){
11930 if(typeof config == "string"){
11931 config = {name: config};
11933 Roo.apply(this, config);
11936 this.type = "auto";
11939 var st = Roo.data.SortTypes;
11940 // named sortTypes are supported, here we look them up
11941 if(typeof this.sortType == "string"){
11942 this.sortType = st[this.sortType];
11945 // set default sortType for strings and dates
11946 if(!this.sortType){
11949 this.sortType = st.asUCString;
11952 this.sortType = st.asDate;
11955 this.sortType = st.none;
11960 var stripRe = /[\$,%]/g;
11962 // prebuilt conversion function for this field, instead of
11963 // switching every time we're reading a value
11965 var cv, dateFormat = this.dateFormat;
11970 cv = function(v){ return v; };
11973 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11977 return v !== undefined && v !== null && v !== '' ?
11978 parseInt(String(v).replace(stripRe, ""), 10) : '';
11983 return v !== undefined && v !== null && v !== '' ?
11984 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11989 cv = function(v){ return v === true || v === "true" || v == 1; };
11996 if(v instanceof Date){
12000 if(dateFormat == "timestamp"){
12001 return new Date(v*1000);
12003 return Date.parseDate(v, dateFormat);
12005 var parsed = Date.parse(v);
12006 return parsed ? new Date(parsed) : null;
12015 Roo.data.Field.prototype = {
12023 * Ext JS Library 1.1.1
12024 * Copyright(c) 2006-2007, Ext JS, LLC.
12026 * Originally Released Under LGPL - original licence link has changed is not relivant.
12029 * <script type="text/javascript">
12032 // Base class for reading structured data from a data source. This class is intended to be
12033 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12036 * @class Roo.data.DataReader
12037 * Base class for reading structured data from a data source. This class is intended to be
12038 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12041 Roo.data.DataReader = function(meta, recordType){
12045 this.recordType = recordType instanceof Array ?
12046 Roo.data.Record.create(recordType) : recordType;
12049 Roo.data.DataReader.prototype = {
12051 * Create an empty record
12052 * @param {Object} data (optional) - overlay some values
12053 * @return {Roo.data.Record} record created.
12055 newRow : function(d) {
12057 this.recordType.prototype.fields.each(function(c) {
12059 case 'int' : da[c.name] = 0; break;
12060 case 'date' : da[c.name] = new Date(); break;
12061 case 'float' : da[c.name] = 0.0; break;
12062 case 'boolean' : da[c.name] = false; break;
12063 default : da[c.name] = ""; break;
12067 return new this.recordType(Roo.apply(da, d));
12072 * Ext JS Library 1.1.1
12073 * Copyright(c) 2006-2007, Ext JS, LLC.
12075 * Originally Released Under LGPL - original licence link has changed is not relivant.
12078 * <script type="text/javascript">
12082 * @class Roo.data.DataProxy
12083 * @extends Roo.data.Observable
12084 * This class is an abstract base class for implementations which provide retrieval of
12085 * unformatted data objects.<br>
12087 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12088 * (of the appropriate type which knows how to parse the data object) to provide a block of
12089 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12091 * Custom implementations must implement the load method as described in
12092 * {@link Roo.data.HttpProxy#load}.
12094 Roo.data.DataProxy = function(){
12097 * @event beforeload
12098 * Fires before a network request is made to retrieve a data object.
12099 * @param {Object} This DataProxy object.
12100 * @param {Object} params The params parameter to the load function.
12105 * Fires before the load method's callback is called.
12106 * @param {Object} This DataProxy object.
12107 * @param {Object} o The data object.
12108 * @param {Object} arg The callback argument object passed to the load function.
12112 * @event loadexception
12113 * Fires if an Exception occurs during data retrieval.
12114 * @param {Object} This DataProxy object.
12115 * @param {Object} o The data object.
12116 * @param {Object} arg The callback argument object passed to the load function.
12117 * @param {Object} e The Exception.
12119 loadexception : true
12121 Roo.data.DataProxy.superclass.constructor.call(this);
12124 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12127 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12131 * Ext JS Library 1.1.1
12132 * Copyright(c) 2006-2007, Ext JS, LLC.
12134 * Originally Released Under LGPL - original licence link has changed is not relivant.
12137 * <script type="text/javascript">
12140 * @class Roo.data.MemoryProxy
12141 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12142 * to the Reader when its load method is called.
12144 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12146 Roo.data.MemoryProxy = function(data){
12150 Roo.data.MemoryProxy.superclass.constructor.call(this);
12154 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12157 * Load data from the requested source (in this case an in-memory
12158 * data object passed to the constructor), read the data object into
12159 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12160 * process that block using the passed callback.
12161 * @param {Object} params This parameter is not used by the MemoryProxy class.
12162 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12163 * object into a block of Roo.data.Records.
12164 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12165 * The function must be passed <ul>
12166 * <li>The Record block object</li>
12167 * <li>The "arg" argument from the load function</li>
12168 * <li>A boolean success indicator</li>
12170 * @param {Object} scope The scope in which to call the callback
12171 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12173 load : function(params, reader, callback, scope, arg){
12174 params = params || {};
12177 result = reader.readRecords(this.data);
12179 this.fireEvent("loadexception", this, arg, null, e);
12180 callback.call(scope, null, arg, false);
12183 callback.call(scope, result, arg, true);
12187 update : function(params, records){
12192 * Ext JS Library 1.1.1
12193 * Copyright(c) 2006-2007, Ext JS, LLC.
12195 * Originally Released Under LGPL - original licence link has changed is not relivant.
12198 * <script type="text/javascript">
12201 * @class Roo.data.HttpProxy
12202 * @extends Roo.data.DataProxy
12203 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12204 * configured to reference a certain URL.<br><br>
12206 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12207 * from which the running page was served.<br><br>
12209 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12211 * Be aware that to enable the browser to parse an XML document, the server must set
12212 * the Content-Type header in the HTTP response to "text/xml".
12214 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12215 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12216 * will be used to make the request.
12218 Roo.data.HttpProxy = function(conn){
12219 Roo.data.HttpProxy.superclass.constructor.call(this);
12220 // is conn a conn config or a real conn?
12222 this.useAjax = !conn || !conn.events;
12226 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12227 // thse are take from connection...
12230 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12233 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12234 * extra parameters to each request made by this object. (defaults to undefined)
12237 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12238 * to each request made by this object. (defaults to undefined)
12241 * @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)
12244 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12247 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12253 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12257 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12258 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12259 * a finer-grained basis than the DataProxy events.
12261 getConnection : function(){
12262 return this.useAjax ? Roo.Ajax : this.conn;
12266 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12267 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12268 * process that block using the passed callback.
12269 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12270 * for the request to the remote server.
12271 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12272 * object into a block of Roo.data.Records.
12273 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12274 * The function must be passed <ul>
12275 * <li>The Record block object</li>
12276 * <li>The "arg" argument from the load function</li>
12277 * <li>A boolean success indicator</li>
12279 * @param {Object} scope The scope in which to call the callback
12280 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12282 load : function(params, reader, callback, scope, arg){
12283 if(this.fireEvent("beforeload", this, params) !== false){
12285 params : params || {},
12287 callback : callback,
12292 callback : this.loadResponse,
12296 Roo.applyIf(o, this.conn);
12297 if(this.activeRequest){
12298 Roo.Ajax.abort(this.activeRequest);
12300 this.activeRequest = Roo.Ajax.request(o);
12302 this.conn.request(o);
12305 callback.call(scope||this, null, arg, false);
12310 loadResponse : function(o, success, response){
12311 delete this.activeRequest;
12313 this.fireEvent("loadexception", this, o, response);
12314 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12319 result = o.reader.read(response);
12321 this.fireEvent("loadexception", this, o, response, e);
12322 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12326 this.fireEvent("load", this, o, o.request.arg);
12327 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12331 update : function(dataSet){
12336 updateResponse : function(dataSet){
12341 * Ext JS Library 1.1.1
12342 * Copyright(c) 2006-2007, Ext JS, LLC.
12344 * Originally Released Under LGPL - original licence link has changed is not relivant.
12347 * <script type="text/javascript">
12351 * @class Roo.data.ScriptTagProxy
12352 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12353 * other than the originating domain of the running page.<br><br>
12355 * <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
12356 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12358 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12359 * source code that is used as the source inside a <script> tag.<br><br>
12361 * In order for the browser to process the returned data, the server must wrap the data object
12362 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12363 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12364 * depending on whether the callback name was passed:
12367 boolean scriptTag = false;
12368 String cb = request.getParameter("callback");
12371 response.setContentType("text/javascript");
12373 response.setContentType("application/x-json");
12375 Writer out = response.getWriter();
12377 out.write(cb + "(");
12379 out.print(dataBlock.toJsonString());
12386 * @param {Object} config A configuration object.
12388 Roo.data.ScriptTagProxy = function(config){
12389 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12390 Roo.apply(this, config);
12391 this.head = document.getElementsByTagName("head")[0];
12394 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12396 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12398 * @cfg {String} url The URL from which to request the data object.
12401 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12405 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12406 * the server the name of the callback function set up by the load call to process the returned data object.
12407 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12408 * javascript output which calls this named function passing the data object as its only parameter.
12410 callbackParam : "callback",
12412 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12413 * name to the request.
12418 * Load data from the configured URL, read the data object into
12419 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12420 * process that block using the passed callback.
12421 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12422 * for the request to the remote server.
12423 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12424 * object into a block of Roo.data.Records.
12425 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12426 * The function must be passed <ul>
12427 * <li>The Record block object</li>
12428 * <li>The "arg" argument from the load function</li>
12429 * <li>A boolean success indicator</li>
12431 * @param {Object} scope The scope in which to call the callback
12432 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12434 load : function(params, reader, callback, scope, arg){
12435 if(this.fireEvent("beforeload", this, params) !== false){
12437 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12439 var url = this.url;
12440 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12442 url += "&_dc=" + (new Date().getTime());
12444 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12447 cb : "stcCallback"+transId,
12448 scriptId : "stcScript"+transId,
12452 callback : callback,
12458 window[trans.cb] = function(o){
12459 conn.handleResponse(o, trans);
12462 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12464 if(this.autoAbort !== false){
12468 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12470 var script = document.createElement("script");
12471 script.setAttribute("src", url);
12472 script.setAttribute("type", "text/javascript");
12473 script.setAttribute("id", trans.scriptId);
12474 this.head.appendChild(script);
12476 this.trans = trans;
12478 callback.call(scope||this, null, arg, false);
12483 isLoading : function(){
12484 return this.trans ? true : false;
12488 * Abort the current server request.
12490 abort : function(){
12491 if(this.isLoading()){
12492 this.destroyTrans(this.trans);
12497 destroyTrans : function(trans, isLoaded){
12498 this.head.removeChild(document.getElementById(trans.scriptId));
12499 clearTimeout(trans.timeoutId);
12501 window[trans.cb] = undefined;
12503 delete window[trans.cb];
12506 // if hasn't been loaded, wait for load to remove it to prevent script error
12507 window[trans.cb] = function(){
12508 window[trans.cb] = undefined;
12510 delete window[trans.cb];
12517 handleResponse : function(o, trans){
12518 this.trans = false;
12519 this.destroyTrans(trans, true);
12522 result = trans.reader.readRecords(o);
12524 this.fireEvent("loadexception", this, o, trans.arg, e);
12525 trans.callback.call(trans.scope||window, null, trans.arg, false);
12528 this.fireEvent("load", this, o, trans.arg);
12529 trans.callback.call(trans.scope||window, result, trans.arg, true);
12533 handleFailure : function(trans){
12534 this.trans = false;
12535 this.destroyTrans(trans, false);
12536 this.fireEvent("loadexception", this, null, trans.arg);
12537 trans.callback.call(trans.scope||window, null, trans.arg, false);
12541 * Ext JS Library 1.1.1
12542 * Copyright(c) 2006-2007, Ext JS, LLC.
12544 * Originally Released Under LGPL - original licence link has changed is not relivant.
12547 * <script type="text/javascript">
12551 * @class Roo.data.JsonReader
12552 * @extends Roo.data.DataReader
12553 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12554 * based on mappings in a provided Roo.data.Record constructor.
12556 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12557 * in the reply previously.
12562 var RecordDef = Roo.data.Record.create([
12563 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12564 {name: 'occupation'} // This field will use "occupation" as the mapping.
12566 var myReader = new Roo.data.JsonReader({
12567 totalProperty: "results", // The property which contains the total dataset size (optional)
12568 root: "rows", // The property which contains an Array of row objects
12569 id: "id" // The property within each row object that provides an ID for the record (optional)
12573 * This would consume a JSON file like this:
12575 { 'results': 2, 'rows': [
12576 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12577 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12580 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12581 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12582 * paged from the remote server.
12583 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12584 * @cfg {String} root name of the property which contains the Array of row objects.
12585 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12586 * @cfg {Array} fields Array of field definition objects
12588 * Create a new JsonReader
12589 * @param {Object} meta Metadata configuration options
12590 * @param {Object} recordType Either an Array of field definition objects,
12591 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12593 Roo.data.JsonReader = function(meta, recordType){
12596 // set some defaults:
12597 Roo.applyIf(meta, {
12598 totalProperty: 'total',
12599 successProperty : 'success',
12604 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12606 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12609 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12610 * Used by Store query builder to append _requestMeta to params.
12613 metaFromRemote : false,
12615 * This method is only used by a DataProxy which has retrieved data from a remote server.
12616 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12617 * @return {Object} data A data block which is used by an Roo.data.Store object as
12618 * a cache of Roo.data.Records.
12620 read : function(response){
12621 var json = response.responseText;
12623 var o = /* eval:var:o */ eval("("+json+")");
12625 throw {message: "JsonReader.read: Json object not found"};
12631 this.metaFromRemote = true;
12632 this.meta = o.metaData;
12633 this.recordType = Roo.data.Record.create(o.metaData.fields);
12634 this.onMetaChange(this.meta, this.recordType, o);
12636 return this.readRecords(o);
12639 // private function a store will implement
12640 onMetaChange : function(meta, recordType, o){
12647 simpleAccess: function(obj, subsc) {
12654 getJsonAccessor: function(){
12656 return function(expr) {
12658 return(re.test(expr))
12659 ? new Function("obj", "return obj." + expr)
12664 return Roo.emptyFn;
12669 * Create a data block containing Roo.data.Records from an XML document.
12670 * @param {Object} o An object which contains an Array of row objects in the property specified
12671 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12672 * which contains the total size of the dataset.
12673 * @return {Object} data A data block which is used by an Roo.data.Store object as
12674 * a cache of Roo.data.Records.
12676 readRecords : function(o){
12678 * After any data loads, the raw JSON data is available for further custom processing.
12682 var s = this.meta, Record = this.recordType,
12683 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12685 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12687 if(s.totalProperty) {
12688 this.getTotal = this.getJsonAccessor(s.totalProperty);
12690 if(s.successProperty) {
12691 this.getSuccess = this.getJsonAccessor(s.successProperty);
12693 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12695 var g = this.getJsonAccessor(s.id);
12696 this.getId = function(rec) {
12698 return (r === undefined || r === "") ? null : r;
12701 this.getId = function(){return null;};
12704 for(var jj = 0; jj < fl; jj++){
12706 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12707 this.ef[jj] = this.getJsonAccessor(map);
12711 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12712 if(s.totalProperty){
12713 var vt = parseInt(this.getTotal(o), 10);
12718 if(s.successProperty){
12719 var vs = this.getSuccess(o);
12720 if(vs === false || vs === 'false'){
12725 for(var i = 0; i < c; i++){
12728 var id = this.getId(n);
12729 for(var j = 0; j < fl; j++){
12731 var v = this.ef[j](n);
12733 Roo.log('missing convert for ' + f.name);
12737 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12739 var record = new Record(values, id);
12741 records[i] = record;
12747 totalRecords : totalRecords
12752 * Ext JS Library 1.1.1
12753 * Copyright(c) 2006-2007, Ext JS, LLC.
12755 * Originally Released Under LGPL - original licence link has changed is not relivant.
12758 * <script type="text/javascript">
12762 * @class Roo.data.ArrayReader
12763 * @extends Roo.data.DataReader
12764 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12765 * Each element of that Array represents a row of data fields. The
12766 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12767 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12771 var RecordDef = Roo.data.Record.create([
12772 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12773 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12775 var myReader = new Roo.data.ArrayReader({
12776 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12780 * This would consume an Array like this:
12782 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12784 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12786 * Create a new JsonReader
12787 * @param {Object} meta Metadata configuration options.
12788 * @param {Object} recordType Either an Array of field definition objects
12789 * as specified to {@link Roo.data.Record#create},
12790 * or an {@link Roo.data.Record} object
12791 * created using {@link Roo.data.Record#create}.
12793 Roo.data.ArrayReader = function(meta, recordType){
12794 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12797 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12799 * Create a data block containing Roo.data.Records from an XML document.
12800 * @param {Object} o An Array of row objects which represents the dataset.
12801 * @return {Object} data A data block which is used by an Roo.data.Store object as
12802 * a cache of Roo.data.Records.
12804 readRecords : function(o){
12805 var sid = this.meta ? this.meta.id : null;
12806 var recordType = this.recordType, fields = recordType.prototype.fields;
12809 for(var i = 0; i < root.length; i++){
12812 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12813 for(var j = 0, jlen = fields.length; j < jlen; j++){
12814 var f = fields.items[j];
12815 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12816 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12818 values[f.name] = v;
12820 var record = new recordType(values, id);
12822 records[records.length] = record;
12826 totalRecords : records.length
12835 * @class Roo.bootstrap.ComboBox
12836 * @extends Roo.bootstrap.TriggerField
12837 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12838 * @cfg {Boolean} append (true|false) default false
12839 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12840 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12841 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12842 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12843 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12844 * @cfg {Boolean} animate default true
12845 * @cfg {Boolean} emptyResultText only for touch device
12846 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12847 * @cfg {String} emptyTitle default ''
12849 * Create a new ComboBox.
12850 * @param {Object} config Configuration options
12852 Roo.bootstrap.ComboBox = function(config){
12853 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12857 * Fires when the dropdown list is expanded
12858 * @param {Roo.bootstrap.ComboBox} combo This combo box
12863 * Fires when the dropdown list is collapsed
12864 * @param {Roo.bootstrap.ComboBox} combo This combo box
12868 * @event beforeselect
12869 * Fires before a list item is selected. Return false to cancel the selection.
12870 * @param {Roo.bootstrap.ComboBox} combo This combo box
12871 * @param {Roo.data.Record} record The data record returned from the underlying store
12872 * @param {Number} index The index of the selected item in the dropdown list
12874 'beforeselect' : true,
12877 * Fires when a list item is selected
12878 * @param {Roo.bootstrap.ComboBox} combo This combo box
12879 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12880 * @param {Number} index The index of the selected item in the dropdown list
12884 * @event beforequery
12885 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12886 * The event object passed has these properties:
12887 * @param {Roo.bootstrap.ComboBox} combo This combo box
12888 * @param {String} query The query
12889 * @param {Boolean} forceAll true to force "all" query
12890 * @param {Boolean} cancel true to cancel the query
12891 * @param {Object} e The query event object
12893 'beforequery': true,
12896 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12897 * @param {Roo.bootstrap.ComboBox} combo This combo box
12902 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12903 * @param {Roo.bootstrap.ComboBox} combo This combo box
12904 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12909 * Fires when the remove value from the combobox array
12910 * @param {Roo.bootstrap.ComboBox} combo This combo box
12914 * @event afterremove
12915 * Fires when the remove value from the combobox array
12916 * @param {Roo.bootstrap.ComboBox} combo This combo box
12918 'afterremove' : true,
12920 * @event specialfilter
12921 * Fires when specialfilter
12922 * @param {Roo.bootstrap.ComboBox} combo This combo box
12924 'specialfilter' : true,
12927 * Fires when tick the element
12928 * @param {Roo.bootstrap.ComboBox} combo This combo box
12932 * @event touchviewdisplay
12933 * Fires when touch view require special display (default is using displayField)
12934 * @param {Roo.bootstrap.ComboBox} combo This combo box
12935 * @param {Object} cfg set html .
12937 'touchviewdisplay' : true
12942 this.tickItems = [];
12944 this.selectedIndex = -1;
12945 if(this.mode == 'local'){
12946 if(config.queryDelay === undefined){
12947 this.queryDelay = 10;
12949 if(config.minChars === undefined){
12955 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12958 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12959 * rendering into an Roo.Editor, defaults to false)
12962 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12963 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12966 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12969 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12970 * the dropdown list (defaults to undefined, with no header element)
12974 * @cfg {String/Roo.Template} tpl The template to use to render the output
12978 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12980 listWidth: undefined,
12982 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12983 * mode = 'remote' or 'text' if mode = 'local')
12985 displayField: undefined,
12988 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12989 * mode = 'remote' or 'value' if mode = 'local').
12990 * Note: use of a valueField requires the user make a selection
12991 * in order for a value to be mapped.
12993 valueField: undefined,
12995 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13000 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13001 * field's data value (defaults to the underlying DOM element's name)
13003 hiddenName: undefined,
13005 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13009 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13011 selectedClass: 'active',
13014 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13018 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13019 * anchor positions (defaults to 'tl-bl')
13021 listAlign: 'tl-bl?',
13023 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13027 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13028 * query specified by the allQuery config option (defaults to 'query')
13030 triggerAction: 'query',
13032 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13033 * (defaults to 4, does not apply if editable = false)
13037 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13038 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13042 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13043 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13047 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13048 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13052 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13053 * when editable = true (defaults to false)
13055 selectOnFocus:false,
13057 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13059 queryParam: 'query',
13061 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13062 * when mode = 'remote' (defaults to 'Loading...')
13064 loadingText: 'Loading...',
13066 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13070 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13074 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13075 * traditional select (defaults to true)
13079 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13083 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13087 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13088 * listWidth has a higher value)
13092 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13093 * allow the user to set arbitrary text into the field (defaults to false)
13095 forceSelection:false,
13097 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13098 * if typeAhead = true (defaults to 250)
13100 typeAheadDelay : 250,
13102 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13103 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13105 valueNotFoundText : undefined,
13107 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13109 blockFocus : false,
13112 * @cfg {Boolean} disableClear Disable showing of clear button.
13114 disableClear : false,
13116 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13118 alwaysQuery : false,
13121 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13126 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13128 invalidClass : "has-warning",
13131 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13133 validClass : "has-success",
13136 * @cfg {Boolean} specialFilter (true|false) special filter default false
13138 specialFilter : false,
13141 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13143 mobileTouchView : true,
13146 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13148 useNativeIOS : false,
13151 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13153 mobile_restrict_height : false,
13155 ios_options : false,
13167 btnPosition : 'right',
13168 triggerList : true,
13169 showToggleBtn : true,
13171 emptyResultText: 'Empty',
13172 triggerText : 'Select',
13175 // element that contains real text value.. (when hidden is used..)
13177 getAutoCreate : function()
13182 * Render classic select for iso
13185 if(Roo.isIOS && this.useNativeIOS){
13186 cfg = this.getAutoCreateNativeIOS();
13194 if(Roo.isTouch && this.mobileTouchView){
13195 cfg = this.getAutoCreateTouchView();
13202 if(!this.tickable){
13203 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13208 * ComboBox with tickable selections
13211 var align = this.labelAlign || this.parentLabelAlign();
13214 cls : 'form-group roo-combobox-tickable' //input-group
13217 var btn_text_select = '';
13218 var btn_text_done = '';
13219 var btn_text_cancel = '';
13221 if (this.btn_text_show) {
13222 btn_text_select = 'Select';
13223 btn_text_done = 'Done';
13224 btn_text_cancel = 'Cancel';
13229 cls : 'tickable-buttons',
13234 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13235 //html : this.triggerText
13236 html: btn_text_select
13242 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13244 html: btn_text_done
13250 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13252 html: btn_text_cancel
13258 buttons.cn.unshift({
13260 cls: 'roo-select2-search-field-input'
13266 Roo.each(buttons.cn, function(c){
13268 c.cls += ' btn-' + _this.size;
13271 if (_this.disabled) {
13282 cls: 'form-hidden-field'
13286 cls: 'roo-select2-choices',
13290 cls: 'roo-select2-search-field',
13301 cls: 'roo-select2-container input-group roo-select2-container-multi',
13306 // cls: 'typeahead typeahead-long dropdown-menu',
13307 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13312 if(this.hasFeedback && !this.allowBlank){
13316 cls: 'glyphicon form-control-feedback'
13319 combobox.cn.push(feedback);
13323 if (align ==='left' && this.fieldLabel.length) {
13325 cfg.cls += ' roo-form-group-label-left';
13330 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13331 tooltip : 'This field is required'
13336 cls : 'control-label',
13337 html : this.fieldLabel
13349 var labelCfg = cfg.cn[1];
13350 var contentCfg = cfg.cn[2];
13353 if(this.indicatorpos == 'right'){
13359 cls : 'control-label',
13363 html : this.fieldLabel
13367 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13368 tooltip : 'This field is required'
13383 labelCfg = cfg.cn[0];
13384 contentCfg = cfg.cn[1];
13388 if(this.labelWidth > 12){
13389 labelCfg.style = "width: " + this.labelWidth + 'px';
13392 if(this.labelWidth < 13 && this.labelmd == 0){
13393 this.labelmd = this.labelWidth;
13396 if(this.labellg > 0){
13397 labelCfg.cls += ' col-lg-' + this.labellg;
13398 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13401 if(this.labelmd > 0){
13402 labelCfg.cls += ' col-md-' + this.labelmd;
13403 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13406 if(this.labelsm > 0){
13407 labelCfg.cls += ' col-sm-' + this.labelsm;
13408 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13411 if(this.labelxs > 0){
13412 labelCfg.cls += ' col-xs-' + this.labelxs;
13413 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13417 } else if ( this.fieldLabel.length) {
13418 // Roo.log(" label");
13422 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13423 tooltip : 'This field is required'
13427 //cls : 'input-group-addon',
13428 html : this.fieldLabel
13433 if(this.indicatorpos == 'right'){
13437 //cls : 'input-group-addon',
13438 html : this.fieldLabel
13442 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13443 tooltip : 'This field is required'
13452 // Roo.log(" no label && no align");
13459 ['xs','sm','md','lg'].map(function(size){
13460 if (settings[size]) {
13461 cfg.cls += ' col-' + size + '-' + settings[size];
13469 _initEventsCalled : false,
13472 initEvents: function()
13474 if (this._initEventsCalled) { // as we call render... prevent looping...
13477 this._initEventsCalled = true;
13480 throw "can not find store for combo";
13483 this.indicator = this.indicatorEl();
13485 this.store = Roo.factory(this.store, Roo.data);
13486 this.store.parent = this;
13488 // if we are building from html. then this element is so complex, that we can not really
13489 // use the rendered HTML.
13490 // so we have to trash and replace the previous code.
13491 if (Roo.XComponent.build_from_html) {
13492 // remove this element....
13493 var e = this.el.dom, k=0;
13494 while (e ) { e = e.previousSibling; ++k;}
13499 this.rendered = false;
13501 this.render(this.parent().getChildContainer(true), k);
13504 if(Roo.isIOS && this.useNativeIOS){
13505 this.initIOSView();
13513 if(Roo.isTouch && this.mobileTouchView){
13514 this.initTouchView();
13519 this.initTickableEvents();
13523 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13525 if(this.hiddenName){
13527 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13529 this.hiddenField.dom.value =
13530 this.hiddenValue !== undefined ? this.hiddenValue :
13531 this.value !== undefined ? this.value : '';
13533 // prevent input submission
13534 this.el.dom.removeAttribute('name');
13535 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13540 // this.el.dom.setAttribute('autocomplete', 'off');
13543 var cls = 'x-combo-list';
13545 //this.list = new Roo.Layer({
13546 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13552 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13553 _this.list.setWidth(lw);
13556 this.list.on('mouseover', this.onViewOver, this);
13557 this.list.on('mousemove', this.onViewMove, this);
13558 this.list.on('scroll', this.onViewScroll, this);
13561 this.list.swallowEvent('mousewheel');
13562 this.assetHeight = 0;
13565 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13566 this.assetHeight += this.header.getHeight();
13569 this.innerList = this.list.createChild({cls:cls+'-inner'});
13570 this.innerList.on('mouseover', this.onViewOver, this);
13571 this.innerList.on('mousemove', this.onViewMove, this);
13572 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13574 if(this.allowBlank && !this.pageSize && !this.disableClear){
13575 this.footer = this.list.createChild({cls:cls+'-ft'});
13576 this.pageTb = new Roo.Toolbar(this.footer);
13580 this.footer = this.list.createChild({cls:cls+'-ft'});
13581 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13582 {pageSize: this.pageSize});
13586 if (this.pageTb && this.allowBlank && !this.disableClear) {
13588 this.pageTb.add(new Roo.Toolbar.Fill(), {
13589 cls: 'x-btn-icon x-btn-clear',
13591 handler: function()
13594 _this.clearValue();
13595 _this.onSelect(false, -1);
13600 this.assetHeight += this.footer.getHeight();
13605 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13608 this.view = new Roo.View(this.list, this.tpl, {
13609 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13611 //this.view.wrapEl.setDisplayed(false);
13612 this.view.on('click', this.onViewClick, this);
13615 this.store.on('beforeload', this.onBeforeLoad, this);
13616 this.store.on('load', this.onLoad, this);
13617 this.store.on('loadexception', this.onLoadException, this);
13619 if(this.resizable){
13620 this.resizer = new Roo.Resizable(this.list, {
13621 pinned:true, handles:'se'
13623 this.resizer.on('resize', function(r, w, h){
13624 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13625 this.listWidth = w;
13626 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13627 this.restrictHeight();
13629 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13632 if(!this.editable){
13633 this.editable = true;
13634 this.setEditable(false);
13639 if (typeof(this.events.add.listeners) != 'undefined') {
13641 this.addicon = this.wrap.createChild(
13642 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13644 this.addicon.on('click', function(e) {
13645 this.fireEvent('add', this);
13648 if (typeof(this.events.edit.listeners) != 'undefined') {
13650 this.editicon = this.wrap.createChild(
13651 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13652 if (this.addicon) {
13653 this.editicon.setStyle('margin-left', '40px');
13655 this.editicon.on('click', function(e) {
13657 // we fire even if inothing is selected..
13658 this.fireEvent('edit', this, this.lastData );
13664 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13665 "up" : function(e){
13666 this.inKeyMode = true;
13670 "down" : function(e){
13671 if(!this.isExpanded()){
13672 this.onTriggerClick();
13674 this.inKeyMode = true;
13679 "enter" : function(e){
13680 // this.onViewClick();
13684 if(this.fireEvent("specialkey", this, e)){
13685 this.onViewClick(false);
13691 "esc" : function(e){
13695 "tab" : function(e){
13698 if(this.fireEvent("specialkey", this, e)){
13699 this.onViewClick(false);
13707 doRelay : function(foo, bar, hname){
13708 if(hname == 'down' || this.scope.isExpanded()){
13709 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13718 this.queryDelay = Math.max(this.queryDelay || 10,
13719 this.mode == 'local' ? 10 : 250);
13722 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13724 if(this.typeAhead){
13725 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13727 if(this.editable !== false){
13728 this.inputEl().on("keyup", this.onKeyUp, this);
13730 if(this.forceSelection){
13731 this.inputEl().on('blur', this.doForce, this);
13735 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13736 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13740 initTickableEvents: function()
13744 if(this.hiddenName){
13746 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13748 this.hiddenField.dom.value =
13749 this.hiddenValue !== undefined ? this.hiddenValue :
13750 this.value !== undefined ? this.value : '';
13752 // prevent input submission
13753 this.el.dom.removeAttribute('name');
13754 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13759 // this.list = this.el.select('ul.dropdown-menu',true).first();
13761 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13762 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13763 if(this.triggerList){
13764 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13767 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13768 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13770 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13771 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13773 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13774 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13776 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13777 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13778 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13781 this.cancelBtn.hide();
13786 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13787 _this.list.setWidth(lw);
13790 this.list.on('mouseover', this.onViewOver, this);
13791 this.list.on('mousemove', this.onViewMove, this);
13793 this.list.on('scroll', this.onViewScroll, this);
13796 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13797 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13800 this.view = new Roo.View(this.list, this.tpl, {
13805 selectedClass: this.selectedClass
13808 //this.view.wrapEl.setDisplayed(false);
13809 this.view.on('click', this.onViewClick, this);
13813 this.store.on('beforeload', this.onBeforeLoad, this);
13814 this.store.on('load', this.onLoad, this);
13815 this.store.on('loadexception', this.onLoadException, this);
13818 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13819 "up" : function(e){
13820 this.inKeyMode = true;
13824 "down" : function(e){
13825 this.inKeyMode = true;
13829 "enter" : function(e){
13830 if(this.fireEvent("specialkey", this, e)){
13831 this.onViewClick(false);
13837 "esc" : function(e){
13838 this.onTickableFooterButtonClick(e, false, false);
13841 "tab" : function(e){
13842 this.fireEvent("specialkey", this, e);
13844 this.onTickableFooterButtonClick(e, false, false);
13851 doRelay : function(e, fn, key){
13852 if(this.scope.isExpanded()){
13853 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13862 this.queryDelay = Math.max(this.queryDelay || 10,
13863 this.mode == 'local' ? 10 : 250);
13866 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13868 if(this.typeAhead){
13869 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13872 if(this.editable !== false){
13873 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13876 this.indicator = this.indicatorEl();
13878 if(this.indicator){
13879 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13880 this.indicator.hide();
13885 onDestroy : function(){
13887 this.view.setStore(null);
13888 this.view.el.removeAllListeners();
13889 this.view.el.remove();
13890 this.view.purgeListeners();
13893 this.list.dom.innerHTML = '';
13897 this.store.un('beforeload', this.onBeforeLoad, this);
13898 this.store.un('load', this.onLoad, this);
13899 this.store.un('loadexception', this.onLoadException, this);
13901 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13905 fireKey : function(e){
13906 if(e.isNavKeyPress() && !this.list.isVisible()){
13907 this.fireEvent("specialkey", this, e);
13912 onResize: function(w, h){
13913 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13915 // if(typeof w != 'number'){
13916 // // we do not handle it!?!?
13919 // var tw = this.trigger.getWidth();
13920 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13921 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13923 // this.inputEl().setWidth( this.adjustWidth('input', x));
13925 // //this.trigger.setStyle('left', x+'px');
13927 // if(this.list && this.listWidth === undefined){
13928 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13929 // this.list.setWidth(lw);
13930 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13938 * Allow or prevent the user from directly editing the field text. If false is passed,
13939 * the user will only be able to select from the items defined in the dropdown list. This method
13940 * is the runtime equivalent of setting the 'editable' config option at config time.
13941 * @param {Boolean} value True to allow the user to directly edit the field text
13943 setEditable : function(value){
13944 if(value == this.editable){
13947 this.editable = value;
13949 this.inputEl().dom.setAttribute('readOnly', true);
13950 this.inputEl().on('mousedown', this.onTriggerClick, this);
13951 this.inputEl().addClass('x-combo-noedit');
13953 this.inputEl().dom.setAttribute('readOnly', false);
13954 this.inputEl().un('mousedown', this.onTriggerClick, this);
13955 this.inputEl().removeClass('x-combo-noedit');
13961 onBeforeLoad : function(combo,opts){
13962 if(!this.hasFocus){
13966 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13968 this.restrictHeight();
13969 this.selectedIndex = -1;
13973 onLoad : function(){
13975 this.hasQuery = false;
13977 if(!this.hasFocus){
13981 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13982 this.loading.hide();
13985 if(this.store.getCount() > 0){
13988 this.restrictHeight();
13989 if(this.lastQuery == this.allQuery){
13990 if(this.editable && !this.tickable){
13991 this.inputEl().dom.select();
13995 !this.selectByValue(this.value, true) &&
13998 !this.store.lastOptions ||
13999 typeof(this.store.lastOptions.add) == 'undefined' ||
14000 this.store.lastOptions.add != true
14003 this.select(0, true);
14006 if(this.autoFocus){
14009 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14010 this.taTask.delay(this.typeAheadDelay);
14014 this.onEmptyResults();
14020 onLoadException : function()
14022 this.hasQuery = false;
14024 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14025 this.loading.hide();
14028 if(this.tickable && this.editable){
14033 // only causes errors at present
14034 //Roo.log(this.store.reader.jsonData);
14035 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14037 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14043 onTypeAhead : function(){
14044 if(this.store.getCount() > 0){
14045 var r = this.store.getAt(0);
14046 var newValue = r.data[this.displayField];
14047 var len = newValue.length;
14048 var selStart = this.getRawValue().length;
14050 if(selStart != len){
14051 this.setRawValue(newValue);
14052 this.selectText(selStart, newValue.length);
14058 onSelect : function(record, index){
14060 if(this.fireEvent('beforeselect', this, record, index) !== false){
14062 this.setFromData(index > -1 ? record.data : false);
14065 this.fireEvent('select', this, record, index);
14070 * Returns the currently selected field value or empty string if no value is set.
14071 * @return {String} value The selected value
14073 getValue : function()
14075 if(Roo.isIOS && this.useNativeIOS){
14076 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14080 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14083 if(this.valueField){
14084 return typeof this.value != 'undefined' ? this.value : '';
14086 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14090 getRawValue : function()
14092 if(Roo.isIOS && this.useNativeIOS){
14093 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14096 var v = this.inputEl().getValue();
14102 * Clears any text/value currently set in the field
14104 clearValue : function(){
14106 if(this.hiddenField){
14107 this.hiddenField.dom.value = '';
14110 this.setRawValue('');
14111 this.lastSelectionText = '';
14112 this.lastData = false;
14114 var close = this.closeTriggerEl();
14125 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14126 * will be displayed in the field. If the value does not match the data value of an existing item,
14127 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14128 * Otherwise the field will be blank (although the value will still be set).
14129 * @param {String} value The value to match
14131 setValue : function(v)
14133 if(Roo.isIOS && this.useNativeIOS){
14134 this.setIOSValue(v);
14144 if(this.valueField){
14145 var r = this.findRecord(this.valueField, v);
14147 text = r.data[this.displayField];
14148 }else if(this.valueNotFoundText !== undefined){
14149 text = this.valueNotFoundText;
14152 this.lastSelectionText = text;
14153 if(this.hiddenField){
14154 this.hiddenField.dom.value = v;
14156 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14159 var close = this.closeTriggerEl();
14162 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14168 * @property {Object} the last set data for the element
14173 * Sets the value of the field based on a object which is related to the record format for the store.
14174 * @param {Object} value the value to set as. or false on reset?
14176 setFromData : function(o){
14183 var dv = ''; // display value
14184 var vv = ''; // value value..
14186 if (this.displayField) {
14187 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14189 // this is an error condition!!!
14190 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14193 if(this.valueField){
14194 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14197 var close = this.closeTriggerEl();
14200 if(dv.length || vv * 1 > 0){
14202 this.blockFocus=true;
14208 if(this.hiddenField){
14209 this.hiddenField.dom.value = vv;
14211 this.lastSelectionText = dv;
14212 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14216 // no hidden field.. - we store the value in 'value', but still display
14217 // display field!!!!
14218 this.lastSelectionText = dv;
14219 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14226 reset : function(){
14227 // overridden so that last data is reset..
14234 this.setValue(this.originalValue);
14235 //this.clearInvalid();
14236 this.lastData = false;
14238 this.view.clearSelections();
14244 findRecord : function(prop, value){
14246 if(this.store.getCount() > 0){
14247 this.store.each(function(r){
14248 if(r.data[prop] == value){
14258 getName: function()
14260 // returns hidden if it's set..
14261 if (!this.rendered) {return ''};
14262 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14266 onViewMove : function(e, t){
14267 this.inKeyMode = false;
14271 onViewOver : function(e, t){
14272 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14275 var item = this.view.findItemFromChild(t);
14278 var index = this.view.indexOf(item);
14279 this.select(index, false);
14284 onViewClick : function(view, doFocus, el, e)
14286 var index = this.view.getSelectedIndexes()[0];
14288 var r = this.store.getAt(index);
14292 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14299 Roo.each(this.tickItems, function(v,k){
14301 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14303 _this.tickItems.splice(k, 1);
14305 if(typeof(e) == 'undefined' && view == false){
14306 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14318 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14319 this.tickItems.push(r.data);
14322 if(typeof(e) == 'undefined' && view == false){
14323 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14330 this.onSelect(r, index);
14332 if(doFocus !== false && !this.blockFocus){
14333 this.inputEl().focus();
14338 restrictHeight : function(){
14339 //this.innerList.dom.style.height = '';
14340 //var inner = this.innerList.dom;
14341 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14342 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14343 //this.list.beginUpdate();
14344 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14345 this.list.alignTo(this.inputEl(), this.listAlign);
14346 this.list.alignTo(this.inputEl(), this.listAlign);
14347 //this.list.endUpdate();
14351 onEmptyResults : function(){
14353 if(this.tickable && this.editable){
14354 this.hasFocus = false;
14355 this.restrictHeight();
14363 * Returns true if the dropdown list is expanded, else false.
14365 isExpanded : function(){
14366 return this.list.isVisible();
14370 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14371 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14372 * @param {String} value The data value of the item to select
14373 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14374 * selected item if it is not currently in view (defaults to true)
14375 * @return {Boolean} True if the value matched an item in the list, else false
14377 selectByValue : function(v, scrollIntoView){
14378 if(v !== undefined && v !== null){
14379 var r = this.findRecord(this.valueField || this.displayField, v);
14381 this.select(this.store.indexOf(r), scrollIntoView);
14389 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14390 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14391 * @param {Number} index The zero-based index of the list item to select
14392 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14393 * selected item if it is not currently in view (defaults to true)
14395 select : function(index, scrollIntoView){
14396 this.selectedIndex = index;
14397 this.view.select(index);
14398 if(scrollIntoView !== false){
14399 var el = this.view.getNode(index);
14401 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14404 this.list.scrollChildIntoView(el, false);
14410 selectNext : function(){
14411 var ct = this.store.getCount();
14413 if(this.selectedIndex == -1){
14415 }else if(this.selectedIndex < ct-1){
14416 this.select(this.selectedIndex+1);
14422 selectPrev : function(){
14423 var ct = this.store.getCount();
14425 if(this.selectedIndex == -1){
14427 }else if(this.selectedIndex != 0){
14428 this.select(this.selectedIndex-1);
14434 onKeyUp : function(e){
14435 if(this.editable !== false && !e.isSpecialKey()){
14436 this.lastKey = e.getKey();
14437 this.dqTask.delay(this.queryDelay);
14442 validateBlur : function(){
14443 return !this.list || !this.list.isVisible();
14447 initQuery : function(){
14449 var v = this.getRawValue();
14451 if(this.tickable && this.editable){
14452 v = this.tickableInputEl().getValue();
14459 doForce : function(){
14460 if(this.inputEl().dom.value.length > 0){
14461 this.inputEl().dom.value =
14462 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14468 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14469 * query allowing the query action to be canceled if needed.
14470 * @param {String} query The SQL query to execute
14471 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14472 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14473 * saved in the current store (defaults to false)
14475 doQuery : function(q, forceAll){
14477 if(q === undefined || q === null){
14482 forceAll: forceAll,
14486 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14491 forceAll = qe.forceAll;
14492 if(forceAll === true || (q.length >= this.minChars)){
14494 this.hasQuery = true;
14496 if(this.lastQuery != q || this.alwaysQuery){
14497 this.lastQuery = q;
14498 if(this.mode == 'local'){
14499 this.selectedIndex = -1;
14501 this.store.clearFilter();
14504 if(this.specialFilter){
14505 this.fireEvent('specialfilter', this);
14510 this.store.filter(this.displayField, q);
14513 this.store.fireEvent("datachanged", this.store);
14520 this.store.baseParams[this.queryParam] = q;
14522 var options = {params : this.getParams(q)};
14525 options.add = true;
14526 options.params.start = this.page * this.pageSize;
14529 this.store.load(options);
14532 * this code will make the page width larger, at the beginning, the list not align correctly,
14533 * we should expand the list on onLoad
14534 * so command out it
14539 this.selectedIndex = -1;
14544 this.loadNext = false;
14548 getParams : function(q){
14550 //p[this.queryParam] = q;
14554 p.limit = this.pageSize;
14560 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14562 collapse : function(){
14563 if(!this.isExpanded()){
14569 this.hasFocus = false;
14573 this.cancelBtn.hide();
14574 this.trigger.show();
14577 this.tickableInputEl().dom.value = '';
14578 this.tickableInputEl().blur();
14583 Roo.get(document).un('mousedown', this.collapseIf, this);
14584 Roo.get(document).un('mousewheel', this.collapseIf, this);
14585 if (!this.editable) {
14586 Roo.get(document).un('keydown', this.listKeyPress, this);
14588 this.fireEvent('collapse', this);
14594 collapseIf : function(e){
14595 var in_combo = e.within(this.el);
14596 var in_list = e.within(this.list);
14597 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14599 if (in_combo || in_list || is_list) {
14600 //e.stopPropagation();
14605 this.onTickableFooterButtonClick(e, false, false);
14613 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14615 expand : function(){
14617 if(this.isExpanded() || !this.hasFocus){
14621 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14622 this.list.setWidth(lw);
14628 this.restrictHeight();
14632 this.tickItems = Roo.apply([], this.item);
14635 this.cancelBtn.show();
14636 this.trigger.hide();
14639 this.tickableInputEl().focus();
14644 Roo.get(document).on('mousedown', this.collapseIf, this);
14645 Roo.get(document).on('mousewheel', this.collapseIf, this);
14646 if (!this.editable) {
14647 Roo.get(document).on('keydown', this.listKeyPress, this);
14650 this.fireEvent('expand', this);
14654 // Implements the default empty TriggerField.onTriggerClick function
14655 onTriggerClick : function(e)
14657 Roo.log('trigger click');
14659 if(this.disabled || !this.triggerList){
14664 this.loadNext = false;
14666 if(this.isExpanded()){
14668 if (!this.blockFocus) {
14669 this.inputEl().focus();
14673 this.hasFocus = true;
14674 if(this.triggerAction == 'all') {
14675 this.doQuery(this.allQuery, true);
14677 this.doQuery(this.getRawValue());
14679 if (!this.blockFocus) {
14680 this.inputEl().focus();
14685 onTickableTriggerClick : function(e)
14692 this.loadNext = false;
14693 this.hasFocus = true;
14695 if(this.triggerAction == 'all') {
14696 this.doQuery(this.allQuery, true);
14698 this.doQuery(this.getRawValue());
14702 onSearchFieldClick : function(e)
14704 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14705 this.onTickableFooterButtonClick(e, false, false);
14709 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14714 this.loadNext = false;
14715 this.hasFocus = true;
14717 if(this.triggerAction == 'all') {
14718 this.doQuery(this.allQuery, true);
14720 this.doQuery(this.getRawValue());
14724 listKeyPress : function(e)
14726 //Roo.log('listkeypress');
14727 // scroll to first matching element based on key pres..
14728 if (e.isSpecialKey()) {
14731 var k = String.fromCharCode(e.getKey()).toUpperCase();
14734 var csel = this.view.getSelectedNodes();
14735 var cselitem = false;
14737 var ix = this.view.indexOf(csel[0]);
14738 cselitem = this.store.getAt(ix);
14739 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14745 this.store.each(function(v) {
14747 // start at existing selection.
14748 if (cselitem.id == v.id) {
14754 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14755 match = this.store.indexOf(v);
14761 if (match === false) {
14762 return true; // no more action?
14765 this.view.select(match);
14766 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14767 sn.scrollIntoView(sn.dom.parentNode, false);
14770 onViewScroll : function(e, t){
14772 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){
14776 this.hasQuery = true;
14778 this.loading = this.list.select('.loading', true).first();
14780 if(this.loading === null){
14781 this.list.createChild({
14783 cls: 'loading roo-select2-more-results roo-select2-active',
14784 html: 'Loading more results...'
14787 this.loading = this.list.select('.loading', true).first();
14789 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14791 this.loading.hide();
14794 this.loading.show();
14799 this.loadNext = true;
14801 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14806 addItem : function(o)
14808 var dv = ''; // display value
14810 if (this.displayField) {
14811 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14813 // this is an error condition!!!
14814 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14821 var choice = this.choices.createChild({
14823 cls: 'roo-select2-search-choice',
14832 cls: 'roo-select2-search-choice-close fa fa-times',
14837 }, this.searchField);
14839 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14841 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14849 this.inputEl().dom.value = '';
14854 onRemoveItem : function(e, _self, o)
14856 e.preventDefault();
14858 this.lastItem = Roo.apply([], this.item);
14860 var index = this.item.indexOf(o.data) * 1;
14863 Roo.log('not this item?!');
14867 this.item.splice(index, 1);
14872 this.fireEvent('remove', this, e);
14878 syncValue : function()
14880 if(!this.item.length){
14887 Roo.each(this.item, function(i){
14888 if(_this.valueField){
14889 value.push(i[_this.valueField]);
14896 this.value = value.join(',');
14898 if(this.hiddenField){
14899 this.hiddenField.dom.value = this.value;
14902 this.store.fireEvent("datachanged", this.store);
14907 clearItem : function()
14909 if(!this.multiple){
14915 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14923 if(this.tickable && !Roo.isTouch){
14924 this.view.refresh();
14928 inputEl: function ()
14930 if(Roo.isIOS && this.useNativeIOS){
14931 return this.el.select('select.roo-ios-select', true).first();
14934 if(Roo.isTouch && this.mobileTouchView){
14935 return this.el.select('input.form-control',true).first();
14939 return this.searchField;
14942 return this.el.select('input.form-control',true).first();
14945 onTickableFooterButtonClick : function(e, btn, el)
14947 e.preventDefault();
14949 this.lastItem = Roo.apply([], this.item);
14951 if(btn && btn.name == 'cancel'){
14952 this.tickItems = Roo.apply([], this.item);
14961 Roo.each(this.tickItems, function(o){
14969 validate : function()
14971 if(this.getVisibilityEl().hasClass('hidden')){
14975 var v = this.getRawValue();
14978 v = this.getValue();
14981 if(this.disabled || this.allowBlank || v.length){
14986 this.markInvalid();
14990 tickableInputEl : function()
14992 if(!this.tickable || !this.editable){
14993 return this.inputEl();
14996 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15000 getAutoCreateTouchView : function()
15005 cls: 'form-group' //input-group
15011 type : this.inputType,
15012 cls : 'form-control x-combo-noedit',
15013 autocomplete: 'new-password',
15014 placeholder : this.placeholder || '',
15019 input.name = this.name;
15023 input.cls += ' input-' + this.size;
15026 if (this.disabled) {
15027 input.disabled = true;
15038 inputblock.cls += ' input-group';
15040 inputblock.cn.unshift({
15042 cls : 'input-group-addon',
15047 if(this.removable && !this.multiple){
15048 inputblock.cls += ' roo-removable';
15050 inputblock.cn.push({
15053 cls : 'roo-combo-removable-btn close'
15057 if(this.hasFeedback && !this.allowBlank){
15059 inputblock.cls += ' has-feedback';
15061 inputblock.cn.push({
15063 cls: 'glyphicon form-control-feedback'
15070 inputblock.cls += (this.before) ? '' : ' input-group';
15072 inputblock.cn.push({
15074 cls : 'input-group-addon',
15085 cls: 'form-hidden-field'
15099 cls: 'form-hidden-field'
15103 cls: 'roo-select2-choices',
15107 cls: 'roo-select2-search-field',
15120 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15126 if(!this.multiple && this.showToggleBtn){
15133 if (this.caret != false) {
15136 cls: 'fa fa-' + this.caret
15143 cls : 'input-group-addon btn dropdown-toggle',
15148 cls: 'combobox-clear',
15162 combobox.cls += ' roo-select2-container-multi';
15165 var align = this.labelAlign || this.parentLabelAlign();
15167 if (align ==='left' && this.fieldLabel.length) {
15172 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15173 tooltip : 'This field is required'
15177 cls : 'control-label',
15178 html : this.fieldLabel
15189 var labelCfg = cfg.cn[1];
15190 var contentCfg = cfg.cn[2];
15193 if(this.indicatorpos == 'right'){
15198 cls : 'control-label',
15202 html : this.fieldLabel
15206 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15207 tooltip : 'This field is required'
15220 labelCfg = cfg.cn[0];
15221 contentCfg = cfg.cn[1];
15226 if(this.labelWidth > 12){
15227 labelCfg.style = "width: " + this.labelWidth + 'px';
15230 if(this.labelWidth < 13 && this.labelmd == 0){
15231 this.labelmd = this.labelWidth;
15234 if(this.labellg > 0){
15235 labelCfg.cls += ' col-lg-' + this.labellg;
15236 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15239 if(this.labelmd > 0){
15240 labelCfg.cls += ' col-md-' + this.labelmd;
15241 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15244 if(this.labelsm > 0){
15245 labelCfg.cls += ' col-sm-' + this.labelsm;
15246 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15249 if(this.labelxs > 0){
15250 labelCfg.cls += ' col-xs-' + this.labelxs;
15251 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15255 } else if ( this.fieldLabel.length) {
15259 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15260 tooltip : 'This field is required'
15264 cls : 'control-label',
15265 html : this.fieldLabel
15276 if(this.indicatorpos == 'right'){
15280 cls : 'control-label',
15281 html : this.fieldLabel,
15285 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15286 tooltip : 'This field is required'
15303 var settings = this;
15305 ['xs','sm','md','lg'].map(function(size){
15306 if (settings[size]) {
15307 cfg.cls += ' col-' + size + '-' + settings[size];
15314 initTouchView : function()
15316 this.renderTouchView();
15318 this.touchViewEl.on('scroll', function(){
15319 this.el.dom.scrollTop = 0;
15322 this.originalValue = this.getValue();
15324 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15326 this.inputEl().on("click", this.showTouchView, this);
15327 if (this.triggerEl) {
15328 this.triggerEl.on("click", this.showTouchView, this);
15332 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15333 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15335 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15337 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15338 this.store.on('load', this.onTouchViewLoad, this);
15339 this.store.on('loadexception', this.onTouchViewLoadException, this);
15341 if(this.hiddenName){
15343 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15345 this.hiddenField.dom.value =
15346 this.hiddenValue !== undefined ? this.hiddenValue :
15347 this.value !== undefined ? this.value : '';
15349 this.el.dom.removeAttribute('name');
15350 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15354 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15355 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15358 if(this.removable && !this.multiple){
15359 var close = this.closeTriggerEl();
15361 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15362 close.on('click', this.removeBtnClick, this, close);
15366 * fix the bug in Safari iOS8
15368 this.inputEl().on("focus", function(e){
15369 document.activeElement.blur();
15372 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15379 renderTouchView : function()
15381 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15382 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15384 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15385 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15387 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15388 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15389 this.touchViewBodyEl.setStyle('overflow', 'auto');
15391 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15392 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15394 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15395 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15399 showTouchView : function()
15405 this.touchViewHeaderEl.hide();
15407 if(this.modalTitle.length){
15408 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15409 this.touchViewHeaderEl.show();
15412 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15413 this.touchViewEl.show();
15415 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15417 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15418 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15420 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15422 if(this.modalTitle.length){
15423 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15426 this.touchViewBodyEl.setHeight(bodyHeight);
15430 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15432 this.touchViewEl.addClass('in');
15435 if(this._touchViewMask){
15436 Roo.get(document.body).addClass("x-body-masked");
15437 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15438 this._touchViewMask.setStyle('z-index', 10000);
15439 this._touchViewMask.addClass('show');
15442 this.doTouchViewQuery();
15446 hideTouchView : function()
15448 this.touchViewEl.removeClass('in');
15452 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15454 this.touchViewEl.setStyle('display', 'none');
15457 if(this._touchViewMask){
15458 this._touchViewMask.removeClass('show');
15459 Roo.get(document.body).removeClass("x-body-masked");
15463 setTouchViewValue : function()
15470 Roo.each(this.tickItems, function(o){
15475 this.hideTouchView();
15478 doTouchViewQuery : function()
15487 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15491 if(!this.alwaysQuery || this.mode == 'local'){
15492 this.onTouchViewLoad();
15499 onTouchViewBeforeLoad : function(combo,opts)
15505 onTouchViewLoad : function()
15507 if(this.store.getCount() < 1){
15508 this.onTouchViewEmptyResults();
15512 this.clearTouchView();
15514 var rawValue = this.getRawValue();
15516 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15518 this.tickItems = [];
15520 this.store.data.each(function(d, rowIndex){
15521 var row = this.touchViewListGroup.createChild(template);
15523 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15524 row.addClass(d.data.cls);
15527 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15530 html : d.data[this.displayField]
15533 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15534 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15537 row.removeClass('selected');
15538 if(!this.multiple && this.valueField &&
15539 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15542 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15543 row.addClass('selected');
15546 if(this.multiple && this.valueField &&
15547 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15551 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15552 this.tickItems.push(d.data);
15555 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15559 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15561 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15563 if(this.modalTitle.length){
15564 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15567 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15569 if(this.mobile_restrict_height && listHeight < bodyHeight){
15570 this.touchViewBodyEl.setHeight(listHeight);
15575 if(firstChecked && listHeight > bodyHeight){
15576 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15581 onTouchViewLoadException : function()
15583 this.hideTouchView();
15586 onTouchViewEmptyResults : function()
15588 this.clearTouchView();
15590 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15592 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15596 clearTouchView : function()
15598 this.touchViewListGroup.dom.innerHTML = '';
15601 onTouchViewClick : function(e, el, o)
15603 e.preventDefault();
15606 var rowIndex = o.rowIndex;
15608 var r = this.store.getAt(rowIndex);
15610 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15612 if(!this.multiple){
15613 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15614 c.dom.removeAttribute('checked');
15617 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15619 this.setFromData(r.data);
15621 var close = this.closeTriggerEl();
15627 this.hideTouchView();
15629 this.fireEvent('select', this, r, rowIndex);
15634 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15635 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15636 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15640 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15641 this.addItem(r.data);
15642 this.tickItems.push(r.data);
15646 getAutoCreateNativeIOS : function()
15649 cls: 'form-group' //input-group,
15654 cls : 'roo-ios-select'
15658 combobox.name = this.name;
15661 if (this.disabled) {
15662 combobox.disabled = true;
15665 var settings = this;
15667 ['xs','sm','md','lg'].map(function(size){
15668 if (settings[size]) {
15669 cfg.cls += ' col-' + size + '-' + settings[size];
15679 initIOSView : function()
15681 this.store.on('load', this.onIOSViewLoad, this);
15686 onIOSViewLoad : function()
15688 if(this.store.getCount() < 1){
15692 this.clearIOSView();
15694 if(this.allowBlank) {
15696 var default_text = '-- SELECT --';
15698 if(this.placeholder.length){
15699 default_text = this.placeholder;
15702 if(this.emptyTitle.length){
15703 default_text += ' - ' + this.emptyTitle + ' -';
15706 var opt = this.inputEl().createChild({
15709 html : default_text
15713 o[this.valueField] = 0;
15714 o[this.displayField] = default_text;
15716 this.ios_options.push({
15723 this.store.data.each(function(d, rowIndex){
15727 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15728 html = d.data[this.displayField];
15733 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15734 value = d.data[this.valueField];
15743 if(this.value == d.data[this.valueField]){
15744 option['selected'] = true;
15747 var opt = this.inputEl().createChild(option);
15749 this.ios_options.push({
15756 this.inputEl().on('change', function(){
15757 this.fireEvent('select', this);
15762 clearIOSView: function()
15764 this.inputEl().dom.innerHTML = '';
15766 this.ios_options = [];
15769 setIOSValue: function(v)
15773 if(!this.ios_options){
15777 Roo.each(this.ios_options, function(opts){
15779 opts.el.dom.removeAttribute('selected');
15781 if(opts.data[this.valueField] != v){
15785 opts.el.dom.setAttribute('selected', true);
15791 * @cfg {Boolean} grow
15795 * @cfg {Number} growMin
15799 * @cfg {Number} growMax
15808 Roo.apply(Roo.bootstrap.ComboBox, {
15812 cls: 'modal-header',
15834 cls: 'list-group-item',
15838 cls: 'roo-combobox-list-group-item-value'
15842 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15856 listItemCheckbox : {
15858 cls: 'list-group-item',
15862 cls: 'roo-combobox-list-group-item-value'
15866 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15882 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15887 cls: 'modal-footer',
15895 cls: 'col-xs-6 text-left',
15898 cls: 'btn btn-danger roo-touch-view-cancel',
15904 cls: 'col-xs-6 text-right',
15907 cls: 'btn btn-success roo-touch-view-ok',
15918 Roo.apply(Roo.bootstrap.ComboBox, {
15920 touchViewTemplate : {
15922 cls: 'modal fade roo-combobox-touch-view',
15926 cls: 'modal-dialog',
15927 style : 'position:fixed', // we have to fix position....
15931 cls: 'modal-content',
15933 Roo.bootstrap.ComboBox.header,
15934 Roo.bootstrap.ComboBox.body,
15935 Roo.bootstrap.ComboBox.footer
15944 * Ext JS Library 1.1.1
15945 * Copyright(c) 2006-2007, Ext JS, LLC.
15947 * Originally Released Under LGPL - original licence link has changed is not relivant.
15950 * <script type="text/javascript">
15955 * @extends Roo.util.Observable
15956 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15957 * This class also supports single and multi selection modes. <br>
15958 * Create a data model bound view:
15960 var store = new Roo.data.Store(...);
15962 var view = new Roo.View({
15964 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15966 singleSelect: true,
15967 selectedClass: "ydataview-selected",
15971 // listen for node click?
15972 view.on("click", function(vw, index, node, e){
15973 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15977 dataModel.load("foobar.xml");
15979 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15981 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15982 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15984 * Note: old style constructor is still suported (container, template, config)
15987 * Create a new View
15988 * @param {Object} config The config object
15991 Roo.View = function(config, depreciated_tpl, depreciated_config){
15993 this.parent = false;
15995 if (typeof(depreciated_tpl) == 'undefined') {
15996 // new way.. - universal constructor.
15997 Roo.apply(this, config);
15998 this.el = Roo.get(this.el);
16001 this.el = Roo.get(config);
16002 this.tpl = depreciated_tpl;
16003 Roo.apply(this, depreciated_config);
16005 this.wrapEl = this.el.wrap().wrap();
16006 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16009 if(typeof(this.tpl) == "string"){
16010 this.tpl = new Roo.Template(this.tpl);
16012 // support xtype ctors..
16013 this.tpl = new Roo.factory(this.tpl, Roo);
16017 this.tpl.compile();
16022 * @event beforeclick
16023 * Fires before a click is processed. Returns false to cancel the default action.
16024 * @param {Roo.View} this
16025 * @param {Number} index The index of the target node
16026 * @param {HTMLElement} node The target node
16027 * @param {Roo.EventObject} e The raw event object
16029 "beforeclick" : true,
16032 * Fires when a template node is clicked.
16033 * @param {Roo.View} this
16034 * @param {Number} index The index of the target node
16035 * @param {HTMLElement} node The target node
16036 * @param {Roo.EventObject} e The raw event object
16041 * Fires when a template node is double clicked.
16042 * @param {Roo.View} this
16043 * @param {Number} index The index of the target node
16044 * @param {HTMLElement} node The target node
16045 * @param {Roo.EventObject} e The raw event object
16049 * @event contextmenu
16050 * Fires when a template node is right clicked.
16051 * @param {Roo.View} this
16052 * @param {Number} index The index of the target node
16053 * @param {HTMLElement} node The target node
16054 * @param {Roo.EventObject} e The raw event object
16056 "contextmenu" : true,
16058 * @event selectionchange
16059 * Fires when the selected nodes change.
16060 * @param {Roo.View} this
16061 * @param {Array} selections Array of the selected nodes
16063 "selectionchange" : true,
16066 * @event beforeselect
16067 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16068 * @param {Roo.View} this
16069 * @param {HTMLElement} node The node to be selected
16070 * @param {Array} selections Array of currently selected nodes
16072 "beforeselect" : true,
16074 * @event preparedata
16075 * Fires on every row to render, to allow you to change the data.
16076 * @param {Roo.View} this
16077 * @param {Object} data to be rendered (change this)
16079 "preparedata" : true
16087 "click": this.onClick,
16088 "dblclick": this.onDblClick,
16089 "contextmenu": this.onContextMenu,
16093 this.selections = [];
16095 this.cmp = new Roo.CompositeElementLite([]);
16097 this.store = Roo.factory(this.store, Roo.data);
16098 this.setStore(this.store, true);
16101 if ( this.footer && this.footer.xtype) {
16103 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16105 this.footer.dataSource = this.store;
16106 this.footer.container = fctr;
16107 this.footer = Roo.factory(this.footer, Roo);
16108 fctr.insertFirst(this.el);
16110 // this is a bit insane - as the paging toolbar seems to detach the el..
16111 // dom.parentNode.parentNode.parentNode
16112 // they get detached?
16116 Roo.View.superclass.constructor.call(this);
16121 Roo.extend(Roo.View, Roo.util.Observable, {
16124 * @cfg {Roo.data.Store} store Data store to load data from.
16129 * @cfg {String|Roo.Element} el The container element.
16134 * @cfg {String|Roo.Template} tpl The template used by this View
16138 * @cfg {String} dataName the named area of the template to use as the data area
16139 * Works with domtemplates roo-name="name"
16143 * @cfg {String} selectedClass The css class to add to selected nodes
16145 selectedClass : "x-view-selected",
16147 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16152 * @cfg {String} text to display on mask (default Loading)
16156 * @cfg {Boolean} multiSelect Allow multiple selection
16158 multiSelect : false,
16160 * @cfg {Boolean} singleSelect Allow single selection
16162 singleSelect: false,
16165 * @cfg {Boolean} toggleSelect - selecting
16167 toggleSelect : false,
16170 * @cfg {Boolean} tickable - selecting
16175 * Returns the element this view is bound to.
16176 * @return {Roo.Element}
16178 getEl : function(){
16179 return this.wrapEl;
16185 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16187 refresh : function(){
16188 //Roo.log('refresh');
16191 // if we are using something like 'domtemplate', then
16192 // the what gets used is:
16193 // t.applySubtemplate(NAME, data, wrapping data..)
16194 // the outer template then get' applied with
16195 // the store 'extra data'
16196 // and the body get's added to the
16197 // roo-name="data" node?
16198 // <span class='roo-tpl-{name}'></span> ?????
16202 this.clearSelections();
16203 this.el.update("");
16205 var records = this.store.getRange();
16206 if(records.length < 1) {
16208 // is this valid?? = should it render a template??
16210 this.el.update(this.emptyText);
16214 if (this.dataName) {
16215 this.el.update(t.apply(this.store.meta)); //????
16216 el = this.el.child('.roo-tpl-' + this.dataName);
16219 for(var i = 0, len = records.length; i < len; i++){
16220 var data = this.prepareData(records[i].data, i, records[i]);
16221 this.fireEvent("preparedata", this, data, i, records[i]);
16223 var d = Roo.apply({}, data);
16226 Roo.apply(d, {'roo-id' : Roo.id()});
16230 Roo.each(this.parent.item, function(item){
16231 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16234 Roo.apply(d, {'roo-data-checked' : 'checked'});
16238 html[html.length] = Roo.util.Format.trim(
16240 t.applySubtemplate(this.dataName, d, this.store.meta) :
16247 el.update(html.join(""));
16248 this.nodes = el.dom.childNodes;
16249 this.updateIndexes(0);
16254 * Function to override to reformat the data that is sent to
16255 * the template for each node.
16256 * DEPRICATED - use the preparedata event handler.
16257 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16258 * a JSON object for an UpdateManager bound view).
16260 prepareData : function(data, index, record)
16262 this.fireEvent("preparedata", this, data, index, record);
16266 onUpdate : function(ds, record){
16267 // Roo.log('on update');
16268 this.clearSelections();
16269 var index = this.store.indexOf(record);
16270 var n = this.nodes[index];
16271 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16272 n.parentNode.removeChild(n);
16273 this.updateIndexes(index, index);
16279 onAdd : function(ds, records, index)
16281 //Roo.log(['on Add', ds, records, index] );
16282 this.clearSelections();
16283 if(this.nodes.length == 0){
16287 var n = this.nodes[index];
16288 for(var i = 0, len = records.length; i < len; i++){
16289 var d = this.prepareData(records[i].data, i, records[i]);
16291 this.tpl.insertBefore(n, d);
16294 this.tpl.append(this.el, d);
16297 this.updateIndexes(index);
16300 onRemove : function(ds, record, index){
16301 // Roo.log('onRemove');
16302 this.clearSelections();
16303 var el = this.dataName ?
16304 this.el.child('.roo-tpl-' + this.dataName) :
16307 el.dom.removeChild(this.nodes[index]);
16308 this.updateIndexes(index);
16312 * Refresh an individual node.
16313 * @param {Number} index
16315 refreshNode : function(index){
16316 this.onUpdate(this.store, this.store.getAt(index));
16319 updateIndexes : function(startIndex, endIndex){
16320 var ns = this.nodes;
16321 startIndex = startIndex || 0;
16322 endIndex = endIndex || ns.length - 1;
16323 for(var i = startIndex; i <= endIndex; i++){
16324 ns[i].nodeIndex = i;
16329 * Changes the data store this view uses and refresh the view.
16330 * @param {Store} store
16332 setStore : function(store, initial){
16333 if(!initial && this.store){
16334 this.store.un("datachanged", this.refresh);
16335 this.store.un("add", this.onAdd);
16336 this.store.un("remove", this.onRemove);
16337 this.store.un("update", this.onUpdate);
16338 this.store.un("clear", this.refresh);
16339 this.store.un("beforeload", this.onBeforeLoad);
16340 this.store.un("load", this.onLoad);
16341 this.store.un("loadexception", this.onLoad);
16345 store.on("datachanged", this.refresh, this);
16346 store.on("add", this.onAdd, this);
16347 store.on("remove", this.onRemove, this);
16348 store.on("update", this.onUpdate, this);
16349 store.on("clear", this.refresh, this);
16350 store.on("beforeload", this.onBeforeLoad, this);
16351 store.on("load", this.onLoad, this);
16352 store.on("loadexception", this.onLoad, this);
16360 * onbeforeLoad - masks the loading area.
16363 onBeforeLoad : function(store,opts)
16365 //Roo.log('onBeforeLoad');
16367 this.el.update("");
16369 this.el.mask(this.mask ? this.mask : "Loading" );
16371 onLoad : function ()
16378 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16379 * @param {HTMLElement} node
16380 * @return {HTMLElement} The template node
16382 findItemFromChild : function(node){
16383 var el = this.dataName ?
16384 this.el.child('.roo-tpl-' + this.dataName,true) :
16387 if(!node || node.parentNode == el){
16390 var p = node.parentNode;
16391 while(p && p != el){
16392 if(p.parentNode == el){
16401 onClick : function(e){
16402 var item = this.findItemFromChild(e.getTarget());
16404 var index = this.indexOf(item);
16405 if(this.onItemClick(item, index, e) !== false){
16406 this.fireEvent("click", this, index, item, e);
16409 this.clearSelections();
16414 onContextMenu : function(e){
16415 var item = this.findItemFromChild(e.getTarget());
16417 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16422 onDblClick : function(e){
16423 var item = this.findItemFromChild(e.getTarget());
16425 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16429 onItemClick : function(item, index, e)
16431 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16434 if (this.toggleSelect) {
16435 var m = this.isSelected(item) ? 'unselect' : 'select';
16438 _t[m](item, true, false);
16441 if(this.multiSelect || this.singleSelect){
16442 if(this.multiSelect && e.shiftKey && this.lastSelection){
16443 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16445 this.select(item, this.multiSelect && e.ctrlKey);
16446 this.lastSelection = item;
16449 if(!this.tickable){
16450 e.preventDefault();
16458 * Get the number of selected nodes.
16461 getSelectionCount : function(){
16462 return this.selections.length;
16466 * Get the currently selected nodes.
16467 * @return {Array} An array of HTMLElements
16469 getSelectedNodes : function(){
16470 return this.selections;
16474 * Get the indexes of the selected nodes.
16477 getSelectedIndexes : function(){
16478 var indexes = [], s = this.selections;
16479 for(var i = 0, len = s.length; i < len; i++){
16480 indexes.push(s[i].nodeIndex);
16486 * Clear all selections
16487 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16489 clearSelections : function(suppressEvent){
16490 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16491 this.cmp.elements = this.selections;
16492 this.cmp.removeClass(this.selectedClass);
16493 this.selections = [];
16494 if(!suppressEvent){
16495 this.fireEvent("selectionchange", this, this.selections);
16501 * Returns true if the passed node is selected
16502 * @param {HTMLElement/Number} node The node or node index
16503 * @return {Boolean}
16505 isSelected : function(node){
16506 var s = this.selections;
16510 node = this.getNode(node);
16511 return s.indexOf(node) !== -1;
16516 * @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
16517 * @param {Boolean} keepExisting (optional) true to keep existing selections
16518 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16520 select : function(nodeInfo, keepExisting, suppressEvent){
16521 if(nodeInfo instanceof Array){
16523 this.clearSelections(true);
16525 for(var i = 0, len = nodeInfo.length; i < len; i++){
16526 this.select(nodeInfo[i], true, true);
16530 var node = this.getNode(nodeInfo);
16531 if(!node || this.isSelected(node)){
16532 return; // already selected.
16535 this.clearSelections(true);
16538 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16539 Roo.fly(node).addClass(this.selectedClass);
16540 this.selections.push(node);
16541 if(!suppressEvent){
16542 this.fireEvent("selectionchange", this, this.selections);
16550 * @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
16551 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16552 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16554 unselect : function(nodeInfo, keepExisting, suppressEvent)
16556 if(nodeInfo instanceof Array){
16557 Roo.each(this.selections, function(s) {
16558 this.unselect(s, nodeInfo);
16562 var node = this.getNode(nodeInfo);
16563 if(!node || !this.isSelected(node)){
16564 //Roo.log("not selected");
16565 return; // not selected.
16569 Roo.each(this.selections, function(s) {
16571 Roo.fly(node).removeClass(this.selectedClass);
16578 this.selections= ns;
16579 this.fireEvent("selectionchange", this, this.selections);
16583 * Gets a template node.
16584 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16585 * @return {HTMLElement} The node or null if it wasn't found
16587 getNode : function(nodeInfo){
16588 if(typeof nodeInfo == "string"){
16589 return document.getElementById(nodeInfo);
16590 }else if(typeof nodeInfo == "number"){
16591 return this.nodes[nodeInfo];
16597 * Gets a range template nodes.
16598 * @param {Number} startIndex
16599 * @param {Number} endIndex
16600 * @return {Array} An array of nodes
16602 getNodes : function(start, end){
16603 var ns = this.nodes;
16604 start = start || 0;
16605 end = typeof end == "undefined" ? ns.length - 1 : end;
16608 for(var i = start; i <= end; i++){
16612 for(var i = start; i >= end; i--){
16620 * Finds the index of the passed node
16621 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16622 * @return {Number} The index of the node or -1
16624 indexOf : function(node){
16625 node = this.getNode(node);
16626 if(typeof node.nodeIndex == "number"){
16627 return node.nodeIndex;
16629 var ns = this.nodes;
16630 for(var i = 0, len = ns.length; i < len; i++){
16641 * based on jquery fullcalendar
16645 Roo.bootstrap = Roo.bootstrap || {};
16647 * @class Roo.bootstrap.Calendar
16648 * @extends Roo.bootstrap.Component
16649 * Bootstrap Calendar class
16650 * @cfg {Boolean} loadMask (true|false) default false
16651 * @cfg {Object} header generate the user specific header of the calendar, default false
16654 * Create a new Container
16655 * @param {Object} config The config object
16660 Roo.bootstrap.Calendar = function(config){
16661 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16665 * Fires when a date is selected
16666 * @param {DatePicker} this
16667 * @param {Date} date The selected date
16671 * @event monthchange
16672 * Fires when the displayed month changes
16673 * @param {DatePicker} this
16674 * @param {Date} date The selected month
16676 'monthchange': true,
16678 * @event evententer
16679 * Fires when mouse over an event
16680 * @param {Calendar} this
16681 * @param {event} Event
16683 'evententer': true,
16685 * @event eventleave
16686 * Fires when the mouse leaves an
16687 * @param {Calendar} this
16690 'eventleave': true,
16692 * @event eventclick
16693 * Fires when the mouse click an
16694 * @param {Calendar} this
16703 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16706 * @cfg {Number} startDay
16707 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16715 getAutoCreate : function(){
16718 var fc_button = function(name, corner, style, content ) {
16719 return Roo.apply({},{
16721 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16723 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16726 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16737 style : 'width:100%',
16744 cls : 'fc-header-left',
16746 fc_button('prev', 'left', 'arrow', '‹' ),
16747 fc_button('next', 'right', 'arrow', '›' ),
16748 { tag: 'span', cls: 'fc-header-space' },
16749 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16757 cls : 'fc-header-center',
16761 cls: 'fc-header-title',
16764 html : 'month / year'
16772 cls : 'fc-header-right',
16774 /* fc_button('month', 'left', '', 'month' ),
16775 fc_button('week', '', '', 'week' ),
16776 fc_button('day', 'right', '', 'day' )
16788 header = this.header;
16791 var cal_heads = function() {
16793 // fixme - handle this.
16795 for (var i =0; i < Date.dayNames.length; i++) {
16796 var d = Date.dayNames[i];
16799 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16800 html : d.substring(0,3)
16804 ret[0].cls += ' fc-first';
16805 ret[6].cls += ' fc-last';
16808 var cal_cell = function(n) {
16811 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16816 cls: 'fc-day-number',
16820 cls: 'fc-day-content',
16824 style: 'position: relative;' // height: 17px;
16836 var cal_rows = function() {
16839 for (var r = 0; r < 6; r++) {
16846 for (var i =0; i < Date.dayNames.length; i++) {
16847 var d = Date.dayNames[i];
16848 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16851 row.cn[0].cls+=' fc-first';
16852 row.cn[0].cn[0].style = 'min-height:90px';
16853 row.cn[6].cls+=' fc-last';
16857 ret[0].cls += ' fc-first';
16858 ret[4].cls += ' fc-prev-last';
16859 ret[5].cls += ' fc-last';
16866 cls: 'fc-border-separate',
16867 style : 'width:100%',
16875 cls : 'fc-first fc-last',
16893 cls : 'fc-content',
16894 style : "position: relative;",
16897 cls : 'fc-view fc-view-month fc-grid',
16898 style : 'position: relative',
16899 unselectable : 'on',
16902 cls : 'fc-event-container',
16903 style : 'position:absolute;z-index:8;top:0;left:0;'
16921 initEvents : function()
16924 throw "can not find store for calendar";
16930 style: "text-align:center",
16934 style: "background-color:white;width:50%;margin:250 auto",
16938 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16949 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16951 var size = this.el.select('.fc-content', true).first().getSize();
16952 this.maskEl.setSize(size.width, size.height);
16953 this.maskEl.enableDisplayMode("block");
16954 if(!this.loadMask){
16955 this.maskEl.hide();
16958 this.store = Roo.factory(this.store, Roo.data);
16959 this.store.on('load', this.onLoad, this);
16960 this.store.on('beforeload', this.onBeforeLoad, this);
16964 this.cells = this.el.select('.fc-day',true);
16965 //Roo.log(this.cells);
16966 this.textNodes = this.el.query('.fc-day-number');
16967 this.cells.addClassOnOver('fc-state-hover');
16969 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16970 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16971 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16972 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16974 this.on('monthchange', this.onMonthChange, this);
16976 this.update(new Date().clearTime());
16979 resize : function() {
16980 var sz = this.el.getSize();
16982 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16983 this.el.select('.fc-day-content div',true).setHeight(34);
16988 showPrevMonth : function(e){
16989 this.update(this.activeDate.add("mo", -1));
16991 showToday : function(e){
16992 this.update(new Date().clearTime());
16995 showNextMonth : function(e){
16996 this.update(this.activeDate.add("mo", 1));
17000 showPrevYear : function(){
17001 this.update(this.activeDate.add("y", -1));
17005 showNextYear : function(){
17006 this.update(this.activeDate.add("y", 1));
17011 update : function(date)
17013 var vd = this.activeDate;
17014 this.activeDate = date;
17015 // if(vd && this.el){
17016 // var t = date.getTime();
17017 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17018 // Roo.log('using add remove');
17020 // this.fireEvent('monthchange', this, date);
17022 // this.cells.removeClass("fc-state-highlight");
17023 // this.cells.each(function(c){
17024 // if(c.dateValue == t){
17025 // c.addClass("fc-state-highlight");
17026 // setTimeout(function(){
17027 // try{c.dom.firstChild.focus();}catch(e){}
17037 var days = date.getDaysInMonth();
17039 var firstOfMonth = date.getFirstDateOfMonth();
17040 var startingPos = firstOfMonth.getDay()-this.startDay;
17042 if(startingPos < this.startDay){
17046 var pm = date.add(Date.MONTH, -1);
17047 var prevStart = pm.getDaysInMonth()-startingPos;
17049 this.cells = this.el.select('.fc-day',true);
17050 this.textNodes = this.el.query('.fc-day-number');
17051 this.cells.addClassOnOver('fc-state-hover');
17053 var cells = this.cells.elements;
17054 var textEls = this.textNodes;
17056 Roo.each(cells, function(cell){
17057 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17060 days += startingPos;
17062 // convert everything to numbers so it's fast
17063 var day = 86400000;
17064 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17067 //Roo.log(prevStart);
17069 var today = new Date().clearTime().getTime();
17070 var sel = date.clearTime().getTime();
17071 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17072 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17073 var ddMatch = this.disabledDatesRE;
17074 var ddText = this.disabledDatesText;
17075 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17076 var ddaysText = this.disabledDaysText;
17077 var format = this.format;
17079 var setCellClass = function(cal, cell){
17083 //Roo.log('set Cell Class');
17085 var t = d.getTime();
17089 cell.dateValue = t;
17091 cell.className += " fc-today";
17092 cell.className += " fc-state-highlight";
17093 cell.title = cal.todayText;
17096 // disable highlight in other month..
17097 //cell.className += " fc-state-highlight";
17102 cell.className = " fc-state-disabled";
17103 cell.title = cal.minText;
17107 cell.className = " fc-state-disabled";
17108 cell.title = cal.maxText;
17112 if(ddays.indexOf(d.getDay()) != -1){
17113 cell.title = ddaysText;
17114 cell.className = " fc-state-disabled";
17117 if(ddMatch && format){
17118 var fvalue = d.dateFormat(format);
17119 if(ddMatch.test(fvalue)){
17120 cell.title = ddText.replace("%0", fvalue);
17121 cell.className = " fc-state-disabled";
17125 if (!cell.initialClassName) {
17126 cell.initialClassName = cell.dom.className;
17129 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17134 for(; i < startingPos; i++) {
17135 textEls[i].innerHTML = (++prevStart);
17136 d.setDate(d.getDate()+1);
17138 cells[i].className = "fc-past fc-other-month";
17139 setCellClass(this, cells[i]);
17144 for(; i < days; i++){
17145 intDay = i - startingPos + 1;
17146 textEls[i].innerHTML = (intDay);
17147 d.setDate(d.getDate()+1);
17149 cells[i].className = ''; // "x-date-active";
17150 setCellClass(this, cells[i]);
17154 for(; i < 42; i++) {
17155 textEls[i].innerHTML = (++extraDays);
17156 d.setDate(d.getDate()+1);
17158 cells[i].className = "fc-future fc-other-month";
17159 setCellClass(this, cells[i]);
17162 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17164 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17166 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17167 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17169 if(totalRows != 6){
17170 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17171 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17174 this.fireEvent('monthchange', this, date);
17178 if(!this.internalRender){
17179 var main = this.el.dom.firstChild;
17180 var w = main.offsetWidth;
17181 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17182 Roo.fly(main).setWidth(w);
17183 this.internalRender = true;
17184 // opera does not respect the auto grow header center column
17185 // then, after it gets a width opera refuses to recalculate
17186 // without a second pass
17187 if(Roo.isOpera && !this.secondPass){
17188 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17189 this.secondPass = true;
17190 this.update.defer(10, this, [date]);
17197 findCell : function(dt) {
17198 dt = dt.clearTime().getTime();
17200 this.cells.each(function(c){
17201 //Roo.log("check " +c.dateValue + '?=' + dt);
17202 if(c.dateValue == dt){
17212 findCells : function(ev) {
17213 var s = ev.start.clone().clearTime().getTime();
17215 var e= ev.end.clone().clearTime().getTime();
17218 this.cells.each(function(c){
17219 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17221 if(c.dateValue > e){
17224 if(c.dateValue < s){
17233 // findBestRow: function(cells)
17237 // for (var i =0 ; i < cells.length;i++) {
17238 // ret = Math.max(cells[i].rows || 0,ret);
17245 addItem : function(ev)
17247 // look for vertical location slot in
17248 var cells = this.findCells(ev);
17250 // ev.row = this.findBestRow(cells);
17252 // work out the location.
17256 for(var i =0; i < cells.length; i++) {
17258 cells[i].row = cells[0].row;
17261 cells[i].row = cells[i].row + 1;
17271 if (crow.start.getY() == cells[i].getY()) {
17273 crow.end = cells[i];
17290 cells[0].events.push(ev);
17292 this.calevents.push(ev);
17295 clearEvents: function() {
17297 if(!this.calevents){
17301 Roo.each(this.cells.elements, function(c){
17307 Roo.each(this.calevents, function(e) {
17308 Roo.each(e.els, function(el) {
17309 el.un('mouseenter' ,this.onEventEnter, this);
17310 el.un('mouseleave' ,this.onEventLeave, this);
17315 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17321 renderEvents: function()
17325 this.cells.each(function(c) {
17334 if(c.row != c.events.length){
17335 r = 4 - (4 - (c.row - c.events.length));
17338 c.events = ev.slice(0, r);
17339 c.more = ev.slice(r);
17341 if(c.more.length && c.more.length == 1){
17342 c.events.push(c.more.pop());
17345 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17349 this.cells.each(function(c) {
17351 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17354 for (var e = 0; e < c.events.length; e++){
17355 var ev = c.events[e];
17356 var rows = ev.rows;
17358 for(var i = 0; i < rows.length; i++) {
17360 // how many rows should it span..
17363 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17364 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17366 unselectable : "on",
17369 cls: 'fc-event-inner',
17373 // cls: 'fc-event-time',
17374 // html : cells.length > 1 ? '' : ev.time
17378 cls: 'fc-event-title',
17379 html : String.format('{0}', ev.title)
17386 cls: 'ui-resizable-handle ui-resizable-e',
17387 html : '  '
17394 cfg.cls += ' fc-event-start';
17396 if ((i+1) == rows.length) {
17397 cfg.cls += ' fc-event-end';
17400 var ctr = _this.el.select('.fc-event-container',true).first();
17401 var cg = ctr.createChild(cfg);
17403 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17404 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17406 var r = (c.more.length) ? 1 : 0;
17407 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17408 cg.setWidth(ebox.right - sbox.x -2);
17410 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17411 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17412 cg.on('click', _this.onEventClick, _this, ev);
17423 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17424 style : 'position: absolute',
17425 unselectable : "on",
17428 cls: 'fc-event-inner',
17432 cls: 'fc-event-title',
17440 cls: 'ui-resizable-handle ui-resizable-e',
17441 html : '  '
17447 var ctr = _this.el.select('.fc-event-container',true).first();
17448 var cg = ctr.createChild(cfg);
17450 var sbox = c.select('.fc-day-content',true).first().getBox();
17451 var ebox = c.select('.fc-day-content',true).first().getBox();
17453 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17454 cg.setWidth(ebox.right - sbox.x -2);
17456 cg.on('click', _this.onMoreEventClick, _this, c.more);
17466 onEventEnter: function (e, el,event,d) {
17467 this.fireEvent('evententer', this, el, event);
17470 onEventLeave: function (e, el,event,d) {
17471 this.fireEvent('eventleave', this, el, event);
17474 onEventClick: function (e, el,event,d) {
17475 this.fireEvent('eventclick', this, el, event);
17478 onMonthChange: function () {
17482 onMoreEventClick: function(e, el, more)
17486 this.calpopover.placement = 'right';
17487 this.calpopover.setTitle('More');
17489 this.calpopover.setContent('');
17491 var ctr = this.calpopover.el.select('.popover-content', true).first();
17493 Roo.each(more, function(m){
17495 cls : 'fc-event-hori fc-event-draggable',
17498 var cg = ctr.createChild(cfg);
17500 cg.on('click', _this.onEventClick, _this, m);
17503 this.calpopover.show(el);
17508 onLoad: function ()
17510 this.calevents = [];
17513 if(this.store.getCount() > 0){
17514 this.store.data.each(function(d){
17517 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17518 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17519 time : d.data.start_time,
17520 title : d.data.title,
17521 description : d.data.description,
17522 venue : d.data.venue
17527 this.renderEvents();
17529 if(this.calevents.length && this.loadMask){
17530 this.maskEl.hide();
17534 onBeforeLoad: function()
17536 this.clearEvents();
17538 this.maskEl.show();
17552 * @class Roo.bootstrap.Popover
17553 * @extends Roo.bootstrap.Component
17554 * Bootstrap Popover class
17555 * @cfg {String} html contents of the popover (or false to use children..)
17556 * @cfg {String} title of popover (or false to hide)
17557 * @cfg {String} placement how it is placed
17558 * @cfg {String} trigger click || hover (or false to trigger manually)
17559 * @cfg {String} over what (parent or false to trigger manually.)
17560 * @cfg {Number} delay - delay before showing
17563 * Create a new Popover
17564 * @param {Object} config The config object
17567 Roo.bootstrap.Popover = function(config){
17568 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17574 * After the popover show
17576 * @param {Roo.bootstrap.Popover} this
17581 * After the popover hide
17583 * @param {Roo.bootstrap.Popover} this
17589 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17591 title: 'Fill in a title',
17594 placement : 'right',
17595 trigger : 'hover', // hover
17601 can_build_overlaid : false,
17603 getChildContainer : function()
17605 return this.el.select('.popover-content',true).first();
17608 getAutoCreate : function(){
17611 cls : 'popover roo-dynamic',
17612 style: 'display:block',
17618 cls : 'popover-inner',
17622 cls: 'popover-title',
17626 cls : 'popover-content',
17637 setTitle: function(str)
17640 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17642 setContent: function(str)
17645 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17647 // as it get's added to the bottom of the page.
17648 onRender : function(ct, position)
17650 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17652 var cfg = Roo.apply({}, this.getAutoCreate());
17656 cfg.cls += ' ' + this.cls;
17659 cfg.style = this.style;
17661 //Roo.log("adding to ");
17662 this.el = Roo.get(document.body).createChild(cfg, position);
17663 // Roo.log(this.el);
17668 initEvents : function()
17670 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17671 this.el.enableDisplayMode('block');
17673 if (this.over === false) {
17676 if (this.triggers === false) {
17679 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17680 var triggers = this.trigger ? this.trigger.split(' ') : [];
17681 Roo.each(triggers, function(trigger) {
17683 if (trigger == 'click') {
17684 on_el.on('click', this.toggle, this);
17685 } else if (trigger != 'manual') {
17686 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17687 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17689 on_el.on(eventIn ,this.enter, this);
17690 on_el.on(eventOut, this.leave, this);
17701 toggle : function () {
17702 this.hoverState == 'in' ? this.leave() : this.enter();
17705 enter : function () {
17707 clearTimeout(this.timeout);
17709 this.hoverState = 'in';
17711 if (!this.delay || !this.delay.show) {
17716 this.timeout = setTimeout(function () {
17717 if (_t.hoverState == 'in') {
17720 }, this.delay.show)
17723 leave : function() {
17724 clearTimeout(this.timeout);
17726 this.hoverState = 'out';
17728 if (!this.delay || !this.delay.hide) {
17733 this.timeout = setTimeout(function () {
17734 if (_t.hoverState == 'out') {
17737 }, this.delay.hide)
17740 show : function (on_el)
17743 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17747 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17748 if (this.html !== false) {
17749 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17751 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17752 if (!this.title.length) {
17753 this.el.select('.popover-title',true).hide();
17756 var placement = typeof this.placement == 'function' ?
17757 this.placement.call(this, this.el, on_el) :
17760 var autoToken = /\s?auto?\s?/i;
17761 var autoPlace = autoToken.test(placement);
17763 placement = placement.replace(autoToken, '') || 'top';
17767 //this.el.setXY([0,0]);
17769 this.el.dom.style.display='block';
17770 this.el.addClass(placement);
17772 //this.el.appendTo(on_el);
17774 var p = this.getPosition();
17775 var box = this.el.getBox();
17780 var align = Roo.bootstrap.Popover.alignment[placement];
17783 this.el.alignTo(on_el, align[0],align[1]);
17784 //var arrow = this.el.select('.arrow',true).first();
17785 //arrow.set(align[2],
17787 this.el.addClass('in');
17790 if (this.el.hasClass('fade')) {
17794 this.hoverState = 'in';
17796 this.fireEvent('show', this);
17801 this.el.setXY([0,0]);
17802 this.el.removeClass('in');
17804 this.hoverState = null;
17806 this.fireEvent('hide', this);
17811 Roo.bootstrap.Popover.alignment = {
17812 'left' : ['r-l', [-10,0], 'right'],
17813 'right' : ['l-r', [10,0], 'left'],
17814 'bottom' : ['t-b', [0,10], 'top'],
17815 'top' : [ 'b-t', [0,-10], 'bottom']
17826 * @class Roo.bootstrap.Progress
17827 * @extends Roo.bootstrap.Component
17828 * Bootstrap Progress class
17829 * @cfg {Boolean} striped striped of the progress bar
17830 * @cfg {Boolean} active animated of the progress bar
17834 * Create a new Progress
17835 * @param {Object} config The config object
17838 Roo.bootstrap.Progress = function(config){
17839 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17842 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17847 getAutoCreate : function(){
17855 cfg.cls += ' progress-striped';
17859 cfg.cls += ' active';
17878 * @class Roo.bootstrap.ProgressBar
17879 * @extends Roo.bootstrap.Component
17880 * Bootstrap ProgressBar class
17881 * @cfg {Number} aria_valuenow aria-value now
17882 * @cfg {Number} aria_valuemin aria-value min
17883 * @cfg {Number} aria_valuemax aria-value max
17884 * @cfg {String} label label for the progress bar
17885 * @cfg {String} panel (success | info | warning | danger )
17886 * @cfg {String} role role of the progress bar
17887 * @cfg {String} sr_only text
17891 * Create a new ProgressBar
17892 * @param {Object} config The config object
17895 Roo.bootstrap.ProgressBar = function(config){
17896 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17899 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17903 aria_valuemax : 100,
17909 getAutoCreate : function()
17914 cls: 'progress-bar',
17915 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17927 cfg.role = this.role;
17930 if(this.aria_valuenow){
17931 cfg['aria-valuenow'] = this.aria_valuenow;
17934 if(this.aria_valuemin){
17935 cfg['aria-valuemin'] = this.aria_valuemin;
17938 if(this.aria_valuemax){
17939 cfg['aria-valuemax'] = this.aria_valuemax;
17942 if(this.label && !this.sr_only){
17943 cfg.html = this.label;
17947 cfg.cls += ' progress-bar-' + this.panel;
17953 update : function(aria_valuenow)
17955 this.aria_valuenow = aria_valuenow;
17957 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17972 * @class Roo.bootstrap.TabGroup
17973 * @extends Roo.bootstrap.Column
17974 * Bootstrap Column class
17975 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17976 * @cfg {Boolean} carousel true to make the group behave like a carousel
17977 * @cfg {Boolean} bullets show bullets for the panels
17978 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17979 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17980 * @cfg {Boolean} showarrow (true|false) show arrow default true
17983 * Create a new TabGroup
17984 * @param {Object} config The config object
17987 Roo.bootstrap.TabGroup = function(config){
17988 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17990 this.navId = Roo.id();
17993 Roo.bootstrap.TabGroup.register(this);
17997 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18000 transition : false,
18005 slideOnTouch : false,
18008 getAutoCreate : function()
18010 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18012 cfg.cls += ' tab-content';
18014 if (this.carousel) {
18015 cfg.cls += ' carousel slide';
18018 cls : 'carousel-inner',
18022 if(this.bullets && !Roo.isTouch){
18025 cls : 'carousel-bullets',
18029 if(this.bullets_cls){
18030 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18037 cfg.cn[0].cn.push(bullets);
18040 if(this.showarrow){
18041 cfg.cn[0].cn.push({
18043 class : 'carousel-arrow',
18047 class : 'carousel-prev',
18051 class : 'fa fa-chevron-left'
18057 class : 'carousel-next',
18061 class : 'fa fa-chevron-right'
18074 initEvents: function()
18076 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18077 // this.el.on("touchstart", this.onTouchStart, this);
18080 if(this.autoslide){
18083 this.slideFn = window.setInterval(function() {
18084 _this.showPanelNext();
18088 if(this.showarrow){
18089 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18090 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18096 // onTouchStart : function(e, el, o)
18098 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18102 // this.showPanelNext();
18106 getChildContainer : function()
18108 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18112 * register a Navigation item
18113 * @param {Roo.bootstrap.NavItem} the navitem to add
18115 register : function(item)
18117 this.tabs.push( item);
18118 item.navId = this.navId; // not really needed..
18123 getActivePanel : function()
18126 Roo.each(this.tabs, function(t) {
18136 getPanelByName : function(n)
18139 Roo.each(this.tabs, function(t) {
18140 if (t.tabId == n) {
18148 indexOfPanel : function(p)
18151 Roo.each(this.tabs, function(t,i) {
18152 if (t.tabId == p.tabId) {
18161 * show a specific panel
18162 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18163 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18165 showPanel : function (pan)
18167 if(this.transition || typeof(pan) == 'undefined'){
18168 Roo.log("waiting for the transitionend");
18172 if (typeof(pan) == 'number') {
18173 pan = this.tabs[pan];
18176 if (typeof(pan) == 'string') {
18177 pan = this.getPanelByName(pan);
18180 var cur = this.getActivePanel();
18183 Roo.log('pan or acitve pan is undefined');
18187 if (pan.tabId == this.getActivePanel().tabId) {
18191 if (false === cur.fireEvent('beforedeactivate')) {
18195 if(this.bullets > 0 && !Roo.isTouch){
18196 this.setActiveBullet(this.indexOfPanel(pan));
18199 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18201 this.transition = true;
18202 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18203 var lr = dir == 'next' ? 'left' : 'right';
18204 pan.el.addClass(dir); // or prev
18205 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18206 cur.el.addClass(lr); // or right
18207 pan.el.addClass(lr);
18210 cur.el.on('transitionend', function() {
18211 Roo.log("trans end?");
18213 pan.el.removeClass([lr,dir]);
18214 pan.setActive(true);
18216 cur.el.removeClass([lr]);
18217 cur.setActive(false);
18219 _this.transition = false;
18221 }, this, { single: true } );
18226 cur.setActive(false);
18227 pan.setActive(true);
18232 showPanelNext : function()
18234 var i = this.indexOfPanel(this.getActivePanel());
18236 if (i >= this.tabs.length - 1 && !this.autoslide) {
18240 if (i >= this.tabs.length - 1 && this.autoslide) {
18244 this.showPanel(this.tabs[i+1]);
18247 showPanelPrev : function()
18249 var i = this.indexOfPanel(this.getActivePanel());
18251 if (i < 1 && !this.autoslide) {
18255 if (i < 1 && this.autoslide) {
18256 i = this.tabs.length;
18259 this.showPanel(this.tabs[i-1]);
18263 addBullet: function()
18265 if(!this.bullets || Roo.isTouch){
18268 var ctr = this.el.select('.carousel-bullets',true).first();
18269 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18270 var bullet = ctr.createChild({
18271 cls : 'bullet bullet-' + i
18272 },ctr.dom.lastChild);
18277 bullet.on('click', (function(e, el, o, ii, t){
18279 e.preventDefault();
18281 this.showPanel(ii);
18283 if(this.autoslide && this.slideFn){
18284 clearInterval(this.slideFn);
18285 this.slideFn = window.setInterval(function() {
18286 _this.showPanelNext();
18290 }).createDelegate(this, [i, bullet], true));
18295 setActiveBullet : function(i)
18301 Roo.each(this.el.select('.bullet', true).elements, function(el){
18302 el.removeClass('selected');
18305 var bullet = this.el.select('.bullet-' + i, true).first();
18311 bullet.addClass('selected');
18322 Roo.apply(Roo.bootstrap.TabGroup, {
18326 * register a Navigation Group
18327 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18329 register : function(navgrp)
18331 this.groups[navgrp.navId] = navgrp;
18335 * fetch a Navigation Group based on the navigation ID
18336 * if one does not exist , it will get created.
18337 * @param {string} the navgroup to add
18338 * @returns {Roo.bootstrap.NavGroup} the navgroup
18340 get: function(navId) {
18341 if (typeof(this.groups[navId]) == 'undefined') {
18342 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18344 return this.groups[navId] ;
18359 * @class Roo.bootstrap.TabPanel
18360 * @extends Roo.bootstrap.Component
18361 * Bootstrap TabPanel class
18362 * @cfg {Boolean} active panel active
18363 * @cfg {String} html panel content
18364 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18365 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18366 * @cfg {String} href click to link..
18370 * Create a new TabPanel
18371 * @param {Object} config The config object
18374 Roo.bootstrap.TabPanel = function(config){
18375 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18379 * Fires when the active status changes
18380 * @param {Roo.bootstrap.TabPanel} this
18381 * @param {Boolean} state the new state
18386 * @event beforedeactivate
18387 * Fires before a tab is de-activated - can be used to do validation on a form.
18388 * @param {Roo.bootstrap.TabPanel} this
18389 * @return {Boolean} false if there is an error
18392 'beforedeactivate': true
18395 this.tabId = this.tabId || Roo.id();
18399 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18407 getAutoCreate : function(){
18410 // item is needed for carousel - not sure if it has any effect otherwise
18411 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18412 html: this.html || ''
18416 cfg.cls += ' active';
18420 cfg.tabId = this.tabId;
18427 initEvents: function()
18429 var p = this.parent();
18431 this.navId = this.navId || p.navId;
18433 if (typeof(this.navId) != 'undefined') {
18434 // not really needed.. but just in case.. parent should be a NavGroup.
18435 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18439 var i = tg.tabs.length - 1;
18441 if(this.active && tg.bullets > 0 && i < tg.bullets){
18442 tg.setActiveBullet(i);
18446 this.el.on('click', this.onClick, this);
18449 this.el.on("touchstart", this.onTouchStart, this);
18450 this.el.on("touchmove", this.onTouchMove, this);
18451 this.el.on("touchend", this.onTouchEnd, this);
18456 onRender : function(ct, position)
18458 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18461 setActive : function(state)
18463 Roo.log("panel - set active " + this.tabId + "=" + state);
18465 this.active = state;
18467 this.el.removeClass('active');
18469 } else if (!this.el.hasClass('active')) {
18470 this.el.addClass('active');
18473 this.fireEvent('changed', this, state);
18476 onClick : function(e)
18478 e.preventDefault();
18480 if(!this.href.length){
18484 window.location.href = this.href;
18493 onTouchStart : function(e)
18495 this.swiping = false;
18497 this.startX = e.browserEvent.touches[0].clientX;
18498 this.startY = e.browserEvent.touches[0].clientY;
18501 onTouchMove : function(e)
18503 this.swiping = true;
18505 this.endX = e.browserEvent.touches[0].clientX;
18506 this.endY = e.browserEvent.touches[0].clientY;
18509 onTouchEnd : function(e)
18516 var tabGroup = this.parent();
18518 if(this.endX > this.startX){ // swiping right
18519 tabGroup.showPanelPrev();
18523 if(this.startX > this.endX){ // swiping left
18524 tabGroup.showPanelNext();
18543 * @class Roo.bootstrap.DateField
18544 * @extends Roo.bootstrap.Input
18545 * Bootstrap DateField class
18546 * @cfg {Number} weekStart default 0
18547 * @cfg {String} viewMode default empty, (months|years)
18548 * @cfg {String} minViewMode default empty, (months|years)
18549 * @cfg {Number} startDate default -Infinity
18550 * @cfg {Number} endDate default Infinity
18551 * @cfg {Boolean} todayHighlight default false
18552 * @cfg {Boolean} todayBtn default false
18553 * @cfg {Boolean} calendarWeeks default false
18554 * @cfg {Object} daysOfWeekDisabled default empty
18555 * @cfg {Boolean} singleMode default false (true | false)
18557 * @cfg {Boolean} keyboardNavigation default true
18558 * @cfg {String} language default en
18561 * Create a new DateField
18562 * @param {Object} config The config object
18565 Roo.bootstrap.DateField = function(config){
18566 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18570 * Fires when this field show.
18571 * @param {Roo.bootstrap.DateField} this
18572 * @param {Mixed} date The date value
18577 * Fires when this field hide.
18578 * @param {Roo.bootstrap.DateField} this
18579 * @param {Mixed} date The date value
18584 * Fires when select a date.
18585 * @param {Roo.bootstrap.DateField} this
18586 * @param {Mixed} date The date value
18590 * @event beforeselect
18591 * Fires when before select a date.
18592 * @param {Roo.bootstrap.DateField} this
18593 * @param {Mixed} date The date value
18595 beforeselect : true
18599 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18602 * @cfg {String} format
18603 * The default date format string which can be overriden for localization support. The format must be
18604 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18608 * @cfg {String} altFormats
18609 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18610 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18612 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18620 todayHighlight : false,
18626 keyboardNavigation: true,
18628 calendarWeeks: false,
18630 startDate: -Infinity,
18634 daysOfWeekDisabled: [],
18638 singleMode : false,
18640 UTCDate: function()
18642 return new Date(Date.UTC.apply(Date, arguments));
18645 UTCToday: function()
18647 var today = new Date();
18648 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18651 getDate: function() {
18652 var d = this.getUTCDate();
18653 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18656 getUTCDate: function() {
18660 setDate: function(d) {
18661 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18664 setUTCDate: function(d) {
18666 this.setValue(this.formatDate(this.date));
18669 onRender: function(ct, position)
18672 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18674 this.language = this.language || 'en';
18675 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18676 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18678 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18679 this.format = this.format || 'm/d/y';
18680 this.isInline = false;
18681 this.isInput = true;
18682 this.component = this.el.select('.add-on', true).first() || false;
18683 this.component = (this.component && this.component.length === 0) ? false : this.component;
18684 this.hasInput = this.component && this.inputEl().length;
18686 if (typeof(this.minViewMode === 'string')) {
18687 switch (this.minViewMode) {
18689 this.minViewMode = 1;
18692 this.minViewMode = 2;
18695 this.minViewMode = 0;
18700 if (typeof(this.viewMode === 'string')) {
18701 switch (this.viewMode) {
18714 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18716 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18718 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18720 this.picker().on('mousedown', this.onMousedown, this);
18721 this.picker().on('click', this.onClick, this);
18723 this.picker().addClass('datepicker-dropdown');
18725 this.startViewMode = this.viewMode;
18727 if(this.singleMode){
18728 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18729 v.setVisibilityMode(Roo.Element.DISPLAY);
18733 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18734 v.setStyle('width', '189px');
18738 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18739 if(!this.calendarWeeks){
18744 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18745 v.attr('colspan', function(i, val){
18746 return parseInt(val) + 1;
18751 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18753 this.setStartDate(this.startDate);
18754 this.setEndDate(this.endDate);
18756 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18763 if(this.isInline) {
18768 picker : function()
18770 return this.pickerEl;
18771 // return this.el.select('.datepicker', true).first();
18774 fillDow: function()
18776 var dowCnt = this.weekStart;
18785 if(this.calendarWeeks){
18793 while (dowCnt < this.weekStart + 7) {
18797 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18801 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18804 fillMonths: function()
18807 var months = this.picker().select('>.datepicker-months td', true).first();
18809 months.dom.innerHTML = '';
18815 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18818 months.createChild(month);
18825 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;
18827 if (this.date < this.startDate) {
18828 this.viewDate = new Date(this.startDate);
18829 } else if (this.date > this.endDate) {
18830 this.viewDate = new Date(this.endDate);
18832 this.viewDate = new Date(this.date);
18840 var d = new Date(this.viewDate),
18841 year = d.getUTCFullYear(),
18842 month = d.getUTCMonth(),
18843 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18844 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18845 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18846 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18847 currentDate = this.date && this.date.valueOf(),
18848 today = this.UTCToday();
18850 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18852 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18854 // this.picker.select('>tfoot th.today').
18855 // .text(dates[this.language].today)
18856 // .toggle(this.todayBtn !== false);
18858 this.updateNavArrows();
18861 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18863 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18865 prevMonth.setUTCDate(day);
18867 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18869 var nextMonth = new Date(prevMonth);
18871 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18873 nextMonth = nextMonth.valueOf();
18875 var fillMonths = false;
18877 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18879 while(prevMonth.valueOf() <= nextMonth) {
18882 if (prevMonth.getUTCDay() === this.weekStart) {
18884 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18892 if(this.calendarWeeks){
18893 // ISO 8601: First week contains first thursday.
18894 // ISO also states week starts on Monday, but we can be more abstract here.
18896 // Start of current week: based on weekstart/current date
18897 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18898 // Thursday of this week
18899 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18900 // First Thursday of year, year from thursday
18901 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18902 // Calendar week: ms between thursdays, div ms per day, div 7 days
18903 calWeek = (th - yth) / 864e5 / 7 + 1;
18905 fillMonths.cn.push({
18913 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18915 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18918 if (this.todayHighlight &&
18919 prevMonth.getUTCFullYear() == today.getFullYear() &&
18920 prevMonth.getUTCMonth() == today.getMonth() &&
18921 prevMonth.getUTCDate() == today.getDate()) {
18922 clsName += ' today';
18925 if (currentDate && prevMonth.valueOf() === currentDate) {
18926 clsName += ' active';
18929 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18930 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18931 clsName += ' disabled';
18934 fillMonths.cn.push({
18936 cls: 'day ' + clsName,
18937 html: prevMonth.getDate()
18940 prevMonth.setDate(prevMonth.getDate()+1);
18943 var currentYear = this.date && this.date.getUTCFullYear();
18944 var currentMonth = this.date && this.date.getUTCMonth();
18946 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18948 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18949 v.removeClass('active');
18951 if(currentYear === year && k === currentMonth){
18952 v.addClass('active');
18955 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18956 v.addClass('disabled');
18962 year = parseInt(year/10, 10) * 10;
18964 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18966 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18969 for (var i = -1; i < 11; i++) {
18970 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18972 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18980 showMode: function(dir)
18983 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18986 Roo.each(this.picker().select('>div',true).elements, function(v){
18987 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18990 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18995 if(this.isInline) {
18999 this.picker().removeClass(['bottom', 'top']);
19001 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19003 * place to the top of element!
19007 this.picker().addClass('top');
19008 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19013 this.picker().addClass('bottom');
19015 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19018 parseDate : function(value)
19020 if(!value || value instanceof Date){
19023 var v = Date.parseDate(value, this.format);
19024 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19025 v = Date.parseDate(value, 'Y-m-d');
19027 if(!v && this.altFormats){
19028 if(!this.altFormatsArray){
19029 this.altFormatsArray = this.altFormats.split("|");
19031 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19032 v = Date.parseDate(value, this.altFormatsArray[i]);
19038 formatDate : function(date, fmt)
19040 return (!date || !(date instanceof Date)) ?
19041 date : date.dateFormat(fmt || this.format);
19044 onFocus : function()
19046 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19050 onBlur : function()
19052 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19054 var d = this.inputEl().getValue();
19061 showPopup : function()
19063 this.picker().show();
19067 this.fireEvent('showpopup', this, this.date);
19070 hidePopup : function()
19072 if(this.isInline) {
19075 this.picker().hide();
19076 this.viewMode = this.startViewMode;
19079 this.fireEvent('hidepopup', this, this.date);
19083 onMousedown: function(e)
19085 e.stopPropagation();
19086 e.preventDefault();
19091 Roo.bootstrap.DateField.superclass.keyup.call(this);
19095 setValue: function(v)
19097 if(this.fireEvent('beforeselect', this, v) !== false){
19098 var d = new Date(this.parseDate(v) ).clearTime();
19100 if(isNaN(d.getTime())){
19101 this.date = this.viewDate = '';
19102 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19106 v = this.formatDate(d);
19108 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19110 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19114 this.fireEvent('select', this, this.date);
19118 getValue: function()
19120 return this.formatDate(this.date);
19123 fireKey: function(e)
19125 if (!this.picker().isVisible()){
19126 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19132 var dateChanged = false,
19134 newDate, newViewDate;
19139 e.preventDefault();
19143 if (!this.keyboardNavigation) {
19146 dir = e.keyCode == 37 ? -1 : 1;
19149 newDate = this.moveYear(this.date, dir);
19150 newViewDate = this.moveYear(this.viewDate, dir);
19151 } else if (e.shiftKey){
19152 newDate = this.moveMonth(this.date, dir);
19153 newViewDate = this.moveMonth(this.viewDate, dir);
19155 newDate = new Date(this.date);
19156 newDate.setUTCDate(this.date.getUTCDate() + dir);
19157 newViewDate = new Date(this.viewDate);
19158 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19160 if (this.dateWithinRange(newDate)){
19161 this.date = newDate;
19162 this.viewDate = newViewDate;
19163 this.setValue(this.formatDate(this.date));
19165 e.preventDefault();
19166 dateChanged = true;
19171 if (!this.keyboardNavigation) {
19174 dir = e.keyCode == 38 ? -1 : 1;
19176 newDate = this.moveYear(this.date, dir);
19177 newViewDate = this.moveYear(this.viewDate, dir);
19178 } else if (e.shiftKey){
19179 newDate = this.moveMonth(this.date, dir);
19180 newViewDate = this.moveMonth(this.viewDate, dir);
19182 newDate = new Date(this.date);
19183 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19184 newViewDate = new Date(this.viewDate);
19185 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19187 if (this.dateWithinRange(newDate)){
19188 this.date = newDate;
19189 this.viewDate = newViewDate;
19190 this.setValue(this.formatDate(this.date));
19192 e.preventDefault();
19193 dateChanged = true;
19197 this.setValue(this.formatDate(this.date));
19199 e.preventDefault();
19202 this.setValue(this.formatDate(this.date));
19216 onClick: function(e)
19218 e.stopPropagation();
19219 e.preventDefault();
19221 var target = e.getTarget();
19223 if(target.nodeName.toLowerCase() === 'i'){
19224 target = Roo.get(target).dom.parentNode;
19227 var nodeName = target.nodeName;
19228 var className = target.className;
19229 var html = target.innerHTML;
19230 //Roo.log(nodeName);
19232 switch(nodeName.toLowerCase()) {
19234 switch(className) {
19240 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19241 switch(this.viewMode){
19243 this.viewDate = this.moveMonth(this.viewDate, dir);
19247 this.viewDate = this.moveYear(this.viewDate, dir);
19253 var date = new Date();
19254 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19256 this.setValue(this.formatDate(this.date));
19263 if (className.indexOf('disabled') < 0) {
19264 this.viewDate.setUTCDate(1);
19265 if (className.indexOf('month') > -1) {
19266 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19268 var year = parseInt(html, 10) || 0;
19269 this.viewDate.setUTCFullYear(year);
19273 if(this.singleMode){
19274 this.setValue(this.formatDate(this.viewDate));
19285 //Roo.log(className);
19286 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19287 var day = parseInt(html, 10) || 1;
19288 var year = this.viewDate.getUTCFullYear(),
19289 month = this.viewDate.getUTCMonth();
19291 if (className.indexOf('old') > -1) {
19298 } else if (className.indexOf('new') > -1) {
19306 //Roo.log([year,month,day]);
19307 this.date = this.UTCDate(year, month, day,0,0,0,0);
19308 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19310 //Roo.log(this.formatDate(this.date));
19311 this.setValue(this.formatDate(this.date));
19318 setStartDate: function(startDate)
19320 this.startDate = startDate || -Infinity;
19321 if (this.startDate !== -Infinity) {
19322 this.startDate = this.parseDate(this.startDate);
19325 this.updateNavArrows();
19328 setEndDate: function(endDate)
19330 this.endDate = endDate || Infinity;
19331 if (this.endDate !== Infinity) {
19332 this.endDate = this.parseDate(this.endDate);
19335 this.updateNavArrows();
19338 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19340 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19341 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19342 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19344 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19345 return parseInt(d, 10);
19348 this.updateNavArrows();
19351 updateNavArrows: function()
19353 if(this.singleMode){
19357 var d = new Date(this.viewDate),
19358 year = d.getUTCFullYear(),
19359 month = d.getUTCMonth();
19361 Roo.each(this.picker().select('.prev', true).elements, function(v){
19363 switch (this.viewMode) {
19366 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19372 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19379 Roo.each(this.picker().select('.next', true).elements, function(v){
19381 switch (this.viewMode) {
19384 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19390 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19398 moveMonth: function(date, dir)
19403 var new_date = new Date(date.valueOf()),
19404 day = new_date.getUTCDate(),
19405 month = new_date.getUTCMonth(),
19406 mag = Math.abs(dir),
19408 dir = dir > 0 ? 1 : -1;
19411 // If going back one month, make sure month is not current month
19412 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19414 return new_date.getUTCMonth() == month;
19416 // If going forward one month, make sure month is as expected
19417 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19419 return new_date.getUTCMonth() != new_month;
19421 new_month = month + dir;
19422 new_date.setUTCMonth(new_month);
19423 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19424 if (new_month < 0 || new_month > 11) {
19425 new_month = (new_month + 12) % 12;
19428 // For magnitudes >1, move one month at a time...
19429 for (var i=0; i<mag; i++) {
19430 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19431 new_date = this.moveMonth(new_date, dir);
19433 // ...then reset the day, keeping it in the new month
19434 new_month = new_date.getUTCMonth();
19435 new_date.setUTCDate(day);
19437 return new_month != new_date.getUTCMonth();
19440 // Common date-resetting loop -- if date is beyond end of month, make it
19443 new_date.setUTCDate(--day);
19444 new_date.setUTCMonth(new_month);
19449 moveYear: function(date, dir)
19451 return this.moveMonth(date, dir*12);
19454 dateWithinRange: function(date)
19456 return date >= this.startDate && date <= this.endDate;
19462 this.picker().remove();
19465 validateValue : function(value)
19467 if(this.getVisibilityEl().hasClass('hidden')){
19471 if(value.length < 1) {
19472 if(this.allowBlank){
19478 if(value.length < this.minLength){
19481 if(value.length > this.maxLength){
19485 var vt = Roo.form.VTypes;
19486 if(!vt[this.vtype](value, this)){
19490 if(typeof this.validator == "function"){
19491 var msg = this.validator(value);
19497 if(this.regex && !this.regex.test(value)){
19501 if(typeof(this.parseDate(value)) == 'undefined'){
19505 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19509 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19519 this.date = this.viewDate = '';
19521 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19526 Roo.apply(Roo.bootstrap.DateField, {
19537 html: '<i class="fa fa-arrow-left"/>'
19547 html: '<i class="fa fa-arrow-right"/>'
19589 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19590 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19591 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19592 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19593 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19606 navFnc: 'FullYear',
19611 navFnc: 'FullYear',
19616 Roo.apply(Roo.bootstrap.DateField, {
19620 cls: 'datepicker dropdown-menu roo-dynamic',
19624 cls: 'datepicker-days',
19628 cls: 'table-condensed',
19630 Roo.bootstrap.DateField.head,
19634 Roo.bootstrap.DateField.footer
19641 cls: 'datepicker-months',
19645 cls: 'table-condensed',
19647 Roo.bootstrap.DateField.head,
19648 Roo.bootstrap.DateField.content,
19649 Roo.bootstrap.DateField.footer
19656 cls: 'datepicker-years',
19660 cls: 'table-condensed',
19662 Roo.bootstrap.DateField.head,
19663 Roo.bootstrap.DateField.content,
19664 Roo.bootstrap.DateField.footer
19683 * @class Roo.bootstrap.TimeField
19684 * @extends Roo.bootstrap.Input
19685 * Bootstrap DateField class
19689 * Create a new TimeField
19690 * @param {Object} config The config object
19693 Roo.bootstrap.TimeField = function(config){
19694 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19698 * Fires when this field show.
19699 * @param {Roo.bootstrap.DateField} thisthis
19700 * @param {Mixed} date The date value
19705 * Fires when this field hide.
19706 * @param {Roo.bootstrap.DateField} this
19707 * @param {Mixed} date The date value
19712 * Fires when select a date.
19713 * @param {Roo.bootstrap.DateField} this
19714 * @param {Mixed} date The date value
19720 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19723 * @cfg {String} format
19724 * The default time format string which can be overriden for localization support. The format must be
19725 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19729 onRender: function(ct, position)
19732 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19734 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19736 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19738 this.pop = this.picker().select('>.datepicker-time',true).first();
19739 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19741 this.picker().on('mousedown', this.onMousedown, this);
19742 this.picker().on('click', this.onClick, this);
19744 this.picker().addClass('datepicker-dropdown');
19749 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19750 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19751 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19752 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19753 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19754 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19758 fireKey: function(e){
19759 if (!this.picker().isVisible()){
19760 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19766 e.preventDefault();
19774 this.onTogglePeriod();
19777 this.onIncrementMinutes();
19780 this.onDecrementMinutes();
19789 onClick: function(e) {
19790 e.stopPropagation();
19791 e.preventDefault();
19794 picker : function()
19796 return this.el.select('.datepicker', true).first();
19799 fillTime: function()
19801 var time = this.pop.select('tbody', true).first();
19803 time.dom.innerHTML = '';
19818 cls: 'hours-up glyphicon glyphicon-chevron-up'
19838 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19859 cls: 'timepicker-hour',
19874 cls: 'timepicker-minute',
19889 cls: 'btn btn-primary period',
19911 cls: 'hours-down glyphicon glyphicon-chevron-down'
19931 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19949 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19956 var hours = this.time.getHours();
19957 var minutes = this.time.getMinutes();
19970 hours = hours - 12;
19974 hours = '0' + hours;
19978 minutes = '0' + minutes;
19981 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19982 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19983 this.pop.select('button', true).first().dom.innerHTML = period;
19989 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19991 var cls = ['bottom'];
19993 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20000 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20005 this.picker().addClass(cls.join('-'));
20009 Roo.each(cls, function(c){
20011 _this.picker().setTop(_this.inputEl().getHeight());
20015 _this.picker().setTop(0 - _this.picker().getHeight());
20020 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20024 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20031 onFocus : function()
20033 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20037 onBlur : function()
20039 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20045 this.picker().show();
20050 this.fireEvent('show', this, this.date);
20055 this.picker().hide();
20058 this.fireEvent('hide', this, this.date);
20061 setTime : function()
20064 this.setValue(this.time.format(this.format));
20066 this.fireEvent('select', this, this.date);
20071 onMousedown: function(e){
20072 e.stopPropagation();
20073 e.preventDefault();
20076 onIncrementHours: function()
20078 Roo.log('onIncrementHours');
20079 this.time = this.time.add(Date.HOUR, 1);
20084 onDecrementHours: function()
20086 Roo.log('onDecrementHours');
20087 this.time = this.time.add(Date.HOUR, -1);
20091 onIncrementMinutes: function()
20093 Roo.log('onIncrementMinutes');
20094 this.time = this.time.add(Date.MINUTE, 1);
20098 onDecrementMinutes: function()
20100 Roo.log('onDecrementMinutes');
20101 this.time = this.time.add(Date.MINUTE, -1);
20105 onTogglePeriod: function()
20107 Roo.log('onTogglePeriod');
20108 this.time = this.time.add(Date.HOUR, 12);
20115 Roo.apply(Roo.bootstrap.TimeField, {
20145 cls: 'btn btn-info ok',
20157 Roo.apply(Roo.bootstrap.TimeField, {
20161 cls: 'datepicker dropdown-menu',
20165 cls: 'datepicker-time',
20169 cls: 'table-condensed',
20171 Roo.bootstrap.TimeField.content,
20172 Roo.bootstrap.TimeField.footer
20191 * @class Roo.bootstrap.MonthField
20192 * @extends Roo.bootstrap.Input
20193 * Bootstrap MonthField class
20195 * @cfg {String} language default en
20198 * Create a new MonthField
20199 * @param {Object} config The config object
20202 Roo.bootstrap.MonthField = function(config){
20203 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20208 * Fires when this field show.
20209 * @param {Roo.bootstrap.MonthField} this
20210 * @param {Mixed} date The date value
20215 * Fires when this field hide.
20216 * @param {Roo.bootstrap.MonthField} this
20217 * @param {Mixed} date The date value
20222 * Fires when select a date.
20223 * @param {Roo.bootstrap.MonthField} this
20224 * @param {String} oldvalue The old value
20225 * @param {String} newvalue The new value
20231 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20233 onRender: function(ct, position)
20236 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20238 this.language = this.language || 'en';
20239 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20240 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20242 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20243 this.isInline = false;
20244 this.isInput = true;
20245 this.component = this.el.select('.add-on', true).first() || false;
20246 this.component = (this.component && this.component.length === 0) ? false : this.component;
20247 this.hasInput = this.component && this.inputEL().length;
20249 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20251 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20253 this.picker().on('mousedown', this.onMousedown, this);
20254 this.picker().on('click', this.onClick, this);
20256 this.picker().addClass('datepicker-dropdown');
20258 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20259 v.setStyle('width', '189px');
20266 if(this.isInline) {
20272 setValue: function(v, suppressEvent)
20274 var o = this.getValue();
20276 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20280 if(suppressEvent !== true){
20281 this.fireEvent('select', this, o, v);
20286 getValue: function()
20291 onClick: function(e)
20293 e.stopPropagation();
20294 e.preventDefault();
20296 var target = e.getTarget();
20298 if(target.nodeName.toLowerCase() === 'i'){
20299 target = Roo.get(target).dom.parentNode;
20302 var nodeName = target.nodeName;
20303 var className = target.className;
20304 var html = target.innerHTML;
20306 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20310 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20312 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20318 picker : function()
20320 return this.pickerEl;
20323 fillMonths: function()
20326 var months = this.picker().select('>.datepicker-months td', true).first();
20328 months.dom.innerHTML = '';
20334 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20337 months.createChild(month);
20346 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20347 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20350 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20351 e.removeClass('active');
20353 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20354 e.addClass('active');
20361 if(this.isInline) {
20365 this.picker().removeClass(['bottom', 'top']);
20367 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20369 * place to the top of element!
20373 this.picker().addClass('top');
20374 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20379 this.picker().addClass('bottom');
20381 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20384 onFocus : function()
20386 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20390 onBlur : function()
20392 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20394 var d = this.inputEl().getValue();
20403 this.picker().show();
20404 this.picker().select('>.datepicker-months', true).first().show();
20408 this.fireEvent('show', this, this.date);
20413 if(this.isInline) {
20416 this.picker().hide();
20417 this.fireEvent('hide', this, this.date);
20421 onMousedown: function(e)
20423 e.stopPropagation();
20424 e.preventDefault();
20429 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20433 fireKey: function(e)
20435 if (!this.picker().isVisible()){
20436 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20447 e.preventDefault();
20451 dir = e.keyCode == 37 ? -1 : 1;
20453 this.vIndex = this.vIndex + dir;
20455 if(this.vIndex < 0){
20459 if(this.vIndex > 11){
20463 if(isNaN(this.vIndex)){
20467 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20473 dir = e.keyCode == 38 ? -1 : 1;
20475 this.vIndex = this.vIndex + dir * 4;
20477 if(this.vIndex < 0){
20481 if(this.vIndex > 11){
20485 if(isNaN(this.vIndex)){
20489 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20494 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20495 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20499 e.preventDefault();
20502 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20503 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20519 this.picker().remove();
20524 Roo.apply(Roo.bootstrap.MonthField, {
20543 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20544 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20549 Roo.apply(Roo.bootstrap.MonthField, {
20553 cls: 'datepicker dropdown-menu roo-dynamic',
20557 cls: 'datepicker-months',
20561 cls: 'table-condensed',
20563 Roo.bootstrap.DateField.content
20583 * @class Roo.bootstrap.CheckBox
20584 * @extends Roo.bootstrap.Input
20585 * Bootstrap CheckBox class
20587 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20588 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20589 * @cfg {String} boxLabel The text that appears beside the checkbox
20590 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20591 * @cfg {Boolean} checked initnal the element
20592 * @cfg {Boolean} inline inline the element (default false)
20593 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20594 * @cfg {String} tooltip label tooltip
20597 * Create a new CheckBox
20598 * @param {Object} config The config object
20601 Roo.bootstrap.CheckBox = function(config){
20602 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20607 * Fires when the element is checked or unchecked.
20608 * @param {Roo.bootstrap.CheckBox} this This input
20609 * @param {Boolean} checked The new checked value
20614 * Fires when the element is click.
20615 * @param {Roo.bootstrap.CheckBox} this This input
20622 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20624 inputType: 'checkbox',
20633 getAutoCreate : function()
20635 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20641 cfg.cls = 'form-group ' + this.inputType; //input-group
20644 cfg.cls += ' ' + this.inputType + '-inline';
20650 type : this.inputType,
20651 value : this.inputValue,
20652 cls : 'roo-' + this.inputType, //'form-box',
20653 placeholder : this.placeholder || ''
20657 if(this.inputType != 'radio'){
20661 cls : 'roo-hidden-value',
20662 value : this.checked ? this.inputValue : this.valueOff
20667 if (this.weight) { // Validity check?
20668 cfg.cls += " " + this.inputType + "-" + this.weight;
20671 if (this.disabled) {
20672 input.disabled=true;
20676 input.checked = this.checked;
20681 input.name = this.name;
20683 if(this.inputType != 'radio'){
20684 hidden.name = this.name;
20685 input.name = '_hidden_' + this.name;
20690 input.cls += ' input-' + this.size;
20695 ['xs','sm','md','lg'].map(function(size){
20696 if (settings[size]) {
20697 cfg.cls += ' col-' + size + '-' + settings[size];
20701 var inputblock = input;
20703 if (this.before || this.after) {
20706 cls : 'input-group',
20711 inputblock.cn.push({
20713 cls : 'input-group-addon',
20718 inputblock.cn.push(input);
20720 if(this.inputType != 'radio'){
20721 inputblock.cn.push(hidden);
20725 inputblock.cn.push({
20727 cls : 'input-group-addon',
20734 if (align ==='left' && this.fieldLabel.length) {
20735 // Roo.log("left and has label");
20740 cls : 'control-label',
20741 html : this.fieldLabel
20751 if(this.labelWidth > 12){
20752 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20755 if(this.labelWidth < 13 && this.labelmd == 0){
20756 this.labelmd = this.labelWidth;
20759 if(this.labellg > 0){
20760 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20761 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20764 if(this.labelmd > 0){
20765 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20766 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20769 if(this.labelsm > 0){
20770 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20771 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20774 if(this.labelxs > 0){
20775 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20776 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20779 } else if ( this.fieldLabel.length) {
20780 // Roo.log(" label");
20784 tag: this.boxLabel ? 'span' : 'label',
20786 cls: 'control-label box-input-label',
20787 //cls : 'input-group-addon',
20788 html : this.fieldLabel
20797 // Roo.log(" no label && no align");
20798 cfg.cn = [ inputblock ] ;
20804 var boxLabelCfg = {
20806 //'for': id, // box label is handled by onclick - so no for...
20808 html: this.boxLabel
20812 boxLabelCfg.tooltip = this.tooltip;
20815 cfg.cn.push(boxLabelCfg);
20818 if(this.inputType != 'radio'){
20819 cfg.cn.push(hidden);
20827 * return the real input element.
20829 inputEl: function ()
20831 return this.el.select('input.roo-' + this.inputType,true).first();
20833 hiddenEl: function ()
20835 return this.el.select('input.roo-hidden-value',true).first();
20838 labelEl: function()
20840 return this.el.select('label.control-label',true).first();
20842 /* depricated... */
20846 return this.labelEl();
20849 boxLabelEl: function()
20851 return this.el.select('label.box-label',true).first();
20854 initEvents : function()
20856 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20858 this.inputEl().on('click', this.onClick, this);
20860 if (this.boxLabel) {
20861 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20864 this.startValue = this.getValue();
20867 Roo.bootstrap.CheckBox.register(this);
20871 onClick : function(e)
20873 if(this.fireEvent('click', this, e) !== false){
20874 this.setChecked(!this.checked);
20879 setChecked : function(state,suppressEvent)
20881 this.startValue = this.getValue();
20883 if(this.inputType == 'radio'){
20885 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20886 e.dom.checked = false;
20889 this.inputEl().dom.checked = true;
20891 this.inputEl().dom.value = this.inputValue;
20893 if(suppressEvent !== true){
20894 this.fireEvent('check', this, true);
20902 this.checked = state;
20904 this.inputEl().dom.checked = state;
20907 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20909 if(suppressEvent !== true){
20910 this.fireEvent('check', this, state);
20916 getValue : function()
20918 if(this.inputType == 'radio'){
20919 return this.getGroupValue();
20922 return this.hiddenEl().dom.value;
20926 getGroupValue : function()
20928 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20932 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20935 setValue : function(v,suppressEvent)
20937 if(this.inputType == 'radio'){
20938 this.setGroupValue(v, suppressEvent);
20942 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20947 setGroupValue : function(v, suppressEvent)
20949 this.startValue = this.getValue();
20951 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20952 e.dom.checked = false;
20954 if(e.dom.value == v){
20955 e.dom.checked = true;
20959 if(suppressEvent !== true){
20960 this.fireEvent('check', this, true);
20968 validate : function()
20970 if(this.getVisibilityEl().hasClass('hidden')){
20976 (this.inputType == 'radio' && this.validateRadio()) ||
20977 (this.inputType == 'checkbox' && this.validateCheckbox())
20983 this.markInvalid();
20987 validateRadio : function()
20989 if(this.getVisibilityEl().hasClass('hidden')){
20993 if(this.allowBlank){
20999 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21000 if(!e.dom.checked){
21012 validateCheckbox : function()
21015 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21016 //return (this.getValue() == this.inputValue) ? true : false;
21019 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21027 for(var i in group){
21028 if(group[i].el.isVisible(true)){
21036 for(var i in group){
21041 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21048 * Mark this field as valid
21050 markValid : function()
21054 this.fireEvent('valid', this);
21056 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21059 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21066 if(this.inputType == 'radio'){
21067 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21068 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21069 e.findParent('.form-group', false, true).addClass(_this.validClass);
21076 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21077 this.el.findParent('.form-group', false, true).addClass(this.validClass);
21081 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21087 for(var i in group){
21088 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21089 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21094 * Mark this field as invalid
21095 * @param {String} msg The validation message
21097 markInvalid : function(msg)
21099 if(this.allowBlank){
21105 this.fireEvent('invalid', this, msg);
21107 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21110 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21114 label.markInvalid();
21117 if(this.inputType == 'radio'){
21118 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21119 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21120 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21127 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21128 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21132 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21138 for(var i in group){
21139 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21140 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21145 clearInvalid : function()
21147 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21149 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21151 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21153 if (label && label.iconEl) {
21154 label.iconEl.removeClass(label.validClass);
21155 label.iconEl.removeClass(label.invalidClass);
21159 disable : function()
21161 if(this.inputType != 'radio'){
21162 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21169 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21170 _this.getActionEl().addClass(this.disabledClass);
21171 e.dom.disabled = true;
21175 this.disabled = true;
21176 this.fireEvent("disable", this);
21180 enable : function()
21182 if(this.inputType != 'radio'){
21183 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21190 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21191 _this.getActionEl().removeClass(this.disabledClass);
21192 e.dom.disabled = false;
21196 this.disabled = false;
21197 this.fireEvent("enable", this);
21201 setBoxLabel : function(v)
21206 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21212 Roo.apply(Roo.bootstrap.CheckBox, {
21217 * register a CheckBox Group
21218 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21220 register : function(checkbox)
21222 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21223 this.groups[checkbox.groupId] = {};
21226 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21230 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21234 * fetch a CheckBox Group based on the group ID
21235 * @param {string} the group ID
21236 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21238 get: function(groupId) {
21239 if (typeof(this.groups[groupId]) == 'undefined') {
21243 return this.groups[groupId] ;
21256 * @class Roo.bootstrap.Radio
21257 * @extends Roo.bootstrap.Component
21258 * Bootstrap Radio class
21259 * @cfg {String} boxLabel - the label associated
21260 * @cfg {String} value - the value of radio
21263 * Create a new Radio
21264 * @param {Object} config The config object
21266 Roo.bootstrap.Radio = function(config){
21267 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21271 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21277 getAutoCreate : function()
21281 cls : 'form-group radio',
21286 html : this.boxLabel
21294 initEvents : function()
21296 this.parent().register(this);
21298 this.el.on('click', this.onClick, this);
21302 onClick : function(e)
21304 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21305 this.setChecked(true);
21309 setChecked : function(state, suppressEvent)
21311 this.parent().setValue(this.value, suppressEvent);
21315 setBoxLabel : function(v)
21320 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21335 * @class Roo.bootstrap.SecurePass
21336 * @extends Roo.bootstrap.Input
21337 * Bootstrap SecurePass class
21341 * Create a new SecurePass
21342 * @param {Object} config The config object
21345 Roo.bootstrap.SecurePass = function (config) {
21346 // these go here, so the translation tool can replace them..
21348 PwdEmpty: "Please type a password, and then retype it to confirm.",
21349 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21350 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21351 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21352 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21353 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21354 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21355 TooWeak: "Your password is Too Weak."
21357 this.meterLabel = "Password strength:";
21358 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21359 this.meterClass = [
21360 "roo-password-meter-tooweak",
21361 "roo-password-meter-weak",
21362 "roo-password-meter-medium",
21363 "roo-password-meter-strong",
21364 "roo-password-meter-grey"
21369 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21372 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21374 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21376 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21377 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21378 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21379 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21380 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21381 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21382 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21392 * @cfg {String/Object} Label for the strength meter (defaults to
21393 * 'Password strength:')
21398 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21399 * ['Weak', 'Medium', 'Strong'])
21402 pwdStrengths: false,
21415 initEvents: function ()
21417 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21419 if (this.el.is('input[type=password]') && Roo.isSafari) {
21420 this.el.on('keydown', this.SafariOnKeyDown, this);
21423 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21426 onRender: function (ct, position)
21428 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21429 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21430 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21432 this.trigger.createChild({
21437 cls: 'roo-password-meter-grey col-xs-12',
21440 //width: this.meterWidth + 'px'
21444 cls: 'roo-password-meter-text'
21450 if (this.hideTrigger) {
21451 this.trigger.setDisplayed(false);
21453 this.setSize(this.width || '', this.height || '');
21456 onDestroy: function ()
21458 if (this.trigger) {
21459 this.trigger.removeAllListeners();
21460 this.trigger.remove();
21463 this.wrap.remove();
21465 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21468 checkStrength: function ()
21470 var pwd = this.inputEl().getValue();
21471 if (pwd == this._lastPwd) {
21476 if (this.ClientSideStrongPassword(pwd)) {
21478 } else if (this.ClientSideMediumPassword(pwd)) {
21480 } else if (this.ClientSideWeakPassword(pwd)) {
21486 Roo.log('strength1: ' + strength);
21488 //var pm = this.trigger.child('div/div/div').dom;
21489 var pm = this.trigger.child('div/div');
21490 pm.removeClass(this.meterClass);
21491 pm.addClass(this.meterClass[strength]);
21494 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21496 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21498 this._lastPwd = pwd;
21502 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21504 this._lastPwd = '';
21506 var pm = this.trigger.child('div/div');
21507 pm.removeClass(this.meterClass);
21508 pm.addClass('roo-password-meter-grey');
21511 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21514 this.inputEl().dom.type='password';
21517 validateValue: function (value)
21520 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21523 if (value.length == 0) {
21524 if (this.allowBlank) {
21525 this.clearInvalid();
21529 this.markInvalid(this.errors.PwdEmpty);
21530 this.errorMsg = this.errors.PwdEmpty;
21538 if ('[\x21-\x7e]*'.match(value)) {
21539 this.markInvalid(this.errors.PwdBadChar);
21540 this.errorMsg = this.errors.PwdBadChar;
21543 if (value.length < 6) {
21544 this.markInvalid(this.errors.PwdShort);
21545 this.errorMsg = this.errors.PwdShort;
21548 if (value.length > 16) {
21549 this.markInvalid(this.errors.PwdLong);
21550 this.errorMsg = this.errors.PwdLong;
21554 if (this.ClientSideStrongPassword(value)) {
21556 } else if (this.ClientSideMediumPassword(value)) {
21558 } else if (this.ClientSideWeakPassword(value)) {
21565 if (strength < 2) {
21566 //this.markInvalid(this.errors.TooWeak);
21567 this.errorMsg = this.errors.TooWeak;
21572 console.log('strength2: ' + strength);
21574 //var pm = this.trigger.child('div/div/div').dom;
21576 var pm = this.trigger.child('div/div');
21577 pm.removeClass(this.meterClass);
21578 pm.addClass(this.meterClass[strength]);
21580 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21582 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21584 this.errorMsg = '';
21588 CharacterSetChecks: function (type)
21591 this.fResult = false;
21594 isctype: function (character, type)
21597 case this.kCapitalLetter:
21598 if (character >= 'A' && character <= 'Z') {
21603 case this.kSmallLetter:
21604 if (character >= 'a' && character <= 'z') {
21610 if (character >= '0' && character <= '9') {
21615 case this.kPunctuation:
21616 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21627 IsLongEnough: function (pwd, size)
21629 return !(pwd == null || isNaN(size) || pwd.length < size);
21632 SpansEnoughCharacterSets: function (word, nb)
21634 if (!this.IsLongEnough(word, nb))
21639 var characterSetChecks = new Array(
21640 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21641 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21644 for (var index = 0; index < word.length; ++index) {
21645 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21646 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21647 characterSetChecks[nCharSet].fResult = true;
21654 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21655 if (characterSetChecks[nCharSet].fResult) {
21660 if (nCharSets < nb) {
21666 ClientSideStrongPassword: function (pwd)
21668 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21671 ClientSideMediumPassword: function (pwd)
21673 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21676 ClientSideWeakPassword: function (pwd)
21678 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21681 })//<script type="text/javascript">
21684 * Based Ext JS Library 1.1.1
21685 * Copyright(c) 2006-2007, Ext JS, LLC.
21691 * @class Roo.HtmlEditorCore
21692 * @extends Roo.Component
21693 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21695 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21698 Roo.HtmlEditorCore = function(config){
21701 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21706 * @event initialize
21707 * Fires when the editor is fully initialized (including the iframe)
21708 * @param {Roo.HtmlEditorCore} this
21713 * Fires when the editor is first receives the focus. Any insertion must wait
21714 * until after this event.
21715 * @param {Roo.HtmlEditorCore} this
21719 * @event beforesync
21720 * Fires before the textarea is updated with content from the editor iframe. Return false
21721 * to cancel the sync.
21722 * @param {Roo.HtmlEditorCore} this
21723 * @param {String} html
21727 * @event beforepush
21728 * Fires before the iframe editor is updated with content from the textarea. Return false
21729 * to cancel the push.
21730 * @param {Roo.HtmlEditorCore} this
21731 * @param {String} html
21736 * Fires when the textarea is updated with content from the editor iframe.
21737 * @param {Roo.HtmlEditorCore} this
21738 * @param {String} html
21743 * Fires when the iframe editor is updated with content from the textarea.
21744 * @param {Roo.HtmlEditorCore} this
21745 * @param {String} html
21750 * @event editorevent
21751 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21752 * @param {Roo.HtmlEditorCore} this
21758 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21760 // defaults : white / black...
21761 this.applyBlacklists();
21768 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21772 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21778 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21783 * @cfg {Number} height (in pixels)
21787 * @cfg {Number} width (in pixels)
21792 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21795 stylesheets: false,
21800 // private properties
21801 validationEvent : false,
21803 initialized : false,
21805 sourceEditMode : false,
21806 onFocus : Roo.emptyFn,
21808 hideMode:'offsets',
21812 // blacklist + whitelisted elements..
21819 * Protected method that will not generally be called directly. It
21820 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21821 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21823 getDocMarkup : function(){
21827 // inherit styels from page...??
21828 if (this.stylesheets === false) {
21830 Roo.get(document.head).select('style').each(function(node) {
21831 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21834 Roo.get(document.head).select('link').each(function(node) {
21835 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21838 } else if (!this.stylesheets.length) {
21840 st = '<style type="text/css">' +
21841 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21844 st = '<style type="text/css">' +
21849 st += '<style type="text/css">' +
21850 'IMG { cursor: pointer } ' +
21853 var cls = 'roo-htmleditor-body';
21855 if(this.bodyCls.length){
21856 cls += ' ' + this.bodyCls;
21859 return '<html><head>' + st +
21860 //<style type="text/css">' +
21861 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21863 ' </head><body class="' + cls + '"></body></html>';
21867 onRender : function(ct, position)
21870 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21871 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21874 this.el.dom.style.border = '0 none';
21875 this.el.dom.setAttribute('tabIndex', -1);
21876 this.el.addClass('x-hidden hide');
21880 if(Roo.isIE){ // fix IE 1px bogus margin
21881 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21885 this.frameId = Roo.id();
21889 var iframe = this.owner.wrap.createChild({
21891 cls: 'form-control', // bootstrap..
21893 name: this.frameId,
21894 frameBorder : 'no',
21895 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21900 this.iframe = iframe.dom;
21902 this.assignDocWin();
21904 this.doc.designMode = 'on';
21907 this.doc.write(this.getDocMarkup());
21911 var task = { // must defer to wait for browser to be ready
21913 //console.log("run task?" + this.doc.readyState);
21914 this.assignDocWin();
21915 if(this.doc.body || this.doc.readyState == 'complete'){
21917 this.doc.designMode="on";
21921 Roo.TaskMgr.stop(task);
21922 this.initEditor.defer(10, this);
21929 Roo.TaskMgr.start(task);
21934 onResize : function(w, h)
21936 Roo.log('resize: ' +w + ',' + h );
21937 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21941 if(typeof w == 'number'){
21943 this.iframe.style.width = w + 'px';
21945 if(typeof h == 'number'){
21947 this.iframe.style.height = h + 'px';
21949 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21956 * Toggles the editor between standard and source edit mode.
21957 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21959 toggleSourceEdit : function(sourceEditMode){
21961 this.sourceEditMode = sourceEditMode === true;
21963 if(this.sourceEditMode){
21965 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21968 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21969 //this.iframe.className = '';
21972 //this.setSize(this.owner.wrap.getSize());
21973 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21980 * Protected method that will not generally be called directly. If you need/want
21981 * custom HTML cleanup, this is the method you should override.
21982 * @param {String} html The HTML to be cleaned
21983 * return {String} The cleaned HTML
21985 cleanHtml : function(html){
21986 html = String(html);
21987 if(html.length > 5){
21988 if(Roo.isSafari){ // strip safari nonsense
21989 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21992 if(html == ' '){
21999 * HTML Editor -> Textarea
22000 * Protected method that will not generally be called directly. Syncs the contents
22001 * of the editor iframe with the textarea.
22003 syncValue : function(){
22004 if(this.initialized){
22005 var bd = (this.doc.body || this.doc.documentElement);
22006 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22007 var html = bd.innerHTML;
22009 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22010 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22012 html = '<div style="'+m[0]+'">' + html + '</div>';
22015 html = this.cleanHtml(html);
22016 // fix up the special chars.. normaly like back quotes in word...
22017 // however we do not want to do this with chinese..
22018 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22019 var cc = b.charCodeAt();
22021 (cc >= 0x4E00 && cc < 0xA000 ) ||
22022 (cc >= 0x3400 && cc < 0x4E00 ) ||
22023 (cc >= 0xf900 && cc < 0xfb00 )
22029 if(this.owner.fireEvent('beforesync', this, html) !== false){
22030 this.el.dom.value = html;
22031 this.owner.fireEvent('sync', this, html);
22037 * Protected method that will not generally be called directly. Pushes the value of the textarea
22038 * into the iframe editor.
22040 pushValue : function(){
22041 if(this.initialized){
22042 var v = this.el.dom.value.trim();
22044 // if(v.length < 1){
22048 if(this.owner.fireEvent('beforepush', this, v) !== false){
22049 var d = (this.doc.body || this.doc.documentElement);
22051 this.cleanUpPaste();
22052 this.el.dom.value = d.innerHTML;
22053 this.owner.fireEvent('push', this, v);
22059 deferFocus : function(){
22060 this.focus.defer(10, this);
22064 focus : function(){
22065 if(this.win && !this.sourceEditMode){
22072 assignDocWin: function()
22074 var iframe = this.iframe;
22077 this.doc = iframe.contentWindow.document;
22078 this.win = iframe.contentWindow;
22080 // if (!Roo.get(this.frameId)) {
22083 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22084 // this.win = Roo.get(this.frameId).dom.contentWindow;
22086 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22090 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22091 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22096 initEditor : function(){
22097 //console.log("INIT EDITOR");
22098 this.assignDocWin();
22102 this.doc.designMode="on";
22104 this.doc.write(this.getDocMarkup());
22107 var dbody = (this.doc.body || this.doc.documentElement);
22108 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22109 // this copies styles from the containing element into thsi one..
22110 // not sure why we need all of this..
22111 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22113 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22114 //ss['background-attachment'] = 'fixed'; // w3c
22115 dbody.bgProperties = 'fixed'; // ie
22116 //Roo.DomHelper.applyStyles(dbody, ss);
22117 Roo.EventManager.on(this.doc, {
22118 //'mousedown': this.onEditorEvent,
22119 'mouseup': this.onEditorEvent,
22120 'dblclick': this.onEditorEvent,
22121 'click': this.onEditorEvent,
22122 'keyup': this.onEditorEvent,
22127 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22129 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22130 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22132 this.initialized = true;
22134 this.owner.fireEvent('initialize', this);
22139 onDestroy : function(){
22145 //for (var i =0; i < this.toolbars.length;i++) {
22146 // // fixme - ask toolbars for heights?
22147 // this.toolbars[i].onDestroy();
22150 //this.wrap.dom.innerHTML = '';
22151 //this.wrap.remove();
22156 onFirstFocus : function(){
22158 this.assignDocWin();
22161 this.activated = true;
22164 if(Roo.isGecko){ // prevent silly gecko errors
22166 var s = this.win.getSelection();
22167 if(!s.focusNode || s.focusNode.nodeType != 3){
22168 var r = s.getRangeAt(0);
22169 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22174 this.execCmd('useCSS', true);
22175 this.execCmd('styleWithCSS', false);
22178 this.owner.fireEvent('activate', this);
22182 adjustFont: function(btn){
22183 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22184 //if(Roo.isSafari){ // safari
22187 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22188 if(Roo.isSafari){ // safari
22189 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22190 v = (v < 10) ? 10 : v;
22191 v = (v > 48) ? 48 : v;
22192 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22197 v = Math.max(1, v+adjust);
22199 this.execCmd('FontSize', v );
22202 onEditorEvent : function(e)
22204 this.owner.fireEvent('editorevent', this, e);
22205 // this.updateToolbar();
22206 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22209 insertTag : function(tg)
22211 // could be a bit smarter... -> wrap the current selected tRoo..
22212 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22214 range = this.createRange(this.getSelection());
22215 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22216 wrappingNode.appendChild(range.extractContents());
22217 range.insertNode(wrappingNode);
22224 this.execCmd("formatblock", tg);
22228 insertText : function(txt)
22232 var range = this.createRange();
22233 range.deleteContents();
22234 //alert(Sender.getAttribute('label'));
22236 range.insertNode(this.doc.createTextNode(txt));
22242 * Executes a Midas editor command on the editor document and performs necessary focus and
22243 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22244 * @param {String} cmd The Midas command
22245 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22247 relayCmd : function(cmd, value){
22249 this.execCmd(cmd, value);
22250 this.owner.fireEvent('editorevent', this);
22251 //this.updateToolbar();
22252 this.owner.deferFocus();
22256 * Executes a Midas editor command directly on the editor document.
22257 * For visual commands, you should use {@link #relayCmd} instead.
22258 * <b>This should only be called after the editor is initialized.</b>
22259 * @param {String} cmd The Midas command
22260 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22262 execCmd : function(cmd, value){
22263 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22270 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22272 * @param {String} text | dom node..
22274 insertAtCursor : function(text)
22277 if(!this.activated){
22283 var r = this.doc.selection.createRange();
22294 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22298 // from jquery ui (MIT licenced)
22300 var win = this.win;
22302 if (win.getSelection && win.getSelection().getRangeAt) {
22303 range = win.getSelection().getRangeAt(0);
22304 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22305 range.insertNode(node);
22306 } else if (win.document.selection && win.document.selection.createRange) {
22307 // no firefox support
22308 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22309 win.document.selection.createRange().pasteHTML(txt);
22311 // no firefox support
22312 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22313 this.execCmd('InsertHTML', txt);
22322 mozKeyPress : function(e){
22324 var c = e.getCharCode(), cmd;
22327 c = String.fromCharCode(c).toLowerCase();
22341 this.cleanUpPaste.defer(100, this);
22349 e.preventDefault();
22357 fixKeys : function(){ // load time branching for fastest keydown performance
22359 return function(e){
22360 var k = e.getKey(), r;
22363 r = this.doc.selection.createRange();
22366 r.pasteHTML('    ');
22373 r = this.doc.selection.createRange();
22375 var target = r.parentElement();
22376 if(!target || target.tagName.toLowerCase() != 'li'){
22378 r.pasteHTML('<br />');
22384 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22385 this.cleanUpPaste.defer(100, this);
22391 }else if(Roo.isOpera){
22392 return function(e){
22393 var k = e.getKey();
22397 this.execCmd('InsertHTML','    ');
22400 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22401 this.cleanUpPaste.defer(100, this);
22406 }else if(Roo.isSafari){
22407 return function(e){
22408 var k = e.getKey();
22412 this.execCmd('InsertText','\t');
22416 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22417 this.cleanUpPaste.defer(100, this);
22425 getAllAncestors: function()
22427 var p = this.getSelectedNode();
22430 a.push(p); // push blank onto stack..
22431 p = this.getParentElement();
22435 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22439 a.push(this.doc.body);
22443 lastSelNode : false,
22446 getSelection : function()
22448 this.assignDocWin();
22449 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22452 getSelectedNode: function()
22454 // this may only work on Gecko!!!
22456 // should we cache this!!!!
22461 var range = this.createRange(this.getSelection()).cloneRange();
22464 var parent = range.parentElement();
22466 var testRange = range.duplicate();
22467 testRange.moveToElementText(parent);
22468 if (testRange.inRange(range)) {
22471 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22474 parent = parent.parentElement;
22479 // is ancestor a text element.
22480 var ac = range.commonAncestorContainer;
22481 if (ac.nodeType == 3) {
22482 ac = ac.parentNode;
22485 var ar = ac.childNodes;
22488 var other_nodes = [];
22489 var has_other_nodes = false;
22490 for (var i=0;i<ar.length;i++) {
22491 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22494 // fullly contained node.
22496 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22501 // probably selected..
22502 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22503 other_nodes.push(ar[i]);
22507 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22512 has_other_nodes = true;
22514 if (!nodes.length && other_nodes.length) {
22515 nodes= other_nodes;
22517 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22523 createRange: function(sel)
22525 // this has strange effects when using with
22526 // top toolbar - not sure if it's a great idea.
22527 //this.editor.contentWindow.focus();
22528 if (typeof sel != "undefined") {
22530 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22532 return this.doc.createRange();
22535 return this.doc.createRange();
22538 getParentElement: function()
22541 this.assignDocWin();
22542 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22544 var range = this.createRange(sel);
22547 var p = range.commonAncestorContainer;
22548 while (p.nodeType == 3) { // text node
22559 * Range intersection.. the hard stuff...
22563 * [ -- selected range --- ]
22567 * if end is before start or hits it. fail.
22568 * if start is after end or hits it fail.
22570 * if either hits (but other is outside. - then it's not
22576 // @see http://www.thismuchiknow.co.uk/?p=64.
22577 rangeIntersectsNode : function(range, node)
22579 var nodeRange = node.ownerDocument.createRange();
22581 nodeRange.selectNode(node);
22583 nodeRange.selectNodeContents(node);
22586 var rangeStartRange = range.cloneRange();
22587 rangeStartRange.collapse(true);
22589 var rangeEndRange = range.cloneRange();
22590 rangeEndRange.collapse(false);
22592 var nodeStartRange = nodeRange.cloneRange();
22593 nodeStartRange.collapse(true);
22595 var nodeEndRange = nodeRange.cloneRange();
22596 nodeEndRange.collapse(false);
22598 return rangeStartRange.compareBoundaryPoints(
22599 Range.START_TO_START, nodeEndRange) == -1 &&
22600 rangeEndRange.compareBoundaryPoints(
22601 Range.START_TO_START, nodeStartRange) == 1;
22605 rangeCompareNode : function(range, node)
22607 var nodeRange = node.ownerDocument.createRange();
22609 nodeRange.selectNode(node);
22611 nodeRange.selectNodeContents(node);
22615 range.collapse(true);
22617 nodeRange.collapse(true);
22619 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22620 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22622 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22624 var nodeIsBefore = ss == 1;
22625 var nodeIsAfter = ee == -1;
22627 if (nodeIsBefore && nodeIsAfter) {
22630 if (!nodeIsBefore && nodeIsAfter) {
22631 return 1; //right trailed.
22634 if (nodeIsBefore && !nodeIsAfter) {
22635 return 2; // left trailed.
22641 // private? - in a new class?
22642 cleanUpPaste : function()
22644 // cleans up the whole document..
22645 Roo.log('cleanuppaste');
22647 this.cleanUpChildren(this.doc.body);
22648 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22649 if (clean != this.doc.body.innerHTML) {
22650 this.doc.body.innerHTML = clean;
22655 cleanWordChars : function(input) {// change the chars to hex code
22656 var he = Roo.HtmlEditorCore;
22658 var output = input;
22659 Roo.each(he.swapCodes, function(sw) {
22660 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22662 output = output.replace(swapper, sw[1]);
22669 cleanUpChildren : function (n)
22671 if (!n.childNodes.length) {
22674 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22675 this.cleanUpChild(n.childNodes[i]);
22682 cleanUpChild : function (node)
22685 //console.log(node);
22686 if (node.nodeName == "#text") {
22687 // clean up silly Windows -- stuff?
22690 if (node.nodeName == "#comment") {
22691 node.parentNode.removeChild(node);
22692 // clean up silly Windows -- stuff?
22695 var lcname = node.tagName.toLowerCase();
22696 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22697 // whitelist of tags..
22699 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22701 node.parentNode.removeChild(node);
22706 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22708 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22709 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22711 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22712 // remove_keep_children = true;
22715 if (remove_keep_children) {
22716 this.cleanUpChildren(node);
22717 // inserts everything just before this node...
22718 while (node.childNodes.length) {
22719 var cn = node.childNodes[0];
22720 node.removeChild(cn);
22721 node.parentNode.insertBefore(cn, node);
22723 node.parentNode.removeChild(node);
22727 if (!node.attributes || !node.attributes.length) {
22728 this.cleanUpChildren(node);
22732 function cleanAttr(n,v)
22735 if (v.match(/^\./) || v.match(/^\//)) {
22738 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22741 if (v.match(/^#/)) {
22744 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22745 node.removeAttribute(n);
22749 var cwhite = this.cwhite;
22750 var cblack = this.cblack;
22752 function cleanStyle(n,v)
22754 if (v.match(/expression/)) { //XSS?? should we even bother..
22755 node.removeAttribute(n);
22759 var parts = v.split(/;/);
22762 Roo.each(parts, function(p) {
22763 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22767 var l = p.split(':').shift().replace(/\s+/g,'');
22768 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22770 if ( cwhite.length && cblack.indexOf(l) > -1) {
22771 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22772 //node.removeAttribute(n);
22776 // only allow 'c whitelisted system attributes'
22777 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22778 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22779 //node.removeAttribute(n);
22789 if (clean.length) {
22790 node.setAttribute(n, clean.join(';'));
22792 node.removeAttribute(n);
22798 for (var i = node.attributes.length-1; i > -1 ; i--) {
22799 var a = node.attributes[i];
22802 if (a.name.toLowerCase().substr(0,2)=='on') {
22803 node.removeAttribute(a.name);
22806 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22807 node.removeAttribute(a.name);
22810 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22811 cleanAttr(a.name,a.value); // fixme..
22814 if (a.name == 'style') {
22815 cleanStyle(a.name,a.value);
22818 /// clean up MS crap..
22819 // tecnically this should be a list of valid class'es..
22822 if (a.name == 'class') {
22823 if (a.value.match(/^Mso/)) {
22824 node.className = '';
22827 if (a.value.match(/^body$/)) {
22828 node.className = '';
22839 this.cleanUpChildren(node);
22845 * Clean up MS wordisms...
22847 cleanWord : function(node)
22852 this.cleanWord(this.doc.body);
22855 if (node.nodeName == "#text") {
22856 // clean up silly Windows -- stuff?
22859 if (node.nodeName == "#comment") {
22860 node.parentNode.removeChild(node);
22861 // clean up silly Windows -- stuff?
22865 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22866 node.parentNode.removeChild(node);
22870 // remove - but keep children..
22871 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22872 while (node.childNodes.length) {
22873 var cn = node.childNodes[0];
22874 node.removeChild(cn);
22875 node.parentNode.insertBefore(cn, node);
22877 node.parentNode.removeChild(node);
22878 this.iterateChildren(node, this.cleanWord);
22882 if (node.className.length) {
22884 var cn = node.className.split(/\W+/);
22886 Roo.each(cn, function(cls) {
22887 if (cls.match(/Mso[a-zA-Z]+/)) {
22892 node.className = cna.length ? cna.join(' ') : '';
22894 node.removeAttribute("class");
22898 if (node.hasAttribute("lang")) {
22899 node.removeAttribute("lang");
22902 if (node.hasAttribute("style")) {
22904 var styles = node.getAttribute("style").split(";");
22906 Roo.each(styles, function(s) {
22907 if (!s.match(/:/)) {
22910 var kv = s.split(":");
22911 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22914 // what ever is left... we allow.
22917 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22918 if (!nstyle.length) {
22919 node.removeAttribute('style');
22922 this.iterateChildren(node, this.cleanWord);
22928 * iterateChildren of a Node, calling fn each time, using this as the scole..
22929 * @param {DomNode} node node to iterate children of.
22930 * @param {Function} fn method of this class to call on each item.
22932 iterateChildren : function(node, fn)
22934 if (!node.childNodes.length) {
22937 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22938 fn.call(this, node.childNodes[i])
22944 * cleanTableWidths.
22946 * Quite often pasting from word etc.. results in tables with column and widths.
22947 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22950 cleanTableWidths : function(node)
22955 this.cleanTableWidths(this.doc.body);
22960 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22963 Roo.log(node.tagName);
22964 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22965 this.iterateChildren(node, this.cleanTableWidths);
22968 if (node.hasAttribute('width')) {
22969 node.removeAttribute('width');
22973 if (node.hasAttribute("style")) {
22976 var styles = node.getAttribute("style").split(";");
22978 Roo.each(styles, function(s) {
22979 if (!s.match(/:/)) {
22982 var kv = s.split(":");
22983 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22986 // what ever is left... we allow.
22989 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22990 if (!nstyle.length) {
22991 node.removeAttribute('style');
22995 this.iterateChildren(node, this.cleanTableWidths);
23003 domToHTML : function(currentElement, depth, nopadtext) {
23005 depth = depth || 0;
23006 nopadtext = nopadtext || false;
23008 if (!currentElement) {
23009 return this.domToHTML(this.doc.body);
23012 //Roo.log(currentElement);
23014 var allText = false;
23015 var nodeName = currentElement.nodeName;
23016 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23018 if (nodeName == '#text') {
23020 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23025 if (nodeName != 'BODY') {
23028 // Prints the node tagName, such as <A>, <IMG>, etc
23031 for(i = 0; i < currentElement.attributes.length;i++) {
23033 var aname = currentElement.attributes.item(i).name;
23034 if (!currentElement.attributes.item(i).value.length) {
23037 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23040 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23049 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23052 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23057 // Traverse the tree
23059 var currentElementChild = currentElement.childNodes.item(i);
23060 var allText = true;
23061 var innerHTML = '';
23063 while (currentElementChild) {
23064 // Formatting code (indent the tree so it looks nice on the screen)
23065 var nopad = nopadtext;
23066 if (lastnode == 'SPAN') {
23070 if (currentElementChild.nodeName == '#text') {
23071 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23072 toadd = nopadtext ? toadd : toadd.trim();
23073 if (!nopad && toadd.length > 80) {
23074 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23076 innerHTML += toadd;
23079 currentElementChild = currentElement.childNodes.item(i);
23085 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23087 // Recursively traverse the tree structure of the child node
23088 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23089 lastnode = currentElementChild.nodeName;
23091 currentElementChild=currentElement.childNodes.item(i);
23097 // The remaining code is mostly for formatting the tree
23098 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23103 ret+= "</"+tagName+">";
23109 applyBlacklists : function()
23111 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23112 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23116 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23117 if (b.indexOf(tag) > -1) {
23120 this.white.push(tag);
23124 Roo.each(w, function(tag) {
23125 if (b.indexOf(tag) > -1) {
23128 if (this.white.indexOf(tag) > -1) {
23131 this.white.push(tag);
23136 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23137 if (w.indexOf(tag) > -1) {
23140 this.black.push(tag);
23144 Roo.each(b, function(tag) {
23145 if (w.indexOf(tag) > -1) {
23148 if (this.black.indexOf(tag) > -1) {
23151 this.black.push(tag);
23156 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23157 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23161 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23162 if (b.indexOf(tag) > -1) {
23165 this.cwhite.push(tag);
23169 Roo.each(w, function(tag) {
23170 if (b.indexOf(tag) > -1) {
23173 if (this.cwhite.indexOf(tag) > -1) {
23176 this.cwhite.push(tag);
23181 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23182 if (w.indexOf(tag) > -1) {
23185 this.cblack.push(tag);
23189 Roo.each(b, function(tag) {
23190 if (w.indexOf(tag) > -1) {
23193 if (this.cblack.indexOf(tag) > -1) {
23196 this.cblack.push(tag);
23201 setStylesheets : function(stylesheets)
23203 if(typeof(stylesheets) == 'string'){
23204 Roo.get(this.iframe.contentDocument.head).createChild({
23206 rel : 'stylesheet',
23215 Roo.each(stylesheets, function(s) {
23220 Roo.get(_this.iframe.contentDocument.head).createChild({
23222 rel : 'stylesheet',
23231 removeStylesheets : function()
23235 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23240 setStyle : function(style)
23242 Roo.get(this.iframe.contentDocument.head).createChild({
23251 // hide stuff that is not compatible
23265 * @event specialkey
23269 * @cfg {String} fieldClass @hide
23272 * @cfg {String} focusClass @hide
23275 * @cfg {String} autoCreate @hide
23278 * @cfg {String} inputType @hide
23281 * @cfg {String} invalidClass @hide
23284 * @cfg {String} invalidText @hide
23287 * @cfg {String} msgFx @hide
23290 * @cfg {String} validateOnBlur @hide
23294 Roo.HtmlEditorCore.white = [
23295 'area', 'br', 'img', 'input', 'hr', 'wbr',
23297 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23298 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23299 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23300 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23301 'table', 'ul', 'xmp',
23303 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23306 'dir', 'menu', 'ol', 'ul', 'dl',
23312 Roo.HtmlEditorCore.black = [
23313 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23315 'base', 'basefont', 'bgsound', 'blink', 'body',
23316 'frame', 'frameset', 'head', 'html', 'ilayer',
23317 'iframe', 'layer', 'link', 'meta', 'object',
23318 'script', 'style' ,'title', 'xml' // clean later..
23320 Roo.HtmlEditorCore.clean = [
23321 'script', 'style', 'title', 'xml'
23323 Roo.HtmlEditorCore.remove = [
23328 Roo.HtmlEditorCore.ablack = [
23332 Roo.HtmlEditorCore.aclean = [
23333 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23337 Roo.HtmlEditorCore.pwhite= [
23338 'http', 'https', 'mailto'
23341 // white listed style attributes.
23342 Roo.HtmlEditorCore.cwhite= [
23343 // 'text-align', /// default is to allow most things..
23349 // black listed style attributes.
23350 Roo.HtmlEditorCore.cblack= [
23351 // 'font-size' -- this can be set by the project
23355 Roo.HtmlEditorCore.swapCodes =[
23374 * @class Roo.bootstrap.HtmlEditor
23375 * @extends Roo.bootstrap.TextArea
23376 * Bootstrap HtmlEditor class
23379 * Create a new HtmlEditor
23380 * @param {Object} config The config object
23383 Roo.bootstrap.HtmlEditor = function(config){
23384 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23385 if (!this.toolbars) {
23386 this.toolbars = [];
23389 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23392 * @event initialize
23393 * Fires when the editor is fully initialized (including the iframe)
23394 * @param {HtmlEditor} this
23399 * Fires when the editor is first receives the focus. Any insertion must wait
23400 * until after this event.
23401 * @param {HtmlEditor} this
23405 * @event beforesync
23406 * Fires before the textarea is updated with content from the editor iframe. Return false
23407 * to cancel the sync.
23408 * @param {HtmlEditor} this
23409 * @param {String} html
23413 * @event beforepush
23414 * Fires before the iframe editor is updated with content from the textarea. Return false
23415 * to cancel the push.
23416 * @param {HtmlEditor} this
23417 * @param {String} html
23422 * Fires when the textarea is updated with content from the editor iframe.
23423 * @param {HtmlEditor} this
23424 * @param {String} html
23429 * Fires when the iframe editor is updated with content from the textarea.
23430 * @param {HtmlEditor} this
23431 * @param {String} html
23435 * @event editmodechange
23436 * Fires when the editor switches edit modes
23437 * @param {HtmlEditor} this
23438 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23440 editmodechange: true,
23442 * @event editorevent
23443 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23444 * @param {HtmlEditor} this
23448 * @event firstfocus
23449 * Fires when on first focus - needed by toolbars..
23450 * @param {HtmlEditor} this
23455 * Auto save the htmlEditor value as a file into Events
23456 * @param {HtmlEditor} this
23460 * @event savedpreview
23461 * preview the saved version of htmlEditor
23462 * @param {HtmlEditor} this
23469 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23473 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23478 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23483 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23488 * @cfg {Number} height (in pixels)
23492 * @cfg {Number} width (in pixels)
23497 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23500 stylesheets: false,
23505 // private properties
23506 validationEvent : false,
23508 initialized : false,
23511 onFocus : Roo.emptyFn,
23513 hideMode:'offsets',
23515 tbContainer : false,
23519 toolbarContainer :function() {
23520 return this.wrap.select('.x-html-editor-tb',true).first();
23524 * Protected method that will not generally be called directly. It
23525 * is called when the editor creates its toolbar. Override this method if you need to
23526 * add custom toolbar buttons.
23527 * @param {HtmlEditor} editor
23529 createToolbar : function(){
23530 Roo.log('renewing');
23531 Roo.log("create toolbars");
23533 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23534 this.toolbars[0].render(this.toolbarContainer());
23538 // if (!editor.toolbars || !editor.toolbars.length) {
23539 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23542 // for (var i =0 ; i < editor.toolbars.length;i++) {
23543 // editor.toolbars[i] = Roo.factory(
23544 // typeof(editor.toolbars[i]) == 'string' ?
23545 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23546 // Roo.bootstrap.HtmlEditor);
23547 // editor.toolbars[i].init(editor);
23553 onRender : function(ct, position)
23555 // Roo.log("Call onRender: " + this.xtype);
23557 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23559 this.wrap = this.inputEl().wrap({
23560 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23563 this.editorcore.onRender(ct, position);
23565 if (this.resizable) {
23566 this.resizeEl = new Roo.Resizable(this.wrap, {
23570 minHeight : this.height,
23571 height: this.height,
23572 handles : this.resizable,
23575 resize : function(r, w, h) {
23576 _t.onResize(w,h); // -something
23582 this.createToolbar(this);
23585 if(!this.width && this.resizable){
23586 this.setSize(this.wrap.getSize());
23588 if (this.resizeEl) {
23589 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23590 // should trigger onReize..
23596 onResize : function(w, h)
23598 Roo.log('resize: ' +w + ',' + h );
23599 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23603 if(this.inputEl() ){
23604 if(typeof w == 'number'){
23605 var aw = w - this.wrap.getFrameWidth('lr');
23606 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23609 if(typeof h == 'number'){
23610 var tbh = -11; // fixme it needs to tool bar size!
23611 for (var i =0; i < this.toolbars.length;i++) {
23612 // fixme - ask toolbars for heights?
23613 tbh += this.toolbars[i].el.getHeight();
23614 //if (this.toolbars[i].footer) {
23615 // tbh += this.toolbars[i].footer.el.getHeight();
23623 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23624 ah -= 5; // knock a few pixes off for look..
23625 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23629 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23630 this.editorcore.onResize(ew,eh);
23635 * Toggles the editor between standard and source edit mode.
23636 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23638 toggleSourceEdit : function(sourceEditMode)
23640 this.editorcore.toggleSourceEdit(sourceEditMode);
23642 if(this.editorcore.sourceEditMode){
23643 Roo.log('editor - showing textarea');
23646 // Roo.log(this.syncValue());
23648 this.inputEl().removeClass(['hide', 'x-hidden']);
23649 this.inputEl().dom.removeAttribute('tabIndex');
23650 this.inputEl().focus();
23652 Roo.log('editor - hiding textarea');
23654 // Roo.log(this.pushValue());
23657 this.inputEl().addClass(['hide', 'x-hidden']);
23658 this.inputEl().dom.setAttribute('tabIndex', -1);
23659 //this.deferFocus();
23662 if(this.resizable){
23663 this.setSize(this.wrap.getSize());
23666 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23669 // private (for BoxComponent)
23670 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23672 // private (for BoxComponent)
23673 getResizeEl : function(){
23677 // private (for BoxComponent)
23678 getPositionEl : function(){
23683 initEvents : function(){
23684 this.originalValue = this.getValue();
23688 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23691 // markInvalid : Roo.emptyFn,
23693 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23696 // clearInvalid : Roo.emptyFn,
23698 setValue : function(v){
23699 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23700 this.editorcore.pushValue();
23705 deferFocus : function(){
23706 this.focus.defer(10, this);
23710 focus : function(){
23711 this.editorcore.focus();
23717 onDestroy : function(){
23723 for (var i =0; i < this.toolbars.length;i++) {
23724 // fixme - ask toolbars for heights?
23725 this.toolbars[i].onDestroy();
23728 this.wrap.dom.innerHTML = '';
23729 this.wrap.remove();
23734 onFirstFocus : function(){
23735 //Roo.log("onFirstFocus");
23736 this.editorcore.onFirstFocus();
23737 for (var i =0; i < this.toolbars.length;i++) {
23738 this.toolbars[i].onFirstFocus();
23744 syncValue : function()
23746 this.editorcore.syncValue();
23749 pushValue : function()
23751 this.editorcore.pushValue();
23755 // hide stuff that is not compatible
23769 * @event specialkey
23773 * @cfg {String} fieldClass @hide
23776 * @cfg {String} focusClass @hide
23779 * @cfg {String} autoCreate @hide
23782 * @cfg {String} inputType @hide
23785 * @cfg {String} invalidClass @hide
23788 * @cfg {String} invalidText @hide
23791 * @cfg {String} msgFx @hide
23794 * @cfg {String} validateOnBlur @hide
23803 Roo.namespace('Roo.bootstrap.htmleditor');
23805 * @class Roo.bootstrap.HtmlEditorToolbar1
23810 new Roo.bootstrap.HtmlEditor({
23813 new Roo.bootstrap.HtmlEditorToolbar1({
23814 disable : { fonts: 1 , format: 1, ..., ... , ...],
23820 * @cfg {Object} disable List of elements to disable..
23821 * @cfg {Array} btns List of additional buttons.
23825 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23828 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23831 Roo.apply(this, config);
23833 // default disabled, based on 'good practice'..
23834 this.disable = this.disable || {};
23835 Roo.applyIf(this.disable, {
23838 specialElements : true
23840 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23842 this.editor = config.editor;
23843 this.editorcore = config.editor.editorcore;
23845 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23847 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23848 // dont call parent... till later.
23850 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23855 editorcore : false,
23860 "h1","h2","h3","h4","h5","h6",
23862 "abbr", "acronym", "address", "cite", "samp", "var",
23866 onRender : function(ct, position)
23868 // Roo.log("Call onRender: " + this.xtype);
23870 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23872 this.el.dom.style.marginBottom = '0';
23874 var editorcore = this.editorcore;
23875 var editor= this.editor;
23878 var btn = function(id,cmd , toggle, handler, html){
23880 var event = toggle ? 'toggle' : 'click';
23885 xns: Roo.bootstrap,
23888 enableToggle:toggle !== false,
23890 pressed : toggle ? false : null,
23893 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23894 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23900 // var cb_box = function...
23905 xns: Roo.bootstrap,
23906 glyphicon : 'font',
23910 xns: Roo.bootstrap,
23914 Roo.each(this.formats, function(f) {
23915 style.menu.items.push({
23917 xns: Roo.bootstrap,
23918 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23923 editorcore.insertTag(this.tagname);
23930 children.push(style);
23932 btn('bold',false,true);
23933 btn('italic',false,true);
23934 btn('align-left', 'justifyleft',true);
23935 btn('align-center', 'justifycenter',true);
23936 btn('align-right' , 'justifyright',true);
23937 btn('link', false, false, function(btn) {
23938 //Roo.log("create link?");
23939 var url = prompt(this.createLinkText, this.defaultLinkValue);
23940 if(url && url != 'http:/'+'/'){
23941 this.editorcore.relayCmd('createlink', url);
23944 btn('list','insertunorderedlist',true);
23945 btn('pencil', false,true, function(btn){
23947 this.toggleSourceEdit(btn.pressed);
23950 if (this.editor.btns.length > 0) {
23951 for (var i = 0; i<this.editor.btns.length; i++) {
23952 children.push(this.editor.btns[i]);
23960 xns: Roo.bootstrap,
23965 xns: Roo.bootstrap,
23970 cog.menu.items.push({
23972 xns: Roo.bootstrap,
23973 html : Clean styles,
23978 editorcore.insertTag(this.tagname);
23987 this.xtype = 'NavSimplebar';
23989 for(var i=0;i< children.length;i++) {
23991 this.buttons.add(this.addxtypeChild(children[i]));
23995 editor.on('editorevent', this.updateToolbar, this);
23997 onBtnClick : function(id)
23999 this.editorcore.relayCmd(id);
24000 this.editorcore.focus();
24004 * Protected method that will not generally be called directly. It triggers
24005 * a toolbar update by reading the markup state of the current selection in the editor.
24007 updateToolbar: function(){
24009 if(!this.editorcore.activated){
24010 this.editor.onFirstFocus(); // is this neeed?
24014 var btns = this.buttons;
24015 var doc = this.editorcore.doc;
24016 btns.get('bold').setActive(doc.queryCommandState('bold'));
24017 btns.get('italic').setActive(doc.queryCommandState('italic'));
24018 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24020 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24021 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24022 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24024 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24025 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24028 var ans = this.editorcore.getAllAncestors();
24029 if (this.formatCombo) {
24032 var store = this.formatCombo.store;
24033 this.formatCombo.setValue("");
24034 for (var i =0; i < ans.length;i++) {
24035 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24037 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24045 // hides menus... - so this cant be on a menu...
24046 Roo.bootstrap.MenuMgr.hideAll();
24048 Roo.bootstrap.MenuMgr.hideAll();
24049 //this.editorsyncValue();
24051 onFirstFocus: function() {
24052 this.buttons.each(function(item){
24056 toggleSourceEdit : function(sourceEditMode){
24059 if(sourceEditMode){
24060 Roo.log("disabling buttons");
24061 this.buttons.each( function(item){
24062 if(item.cmd != 'pencil'){
24068 Roo.log("enabling buttons");
24069 if(this.editorcore.initialized){
24070 this.buttons.each( function(item){
24076 Roo.log("calling toggole on editor");
24077 // tell the editor that it's been pressed..
24078 this.editor.toggleSourceEdit(sourceEditMode);
24088 * @class Roo.bootstrap.Table.AbstractSelectionModel
24089 * @extends Roo.util.Observable
24090 * Abstract base class for grid SelectionModels. It provides the interface that should be
24091 * implemented by descendant classes. This class should not be directly instantiated.
24094 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24095 this.locked = false;
24096 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24100 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24101 /** @ignore Called by the grid automatically. Do not call directly. */
24102 init : function(grid){
24108 * Locks the selections.
24111 this.locked = true;
24115 * Unlocks the selections.
24117 unlock : function(){
24118 this.locked = false;
24122 * Returns true if the selections are locked.
24123 * @return {Boolean}
24125 isLocked : function(){
24126 return this.locked;
24130 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24131 * @class Roo.bootstrap.Table.RowSelectionModel
24132 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24133 * It supports multiple selections and keyboard selection/navigation.
24135 * @param {Object} config
24138 Roo.bootstrap.Table.RowSelectionModel = function(config){
24139 Roo.apply(this, config);
24140 this.selections = new Roo.util.MixedCollection(false, function(o){
24145 this.lastActive = false;
24149 * @event selectionchange
24150 * Fires when the selection changes
24151 * @param {SelectionModel} this
24153 "selectionchange" : true,
24155 * @event afterselectionchange
24156 * Fires after the selection changes (eg. by key press or clicking)
24157 * @param {SelectionModel} this
24159 "afterselectionchange" : true,
24161 * @event beforerowselect
24162 * Fires when a row is selected being selected, return false to cancel.
24163 * @param {SelectionModel} this
24164 * @param {Number} rowIndex The selected index
24165 * @param {Boolean} keepExisting False if other selections will be cleared
24167 "beforerowselect" : true,
24170 * Fires when a row is selected.
24171 * @param {SelectionModel} this
24172 * @param {Number} rowIndex The selected index
24173 * @param {Roo.data.Record} r The record
24175 "rowselect" : true,
24177 * @event rowdeselect
24178 * Fires when a row is deselected.
24179 * @param {SelectionModel} this
24180 * @param {Number} rowIndex The selected index
24182 "rowdeselect" : true
24184 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24185 this.locked = false;
24188 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24190 * @cfg {Boolean} singleSelect
24191 * True to allow selection of only one row at a time (defaults to false)
24193 singleSelect : false,
24196 initEvents : function()
24199 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24200 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24201 //}else{ // allow click to work like normal
24202 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24204 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24205 this.grid.on("rowclick", this.handleMouseDown, this);
24207 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24208 "up" : function(e){
24210 this.selectPrevious(e.shiftKey);
24211 }else if(this.last !== false && this.lastActive !== false){
24212 var last = this.last;
24213 this.selectRange(this.last, this.lastActive-1);
24214 this.grid.getView().focusRow(this.lastActive);
24215 if(last !== false){
24219 this.selectFirstRow();
24221 this.fireEvent("afterselectionchange", this);
24223 "down" : function(e){
24225 this.selectNext(e.shiftKey);
24226 }else if(this.last !== false && this.lastActive !== false){
24227 var last = this.last;
24228 this.selectRange(this.last, this.lastActive+1);
24229 this.grid.getView().focusRow(this.lastActive);
24230 if(last !== false){
24234 this.selectFirstRow();
24236 this.fireEvent("afterselectionchange", this);
24240 this.grid.store.on('load', function(){
24241 this.selections.clear();
24244 var view = this.grid.view;
24245 view.on("refresh", this.onRefresh, this);
24246 view.on("rowupdated", this.onRowUpdated, this);
24247 view.on("rowremoved", this.onRemove, this);
24252 onRefresh : function()
24254 var ds = this.grid.store, i, v = this.grid.view;
24255 var s = this.selections;
24256 s.each(function(r){
24257 if((i = ds.indexOfId(r.id)) != -1){
24266 onRemove : function(v, index, r){
24267 this.selections.remove(r);
24271 onRowUpdated : function(v, index, r){
24272 if(this.isSelected(r)){
24273 v.onRowSelect(index);
24279 * @param {Array} records The records to select
24280 * @param {Boolean} keepExisting (optional) True to keep existing selections
24282 selectRecords : function(records, keepExisting)
24285 this.clearSelections();
24287 var ds = this.grid.store;
24288 for(var i = 0, len = records.length; i < len; i++){
24289 this.selectRow(ds.indexOf(records[i]), true);
24294 * Gets the number of selected rows.
24297 getCount : function(){
24298 return this.selections.length;
24302 * Selects the first row in the grid.
24304 selectFirstRow : function(){
24309 * Select the last row.
24310 * @param {Boolean} keepExisting (optional) True to keep existing selections
24312 selectLastRow : function(keepExisting){
24313 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24314 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24318 * Selects the row immediately following the last selected row.
24319 * @param {Boolean} keepExisting (optional) True to keep existing selections
24321 selectNext : function(keepExisting)
24323 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24324 this.selectRow(this.last+1, keepExisting);
24325 this.grid.getView().focusRow(this.last);
24330 * Selects the row that precedes the last selected row.
24331 * @param {Boolean} keepExisting (optional) True to keep existing selections
24333 selectPrevious : function(keepExisting){
24335 this.selectRow(this.last-1, keepExisting);
24336 this.grid.getView().focusRow(this.last);
24341 * Returns the selected records
24342 * @return {Array} Array of selected records
24344 getSelections : function(){
24345 return [].concat(this.selections.items);
24349 * Returns the first selected record.
24352 getSelected : function(){
24353 return this.selections.itemAt(0);
24358 * Clears all selections.
24360 clearSelections : function(fast)
24366 var ds = this.grid.store;
24367 var s = this.selections;
24368 s.each(function(r){
24369 this.deselectRow(ds.indexOfId(r.id));
24373 this.selections.clear();
24380 * Selects all rows.
24382 selectAll : function(){
24386 this.selections.clear();
24387 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24388 this.selectRow(i, true);
24393 * Returns True if there is a selection.
24394 * @return {Boolean}
24396 hasSelection : function(){
24397 return this.selections.length > 0;
24401 * Returns True if the specified row is selected.
24402 * @param {Number/Record} record The record or index of the record to check
24403 * @return {Boolean}
24405 isSelected : function(index){
24406 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24407 return (r && this.selections.key(r.id) ? true : false);
24411 * Returns True if the specified record id is selected.
24412 * @param {String} id The id of record to check
24413 * @return {Boolean}
24415 isIdSelected : function(id){
24416 return (this.selections.key(id) ? true : false);
24421 handleMouseDBClick : function(e, t){
24425 handleMouseDown : function(e, t)
24427 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24428 if(this.isLocked() || rowIndex < 0 ){
24431 if(e.shiftKey && this.last !== false){
24432 var last = this.last;
24433 this.selectRange(last, rowIndex, e.ctrlKey);
24434 this.last = last; // reset the last
24438 var isSelected = this.isSelected(rowIndex);
24439 //Roo.log("select row:" + rowIndex);
24441 this.deselectRow(rowIndex);
24443 this.selectRow(rowIndex, true);
24447 if(e.button !== 0 && isSelected){
24448 alert('rowIndex 2: ' + rowIndex);
24449 view.focusRow(rowIndex);
24450 }else if(e.ctrlKey && isSelected){
24451 this.deselectRow(rowIndex);
24452 }else if(!isSelected){
24453 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24454 view.focusRow(rowIndex);
24458 this.fireEvent("afterselectionchange", this);
24461 handleDragableRowClick : function(grid, rowIndex, e)
24463 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24464 this.selectRow(rowIndex, false);
24465 grid.view.focusRow(rowIndex);
24466 this.fireEvent("afterselectionchange", this);
24471 * Selects multiple rows.
24472 * @param {Array} rows Array of the indexes of the row to select
24473 * @param {Boolean} keepExisting (optional) True to keep existing selections
24475 selectRows : function(rows, keepExisting){
24477 this.clearSelections();
24479 for(var i = 0, len = rows.length; i < len; i++){
24480 this.selectRow(rows[i], true);
24485 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24486 * @param {Number} startRow The index of the first row in the range
24487 * @param {Number} endRow The index of the last row in the range
24488 * @param {Boolean} keepExisting (optional) True to retain existing selections
24490 selectRange : function(startRow, endRow, keepExisting){
24495 this.clearSelections();
24497 if(startRow <= endRow){
24498 for(var i = startRow; i <= endRow; i++){
24499 this.selectRow(i, true);
24502 for(var i = startRow; i >= endRow; i--){
24503 this.selectRow(i, true);
24509 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24510 * @param {Number} startRow The index of the first row in the range
24511 * @param {Number} endRow The index of the last row in the range
24513 deselectRange : function(startRow, endRow, preventViewNotify){
24517 for(var i = startRow; i <= endRow; i++){
24518 this.deselectRow(i, preventViewNotify);
24524 * @param {Number} row The index of the row to select
24525 * @param {Boolean} keepExisting (optional) True to keep existing selections
24527 selectRow : function(index, keepExisting, preventViewNotify)
24529 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24532 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24533 if(!keepExisting || this.singleSelect){
24534 this.clearSelections();
24537 var r = this.grid.store.getAt(index);
24538 //console.log('selectRow - record id :' + r.id);
24540 this.selections.add(r);
24541 this.last = this.lastActive = index;
24542 if(!preventViewNotify){
24543 var proxy = new Roo.Element(
24544 this.grid.getRowDom(index)
24546 proxy.addClass('bg-info info');
24548 this.fireEvent("rowselect", this, index, r);
24549 this.fireEvent("selectionchange", this);
24555 * @param {Number} row The index of the row to deselect
24557 deselectRow : function(index, preventViewNotify)
24562 if(this.last == index){
24565 if(this.lastActive == index){
24566 this.lastActive = false;
24569 var r = this.grid.store.getAt(index);
24574 this.selections.remove(r);
24575 //.console.log('deselectRow - record id :' + r.id);
24576 if(!preventViewNotify){
24578 var proxy = new Roo.Element(
24579 this.grid.getRowDom(index)
24581 proxy.removeClass('bg-info info');
24583 this.fireEvent("rowdeselect", this, index);
24584 this.fireEvent("selectionchange", this);
24588 restoreLast : function(){
24590 this.last = this._last;
24595 acceptsNav : function(row, col, cm){
24596 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24600 onEditorKey : function(field, e){
24601 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24606 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24608 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24610 }else if(k == e.ENTER && !e.ctrlKey){
24614 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24616 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24618 }else if(k == e.ESC){
24622 g.startEditing(newCell[0], newCell[1]);
24628 * Ext JS Library 1.1.1
24629 * Copyright(c) 2006-2007, Ext JS, LLC.
24631 * Originally Released Under LGPL - original licence link has changed is not relivant.
24634 * <script type="text/javascript">
24638 * @class Roo.bootstrap.PagingToolbar
24639 * @extends Roo.bootstrap.NavSimplebar
24640 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24642 * Create a new PagingToolbar
24643 * @param {Object} config The config object
24644 * @param {Roo.data.Store} store
24646 Roo.bootstrap.PagingToolbar = function(config)
24648 // old args format still supported... - xtype is prefered..
24649 // created from xtype...
24651 this.ds = config.dataSource;
24653 if (config.store && !this.ds) {
24654 this.store= Roo.factory(config.store, Roo.data);
24655 this.ds = this.store;
24656 this.ds.xmodule = this.xmodule || false;
24659 this.toolbarItems = [];
24660 if (config.items) {
24661 this.toolbarItems = config.items;
24664 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24669 this.bind(this.ds);
24672 if (Roo.bootstrap.version == 4) {
24673 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24675 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24680 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24682 * @cfg {Roo.data.Store} dataSource
24683 * The underlying data store providing the paged data
24686 * @cfg {String/HTMLElement/Element} container
24687 * container The id or element that will contain the toolbar
24690 * @cfg {Boolean} displayInfo
24691 * True to display the displayMsg (defaults to false)
24694 * @cfg {Number} pageSize
24695 * The number of records to display per page (defaults to 20)
24699 * @cfg {String} displayMsg
24700 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24702 displayMsg : 'Displaying {0} - {1} of {2}',
24704 * @cfg {String} emptyMsg
24705 * The message to display when no records are found (defaults to "No data to display")
24707 emptyMsg : 'No data to display',
24709 * Customizable piece of the default paging text (defaults to "Page")
24712 beforePageText : "Page",
24714 * Customizable piece of the default paging text (defaults to "of %0")
24717 afterPageText : "of {0}",
24719 * Customizable piece of the default paging text (defaults to "First Page")
24722 firstText : "First Page",
24724 * Customizable piece of the default paging text (defaults to "Previous Page")
24727 prevText : "Previous Page",
24729 * Customizable piece of the default paging text (defaults to "Next Page")
24732 nextText : "Next Page",
24734 * Customizable piece of the default paging text (defaults to "Last Page")
24737 lastText : "Last Page",
24739 * Customizable piece of the default paging text (defaults to "Refresh")
24742 refreshText : "Refresh",
24746 onRender : function(ct, position)
24748 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24749 this.navgroup.parentId = this.id;
24750 this.navgroup.onRender(this.el, null);
24751 // add the buttons to the navgroup
24753 if(this.displayInfo){
24754 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24755 this.displayEl = this.el.select('.x-paging-info', true).first();
24756 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24757 // this.displayEl = navel.el.select('span',true).first();
24763 Roo.each(_this.buttons, function(e){ // this might need to use render????
24764 Roo.factory(e).render(_this.el);
24768 Roo.each(_this.toolbarItems, function(e) {
24769 _this.navgroup.addItem(e);
24773 this.first = this.navgroup.addItem({
24774 tooltip: this.firstText,
24775 cls: "prev btn-outline-secondary",
24776 html : ' <i class="fa fa-step-backward"></i>',
24778 preventDefault: true,
24779 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24782 this.prev = this.navgroup.addItem({
24783 tooltip: this.prevText,
24784 cls: "prev btn-outline-secondary",
24785 html : ' <i class="fa fa-backward"></i>',
24787 preventDefault: true,
24788 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24790 //this.addSeparator();
24793 var field = this.navgroup.addItem( {
24795 cls : 'x-paging-position btn-outline-secondary',
24797 html : this.beforePageText +
24798 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24799 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24802 this.field = field.el.select('input', true).first();
24803 this.field.on("keydown", this.onPagingKeydown, this);
24804 this.field.on("focus", function(){this.dom.select();});
24807 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24808 //this.field.setHeight(18);
24809 //this.addSeparator();
24810 this.next = this.navgroup.addItem({
24811 tooltip: this.nextText,
24812 cls: "next btn-outline-secondary",
24813 html : ' <i class="fa fa-forward"></i>',
24815 preventDefault: true,
24816 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24818 this.last = this.navgroup.addItem({
24819 tooltip: this.lastText,
24820 html : ' <i class="fa fa-step-forward"></i>',
24821 cls: "next btn-outline-secondary",
24823 preventDefault: true,
24824 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24826 //this.addSeparator();
24827 this.loading = this.navgroup.addItem({
24828 tooltip: this.refreshText,
24829 cls: "btn-outline-secondary",
24830 html : ' <i class="fa fa-step-refresh"></i>',
24832 preventDefault: true,
24833 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24839 updateInfo : function(){
24840 if(this.displayEl){
24841 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24842 var msg = count == 0 ?
24846 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24848 this.displayEl.update(msg);
24853 onLoad : function(ds, r, o)
24855 this.cursor = o.params.start ? o.params.start : 0;
24857 var d = this.getPageData(),
24862 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24863 this.field.dom.value = ap;
24864 this.first.setDisabled(ap == 1);
24865 this.prev.setDisabled(ap == 1);
24866 this.next.setDisabled(ap == ps);
24867 this.last.setDisabled(ap == ps);
24868 this.loading.enable();
24873 getPageData : function(){
24874 var total = this.ds.getTotalCount();
24877 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24878 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24883 onLoadError : function(){
24884 this.loading.enable();
24888 onPagingKeydown : function(e){
24889 var k = e.getKey();
24890 var d = this.getPageData();
24892 var v = this.field.dom.value, pageNum;
24893 if(!v || isNaN(pageNum = parseInt(v, 10))){
24894 this.field.dom.value = d.activePage;
24897 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24898 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24901 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))
24903 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24904 this.field.dom.value = pageNum;
24905 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24908 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24910 var v = this.field.dom.value, pageNum;
24911 var increment = (e.shiftKey) ? 10 : 1;
24912 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24915 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24916 this.field.dom.value = d.activePage;
24919 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24921 this.field.dom.value = parseInt(v, 10) + increment;
24922 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24923 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24930 beforeLoad : function(){
24932 this.loading.disable();
24937 onClick : function(which){
24946 ds.load({params:{start: 0, limit: this.pageSize}});
24949 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24952 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24955 var total = ds.getTotalCount();
24956 var extra = total % this.pageSize;
24957 var lastStart = extra ? (total - extra) : total-this.pageSize;
24958 ds.load({params:{start: lastStart, limit: this.pageSize}});
24961 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24967 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24968 * @param {Roo.data.Store} store The data store to unbind
24970 unbind : function(ds){
24971 ds.un("beforeload", this.beforeLoad, this);
24972 ds.un("load", this.onLoad, this);
24973 ds.un("loadexception", this.onLoadError, this);
24974 ds.un("remove", this.updateInfo, this);
24975 ds.un("add", this.updateInfo, this);
24976 this.ds = undefined;
24980 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24981 * @param {Roo.data.Store} store The data store to bind
24983 bind : function(ds){
24984 ds.on("beforeload", this.beforeLoad, this);
24985 ds.on("load", this.onLoad, this);
24986 ds.on("loadexception", this.onLoadError, this);
24987 ds.on("remove", this.updateInfo, this);
24988 ds.on("add", this.updateInfo, this);
24999 * @class Roo.bootstrap.MessageBar
25000 * @extends Roo.bootstrap.Component
25001 * Bootstrap MessageBar class
25002 * @cfg {String} html contents of the MessageBar
25003 * @cfg {String} weight (info | success | warning | danger) default info
25004 * @cfg {String} beforeClass insert the bar before the given class
25005 * @cfg {Boolean} closable (true | false) default false
25006 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25009 * Create a new Element
25010 * @param {Object} config The config object
25013 Roo.bootstrap.MessageBar = function(config){
25014 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25017 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25023 beforeClass: 'bootstrap-sticky-wrap',
25025 getAutoCreate : function(){
25029 cls: 'alert alert-dismissable alert-' + this.weight,
25034 html: this.html || ''
25040 cfg.cls += ' alert-messages-fixed';
25054 onRender : function(ct, position)
25056 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25059 var cfg = Roo.apply({}, this.getAutoCreate());
25063 cfg.cls += ' ' + this.cls;
25066 cfg.style = this.style;
25068 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25070 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25073 this.el.select('>button.close').on('click', this.hide, this);
25079 if (!this.rendered) {
25085 this.fireEvent('show', this);
25091 if (!this.rendered) {
25097 this.fireEvent('hide', this);
25100 update : function()
25102 // var e = this.el.dom.firstChild;
25104 // if(this.closable){
25105 // e = e.nextSibling;
25108 // e.data = this.html || '';
25110 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25126 * @class Roo.bootstrap.Graph
25127 * @extends Roo.bootstrap.Component
25128 * Bootstrap Graph class
25132 @cfg {String} graphtype bar | vbar | pie
25133 @cfg {number} g_x coodinator | centre x (pie)
25134 @cfg {number} g_y coodinator | centre y (pie)
25135 @cfg {number} g_r radius (pie)
25136 @cfg {number} g_height height of the chart (respected by all elements in the set)
25137 @cfg {number} g_width width of the chart (respected by all elements in the set)
25138 @cfg {Object} title The title of the chart
25141 -opts (object) options for the chart
25143 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25144 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25146 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.
25147 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25149 o stretch (boolean)
25151 -opts (object) options for the pie
25154 o startAngle (number)
25155 o endAngle (number)
25159 * Create a new Input
25160 * @param {Object} config The config object
25163 Roo.bootstrap.Graph = function(config){
25164 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25170 * The img click event for the img.
25171 * @param {Roo.EventObject} e
25177 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25188 //g_colors: this.colors,
25195 getAutoCreate : function(){
25206 onRender : function(ct,position){
25209 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25211 if (typeof(Raphael) == 'undefined') {
25212 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25216 this.raphael = Raphael(this.el.dom);
25218 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25219 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25220 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25221 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25223 r.text(160, 10, "Single Series Chart").attr(txtattr);
25224 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25225 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25226 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25228 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25229 r.barchart(330, 10, 300, 220, data1);
25230 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25231 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25234 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25235 // r.barchart(30, 30, 560, 250, xdata, {
25236 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25237 // axis : "0 0 1 1",
25238 // axisxlabels : xdata
25239 // //yvalues : cols,
25242 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25244 // this.load(null,xdata,{
25245 // axis : "0 0 1 1",
25246 // axisxlabels : xdata
25251 load : function(graphtype,xdata,opts)
25253 this.raphael.clear();
25255 graphtype = this.graphtype;
25260 var r = this.raphael,
25261 fin = function () {
25262 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25264 fout = function () {
25265 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25267 pfin = function() {
25268 this.sector.stop();
25269 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25272 this.label[0].stop();
25273 this.label[0].attr({ r: 7.5 });
25274 this.label[1].attr({ "font-weight": 800 });
25277 pfout = function() {
25278 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25281 this.label[0].animate({ r: 5 }, 500, "bounce");
25282 this.label[1].attr({ "font-weight": 400 });
25288 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25291 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25294 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25295 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25297 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25304 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25309 setTitle: function(o)
25314 initEvents: function() {
25317 this.el.on('click', this.onClick, this);
25321 onClick : function(e)
25323 Roo.log('img onclick');
25324 this.fireEvent('click', this, e);
25336 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25339 * @class Roo.bootstrap.dash.NumberBox
25340 * @extends Roo.bootstrap.Component
25341 * Bootstrap NumberBox class
25342 * @cfg {String} headline Box headline
25343 * @cfg {String} content Box content
25344 * @cfg {String} icon Box icon
25345 * @cfg {String} footer Footer text
25346 * @cfg {String} fhref Footer href
25349 * Create a new NumberBox
25350 * @param {Object} config The config object
25354 Roo.bootstrap.dash.NumberBox = function(config){
25355 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25359 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25368 getAutoCreate : function(){
25372 cls : 'small-box ',
25380 cls : 'roo-headline',
25381 html : this.headline
25385 cls : 'roo-content',
25386 html : this.content
25400 cls : 'ion ' + this.icon
25409 cls : 'small-box-footer',
25410 href : this.fhref || '#',
25414 cfg.cn.push(footer);
25421 onRender : function(ct,position){
25422 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25429 setHeadline: function (value)
25431 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25434 setFooter: function (value, href)
25436 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25439 this.el.select('a.small-box-footer',true).first().attr('href', href);
25444 setContent: function (value)
25446 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25449 initEvents: function()
25463 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25466 * @class Roo.bootstrap.dash.TabBox
25467 * @extends Roo.bootstrap.Component
25468 * Bootstrap TabBox class
25469 * @cfg {String} title Title of the TabBox
25470 * @cfg {String} icon Icon of the TabBox
25471 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25472 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25475 * Create a new TabBox
25476 * @param {Object} config The config object
25480 Roo.bootstrap.dash.TabBox = function(config){
25481 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25486 * When a pane is added
25487 * @param {Roo.bootstrap.dash.TabPane} pane
25491 * @event activatepane
25492 * When a pane is activated
25493 * @param {Roo.bootstrap.dash.TabPane} pane
25495 "activatepane" : true
25503 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25508 tabScrollable : false,
25510 getChildContainer : function()
25512 return this.el.select('.tab-content', true).first();
25515 getAutoCreate : function(){
25519 cls: 'pull-left header',
25527 cls: 'fa ' + this.icon
25533 cls: 'nav nav-tabs pull-right',
25539 if(this.tabScrollable){
25546 cls: 'nav nav-tabs pull-right',
25557 cls: 'nav-tabs-custom',
25562 cls: 'tab-content no-padding',
25570 initEvents : function()
25572 //Roo.log('add add pane handler');
25573 this.on('addpane', this.onAddPane, this);
25576 * Updates the box title
25577 * @param {String} html to set the title to.
25579 setTitle : function(value)
25581 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25583 onAddPane : function(pane)
25585 this.panes.push(pane);
25586 //Roo.log('addpane');
25588 // tabs are rendere left to right..
25589 if(!this.showtabs){
25593 var ctr = this.el.select('.nav-tabs', true).first();
25596 var existing = ctr.select('.nav-tab',true);
25597 var qty = existing.getCount();;
25600 var tab = ctr.createChild({
25602 cls : 'nav-tab' + (qty ? '' : ' active'),
25610 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25613 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25615 pane.el.addClass('active');
25620 onTabClick : function(ev,un,ob,pane)
25622 //Roo.log('tab - prev default');
25623 ev.preventDefault();
25626 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25627 pane.tab.addClass('active');
25628 //Roo.log(pane.title);
25629 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25630 // technically we should have a deactivate event.. but maybe add later.
25631 // and it should not de-activate the selected tab...
25632 this.fireEvent('activatepane', pane);
25633 pane.el.addClass('active');
25634 pane.fireEvent('activate');
25639 getActivePane : function()
25642 Roo.each(this.panes, function(p) {
25643 if(p.el.hasClass('active')){
25664 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25666 * @class Roo.bootstrap.TabPane
25667 * @extends Roo.bootstrap.Component
25668 * Bootstrap TabPane class
25669 * @cfg {Boolean} active (false | true) Default false
25670 * @cfg {String} title title of panel
25674 * Create a new TabPane
25675 * @param {Object} config The config object
25678 Roo.bootstrap.dash.TabPane = function(config){
25679 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25685 * When a pane is activated
25686 * @param {Roo.bootstrap.dash.TabPane} pane
25693 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25698 // the tabBox that this is attached to.
25701 getAutoCreate : function()
25709 cfg.cls += ' active';
25714 initEvents : function()
25716 //Roo.log('trigger add pane handler');
25717 this.parent().fireEvent('addpane', this)
25721 * Updates the tab title
25722 * @param {String} html to set the title to.
25724 setTitle: function(str)
25730 this.tab.select('a', true).first().dom.innerHTML = str;
25747 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25750 * @class Roo.bootstrap.menu.Menu
25751 * @extends Roo.bootstrap.Component
25752 * Bootstrap Menu class - container for Menu
25753 * @cfg {String} html Text of the menu
25754 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25755 * @cfg {String} icon Font awesome icon
25756 * @cfg {String} pos Menu align to (top | bottom) default bottom
25760 * Create a new Menu
25761 * @param {Object} config The config object
25765 Roo.bootstrap.menu.Menu = function(config){
25766 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25770 * @event beforeshow
25771 * Fires before this menu is displayed
25772 * @param {Roo.bootstrap.menu.Menu} this
25776 * @event beforehide
25777 * Fires before this menu is hidden
25778 * @param {Roo.bootstrap.menu.Menu} this
25783 * Fires after this menu is displayed
25784 * @param {Roo.bootstrap.menu.Menu} this
25789 * Fires after this menu is hidden
25790 * @param {Roo.bootstrap.menu.Menu} this
25795 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25796 * @param {Roo.bootstrap.menu.Menu} this
25797 * @param {Roo.EventObject} e
25804 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25808 weight : 'default',
25813 getChildContainer : function() {
25814 if(this.isSubMenu){
25818 return this.el.select('ul.dropdown-menu', true).first();
25821 getAutoCreate : function()
25826 cls : 'roo-menu-text',
25834 cls : 'fa ' + this.icon
25845 cls : 'dropdown-button btn btn-' + this.weight,
25850 cls : 'dropdown-toggle btn btn-' + this.weight,
25860 cls : 'dropdown-menu'
25866 if(this.pos == 'top'){
25867 cfg.cls += ' dropup';
25870 if(this.isSubMenu){
25873 cls : 'dropdown-menu'
25880 onRender : function(ct, position)
25882 this.isSubMenu = ct.hasClass('dropdown-submenu');
25884 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25887 initEvents : function()
25889 if(this.isSubMenu){
25893 this.hidden = true;
25895 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25896 this.triggerEl.on('click', this.onTriggerPress, this);
25898 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25899 this.buttonEl.on('click', this.onClick, this);
25905 if(this.isSubMenu){
25909 return this.el.select('ul.dropdown-menu', true).first();
25912 onClick : function(e)
25914 this.fireEvent("click", this, e);
25917 onTriggerPress : function(e)
25919 if (this.isVisible()) {
25926 isVisible : function(){
25927 return !this.hidden;
25932 this.fireEvent("beforeshow", this);
25934 this.hidden = false;
25935 this.el.addClass('open');
25937 Roo.get(document).on("mouseup", this.onMouseUp, this);
25939 this.fireEvent("show", this);
25946 this.fireEvent("beforehide", this);
25948 this.hidden = true;
25949 this.el.removeClass('open');
25951 Roo.get(document).un("mouseup", this.onMouseUp);
25953 this.fireEvent("hide", this);
25956 onMouseUp : function()
25970 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25973 * @class Roo.bootstrap.menu.Item
25974 * @extends Roo.bootstrap.Component
25975 * Bootstrap MenuItem class
25976 * @cfg {Boolean} submenu (true | false) default false
25977 * @cfg {String} html text of the item
25978 * @cfg {String} href the link
25979 * @cfg {Boolean} disable (true | false) default false
25980 * @cfg {Boolean} preventDefault (true | false) default true
25981 * @cfg {String} icon Font awesome icon
25982 * @cfg {String} pos Submenu align to (left | right) default right
25986 * Create a new Item
25987 * @param {Object} config The config object
25991 Roo.bootstrap.menu.Item = function(config){
25992 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25996 * Fires when the mouse is hovering over this menu
25997 * @param {Roo.bootstrap.menu.Item} this
25998 * @param {Roo.EventObject} e
26003 * Fires when the mouse exits this menu
26004 * @param {Roo.bootstrap.menu.Item} this
26005 * @param {Roo.EventObject} e
26011 * The raw click event for the entire grid.
26012 * @param {Roo.EventObject} e
26018 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26023 preventDefault: true,
26028 getAutoCreate : function()
26033 cls : 'roo-menu-item-text',
26041 cls : 'fa ' + this.icon
26050 href : this.href || '#',
26057 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26061 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26063 if(this.pos == 'left'){
26064 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26071 initEvents : function()
26073 this.el.on('mouseover', this.onMouseOver, this);
26074 this.el.on('mouseout', this.onMouseOut, this);
26076 this.el.select('a', true).first().on('click', this.onClick, this);
26080 onClick : function(e)
26082 if(this.preventDefault){
26083 e.preventDefault();
26086 this.fireEvent("click", this, e);
26089 onMouseOver : function(e)
26091 if(this.submenu && this.pos == 'left'){
26092 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26095 this.fireEvent("mouseover", this, e);
26098 onMouseOut : function(e)
26100 this.fireEvent("mouseout", this, e);
26112 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26115 * @class Roo.bootstrap.menu.Separator
26116 * @extends Roo.bootstrap.Component
26117 * Bootstrap Separator class
26120 * Create a new Separator
26121 * @param {Object} config The config object
26125 Roo.bootstrap.menu.Separator = function(config){
26126 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26129 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26131 getAutoCreate : function(){
26152 * @class Roo.bootstrap.Tooltip
26153 * Bootstrap Tooltip class
26154 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26155 * to determine which dom element triggers the tooltip.
26157 * It needs to add support for additional attributes like tooltip-position
26160 * Create a new Toolti
26161 * @param {Object} config The config object
26164 Roo.bootstrap.Tooltip = function(config){
26165 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26167 this.alignment = Roo.bootstrap.Tooltip.alignment;
26169 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26170 this.alignment = config.alignment;
26175 Roo.apply(Roo.bootstrap.Tooltip, {
26177 * @function init initialize tooltip monitoring.
26181 currentTip : false,
26182 currentRegion : false,
26188 Roo.get(document).on('mouseover', this.enter ,this);
26189 Roo.get(document).on('mouseout', this.leave, this);
26192 this.currentTip = new Roo.bootstrap.Tooltip();
26195 enter : function(ev)
26197 var dom = ev.getTarget();
26199 //Roo.log(['enter',dom]);
26200 var el = Roo.fly(dom);
26201 if (this.currentEl) {
26203 //Roo.log(this.currentEl);
26204 //Roo.log(this.currentEl.contains(dom));
26205 if (this.currentEl == el) {
26208 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26214 if (this.currentTip.el) {
26215 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26219 if(!el || el.dom == document){
26225 // you can not look for children, as if el is the body.. then everythign is the child..
26226 if (!el.attr('tooltip')) { //
26227 if (!el.select("[tooltip]").elements.length) {
26230 // is the mouse over this child...?
26231 bindEl = el.select("[tooltip]").first();
26232 var xy = ev.getXY();
26233 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26234 //Roo.log("not in region.");
26237 //Roo.log("child element over..");
26240 this.currentEl = bindEl;
26241 this.currentTip.bind(bindEl);
26242 this.currentRegion = Roo.lib.Region.getRegion(dom);
26243 this.currentTip.enter();
26246 leave : function(ev)
26248 var dom = ev.getTarget();
26249 //Roo.log(['leave',dom]);
26250 if (!this.currentEl) {
26255 if (dom != this.currentEl.dom) {
26258 var xy = ev.getXY();
26259 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26262 // only activate leave if mouse cursor is outside... bounding box..
26267 if (this.currentTip) {
26268 this.currentTip.leave();
26270 //Roo.log('clear currentEl');
26271 this.currentEl = false;
26276 'left' : ['r-l', [-2,0], 'right'],
26277 'right' : ['l-r', [2,0], 'left'],
26278 'bottom' : ['t-b', [0,2], 'top'],
26279 'top' : [ 'b-t', [0,-2], 'bottom']
26285 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26290 delay : null, // can be { show : 300 , hide: 500}
26294 hoverState : null, //???
26296 placement : 'bottom',
26300 getAutoCreate : function(){
26307 cls : 'tooltip-arrow'
26310 cls : 'tooltip-inner'
26317 bind : function(el)
26323 enter : function () {
26325 if (this.timeout != null) {
26326 clearTimeout(this.timeout);
26329 this.hoverState = 'in';
26330 //Roo.log("enter - show");
26331 if (!this.delay || !this.delay.show) {
26336 this.timeout = setTimeout(function () {
26337 if (_t.hoverState == 'in') {
26340 }, this.delay.show);
26344 clearTimeout(this.timeout);
26346 this.hoverState = 'out';
26347 if (!this.delay || !this.delay.hide) {
26353 this.timeout = setTimeout(function () {
26354 //Roo.log("leave - timeout");
26356 if (_t.hoverState == 'out') {
26358 Roo.bootstrap.Tooltip.currentEl = false;
26363 show : function (msg)
26366 this.render(document.body);
26369 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26371 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26373 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26375 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26377 var placement = typeof this.placement == 'function' ?
26378 this.placement.call(this, this.el, on_el) :
26381 var autoToken = /\s?auto?\s?/i;
26382 var autoPlace = autoToken.test(placement);
26384 placement = placement.replace(autoToken, '') || 'top';
26388 //this.el.setXY([0,0]);
26390 //this.el.dom.style.display='block';
26392 //this.el.appendTo(on_el);
26394 var p = this.getPosition();
26395 var box = this.el.getBox();
26401 var align = this.alignment[placement];
26403 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26405 if(placement == 'top' || placement == 'bottom'){
26407 placement = 'right';
26410 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26411 placement = 'left';
26414 var scroll = Roo.select('body', true).first().getScroll();
26416 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26420 align = this.alignment[placement];
26423 this.el.alignTo(this.bindEl, align[0],align[1]);
26424 //var arrow = this.el.select('.arrow',true).first();
26425 //arrow.set(align[2],
26427 this.el.addClass(placement);
26429 this.el.addClass('in fade');
26431 this.hoverState = null;
26433 if (this.el.hasClass('fade')) {
26444 //this.el.setXY([0,0]);
26445 this.el.removeClass('in');
26461 * @class Roo.bootstrap.LocationPicker
26462 * @extends Roo.bootstrap.Component
26463 * Bootstrap LocationPicker class
26464 * @cfg {Number} latitude Position when init default 0
26465 * @cfg {Number} longitude Position when init default 0
26466 * @cfg {Number} zoom default 15
26467 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26468 * @cfg {Boolean} mapTypeControl default false
26469 * @cfg {Boolean} disableDoubleClickZoom default false
26470 * @cfg {Boolean} scrollwheel default true
26471 * @cfg {Boolean} streetViewControl default false
26472 * @cfg {Number} radius default 0
26473 * @cfg {String} locationName
26474 * @cfg {Boolean} draggable default true
26475 * @cfg {Boolean} enableAutocomplete default false
26476 * @cfg {Boolean} enableReverseGeocode default true
26477 * @cfg {String} markerTitle
26480 * Create a new LocationPicker
26481 * @param {Object} config The config object
26485 Roo.bootstrap.LocationPicker = function(config){
26487 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26492 * Fires when the picker initialized.
26493 * @param {Roo.bootstrap.LocationPicker} this
26494 * @param {Google Location} location
26498 * @event positionchanged
26499 * Fires when the picker position changed.
26500 * @param {Roo.bootstrap.LocationPicker} this
26501 * @param {Google Location} location
26503 positionchanged : true,
26506 * Fires when the map resize.
26507 * @param {Roo.bootstrap.LocationPicker} this
26512 * Fires when the map show.
26513 * @param {Roo.bootstrap.LocationPicker} this
26518 * Fires when the map hide.
26519 * @param {Roo.bootstrap.LocationPicker} this
26524 * Fires when click the map.
26525 * @param {Roo.bootstrap.LocationPicker} this
26526 * @param {Map event} e
26530 * @event mapRightClick
26531 * Fires when right click the map.
26532 * @param {Roo.bootstrap.LocationPicker} this
26533 * @param {Map event} e
26535 mapRightClick : true,
26537 * @event markerClick
26538 * Fires when click the marker.
26539 * @param {Roo.bootstrap.LocationPicker} this
26540 * @param {Map event} e
26542 markerClick : true,
26544 * @event markerRightClick
26545 * Fires when right click the marker.
26546 * @param {Roo.bootstrap.LocationPicker} this
26547 * @param {Map event} e
26549 markerRightClick : true,
26551 * @event OverlayViewDraw
26552 * Fires when OverlayView Draw
26553 * @param {Roo.bootstrap.LocationPicker} this
26555 OverlayViewDraw : true,
26557 * @event OverlayViewOnAdd
26558 * Fires when OverlayView Draw
26559 * @param {Roo.bootstrap.LocationPicker} this
26561 OverlayViewOnAdd : true,
26563 * @event OverlayViewOnRemove
26564 * Fires when OverlayView Draw
26565 * @param {Roo.bootstrap.LocationPicker} this
26567 OverlayViewOnRemove : true,
26569 * @event OverlayViewShow
26570 * Fires when OverlayView Draw
26571 * @param {Roo.bootstrap.LocationPicker} this
26572 * @param {Pixel} cpx
26574 OverlayViewShow : true,
26576 * @event OverlayViewHide
26577 * Fires when OverlayView Draw
26578 * @param {Roo.bootstrap.LocationPicker} this
26580 OverlayViewHide : true,
26582 * @event loadexception
26583 * Fires when load google lib failed.
26584 * @param {Roo.bootstrap.LocationPicker} this
26586 loadexception : true
26591 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26593 gMapContext: false,
26599 mapTypeControl: false,
26600 disableDoubleClickZoom: false,
26602 streetViewControl: false,
26606 enableAutocomplete: false,
26607 enableReverseGeocode: true,
26610 getAutoCreate: function()
26615 cls: 'roo-location-picker'
26621 initEvents: function(ct, position)
26623 if(!this.el.getWidth() || this.isApplied()){
26627 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26632 initial: function()
26634 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26635 this.fireEvent('loadexception', this);
26639 if(!this.mapTypeId){
26640 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26643 this.gMapContext = this.GMapContext();
26645 this.initOverlayView();
26647 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26651 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26652 _this.setPosition(_this.gMapContext.marker.position);
26655 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26656 _this.fireEvent('mapClick', this, event);
26660 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26661 _this.fireEvent('mapRightClick', this, event);
26665 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26666 _this.fireEvent('markerClick', this, event);
26670 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26671 _this.fireEvent('markerRightClick', this, event);
26675 this.setPosition(this.gMapContext.location);
26677 this.fireEvent('initial', this, this.gMapContext.location);
26680 initOverlayView: function()
26684 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26688 _this.fireEvent('OverlayViewDraw', _this);
26693 _this.fireEvent('OverlayViewOnAdd', _this);
26696 onRemove: function()
26698 _this.fireEvent('OverlayViewOnRemove', _this);
26701 show: function(cpx)
26703 _this.fireEvent('OverlayViewShow', _this, cpx);
26708 _this.fireEvent('OverlayViewHide', _this);
26714 fromLatLngToContainerPixel: function(event)
26716 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26719 isApplied: function()
26721 return this.getGmapContext() == false ? false : true;
26724 getGmapContext: function()
26726 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26729 GMapContext: function()
26731 var position = new google.maps.LatLng(this.latitude, this.longitude);
26733 var _map = new google.maps.Map(this.el.dom, {
26736 mapTypeId: this.mapTypeId,
26737 mapTypeControl: this.mapTypeControl,
26738 disableDoubleClickZoom: this.disableDoubleClickZoom,
26739 scrollwheel: this.scrollwheel,
26740 streetViewControl: this.streetViewControl,
26741 locationName: this.locationName,
26742 draggable: this.draggable,
26743 enableAutocomplete: this.enableAutocomplete,
26744 enableReverseGeocode: this.enableReverseGeocode
26747 var _marker = new google.maps.Marker({
26748 position: position,
26750 title: this.markerTitle,
26751 draggable: this.draggable
26758 location: position,
26759 radius: this.radius,
26760 locationName: this.locationName,
26761 addressComponents: {
26762 formatted_address: null,
26763 addressLine1: null,
26764 addressLine2: null,
26766 streetNumber: null,
26770 stateOrProvince: null
26773 domContainer: this.el.dom,
26774 geodecoder: new google.maps.Geocoder()
26778 drawCircle: function(center, radius, options)
26780 if (this.gMapContext.circle != null) {
26781 this.gMapContext.circle.setMap(null);
26785 options = Roo.apply({}, options, {
26786 strokeColor: "#0000FF",
26787 strokeOpacity: .35,
26789 fillColor: "#0000FF",
26793 options.map = this.gMapContext.map;
26794 options.radius = radius;
26795 options.center = center;
26796 this.gMapContext.circle = new google.maps.Circle(options);
26797 return this.gMapContext.circle;
26803 setPosition: function(location)
26805 this.gMapContext.location = location;
26806 this.gMapContext.marker.setPosition(location);
26807 this.gMapContext.map.panTo(location);
26808 this.drawCircle(location, this.gMapContext.radius, {});
26812 if (this.gMapContext.settings.enableReverseGeocode) {
26813 this.gMapContext.geodecoder.geocode({
26814 latLng: this.gMapContext.location
26815 }, function(results, status) {
26817 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26818 _this.gMapContext.locationName = results[0].formatted_address;
26819 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26821 _this.fireEvent('positionchanged', this, location);
26828 this.fireEvent('positionchanged', this, location);
26833 google.maps.event.trigger(this.gMapContext.map, "resize");
26835 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26837 this.fireEvent('resize', this);
26840 setPositionByLatLng: function(latitude, longitude)
26842 this.setPosition(new google.maps.LatLng(latitude, longitude));
26845 getCurrentPosition: function()
26848 latitude: this.gMapContext.location.lat(),
26849 longitude: this.gMapContext.location.lng()
26853 getAddressName: function()
26855 return this.gMapContext.locationName;
26858 getAddressComponents: function()
26860 return this.gMapContext.addressComponents;
26863 address_component_from_google_geocode: function(address_components)
26867 for (var i = 0; i < address_components.length; i++) {
26868 var component = address_components[i];
26869 if (component.types.indexOf("postal_code") >= 0) {
26870 result.postalCode = component.short_name;
26871 } else if (component.types.indexOf("street_number") >= 0) {
26872 result.streetNumber = component.short_name;
26873 } else if (component.types.indexOf("route") >= 0) {
26874 result.streetName = component.short_name;
26875 } else if (component.types.indexOf("neighborhood") >= 0) {
26876 result.city = component.short_name;
26877 } else if (component.types.indexOf("locality") >= 0) {
26878 result.city = component.short_name;
26879 } else if (component.types.indexOf("sublocality") >= 0) {
26880 result.district = component.short_name;
26881 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26882 result.stateOrProvince = component.short_name;
26883 } else if (component.types.indexOf("country") >= 0) {
26884 result.country = component.short_name;
26888 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26889 result.addressLine2 = "";
26893 setZoomLevel: function(zoom)
26895 this.gMapContext.map.setZoom(zoom);
26908 this.fireEvent('show', this);
26919 this.fireEvent('hide', this);
26924 Roo.apply(Roo.bootstrap.LocationPicker, {
26926 OverlayView : function(map, options)
26928 options = options || {};
26942 * @class Roo.bootstrap.Alert
26943 * @extends Roo.bootstrap.Component
26944 * Bootstrap Alert class
26945 * @cfg {String} title The title of alert
26946 * @cfg {String} html The content of alert
26947 * @cfg {String} weight ( success | info | warning | danger )
26948 * @cfg {String} faicon font-awesomeicon
26951 * Create a new alert
26952 * @param {Object} config The config object
26956 Roo.bootstrap.Alert = function(config){
26957 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26961 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26968 getAutoCreate : function()
26977 cls : 'roo-alert-icon'
26982 cls : 'roo-alert-title',
26987 cls : 'roo-alert-text',
26994 cfg.cn[0].cls += ' fa ' + this.faicon;
26998 cfg.cls += ' alert-' + this.weight;
27004 initEvents: function()
27006 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27009 setTitle : function(str)
27011 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27014 setText : function(str)
27016 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27019 setWeight : function(weight)
27022 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27025 this.weight = weight;
27027 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27030 setIcon : function(icon)
27033 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27036 this.faicon = icon;
27038 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27059 * @class Roo.bootstrap.UploadCropbox
27060 * @extends Roo.bootstrap.Component
27061 * Bootstrap UploadCropbox class
27062 * @cfg {String} emptyText show when image has been loaded
27063 * @cfg {String} rotateNotify show when image too small to rotate
27064 * @cfg {Number} errorTimeout default 3000
27065 * @cfg {Number} minWidth default 300
27066 * @cfg {Number} minHeight default 300
27067 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27068 * @cfg {Boolean} isDocument (true|false) default false
27069 * @cfg {String} url action url
27070 * @cfg {String} paramName default 'imageUpload'
27071 * @cfg {String} method default POST
27072 * @cfg {Boolean} loadMask (true|false) default true
27073 * @cfg {Boolean} loadingText default 'Loading...'
27076 * Create a new UploadCropbox
27077 * @param {Object} config The config object
27080 Roo.bootstrap.UploadCropbox = function(config){
27081 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27085 * @event beforeselectfile
27086 * Fire before select file
27087 * @param {Roo.bootstrap.UploadCropbox} this
27089 "beforeselectfile" : true,
27092 * Fire after initEvent
27093 * @param {Roo.bootstrap.UploadCropbox} this
27098 * Fire after initEvent
27099 * @param {Roo.bootstrap.UploadCropbox} this
27100 * @param {String} data
27105 * Fire when preparing the file data
27106 * @param {Roo.bootstrap.UploadCropbox} this
27107 * @param {Object} file
27112 * Fire when get exception
27113 * @param {Roo.bootstrap.UploadCropbox} this
27114 * @param {XMLHttpRequest} xhr
27116 "exception" : true,
27118 * @event beforeloadcanvas
27119 * Fire before load the canvas
27120 * @param {Roo.bootstrap.UploadCropbox} this
27121 * @param {String} src
27123 "beforeloadcanvas" : true,
27126 * Fire when trash image
27127 * @param {Roo.bootstrap.UploadCropbox} this
27132 * Fire when download the image
27133 * @param {Roo.bootstrap.UploadCropbox} this
27137 * @event footerbuttonclick
27138 * Fire when footerbuttonclick
27139 * @param {Roo.bootstrap.UploadCropbox} this
27140 * @param {String} type
27142 "footerbuttonclick" : true,
27146 * @param {Roo.bootstrap.UploadCropbox} this
27151 * Fire when rotate the image
27152 * @param {Roo.bootstrap.UploadCropbox} this
27153 * @param {String} pos
27158 * Fire when inspect the file
27159 * @param {Roo.bootstrap.UploadCropbox} this
27160 * @param {Object} file
27165 * Fire when xhr upload the file
27166 * @param {Roo.bootstrap.UploadCropbox} this
27167 * @param {Object} data
27172 * Fire when arrange the file data
27173 * @param {Roo.bootstrap.UploadCropbox} this
27174 * @param {Object} formData
27179 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27182 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27184 emptyText : 'Click to upload image',
27185 rotateNotify : 'Image is too small to rotate',
27186 errorTimeout : 3000,
27200 cropType : 'image/jpeg',
27202 canvasLoaded : false,
27203 isDocument : false,
27205 paramName : 'imageUpload',
27207 loadingText : 'Loading...',
27210 getAutoCreate : function()
27214 cls : 'roo-upload-cropbox',
27218 cls : 'roo-upload-cropbox-selector',
27223 cls : 'roo-upload-cropbox-body',
27224 style : 'cursor:pointer',
27228 cls : 'roo-upload-cropbox-preview'
27232 cls : 'roo-upload-cropbox-thumb'
27236 cls : 'roo-upload-cropbox-empty-notify',
27237 html : this.emptyText
27241 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27242 html : this.rotateNotify
27248 cls : 'roo-upload-cropbox-footer',
27251 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27261 onRender : function(ct, position)
27263 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27265 if (this.buttons.length) {
27267 Roo.each(this.buttons, function(bb) {
27269 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27271 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27277 this.maskEl = this.el;
27281 initEvents : function()
27283 this.urlAPI = (window.createObjectURL && window) ||
27284 (window.URL && URL.revokeObjectURL && URL) ||
27285 (window.webkitURL && webkitURL);
27287 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27288 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27290 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27291 this.selectorEl.hide();
27293 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27294 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27296 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27297 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27298 this.thumbEl.hide();
27300 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27301 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27303 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27304 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27305 this.errorEl.hide();
27307 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27308 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27309 this.footerEl.hide();
27311 this.setThumbBoxSize();
27317 this.fireEvent('initial', this);
27324 window.addEventListener("resize", function() { _this.resize(); } );
27326 this.bodyEl.on('click', this.beforeSelectFile, this);
27329 this.bodyEl.on('touchstart', this.onTouchStart, this);
27330 this.bodyEl.on('touchmove', this.onTouchMove, this);
27331 this.bodyEl.on('touchend', this.onTouchEnd, this);
27335 this.bodyEl.on('mousedown', this.onMouseDown, this);
27336 this.bodyEl.on('mousemove', this.onMouseMove, this);
27337 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27338 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27339 Roo.get(document).on('mouseup', this.onMouseUp, this);
27342 this.selectorEl.on('change', this.onFileSelected, this);
27348 this.baseScale = 1;
27350 this.baseRotate = 1;
27351 this.dragable = false;
27352 this.pinching = false;
27355 this.cropData = false;
27356 this.notifyEl.dom.innerHTML = this.emptyText;
27358 this.selectorEl.dom.value = '';
27362 resize : function()
27364 if(this.fireEvent('resize', this) != false){
27365 this.setThumbBoxPosition();
27366 this.setCanvasPosition();
27370 onFooterButtonClick : function(e, el, o, type)
27373 case 'rotate-left' :
27374 this.onRotateLeft(e);
27376 case 'rotate-right' :
27377 this.onRotateRight(e);
27380 this.beforeSelectFile(e);
27395 this.fireEvent('footerbuttonclick', this, type);
27398 beforeSelectFile : function(e)
27400 e.preventDefault();
27402 if(this.fireEvent('beforeselectfile', this) != false){
27403 this.selectorEl.dom.click();
27407 onFileSelected : function(e)
27409 e.preventDefault();
27411 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27415 var file = this.selectorEl.dom.files[0];
27417 if(this.fireEvent('inspect', this, file) != false){
27418 this.prepare(file);
27423 trash : function(e)
27425 this.fireEvent('trash', this);
27428 download : function(e)
27430 this.fireEvent('download', this);
27433 loadCanvas : function(src)
27435 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27439 this.imageEl = document.createElement('img');
27443 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27445 this.imageEl.src = src;
27449 onLoadCanvas : function()
27451 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27452 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27454 this.bodyEl.un('click', this.beforeSelectFile, this);
27456 this.notifyEl.hide();
27457 this.thumbEl.show();
27458 this.footerEl.show();
27460 this.baseRotateLevel();
27462 if(this.isDocument){
27463 this.setThumbBoxSize();
27466 this.setThumbBoxPosition();
27468 this.baseScaleLevel();
27474 this.canvasLoaded = true;
27477 this.maskEl.unmask();
27482 setCanvasPosition : function()
27484 if(!this.canvasEl){
27488 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27489 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27491 this.previewEl.setLeft(pw);
27492 this.previewEl.setTop(ph);
27496 onMouseDown : function(e)
27500 this.dragable = true;
27501 this.pinching = false;
27503 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27504 this.dragable = false;
27508 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27509 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27513 onMouseMove : function(e)
27517 if(!this.canvasLoaded){
27521 if (!this.dragable){
27525 var minX = Math.ceil(this.thumbEl.getLeft(true));
27526 var minY = Math.ceil(this.thumbEl.getTop(true));
27528 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27529 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27531 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27532 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27534 x = x - this.mouseX;
27535 y = y - this.mouseY;
27537 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27538 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27540 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27541 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27543 this.previewEl.setLeft(bgX);
27544 this.previewEl.setTop(bgY);
27546 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27547 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27550 onMouseUp : function(e)
27554 this.dragable = false;
27557 onMouseWheel : function(e)
27561 this.startScale = this.scale;
27563 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27565 if(!this.zoomable()){
27566 this.scale = this.startScale;
27575 zoomable : function()
27577 var minScale = this.thumbEl.getWidth() / this.minWidth;
27579 if(this.minWidth < this.minHeight){
27580 minScale = this.thumbEl.getHeight() / this.minHeight;
27583 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27584 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27588 (this.rotate == 0 || this.rotate == 180) &&
27590 width > this.imageEl.OriginWidth ||
27591 height > this.imageEl.OriginHeight ||
27592 (width < this.minWidth && height < this.minHeight)
27600 (this.rotate == 90 || this.rotate == 270) &&
27602 width > this.imageEl.OriginWidth ||
27603 height > this.imageEl.OriginHeight ||
27604 (width < this.minHeight && height < this.minWidth)
27611 !this.isDocument &&
27612 (this.rotate == 0 || this.rotate == 180) &&
27614 width < this.minWidth ||
27615 width > this.imageEl.OriginWidth ||
27616 height < this.minHeight ||
27617 height > this.imageEl.OriginHeight
27624 !this.isDocument &&
27625 (this.rotate == 90 || this.rotate == 270) &&
27627 width < this.minHeight ||
27628 width > this.imageEl.OriginWidth ||
27629 height < this.minWidth ||
27630 height > this.imageEl.OriginHeight
27640 onRotateLeft : function(e)
27642 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27644 var minScale = this.thumbEl.getWidth() / this.minWidth;
27646 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27647 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27649 this.startScale = this.scale;
27651 while (this.getScaleLevel() < minScale){
27653 this.scale = this.scale + 1;
27655 if(!this.zoomable()){
27660 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27661 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27666 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27673 this.scale = this.startScale;
27675 this.onRotateFail();
27680 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27682 if(this.isDocument){
27683 this.setThumbBoxSize();
27684 this.setThumbBoxPosition();
27685 this.setCanvasPosition();
27690 this.fireEvent('rotate', this, 'left');
27694 onRotateRight : function(e)
27696 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27698 var minScale = this.thumbEl.getWidth() / this.minWidth;
27700 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27701 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27703 this.startScale = this.scale;
27705 while (this.getScaleLevel() < minScale){
27707 this.scale = this.scale + 1;
27709 if(!this.zoomable()){
27714 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27715 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27720 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27727 this.scale = this.startScale;
27729 this.onRotateFail();
27734 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27736 if(this.isDocument){
27737 this.setThumbBoxSize();
27738 this.setThumbBoxPosition();
27739 this.setCanvasPosition();
27744 this.fireEvent('rotate', this, 'right');
27747 onRotateFail : function()
27749 this.errorEl.show(true);
27753 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27758 this.previewEl.dom.innerHTML = '';
27760 var canvasEl = document.createElement("canvas");
27762 var contextEl = canvasEl.getContext("2d");
27764 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27765 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27766 var center = this.imageEl.OriginWidth / 2;
27768 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27769 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27770 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27771 center = this.imageEl.OriginHeight / 2;
27774 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27776 contextEl.translate(center, center);
27777 contextEl.rotate(this.rotate * Math.PI / 180);
27779 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27781 this.canvasEl = document.createElement("canvas");
27783 this.contextEl = this.canvasEl.getContext("2d");
27785 switch (this.rotate) {
27788 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27789 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27791 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27796 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27797 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27799 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27800 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);
27804 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27809 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27810 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27812 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27813 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);
27817 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);
27822 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27823 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27825 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27826 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27830 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);
27837 this.previewEl.appendChild(this.canvasEl);
27839 this.setCanvasPosition();
27844 if(!this.canvasLoaded){
27848 var imageCanvas = document.createElement("canvas");
27850 var imageContext = imageCanvas.getContext("2d");
27852 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27853 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27855 var center = imageCanvas.width / 2;
27857 imageContext.translate(center, center);
27859 imageContext.rotate(this.rotate * Math.PI / 180);
27861 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27863 var canvas = document.createElement("canvas");
27865 var context = canvas.getContext("2d");
27867 canvas.width = this.minWidth;
27868 canvas.height = this.minHeight;
27870 switch (this.rotate) {
27873 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27874 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27876 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27877 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27879 var targetWidth = this.minWidth - 2 * x;
27880 var targetHeight = this.minHeight - 2 * y;
27884 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27885 scale = targetWidth / width;
27888 if(x > 0 && y == 0){
27889 scale = targetHeight / height;
27892 if(x > 0 && y > 0){
27893 scale = targetWidth / width;
27895 if(width < height){
27896 scale = targetHeight / height;
27900 context.scale(scale, scale);
27902 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27903 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27905 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27906 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27908 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27913 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27914 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27916 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27917 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27919 var targetWidth = this.minWidth - 2 * x;
27920 var targetHeight = this.minHeight - 2 * y;
27924 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27925 scale = targetWidth / width;
27928 if(x > 0 && y == 0){
27929 scale = targetHeight / height;
27932 if(x > 0 && y > 0){
27933 scale = targetWidth / width;
27935 if(width < height){
27936 scale = targetHeight / height;
27940 context.scale(scale, scale);
27942 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27943 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27945 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27946 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27948 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27950 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27955 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27956 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27958 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27959 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27961 var targetWidth = this.minWidth - 2 * x;
27962 var targetHeight = this.minHeight - 2 * y;
27966 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27967 scale = targetWidth / width;
27970 if(x > 0 && y == 0){
27971 scale = targetHeight / height;
27974 if(x > 0 && y > 0){
27975 scale = targetWidth / width;
27977 if(width < height){
27978 scale = targetHeight / height;
27982 context.scale(scale, scale);
27984 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27985 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27987 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27988 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27990 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27991 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27993 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27998 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27999 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28001 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28002 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28004 var targetWidth = this.minWidth - 2 * x;
28005 var targetHeight = this.minHeight - 2 * y;
28009 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28010 scale = targetWidth / width;
28013 if(x > 0 && y == 0){
28014 scale = targetHeight / height;
28017 if(x > 0 && y > 0){
28018 scale = targetWidth / width;
28020 if(width < height){
28021 scale = targetHeight / height;
28025 context.scale(scale, scale);
28027 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28028 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28030 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28031 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28033 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28035 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28042 this.cropData = canvas.toDataURL(this.cropType);
28044 if(this.fireEvent('crop', this, this.cropData) !== false){
28045 this.process(this.file, this.cropData);
28052 setThumbBoxSize : function()
28056 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28057 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28058 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28060 this.minWidth = width;
28061 this.minHeight = height;
28063 if(this.rotate == 90 || this.rotate == 270){
28064 this.minWidth = height;
28065 this.minHeight = width;
28070 width = Math.ceil(this.minWidth * height / this.minHeight);
28072 if(this.minWidth > this.minHeight){
28074 height = Math.ceil(this.minHeight * width / this.minWidth);
28077 this.thumbEl.setStyle({
28078 width : width + 'px',
28079 height : height + 'px'
28086 setThumbBoxPosition : function()
28088 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28089 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28091 this.thumbEl.setLeft(x);
28092 this.thumbEl.setTop(y);
28096 baseRotateLevel : function()
28098 this.baseRotate = 1;
28101 typeof(this.exif) != 'undefined' &&
28102 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28103 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28105 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28108 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28112 baseScaleLevel : function()
28116 if(this.isDocument){
28118 if(this.baseRotate == 6 || this.baseRotate == 8){
28120 height = this.thumbEl.getHeight();
28121 this.baseScale = height / this.imageEl.OriginWidth;
28123 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28124 width = this.thumbEl.getWidth();
28125 this.baseScale = width / this.imageEl.OriginHeight;
28131 height = this.thumbEl.getHeight();
28132 this.baseScale = height / this.imageEl.OriginHeight;
28134 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28135 width = this.thumbEl.getWidth();
28136 this.baseScale = width / this.imageEl.OriginWidth;
28142 if(this.baseRotate == 6 || this.baseRotate == 8){
28144 width = this.thumbEl.getHeight();
28145 this.baseScale = width / this.imageEl.OriginHeight;
28147 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28148 height = this.thumbEl.getWidth();
28149 this.baseScale = height / this.imageEl.OriginHeight;
28152 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28153 height = this.thumbEl.getWidth();
28154 this.baseScale = height / this.imageEl.OriginHeight;
28156 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28157 width = this.thumbEl.getHeight();
28158 this.baseScale = width / this.imageEl.OriginWidth;
28165 width = this.thumbEl.getWidth();
28166 this.baseScale = width / this.imageEl.OriginWidth;
28168 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28169 height = this.thumbEl.getHeight();
28170 this.baseScale = height / this.imageEl.OriginHeight;
28173 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28175 height = this.thumbEl.getHeight();
28176 this.baseScale = height / this.imageEl.OriginHeight;
28178 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28179 width = this.thumbEl.getWidth();
28180 this.baseScale = width / this.imageEl.OriginWidth;
28188 getScaleLevel : function()
28190 return this.baseScale * Math.pow(1.1, this.scale);
28193 onTouchStart : function(e)
28195 if(!this.canvasLoaded){
28196 this.beforeSelectFile(e);
28200 var touches = e.browserEvent.touches;
28206 if(touches.length == 1){
28207 this.onMouseDown(e);
28211 if(touches.length != 2){
28217 for(var i = 0, finger; finger = touches[i]; i++){
28218 coords.push(finger.pageX, finger.pageY);
28221 var x = Math.pow(coords[0] - coords[2], 2);
28222 var y = Math.pow(coords[1] - coords[3], 2);
28224 this.startDistance = Math.sqrt(x + y);
28226 this.startScale = this.scale;
28228 this.pinching = true;
28229 this.dragable = false;
28233 onTouchMove : function(e)
28235 if(!this.pinching && !this.dragable){
28239 var touches = e.browserEvent.touches;
28246 this.onMouseMove(e);
28252 for(var i = 0, finger; finger = touches[i]; i++){
28253 coords.push(finger.pageX, finger.pageY);
28256 var x = Math.pow(coords[0] - coords[2], 2);
28257 var y = Math.pow(coords[1] - coords[3], 2);
28259 this.endDistance = Math.sqrt(x + y);
28261 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28263 if(!this.zoomable()){
28264 this.scale = this.startScale;
28272 onTouchEnd : function(e)
28274 this.pinching = false;
28275 this.dragable = false;
28279 process : function(file, crop)
28282 this.maskEl.mask(this.loadingText);
28285 this.xhr = new XMLHttpRequest();
28287 file.xhr = this.xhr;
28289 this.xhr.open(this.method, this.url, true);
28292 "Accept": "application/json",
28293 "Cache-Control": "no-cache",
28294 "X-Requested-With": "XMLHttpRequest"
28297 for (var headerName in headers) {
28298 var headerValue = headers[headerName];
28300 this.xhr.setRequestHeader(headerName, headerValue);
28306 this.xhr.onload = function()
28308 _this.xhrOnLoad(_this.xhr);
28311 this.xhr.onerror = function()
28313 _this.xhrOnError(_this.xhr);
28316 var formData = new FormData();
28318 formData.append('returnHTML', 'NO');
28321 formData.append('crop', crop);
28324 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28325 formData.append(this.paramName, file, file.name);
28328 if(typeof(file.filename) != 'undefined'){
28329 formData.append('filename', file.filename);
28332 if(typeof(file.mimetype) != 'undefined'){
28333 formData.append('mimetype', file.mimetype);
28336 if(this.fireEvent('arrange', this, formData) != false){
28337 this.xhr.send(formData);
28341 xhrOnLoad : function(xhr)
28344 this.maskEl.unmask();
28347 if (xhr.readyState !== 4) {
28348 this.fireEvent('exception', this, xhr);
28352 var response = Roo.decode(xhr.responseText);
28354 if(!response.success){
28355 this.fireEvent('exception', this, xhr);
28359 var response = Roo.decode(xhr.responseText);
28361 this.fireEvent('upload', this, response);
28365 xhrOnError : function()
28368 this.maskEl.unmask();
28371 Roo.log('xhr on error');
28373 var response = Roo.decode(xhr.responseText);
28379 prepare : function(file)
28382 this.maskEl.mask(this.loadingText);
28388 if(typeof(file) === 'string'){
28389 this.loadCanvas(file);
28393 if(!file || !this.urlAPI){
28398 this.cropType = file.type;
28402 if(this.fireEvent('prepare', this, this.file) != false){
28404 var reader = new FileReader();
28406 reader.onload = function (e) {
28407 if (e.target.error) {
28408 Roo.log(e.target.error);
28412 var buffer = e.target.result,
28413 dataView = new DataView(buffer),
28415 maxOffset = dataView.byteLength - 4,
28419 if (dataView.getUint16(0) === 0xffd8) {
28420 while (offset < maxOffset) {
28421 markerBytes = dataView.getUint16(offset);
28423 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28424 markerLength = dataView.getUint16(offset + 2) + 2;
28425 if (offset + markerLength > dataView.byteLength) {
28426 Roo.log('Invalid meta data: Invalid segment size.');
28430 if(markerBytes == 0xffe1){
28431 _this.parseExifData(
28438 offset += markerLength;
28448 var url = _this.urlAPI.createObjectURL(_this.file);
28450 _this.loadCanvas(url);
28455 reader.readAsArrayBuffer(this.file);
28461 parseExifData : function(dataView, offset, length)
28463 var tiffOffset = offset + 10,
28467 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28468 // No Exif data, might be XMP data instead
28472 // Check for the ASCII code for "Exif" (0x45786966):
28473 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28474 // No Exif data, might be XMP data instead
28477 if (tiffOffset + 8 > dataView.byteLength) {
28478 Roo.log('Invalid Exif data: Invalid segment size.');
28481 // Check for the two null bytes:
28482 if (dataView.getUint16(offset + 8) !== 0x0000) {
28483 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28486 // Check the byte alignment:
28487 switch (dataView.getUint16(tiffOffset)) {
28489 littleEndian = true;
28492 littleEndian = false;
28495 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28498 // Check for the TIFF tag marker (0x002A):
28499 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28500 Roo.log('Invalid Exif data: Missing TIFF marker.');
28503 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28504 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28506 this.parseExifTags(
28509 tiffOffset + dirOffset,
28514 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28519 if (dirOffset + 6 > dataView.byteLength) {
28520 Roo.log('Invalid Exif data: Invalid directory offset.');
28523 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28524 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28525 if (dirEndOffset + 4 > dataView.byteLength) {
28526 Roo.log('Invalid Exif data: Invalid directory size.');
28529 for (i = 0; i < tagsNumber; i += 1) {
28533 dirOffset + 2 + 12 * i, // tag offset
28537 // Return the offset to the next directory:
28538 return dataView.getUint32(dirEndOffset, littleEndian);
28541 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28543 var tag = dataView.getUint16(offset, littleEndian);
28545 this.exif[tag] = this.getExifValue(
28549 dataView.getUint16(offset + 2, littleEndian), // tag type
28550 dataView.getUint32(offset + 4, littleEndian), // tag length
28555 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28557 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28566 Roo.log('Invalid Exif data: Invalid tag type.');
28570 tagSize = tagType.size * length;
28571 // Determine if the value is contained in the dataOffset bytes,
28572 // or if the value at the dataOffset is a pointer to the actual data:
28573 dataOffset = tagSize > 4 ?
28574 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28575 if (dataOffset + tagSize > dataView.byteLength) {
28576 Roo.log('Invalid Exif data: Invalid data offset.');
28579 if (length === 1) {
28580 return tagType.getValue(dataView, dataOffset, littleEndian);
28583 for (i = 0; i < length; i += 1) {
28584 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28587 if (tagType.ascii) {
28589 // Concatenate the chars:
28590 for (i = 0; i < values.length; i += 1) {
28592 // Ignore the terminating NULL byte(s):
28593 if (c === '\u0000') {
28605 Roo.apply(Roo.bootstrap.UploadCropbox, {
28607 'Orientation': 0x0112
28611 1: 0, //'top-left',
28613 3: 180, //'bottom-right',
28614 // 4: 'bottom-left',
28616 6: 90, //'right-top',
28617 // 7: 'right-bottom',
28618 8: 270 //'left-bottom'
28622 // byte, 8-bit unsigned int:
28624 getValue: function (dataView, dataOffset) {
28625 return dataView.getUint8(dataOffset);
28629 // ascii, 8-bit byte:
28631 getValue: function (dataView, dataOffset) {
28632 return String.fromCharCode(dataView.getUint8(dataOffset));
28637 // short, 16 bit int:
28639 getValue: function (dataView, dataOffset, littleEndian) {
28640 return dataView.getUint16(dataOffset, littleEndian);
28644 // long, 32 bit int:
28646 getValue: function (dataView, dataOffset, littleEndian) {
28647 return dataView.getUint32(dataOffset, littleEndian);
28651 // rational = two long values, first is numerator, second is denominator:
28653 getValue: function (dataView, dataOffset, littleEndian) {
28654 return dataView.getUint32(dataOffset, littleEndian) /
28655 dataView.getUint32(dataOffset + 4, littleEndian);
28659 // slong, 32 bit signed int:
28661 getValue: function (dataView, dataOffset, littleEndian) {
28662 return dataView.getInt32(dataOffset, littleEndian);
28666 // srational, two slongs, first is numerator, second is denominator:
28668 getValue: function (dataView, dataOffset, littleEndian) {
28669 return dataView.getInt32(dataOffset, littleEndian) /
28670 dataView.getInt32(dataOffset + 4, littleEndian);
28680 cls : 'btn-group roo-upload-cropbox-rotate-left',
28681 action : 'rotate-left',
28685 cls : 'btn btn-default',
28686 html : '<i class="fa fa-undo"></i>'
28692 cls : 'btn-group roo-upload-cropbox-picture',
28693 action : 'picture',
28697 cls : 'btn btn-default',
28698 html : '<i class="fa fa-picture-o"></i>'
28704 cls : 'btn-group roo-upload-cropbox-rotate-right',
28705 action : 'rotate-right',
28709 cls : 'btn btn-default',
28710 html : '<i class="fa fa-repeat"></i>'
28718 cls : 'btn-group roo-upload-cropbox-rotate-left',
28719 action : 'rotate-left',
28723 cls : 'btn btn-default',
28724 html : '<i class="fa fa-undo"></i>'
28730 cls : 'btn-group roo-upload-cropbox-download',
28731 action : 'download',
28735 cls : 'btn btn-default',
28736 html : '<i class="fa fa-download"></i>'
28742 cls : 'btn-group roo-upload-cropbox-crop',
28747 cls : 'btn btn-default',
28748 html : '<i class="fa fa-crop"></i>'
28754 cls : 'btn-group roo-upload-cropbox-trash',
28759 cls : 'btn btn-default',
28760 html : '<i class="fa fa-trash"></i>'
28766 cls : 'btn-group roo-upload-cropbox-rotate-right',
28767 action : 'rotate-right',
28771 cls : 'btn btn-default',
28772 html : '<i class="fa fa-repeat"></i>'
28780 cls : 'btn-group roo-upload-cropbox-rotate-left',
28781 action : 'rotate-left',
28785 cls : 'btn btn-default',
28786 html : '<i class="fa fa-undo"></i>'
28792 cls : 'btn-group roo-upload-cropbox-rotate-right',
28793 action : 'rotate-right',
28797 cls : 'btn btn-default',
28798 html : '<i class="fa fa-repeat"></i>'
28811 * @class Roo.bootstrap.DocumentManager
28812 * @extends Roo.bootstrap.Component
28813 * Bootstrap DocumentManager class
28814 * @cfg {String} paramName default 'imageUpload'
28815 * @cfg {String} toolTipName default 'filename'
28816 * @cfg {String} method default POST
28817 * @cfg {String} url action url
28818 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28819 * @cfg {Boolean} multiple multiple upload default true
28820 * @cfg {Number} thumbSize default 300
28821 * @cfg {String} fieldLabel
28822 * @cfg {Number} labelWidth default 4
28823 * @cfg {String} labelAlign (left|top) default left
28824 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28825 * @cfg {Number} labellg set the width of label (1-12)
28826 * @cfg {Number} labelmd set the width of label (1-12)
28827 * @cfg {Number} labelsm set the width of label (1-12)
28828 * @cfg {Number} labelxs set the width of label (1-12)
28831 * Create a new DocumentManager
28832 * @param {Object} config The config object
28835 Roo.bootstrap.DocumentManager = function(config){
28836 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28839 this.delegates = [];
28844 * Fire when initial the DocumentManager
28845 * @param {Roo.bootstrap.DocumentManager} this
28850 * inspect selected file
28851 * @param {Roo.bootstrap.DocumentManager} this
28852 * @param {File} file
28857 * Fire when xhr load exception
28858 * @param {Roo.bootstrap.DocumentManager} this
28859 * @param {XMLHttpRequest} xhr
28861 "exception" : true,
28863 * @event afterupload
28864 * Fire when xhr load exception
28865 * @param {Roo.bootstrap.DocumentManager} this
28866 * @param {XMLHttpRequest} xhr
28868 "afterupload" : true,
28871 * prepare the form data
28872 * @param {Roo.bootstrap.DocumentManager} this
28873 * @param {Object} formData
28878 * Fire when remove the file
28879 * @param {Roo.bootstrap.DocumentManager} this
28880 * @param {Object} file
28885 * Fire after refresh the file
28886 * @param {Roo.bootstrap.DocumentManager} this
28891 * Fire after click the image
28892 * @param {Roo.bootstrap.DocumentManager} this
28893 * @param {Object} file
28898 * Fire when upload a image and editable set to true
28899 * @param {Roo.bootstrap.DocumentManager} this
28900 * @param {Object} file
28904 * @event beforeselectfile
28905 * Fire before select file
28906 * @param {Roo.bootstrap.DocumentManager} this
28908 "beforeselectfile" : true,
28911 * Fire before process file
28912 * @param {Roo.bootstrap.DocumentManager} this
28913 * @param {Object} file
28917 * @event previewrendered
28918 * Fire when preview rendered
28919 * @param {Roo.bootstrap.DocumentManager} this
28920 * @param {Object} file
28922 "previewrendered" : true,
28925 "previewResize" : true
28930 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28939 paramName : 'imageUpload',
28940 toolTipName : 'filename',
28943 labelAlign : 'left',
28953 getAutoCreate : function()
28955 var managerWidget = {
28957 cls : 'roo-document-manager',
28961 cls : 'roo-document-manager-selector',
28966 cls : 'roo-document-manager-uploader',
28970 cls : 'roo-document-manager-upload-btn',
28971 html : '<i class="fa fa-plus"></i>'
28982 cls : 'column col-md-12',
28987 if(this.fieldLabel.length){
28992 cls : 'column col-md-12',
28993 html : this.fieldLabel
28997 cls : 'column col-md-12',
29002 if(this.labelAlign == 'left'){
29007 html : this.fieldLabel
29016 if(this.labelWidth > 12){
29017 content[0].style = "width: " + this.labelWidth + 'px';
29020 if(this.labelWidth < 13 && this.labelmd == 0){
29021 this.labelmd = this.labelWidth;
29024 if(this.labellg > 0){
29025 content[0].cls += ' col-lg-' + this.labellg;
29026 content[1].cls += ' col-lg-' + (12 - this.labellg);
29029 if(this.labelmd > 0){
29030 content[0].cls += ' col-md-' + this.labelmd;
29031 content[1].cls += ' col-md-' + (12 - this.labelmd);
29034 if(this.labelsm > 0){
29035 content[0].cls += ' col-sm-' + this.labelsm;
29036 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29039 if(this.labelxs > 0){
29040 content[0].cls += ' col-xs-' + this.labelxs;
29041 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29049 cls : 'row clearfix',
29057 initEvents : function()
29059 this.managerEl = this.el.select('.roo-document-manager', true).first();
29060 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29062 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29063 this.selectorEl.hide();
29066 this.selectorEl.attr('multiple', 'multiple');
29069 this.selectorEl.on('change', this.onFileSelected, this);
29071 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29072 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29074 this.uploader.on('click', this.onUploaderClick, this);
29076 this.renderProgressDialog();
29080 window.addEventListener("resize", function() { _this.refresh(); } );
29082 this.fireEvent('initial', this);
29085 renderProgressDialog : function()
29089 this.progressDialog = new Roo.bootstrap.Modal({
29090 cls : 'roo-document-manager-progress-dialog',
29091 allow_close : false,
29101 btnclick : function() {
29102 _this.uploadCancel();
29108 this.progressDialog.render(Roo.get(document.body));
29110 this.progress = new Roo.bootstrap.Progress({
29111 cls : 'roo-document-manager-progress',
29116 this.progress.render(this.progressDialog.getChildContainer());
29118 this.progressBar = new Roo.bootstrap.ProgressBar({
29119 cls : 'roo-document-manager-progress-bar',
29122 aria_valuemax : 12,
29126 this.progressBar.render(this.progress.getChildContainer());
29129 onUploaderClick : function(e)
29131 e.preventDefault();
29133 if(this.fireEvent('beforeselectfile', this) != false){
29134 this.selectorEl.dom.click();
29139 onFileSelected : function(e)
29141 e.preventDefault();
29143 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29147 Roo.each(this.selectorEl.dom.files, function(file){
29148 if(this.fireEvent('inspect', this, file) != false){
29149 this.files.push(file);
29159 this.selectorEl.dom.value = '';
29161 if(!this.files || !this.files.length){
29165 if(this.boxes > 0 && this.files.length > this.boxes){
29166 this.files = this.files.slice(0, this.boxes);
29169 this.uploader.show();
29171 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29172 this.uploader.hide();
29181 Roo.each(this.files, function(file){
29183 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29184 var f = this.renderPreview(file);
29189 if(file.type.indexOf('image') != -1){
29190 this.delegates.push(
29192 _this.process(file);
29193 }).createDelegate(this)
29201 _this.process(file);
29202 }).createDelegate(this)
29207 this.files = files;
29209 this.delegates = this.delegates.concat(docs);
29211 if(!this.delegates.length){
29216 this.progressBar.aria_valuemax = this.delegates.length;
29223 arrange : function()
29225 if(!this.delegates.length){
29226 this.progressDialog.hide();
29231 var delegate = this.delegates.shift();
29233 this.progressDialog.show();
29235 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29237 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29242 refresh : function()
29244 this.uploader.show();
29246 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29247 this.uploader.hide();
29250 Roo.isTouch ? this.closable(false) : this.closable(true);
29252 this.fireEvent('refresh', this);
29255 onRemove : function(e, el, o)
29257 e.preventDefault();
29259 this.fireEvent('remove', this, o);
29263 remove : function(o)
29267 Roo.each(this.files, function(file){
29268 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29277 this.files = files;
29284 Roo.each(this.files, function(file){
29289 file.target.remove();
29298 onClick : function(e, el, o)
29300 e.preventDefault();
29302 this.fireEvent('click', this, o);
29306 closable : function(closable)
29308 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29310 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29322 xhrOnLoad : function(xhr)
29324 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29328 if (xhr.readyState !== 4) {
29330 this.fireEvent('exception', this, xhr);
29334 var response = Roo.decode(xhr.responseText);
29336 if(!response.success){
29338 this.fireEvent('exception', this, xhr);
29342 var file = this.renderPreview(response.data);
29344 this.files.push(file);
29348 this.fireEvent('afterupload', this, xhr);
29352 xhrOnError : function(xhr)
29354 Roo.log('xhr on error');
29356 var response = Roo.decode(xhr.responseText);
29363 process : function(file)
29365 if(this.fireEvent('process', this, file) !== false){
29366 if(this.editable && file.type.indexOf('image') != -1){
29367 this.fireEvent('edit', this, file);
29371 this.uploadStart(file, false);
29378 uploadStart : function(file, crop)
29380 this.xhr = new XMLHttpRequest();
29382 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29387 file.xhr = this.xhr;
29389 this.managerEl.createChild({
29391 cls : 'roo-document-manager-loading',
29395 tooltip : file.name,
29396 cls : 'roo-document-manager-thumb',
29397 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29403 this.xhr.open(this.method, this.url, true);
29406 "Accept": "application/json",
29407 "Cache-Control": "no-cache",
29408 "X-Requested-With": "XMLHttpRequest"
29411 for (var headerName in headers) {
29412 var headerValue = headers[headerName];
29414 this.xhr.setRequestHeader(headerName, headerValue);
29420 this.xhr.onload = function()
29422 _this.xhrOnLoad(_this.xhr);
29425 this.xhr.onerror = function()
29427 _this.xhrOnError(_this.xhr);
29430 var formData = new FormData();
29432 formData.append('returnHTML', 'NO');
29435 formData.append('crop', crop);
29438 formData.append(this.paramName, file, file.name);
29445 if(this.fireEvent('prepare', this, formData, options) != false){
29447 if(options.manually){
29451 this.xhr.send(formData);
29455 this.uploadCancel();
29458 uploadCancel : function()
29464 this.delegates = [];
29466 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29473 renderPreview : function(file)
29475 if(typeof(file.target) != 'undefined' && file.target){
29479 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29481 var previewEl = this.managerEl.createChild({
29483 cls : 'roo-document-manager-preview',
29487 tooltip : file[this.toolTipName],
29488 cls : 'roo-document-manager-thumb',
29489 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29494 html : '<i class="fa fa-times-circle"></i>'
29499 var close = previewEl.select('button.close', true).first();
29501 close.on('click', this.onRemove, this, file);
29503 file.target = previewEl;
29505 var image = previewEl.select('img', true).first();
29509 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29511 image.on('click', this.onClick, this, file);
29513 this.fireEvent('previewrendered', this, file);
29519 onPreviewLoad : function(file, image)
29521 if(typeof(file.target) == 'undefined' || !file.target){
29525 var width = image.dom.naturalWidth || image.dom.width;
29526 var height = image.dom.naturalHeight || image.dom.height;
29528 if(!this.previewResize) {
29532 if(width > height){
29533 file.target.addClass('wide');
29537 file.target.addClass('tall');
29542 uploadFromSource : function(file, crop)
29544 this.xhr = new XMLHttpRequest();
29546 this.managerEl.createChild({
29548 cls : 'roo-document-manager-loading',
29552 tooltip : file.name,
29553 cls : 'roo-document-manager-thumb',
29554 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29560 this.xhr.open(this.method, this.url, true);
29563 "Accept": "application/json",
29564 "Cache-Control": "no-cache",
29565 "X-Requested-With": "XMLHttpRequest"
29568 for (var headerName in headers) {
29569 var headerValue = headers[headerName];
29571 this.xhr.setRequestHeader(headerName, headerValue);
29577 this.xhr.onload = function()
29579 _this.xhrOnLoad(_this.xhr);
29582 this.xhr.onerror = function()
29584 _this.xhrOnError(_this.xhr);
29587 var formData = new FormData();
29589 formData.append('returnHTML', 'NO');
29591 formData.append('crop', crop);
29593 if(typeof(file.filename) != 'undefined'){
29594 formData.append('filename', file.filename);
29597 if(typeof(file.mimetype) != 'undefined'){
29598 formData.append('mimetype', file.mimetype);
29603 if(this.fireEvent('prepare', this, formData) != false){
29604 this.xhr.send(formData);
29614 * @class Roo.bootstrap.DocumentViewer
29615 * @extends Roo.bootstrap.Component
29616 * Bootstrap DocumentViewer class
29617 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29618 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29621 * Create a new DocumentViewer
29622 * @param {Object} config The config object
29625 Roo.bootstrap.DocumentViewer = function(config){
29626 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29631 * Fire after initEvent
29632 * @param {Roo.bootstrap.DocumentViewer} this
29638 * @param {Roo.bootstrap.DocumentViewer} this
29643 * Fire after download button
29644 * @param {Roo.bootstrap.DocumentViewer} this
29649 * Fire after trash button
29650 * @param {Roo.bootstrap.DocumentViewer} this
29657 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29659 showDownload : true,
29663 getAutoCreate : function()
29667 cls : 'roo-document-viewer',
29671 cls : 'roo-document-viewer-body',
29675 cls : 'roo-document-viewer-thumb',
29679 cls : 'roo-document-viewer-image'
29687 cls : 'roo-document-viewer-footer',
29690 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29694 cls : 'btn-group roo-document-viewer-download',
29698 cls : 'btn btn-default',
29699 html : '<i class="fa fa-download"></i>'
29705 cls : 'btn-group roo-document-viewer-trash',
29709 cls : 'btn btn-default',
29710 html : '<i class="fa fa-trash"></i>'
29723 initEvents : function()
29725 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29726 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29728 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29729 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29731 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29732 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29734 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29735 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29737 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29738 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29740 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29741 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29743 this.bodyEl.on('click', this.onClick, this);
29744 this.downloadBtn.on('click', this.onDownload, this);
29745 this.trashBtn.on('click', this.onTrash, this);
29747 this.downloadBtn.hide();
29748 this.trashBtn.hide();
29750 if(this.showDownload){
29751 this.downloadBtn.show();
29754 if(this.showTrash){
29755 this.trashBtn.show();
29758 if(!this.showDownload && !this.showTrash) {
29759 this.footerEl.hide();
29764 initial : function()
29766 this.fireEvent('initial', this);
29770 onClick : function(e)
29772 e.preventDefault();
29774 this.fireEvent('click', this);
29777 onDownload : function(e)
29779 e.preventDefault();
29781 this.fireEvent('download', this);
29784 onTrash : function(e)
29786 e.preventDefault();
29788 this.fireEvent('trash', this);
29800 * @class Roo.bootstrap.NavProgressBar
29801 * @extends Roo.bootstrap.Component
29802 * Bootstrap NavProgressBar class
29805 * Create a new nav progress bar
29806 * @param {Object} config The config object
29809 Roo.bootstrap.NavProgressBar = function(config){
29810 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29812 this.bullets = this.bullets || [];
29814 // Roo.bootstrap.NavProgressBar.register(this);
29818 * Fires when the active item changes
29819 * @param {Roo.bootstrap.NavProgressBar} this
29820 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29821 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29828 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29833 getAutoCreate : function()
29835 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29839 cls : 'roo-navigation-bar-group',
29843 cls : 'roo-navigation-top-bar'
29847 cls : 'roo-navigation-bullets-bar',
29851 cls : 'roo-navigation-bar'
29858 cls : 'roo-navigation-bottom-bar'
29868 initEvents: function()
29873 onRender : function(ct, position)
29875 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29877 if(this.bullets.length){
29878 Roo.each(this.bullets, function(b){
29887 addItem : function(cfg)
29889 var item = new Roo.bootstrap.NavProgressItem(cfg);
29891 item.parentId = this.id;
29892 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29895 var top = new Roo.bootstrap.Element({
29897 cls : 'roo-navigation-bar-text'
29900 var bottom = new Roo.bootstrap.Element({
29902 cls : 'roo-navigation-bar-text'
29905 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29906 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29908 var topText = new Roo.bootstrap.Element({
29910 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29913 var bottomText = new Roo.bootstrap.Element({
29915 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29918 topText.onRender(top.el, null);
29919 bottomText.onRender(bottom.el, null);
29922 item.bottomEl = bottom;
29925 this.barItems.push(item);
29930 getActive : function()
29932 var active = false;
29934 Roo.each(this.barItems, function(v){
29936 if (!v.isActive()) {
29948 setActiveItem : function(item)
29952 Roo.each(this.barItems, function(v){
29953 if (v.rid == item.rid) {
29957 if (v.isActive()) {
29958 v.setActive(false);
29963 item.setActive(true);
29965 this.fireEvent('changed', this, item, prev);
29968 getBarItem: function(rid)
29972 Roo.each(this.barItems, function(e) {
29973 if (e.rid != rid) {
29984 indexOfItem : function(item)
29988 Roo.each(this.barItems, function(v, i){
29990 if (v.rid != item.rid) {
30001 setActiveNext : function()
30003 var i = this.indexOfItem(this.getActive());
30005 if (i > this.barItems.length) {
30009 this.setActiveItem(this.barItems[i+1]);
30012 setActivePrev : function()
30014 var i = this.indexOfItem(this.getActive());
30020 this.setActiveItem(this.barItems[i-1]);
30023 format : function()
30025 if(!this.barItems.length){
30029 var width = 100 / this.barItems.length;
30031 Roo.each(this.barItems, function(i){
30032 i.el.setStyle('width', width + '%');
30033 i.topEl.el.setStyle('width', width + '%');
30034 i.bottomEl.el.setStyle('width', width + '%');
30043 * Nav Progress Item
30048 * @class Roo.bootstrap.NavProgressItem
30049 * @extends Roo.bootstrap.Component
30050 * Bootstrap NavProgressItem class
30051 * @cfg {String} rid the reference id
30052 * @cfg {Boolean} active (true|false) Is item active default false
30053 * @cfg {Boolean} disabled (true|false) Is item active default false
30054 * @cfg {String} html
30055 * @cfg {String} position (top|bottom) text position default bottom
30056 * @cfg {String} icon show icon instead of number
30059 * Create a new NavProgressItem
30060 * @param {Object} config The config object
30062 Roo.bootstrap.NavProgressItem = function(config){
30063 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30068 * The raw click event for the entire grid.
30069 * @param {Roo.bootstrap.NavProgressItem} this
30070 * @param {Roo.EventObject} e
30077 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30083 position : 'bottom',
30086 getAutoCreate : function()
30088 var iconCls = 'roo-navigation-bar-item-icon';
30090 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30094 cls: 'roo-navigation-bar-item',
30104 cfg.cls += ' active';
30107 cfg.cls += ' disabled';
30113 disable : function()
30115 this.setDisabled(true);
30118 enable : function()
30120 this.setDisabled(false);
30123 initEvents: function()
30125 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30127 this.iconEl.on('click', this.onClick, this);
30130 onClick : function(e)
30132 e.preventDefault();
30138 if(this.fireEvent('click', this, e) === false){
30142 this.parent().setActiveItem(this);
30145 isActive: function ()
30147 return this.active;
30150 setActive : function(state)
30152 if(this.active == state){
30156 this.active = state;
30159 this.el.addClass('active');
30163 this.el.removeClass('active');
30168 setDisabled : function(state)
30170 if(this.disabled == state){
30174 this.disabled = state;
30177 this.el.addClass('disabled');
30181 this.el.removeClass('disabled');
30184 tooltipEl : function()
30186 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30199 * @class Roo.bootstrap.FieldLabel
30200 * @extends Roo.bootstrap.Component
30201 * Bootstrap FieldLabel class
30202 * @cfg {String} html contents of the element
30203 * @cfg {String} tag tag of the element default label
30204 * @cfg {String} cls class of the element
30205 * @cfg {String} target label target
30206 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30207 * @cfg {String} invalidClass default "text-warning"
30208 * @cfg {String} validClass default "text-success"
30209 * @cfg {String} iconTooltip default "This field is required"
30210 * @cfg {String} indicatorpos (left|right) default left
30213 * Create a new FieldLabel
30214 * @param {Object} config The config object
30217 Roo.bootstrap.FieldLabel = function(config){
30218 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30223 * Fires after the field has been marked as invalid.
30224 * @param {Roo.form.FieldLabel} this
30225 * @param {String} msg The validation message
30230 * Fires after the field has been validated with no errors.
30231 * @param {Roo.form.FieldLabel} this
30237 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30244 invalidClass : 'has-warning',
30245 validClass : 'has-success',
30246 iconTooltip : 'This field is required',
30247 indicatorpos : 'left',
30249 getAutoCreate : function(){
30252 if (!this.allowBlank) {
30258 cls : 'roo-bootstrap-field-label ' + this.cls,
30263 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30264 tooltip : this.iconTooltip
30273 if(this.indicatorpos == 'right'){
30276 cls : 'roo-bootstrap-field-label ' + this.cls,
30285 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30286 tooltip : this.iconTooltip
30295 initEvents: function()
30297 Roo.bootstrap.Element.superclass.initEvents.call(this);
30299 this.indicator = this.indicatorEl();
30301 if(this.indicator){
30302 this.indicator.removeClass('visible');
30303 this.indicator.addClass('invisible');
30306 Roo.bootstrap.FieldLabel.register(this);
30309 indicatorEl : function()
30311 var indicator = this.el.select('i.roo-required-indicator',true).first();
30322 * Mark this field as valid
30324 markValid : function()
30326 if(this.indicator){
30327 this.indicator.removeClass('visible');
30328 this.indicator.addClass('invisible');
30331 this.el.removeClass(this.invalidClass);
30333 this.el.addClass(this.validClass);
30335 this.fireEvent('valid', this);
30339 * Mark this field as invalid
30340 * @param {String} msg The validation message
30342 markInvalid : function(msg)
30344 if(this.indicator){
30345 this.indicator.removeClass('invisible');
30346 this.indicator.addClass('visible');
30349 this.el.removeClass(this.validClass);
30351 this.el.addClass(this.invalidClass);
30353 this.fireEvent('invalid', this, msg);
30359 Roo.apply(Roo.bootstrap.FieldLabel, {
30364 * register a FieldLabel Group
30365 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30367 register : function(label)
30369 if(this.groups.hasOwnProperty(label.target)){
30373 this.groups[label.target] = label;
30377 * fetch a FieldLabel Group based on the target
30378 * @param {string} target
30379 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30381 get: function(target) {
30382 if (typeof(this.groups[target]) == 'undefined') {
30386 return this.groups[target] ;
30395 * page DateSplitField.
30401 * @class Roo.bootstrap.DateSplitField
30402 * @extends Roo.bootstrap.Component
30403 * Bootstrap DateSplitField class
30404 * @cfg {string} fieldLabel - the label associated
30405 * @cfg {Number} labelWidth set the width of label (0-12)
30406 * @cfg {String} labelAlign (top|left)
30407 * @cfg {Boolean} dayAllowBlank (true|false) default false
30408 * @cfg {Boolean} monthAllowBlank (true|false) default false
30409 * @cfg {Boolean} yearAllowBlank (true|false) default false
30410 * @cfg {string} dayPlaceholder
30411 * @cfg {string} monthPlaceholder
30412 * @cfg {string} yearPlaceholder
30413 * @cfg {string} dayFormat default 'd'
30414 * @cfg {string} monthFormat default 'm'
30415 * @cfg {string} yearFormat default 'Y'
30416 * @cfg {Number} labellg set the width of label (1-12)
30417 * @cfg {Number} labelmd set the width of label (1-12)
30418 * @cfg {Number} labelsm set the width of label (1-12)
30419 * @cfg {Number} labelxs set the width of label (1-12)
30423 * Create a new DateSplitField
30424 * @param {Object} config The config object
30427 Roo.bootstrap.DateSplitField = function(config){
30428 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30434 * getting the data of years
30435 * @param {Roo.bootstrap.DateSplitField} this
30436 * @param {Object} years
30441 * getting the data of days
30442 * @param {Roo.bootstrap.DateSplitField} this
30443 * @param {Object} days
30448 * Fires after the field has been marked as invalid.
30449 * @param {Roo.form.Field} this
30450 * @param {String} msg The validation message
30455 * Fires after the field has been validated with no errors.
30456 * @param {Roo.form.Field} this
30462 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30465 labelAlign : 'top',
30467 dayAllowBlank : false,
30468 monthAllowBlank : false,
30469 yearAllowBlank : false,
30470 dayPlaceholder : '',
30471 monthPlaceholder : '',
30472 yearPlaceholder : '',
30476 isFormField : true,
30482 getAutoCreate : function()
30486 cls : 'row roo-date-split-field-group',
30491 cls : 'form-hidden-field roo-date-split-field-group-value',
30497 var labelCls = 'col-md-12';
30498 var contentCls = 'col-md-4';
30500 if(this.fieldLabel){
30504 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30508 html : this.fieldLabel
30513 if(this.labelAlign == 'left'){
30515 if(this.labelWidth > 12){
30516 label.style = "width: " + this.labelWidth + 'px';
30519 if(this.labelWidth < 13 && this.labelmd == 0){
30520 this.labelmd = this.labelWidth;
30523 if(this.labellg > 0){
30524 labelCls = ' col-lg-' + this.labellg;
30525 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30528 if(this.labelmd > 0){
30529 labelCls = ' col-md-' + this.labelmd;
30530 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30533 if(this.labelsm > 0){
30534 labelCls = ' col-sm-' + this.labelsm;
30535 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30538 if(this.labelxs > 0){
30539 labelCls = ' col-xs-' + this.labelxs;
30540 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30544 label.cls += ' ' + labelCls;
30546 cfg.cn.push(label);
30549 Roo.each(['day', 'month', 'year'], function(t){
30552 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30559 inputEl: function ()
30561 return this.el.select('.roo-date-split-field-group-value', true).first();
30564 onRender : function(ct, position)
30568 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30570 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30572 this.dayField = new Roo.bootstrap.ComboBox({
30573 allowBlank : this.dayAllowBlank,
30574 alwaysQuery : true,
30575 displayField : 'value',
30578 forceSelection : true,
30580 placeholder : this.dayPlaceholder,
30581 selectOnFocus : true,
30582 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30583 triggerAction : 'all',
30585 valueField : 'value',
30586 store : new Roo.data.SimpleStore({
30587 data : (function() {
30589 _this.fireEvent('days', _this, days);
30592 fields : [ 'value' ]
30595 select : function (_self, record, index)
30597 _this.setValue(_this.getValue());
30602 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30604 this.monthField = new Roo.bootstrap.MonthField({
30605 after : '<i class=\"fa fa-calendar\"></i>',
30606 allowBlank : this.monthAllowBlank,
30607 placeholder : this.monthPlaceholder,
30610 render : function (_self)
30612 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30613 e.preventDefault();
30617 select : function (_self, oldvalue, newvalue)
30619 _this.setValue(_this.getValue());
30624 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30626 this.yearField = new Roo.bootstrap.ComboBox({
30627 allowBlank : this.yearAllowBlank,
30628 alwaysQuery : true,
30629 displayField : 'value',
30632 forceSelection : true,
30634 placeholder : this.yearPlaceholder,
30635 selectOnFocus : true,
30636 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30637 triggerAction : 'all',
30639 valueField : 'value',
30640 store : new Roo.data.SimpleStore({
30641 data : (function() {
30643 _this.fireEvent('years', _this, years);
30646 fields : [ 'value' ]
30649 select : function (_self, record, index)
30651 _this.setValue(_this.getValue());
30656 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30659 setValue : function(v, format)
30661 this.inputEl.dom.value = v;
30663 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30665 var d = Date.parseDate(v, f);
30672 this.setDay(d.format(this.dayFormat));
30673 this.setMonth(d.format(this.monthFormat));
30674 this.setYear(d.format(this.yearFormat));
30681 setDay : function(v)
30683 this.dayField.setValue(v);
30684 this.inputEl.dom.value = this.getValue();
30689 setMonth : function(v)
30691 this.monthField.setValue(v, true);
30692 this.inputEl.dom.value = this.getValue();
30697 setYear : function(v)
30699 this.yearField.setValue(v);
30700 this.inputEl.dom.value = this.getValue();
30705 getDay : function()
30707 return this.dayField.getValue();
30710 getMonth : function()
30712 return this.monthField.getValue();
30715 getYear : function()
30717 return this.yearField.getValue();
30720 getValue : function()
30722 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30724 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30734 this.inputEl.dom.value = '';
30739 validate : function()
30741 var d = this.dayField.validate();
30742 var m = this.monthField.validate();
30743 var y = this.yearField.validate();
30748 (!this.dayAllowBlank && !d) ||
30749 (!this.monthAllowBlank && !m) ||
30750 (!this.yearAllowBlank && !y)
30755 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30764 this.markInvalid();
30769 markValid : function()
30772 var label = this.el.select('label', true).first();
30773 var icon = this.el.select('i.fa-star', true).first();
30779 this.fireEvent('valid', this);
30783 * Mark this field as invalid
30784 * @param {String} msg The validation message
30786 markInvalid : function(msg)
30789 var label = this.el.select('label', true).first();
30790 var icon = this.el.select('i.fa-star', true).first();
30792 if(label && !icon){
30793 this.el.select('.roo-date-split-field-label', true).createChild({
30795 cls : 'text-danger fa fa-lg fa-star',
30796 tooltip : 'This field is required',
30797 style : 'margin-right:5px;'
30801 this.fireEvent('invalid', this, msg);
30804 clearInvalid : function()
30806 var label = this.el.select('label', true).first();
30807 var icon = this.el.select('i.fa-star', true).first();
30813 this.fireEvent('valid', this);
30816 getName: function()
30826 * http://masonry.desandro.com
30828 * The idea is to render all the bricks based on vertical width...
30830 * The original code extends 'outlayer' - we might need to use that....
30836 * @class Roo.bootstrap.LayoutMasonry
30837 * @extends Roo.bootstrap.Component
30838 * Bootstrap Layout Masonry class
30841 * Create a new Element
30842 * @param {Object} config The config object
30845 Roo.bootstrap.LayoutMasonry = function(config){
30847 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30851 Roo.bootstrap.LayoutMasonry.register(this);
30857 * Fire after layout the items
30858 * @param {Roo.bootstrap.LayoutMasonry} this
30859 * @param {Roo.EventObject} e
30866 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30869 * @cfg {Boolean} isLayoutInstant = no animation?
30871 isLayoutInstant : false, // needed?
30874 * @cfg {Number} boxWidth width of the columns
30879 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30884 * @cfg {Number} padWidth padding below box..
30889 * @cfg {Number} gutter gutter width..
30894 * @cfg {Number} maxCols maximum number of columns
30900 * @cfg {Boolean} isAutoInitial defalut true
30902 isAutoInitial : true,
30907 * @cfg {Boolean} isHorizontal defalut false
30909 isHorizontal : false,
30911 currentSize : null,
30917 bricks: null, //CompositeElement
30921 _isLayoutInited : false,
30923 // isAlternative : false, // only use for vertical layout...
30926 * @cfg {Number} alternativePadWidth padding below box..
30928 alternativePadWidth : 50,
30930 selectedBrick : [],
30932 getAutoCreate : function(){
30934 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30938 cls: 'blog-masonary-wrapper ' + this.cls,
30940 cls : 'mas-boxes masonary'
30947 getChildContainer: function( )
30949 if (this.boxesEl) {
30950 return this.boxesEl;
30953 this.boxesEl = this.el.select('.mas-boxes').first();
30955 return this.boxesEl;
30959 initEvents : function()
30963 if(this.isAutoInitial){
30964 Roo.log('hook children rendered');
30965 this.on('childrenrendered', function() {
30966 Roo.log('children rendered');
30972 initial : function()
30974 this.selectedBrick = [];
30976 this.currentSize = this.el.getBox(true);
30978 Roo.EventManager.onWindowResize(this.resize, this);
30980 if(!this.isAutoInitial){
30988 //this.layout.defer(500,this);
30992 resize : function()
30994 var cs = this.el.getBox(true);
30997 this.currentSize.width == cs.width &&
30998 this.currentSize.x == cs.x &&
30999 this.currentSize.height == cs.height &&
31000 this.currentSize.y == cs.y
31002 Roo.log("no change in with or X or Y");
31006 this.currentSize = cs;
31012 layout : function()
31014 this._resetLayout();
31016 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31018 this.layoutItems( isInstant );
31020 this._isLayoutInited = true;
31022 this.fireEvent('layout', this);
31026 _resetLayout : function()
31028 if(this.isHorizontal){
31029 this.horizontalMeasureColumns();
31033 this.verticalMeasureColumns();
31037 verticalMeasureColumns : function()
31039 this.getContainerWidth();
31041 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31042 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31046 var boxWidth = this.boxWidth + this.padWidth;
31048 if(this.containerWidth < this.boxWidth){
31049 boxWidth = this.containerWidth
31052 var containerWidth = this.containerWidth;
31054 var cols = Math.floor(containerWidth / boxWidth);
31056 this.cols = Math.max( cols, 1 );
31058 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31060 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31062 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31064 this.colWidth = boxWidth + avail - this.padWidth;
31066 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31067 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31070 horizontalMeasureColumns : function()
31072 this.getContainerWidth();
31074 var boxWidth = this.boxWidth;
31076 if(this.containerWidth < boxWidth){
31077 boxWidth = this.containerWidth;
31080 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31082 this.el.setHeight(boxWidth);
31086 getContainerWidth : function()
31088 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31091 layoutItems : function( isInstant )
31093 Roo.log(this.bricks);
31095 var items = Roo.apply([], this.bricks);
31097 if(this.isHorizontal){
31098 this._horizontalLayoutItems( items , isInstant );
31102 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31103 // this._verticalAlternativeLayoutItems( items , isInstant );
31107 this._verticalLayoutItems( items , isInstant );
31111 _verticalLayoutItems : function ( items , isInstant)
31113 if ( !items || !items.length ) {
31118 ['xs', 'xs', 'xs', 'tall'],
31119 ['xs', 'xs', 'tall'],
31120 ['xs', 'xs', 'sm'],
31121 ['xs', 'xs', 'xs'],
31127 ['sm', 'xs', 'xs'],
31131 ['tall', 'xs', 'xs', 'xs'],
31132 ['tall', 'xs', 'xs'],
31144 Roo.each(items, function(item, k){
31146 switch (item.size) {
31147 // these layouts take up a full box,
31158 boxes.push([item]);
31181 var filterPattern = function(box, length)
31189 var pattern = box.slice(0, length);
31193 Roo.each(pattern, function(i){
31194 format.push(i.size);
31197 Roo.each(standard, function(s){
31199 if(String(s) != String(format)){
31208 if(!match && length == 1){
31213 filterPattern(box, length - 1);
31217 queue.push(pattern);
31219 box = box.slice(length, box.length);
31221 filterPattern(box, 4);
31227 Roo.each(boxes, function(box, k){
31233 if(box.length == 1){
31238 filterPattern(box, 4);
31242 this._processVerticalLayoutQueue( queue, isInstant );
31246 // _verticalAlternativeLayoutItems : function( items , isInstant )
31248 // if ( !items || !items.length ) {
31252 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31256 _horizontalLayoutItems : function ( items , isInstant)
31258 if ( !items || !items.length || items.length < 3) {
31264 var eItems = items.slice(0, 3);
31266 items = items.slice(3, items.length);
31269 ['xs', 'xs', 'xs', 'wide'],
31270 ['xs', 'xs', 'wide'],
31271 ['xs', 'xs', 'sm'],
31272 ['xs', 'xs', 'xs'],
31278 ['sm', 'xs', 'xs'],
31282 ['wide', 'xs', 'xs', 'xs'],
31283 ['wide', 'xs', 'xs'],
31296 Roo.each(items, function(item, k){
31298 switch (item.size) {
31309 boxes.push([item]);
31333 var filterPattern = function(box, length)
31341 var pattern = box.slice(0, length);
31345 Roo.each(pattern, function(i){
31346 format.push(i.size);
31349 Roo.each(standard, function(s){
31351 if(String(s) != String(format)){
31360 if(!match && length == 1){
31365 filterPattern(box, length - 1);
31369 queue.push(pattern);
31371 box = box.slice(length, box.length);
31373 filterPattern(box, 4);
31379 Roo.each(boxes, function(box, k){
31385 if(box.length == 1){
31390 filterPattern(box, 4);
31397 var pos = this.el.getBox(true);
31401 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31403 var hit_end = false;
31405 Roo.each(queue, function(box){
31409 Roo.each(box, function(b){
31411 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31421 Roo.each(box, function(b){
31423 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31426 mx = Math.max(mx, b.x);
31430 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31434 Roo.each(box, function(b){
31436 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31450 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31453 /** Sets position of item in DOM
31454 * @param {Element} item
31455 * @param {Number} x - horizontal position
31456 * @param {Number} y - vertical position
31457 * @param {Boolean} isInstant - disables transitions
31459 _processVerticalLayoutQueue : function( queue, isInstant )
31461 var pos = this.el.getBox(true);
31466 for (var i = 0; i < this.cols; i++){
31470 Roo.each(queue, function(box, k){
31472 var col = k % this.cols;
31474 Roo.each(box, function(b,kk){
31476 b.el.position('absolute');
31478 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31479 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31481 if(b.size == 'md-left' || b.size == 'md-right'){
31482 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31483 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31486 b.el.setWidth(width);
31487 b.el.setHeight(height);
31489 b.el.select('iframe',true).setSize(width,height);
31493 for (var i = 0; i < this.cols; i++){
31495 if(maxY[i] < maxY[col]){
31500 col = Math.min(col, i);
31504 x = pos.x + col * (this.colWidth + this.padWidth);
31508 var positions = [];
31510 switch (box.length){
31512 positions = this.getVerticalOneBoxColPositions(x, y, box);
31515 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31518 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31521 positions = this.getVerticalFourBoxColPositions(x, y, box);
31527 Roo.each(box, function(b,kk){
31529 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31531 var sz = b.el.getSize();
31533 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31541 for (var i = 0; i < this.cols; i++){
31542 mY = Math.max(mY, maxY[i]);
31545 this.el.setHeight(mY - pos.y);
31549 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31551 // var pos = this.el.getBox(true);
31554 // var maxX = pos.right;
31556 // var maxHeight = 0;
31558 // Roo.each(items, function(item, k){
31562 // item.el.position('absolute');
31564 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31566 // item.el.setWidth(width);
31568 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31570 // item.el.setHeight(height);
31573 // item.el.setXY([x, y], isInstant ? false : true);
31575 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31578 // y = y + height + this.alternativePadWidth;
31580 // maxHeight = maxHeight + height + this.alternativePadWidth;
31584 // this.el.setHeight(maxHeight);
31588 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31590 var pos = this.el.getBox(true);
31595 var maxX = pos.right;
31597 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31599 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31601 Roo.each(queue, function(box, k){
31603 Roo.each(box, function(b, kk){
31605 b.el.position('absolute');
31607 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31608 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31610 if(b.size == 'md-left' || b.size == 'md-right'){
31611 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31612 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31615 b.el.setWidth(width);
31616 b.el.setHeight(height);
31624 var positions = [];
31626 switch (box.length){
31628 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31631 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31634 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31637 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31643 Roo.each(box, function(b,kk){
31645 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31647 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31655 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31657 Roo.each(eItems, function(b,k){
31659 b.size = (k == 0) ? 'sm' : 'xs';
31660 b.x = (k == 0) ? 2 : 1;
31661 b.y = (k == 0) ? 2 : 1;
31663 b.el.position('absolute');
31665 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31667 b.el.setWidth(width);
31669 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31671 b.el.setHeight(height);
31675 var positions = [];
31678 x : maxX - this.unitWidth * 2 - this.gutter,
31683 x : maxX - this.unitWidth,
31684 y : minY + (this.unitWidth + this.gutter) * 2
31688 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31692 Roo.each(eItems, function(b,k){
31694 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31700 getVerticalOneBoxColPositions : function(x, y, box)
31704 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31706 if(box[0].size == 'md-left'){
31710 if(box[0].size == 'md-right'){
31715 x : x + (this.unitWidth + this.gutter) * rand,
31722 getVerticalTwoBoxColPositions : function(x, y, box)
31726 if(box[0].size == 'xs'){
31730 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31734 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31748 x : x + (this.unitWidth + this.gutter) * 2,
31749 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31756 getVerticalThreeBoxColPositions : function(x, y, box)
31760 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31768 x : x + (this.unitWidth + this.gutter) * 1,
31773 x : x + (this.unitWidth + this.gutter) * 2,
31781 if(box[0].size == 'xs' && box[1].size == 'xs'){
31790 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31794 x : x + (this.unitWidth + this.gutter) * 1,
31808 x : x + (this.unitWidth + this.gutter) * 2,
31813 x : x + (this.unitWidth + this.gutter) * 2,
31814 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31821 getVerticalFourBoxColPositions : function(x, y, box)
31825 if(box[0].size == 'xs'){
31834 y : y + (this.unitHeight + this.gutter) * 1
31839 y : y + (this.unitHeight + this.gutter) * 2
31843 x : x + (this.unitWidth + this.gutter) * 1,
31857 x : x + (this.unitWidth + this.gutter) * 2,
31862 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31863 y : y + (this.unitHeight + this.gutter) * 1
31867 x : x + (this.unitWidth + this.gutter) * 2,
31868 y : y + (this.unitWidth + this.gutter) * 2
31875 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31879 if(box[0].size == 'md-left'){
31881 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31888 if(box[0].size == 'md-right'){
31890 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31891 y : minY + (this.unitWidth + this.gutter) * 1
31897 var rand = Math.floor(Math.random() * (4 - box[0].y));
31900 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31901 y : minY + (this.unitWidth + this.gutter) * rand
31908 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31912 if(box[0].size == 'xs'){
31915 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31920 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31921 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31929 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31934 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31935 y : minY + (this.unitWidth + this.gutter) * 2
31942 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31946 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31949 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31954 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31955 y : minY + (this.unitWidth + this.gutter) * 1
31959 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31960 y : minY + (this.unitWidth + this.gutter) * 2
31967 if(box[0].size == 'xs' && box[1].size == 'xs'){
31970 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31975 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31980 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31981 y : minY + (this.unitWidth + this.gutter) * 1
31989 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31994 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31995 y : minY + (this.unitWidth + this.gutter) * 2
31999 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32000 y : minY + (this.unitWidth + this.gutter) * 2
32007 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32011 if(box[0].size == 'xs'){
32014 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32019 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32024 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),
32029 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32030 y : minY + (this.unitWidth + this.gutter) * 1
32038 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32043 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32044 y : minY + (this.unitWidth + this.gutter) * 2
32048 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32049 y : minY + (this.unitWidth + this.gutter) * 2
32053 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),
32054 y : minY + (this.unitWidth + this.gutter) * 2
32062 * remove a Masonry Brick
32063 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32065 removeBrick : function(brick_id)
32071 for (var i = 0; i<this.bricks.length; i++) {
32072 if (this.bricks[i].id == brick_id) {
32073 this.bricks.splice(i,1);
32074 this.el.dom.removeChild(Roo.get(brick_id).dom);
32081 * adds a Masonry Brick
32082 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32084 addBrick : function(cfg)
32086 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32087 //this.register(cn);
32088 cn.parentId = this.id;
32089 cn.render(this.el);
32094 * register a Masonry Brick
32095 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32098 register : function(brick)
32100 this.bricks.push(brick);
32101 brick.masonryId = this.id;
32105 * clear all the Masonry Brick
32107 clearAll : function()
32110 //this.getChildContainer().dom.innerHTML = "";
32111 this.el.dom.innerHTML = '';
32114 getSelected : function()
32116 if (!this.selectedBrick) {
32120 return this.selectedBrick;
32124 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32128 * register a Masonry Layout
32129 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32132 register : function(layout)
32134 this.groups[layout.id] = layout;
32137 * fetch a Masonry Layout based on the masonry layout ID
32138 * @param {string} the masonry layout to add
32139 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32142 get: function(layout_id) {
32143 if (typeof(this.groups[layout_id]) == 'undefined') {
32146 return this.groups[layout_id] ;
32158 * http://masonry.desandro.com
32160 * The idea is to render all the bricks based on vertical width...
32162 * The original code extends 'outlayer' - we might need to use that....
32168 * @class Roo.bootstrap.LayoutMasonryAuto
32169 * @extends Roo.bootstrap.Component
32170 * Bootstrap Layout Masonry class
32173 * Create a new Element
32174 * @param {Object} config The config object
32177 Roo.bootstrap.LayoutMasonryAuto = function(config){
32178 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32181 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32184 * @cfg {Boolean} isFitWidth - resize the width..
32186 isFitWidth : false, // options..
32188 * @cfg {Boolean} isOriginLeft = left align?
32190 isOriginLeft : true,
32192 * @cfg {Boolean} isOriginTop = top align?
32194 isOriginTop : false,
32196 * @cfg {Boolean} isLayoutInstant = no animation?
32198 isLayoutInstant : false, // needed?
32200 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32202 isResizingContainer : true,
32204 * @cfg {Number} columnWidth width of the columns
32210 * @cfg {Number} maxCols maximum number of columns
32215 * @cfg {Number} padHeight padding below box..
32221 * @cfg {Boolean} isAutoInitial defalut true
32224 isAutoInitial : true,
32230 initialColumnWidth : 0,
32231 currentSize : null,
32233 colYs : null, // array.
32240 bricks: null, //CompositeElement
32241 cols : 0, // array?
32242 // element : null, // wrapped now this.el
32243 _isLayoutInited : null,
32246 getAutoCreate : function(){
32250 cls: 'blog-masonary-wrapper ' + this.cls,
32252 cls : 'mas-boxes masonary'
32259 getChildContainer: function( )
32261 if (this.boxesEl) {
32262 return this.boxesEl;
32265 this.boxesEl = this.el.select('.mas-boxes').first();
32267 return this.boxesEl;
32271 initEvents : function()
32275 if(this.isAutoInitial){
32276 Roo.log('hook children rendered');
32277 this.on('childrenrendered', function() {
32278 Roo.log('children rendered');
32285 initial : function()
32287 this.reloadItems();
32289 this.currentSize = this.el.getBox(true);
32291 /// was window resize... - let's see if this works..
32292 Roo.EventManager.onWindowResize(this.resize, this);
32294 if(!this.isAutoInitial){
32299 this.layout.defer(500,this);
32302 reloadItems: function()
32304 this.bricks = this.el.select('.masonry-brick', true);
32306 this.bricks.each(function(b) {
32307 //Roo.log(b.getSize());
32308 if (!b.attr('originalwidth')) {
32309 b.attr('originalwidth', b.getSize().width);
32314 Roo.log(this.bricks.elements.length);
32317 resize : function()
32320 var cs = this.el.getBox(true);
32322 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32323 Roo.log("no change in with or X");
32326 this.currentSize = cs;
32330 layout : function()
32333 this._resetLayout();
32334 //this._manageStamps();
32336 // don't animate first layout
32337 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32338 this.layoutItems( isInstant );
32340 // flag for initalized
32341 this._isLayoutInited = true;
32344 layoutItems : function( isInstant )
32346 //var items = this._getItemsForLayout( this.items );
32347 // original code supports filtering layout items.. we just ignore it..
32349 this._layoutItems( this.bricks , isInstant );
32351 this._postLayout();
32353 _layoutItems : function ( items , isInstant)
32355 //this.fireEvent( 'layout', this, items );
32358 if ( !items || !items.elements.length ) {
32359 // no items, emit event with empty array
32364 items.each(function(item) {
32365 Roo.log("layout item");
32367 // get x/y object from method
32368 var position = this._getItemLayoutPosition( item );
32370 position.item = item;
32371 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32372 queue.push( position );
32375 this._processLayoutQueue( queue );
32377 /** Sets position of item in DOM
32378 * @param {Element} item
32379 * @param {Number} x - horizontal position
32380 * @param {Number} y - vertical position
32381 * @param {Boolean} isInstant - disables transitions
32383 _processLayoutQueue : function( queue )
32385 for ( var i=0, len = queue.length; i < len; i++ ) {
32386 var obj = queue[i];
32387 obj.item.position('absolute');
32388 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32394 * Any logic you want to do after each layout,
32395 * i.e. size the container
32397 _postLayout : function()
32399 this.resizeContainer();
32402 resizeContainer : function()
32404 if ( !this.isResizingContainer ) {
32407 var size = this._getContainerSize();
32409 this.el.setSize(size.width,size.height);
32410 this.boxesEl.setSize(size.width,size.height);
32416 _resetLayout : function()
32418 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32419 this.colWidth = this.el.getWidth();
32420 //this.gutter = this.el.getWidth();
32422 this.measureColumns();
32428 this.colYs.push( 0 );
32434 measureColumns : function()
32436 this.getContainerWidth();
32437 // if columnWidth is 0, default to outerWidth of first item
32438 if ( !this.columnWidth ) {
32439 var firstItem = this.bricks.first();
32440 Roo.log(firstItem);
32441 this.columnWidth = this.containerWidth;
32442 if (firstItem && firstItem.attr('originalwidth') ) {
32443 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32445 // columnWidth fall back to item of first element
32446 Roo.log("set column width?");
32447 this.initialColumnWidth = this.columnWidth ;
32449 // if first elem has no width, default to size of container
32454 if (this.initialColumnWidth) {
32455 this.columnWidth = this.initialColumnWidth;
32460 // column width is fixed at the top - however if container width get's smaller we should
32463 // this bit calcs how man columns..
32465 var columnWidth = this.columnWidth += this.gutter;
32467 // calculate columns
32468 var containerWidth = this.containerWidth + this.gutter;
32470 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32471 // fix rounding errors, typically with gutters
32472 var excess = columnWidth - containerWidth % columnWidth;
32475 // if overshoot is less than a pixel, round up, otherwise floor it
32476 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32477 cols = Math[ mathMethod ]( cols );
32478 this.cols = Math.max( cols, 1 );
32479 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32481 // padding positioning..
32482 var totalColWidth = this.cols * this.columnWidth;
32483 var padavail = this.containerWidth - totalColWidth;
32484 // so for 2 columns - we need 3 'pads'
32486 var padNeeded = (1+this.cols) * this.padWidth;
32488 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32490 this.columnWidth += padExtra
32491 //this.padWidth = Math.floor(padavail / ( this.cols));
32493 // adjust colum width so that padding is fixed??
32495 // we have 3 columns ... total = width * 3
32496 // we have X left over... that should be used by
32498 //if (this.expandC) {
32506 getContainerWidth : function()
32508 /* // container is parent if fit width
32509 var container = this.isFitWidth ? this.element.parentNode : this.element;
32510 // check that this.size and size are there
32511 // IE8 triggers resize on body size change, so they might not be
32513 var size = getSize( container ); //FIXME
32514 this.containerWidth = size && size.innerWidth; //FIXME
32517 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32521 _getItemLayoutPosition : function( item ) // what is item?
32523 // we resize the item to our columnWidth..
32525 item.setWidth(this.columnWidth);
32526 item.autoBoxAdjust = false;
32528 var sz = item.getSize();
32530 // how many columns does this brick span
32531 var remainder = this.containerWidth % this.columnWidth;
32533 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32534 // round if off by 1 pixel, otherwise use ceil
32535 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32536 colSpan = Math.min( colSpan, this.cols );
32538 // normally this should be '1' as we dont' currently allow multi width columns..
32540 var colGroup = this._getColGroup( colSpan );
32541 // get the minimum Y value from the columns
32542 var minimumY = Math.min.apply( Math, colGroup );
32543 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32545 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32547 // position the brick
32549 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32550 y: this.currentSize.y + minimumY + this.padHeight
32554 // apply setHeight to necessary columns
32555 var setHeight = minimumY + sz.height + this.padHeight;
32556 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32558 var setSpan = this.cols + 1 - colGroup.length;
32559 for ( var i = 0; i < setSpan; i++ ) {
32560 this.colYs[ shortColIndex + i ] = setHeight ;
32567 * @param {Number} colSpan - number of columns the element spans
32568 * @returns {Array} colGroup
32570 _getColGroup : function( colSpan )
32572 if ( colSpan < 2 ) {
32573 // if brick spans only one column, use all the column Ys
32578 // how many different places could this brick fit horizontally
32579 var groupCount = this.cols + 1 - colSpan;
32580 // for each group potential horizontal position
32581 for ( var i = 0; i < groupCount; i++ ) {
32582 // make an array of colY values for that one group
32583 var groupColYs = this.colYs.slice( i, i + colSpan );
32584 // and get the max value of the array
32585 colGroup[i] = Math.max.apply( Math, groupColYs );
32590 _manageStamp : function( stamp )
32592 var stampSize = stamp.getSize();
32593 var offset = stamp.getBox();
32594 // get the columns that this stamp affects
32595 var firstX = this.isOriginLeft ? offset.x : offset.right;
32596 var lastX = firstX + stampSize.width;
32597 var firstCol = Math.floor( firstX / this.columnWidth );
32598 firstCol = Math.max( 0, firstCol );
32600 var lastCol = Math.floor( lastX / this.columnWidth );
32601 // lastCol should not go over if multiple of columnWidth #425
32602 lastCol -= lastX % this.columnWidth ? 0 : 1;
32603 lastCol = Math.min( this.cols - 1, lastCol );
32605 // set colYs to bottom of the stamp
32606 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32609 for ( var i = firstCol; i <= lastCol; i++ ) {
32610 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32615 _getContainerSize : function()
32617 this.maxY = Math.max.apply( Math, this.colYs );
32622 if ( this.isFitWidth ) {
32623 size.width = this._getContainerFitWidth();
32629 _getContainerFitWidth : function()
32631 var unusedCols = 0;
32632 // count unused columns
32635 if ( this.colYs[i] !== 0 ) {
32640 // fit container to columns that have been used
32641 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32644 needsResizeLayout : function()
32646 var previousWidth = this.containerWidth;
32647 this.getContainerWidth();
32648 return previousWidth !== this.containerWidth;
32663 * @class Roo.bootstrap.MasonryBrick
32664 * @extends Roo.bootstrap.Component
32665 * Bootstrap MasonryBrick class
32668 * Create a new MasonryBrick
32669 * @param {Object} config The config object
32672 Roo.bootstrap.MasonryBrick = function(config){
32674 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32676 Roo.bootstrap.MasonryBrick.register(this);
32682 * When a MasonryBrick is clcik
32683 * @param {Roo.bootstrap.MasonryBrick} this
32684 * @param {Roo.EventObject} e
32690 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32693 * @cfg {String} title
32697 * @cfg {String} html
32701 * @cfg {String} bgimage
32705 * @cfg {String} videourl
32709 * @cfg {String} cls
32713 * @cfg {String} href
32717 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32722 * @cfg {String} placetitle (center|bottom)
32727 * @cfg {Boolean} isFitContainer defalut true
32729 isFitContainer : true,
32732 * @cfg {Boolean} preventDefault defalut false
32734 preventDefault : false,
32737 * @cfg {Boolean} inverse defalut false
32739 maskInverse : false,
32741 getAutoCreate : function()
32743 if(!this.isFitContainer){
32744 return this.getSplitAutoCreate();
32747 var cls = 'masonry-brick masonry-brick-full';
32749 if(this.href.length){
32750 cls += ' masonry-brick-link';
32753 if(this.bgimage.length){
32754 cls += ' masonry-brick-image';
32757 if(this.maskInverse){
32758 cls += ' mask-inverse';
32761 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32762 cls += ' enable-mask';
32766 cls += ' masonry-' + this.size + '-brick';
32769 if(this.placetitle.length){
32771 switch (this.placetitle) {
32773 cls += ' masonry-center-title';
32776 cls += ' masonry-bottom-title';
32783 if(!this.html.length && !this.bgimage.length){
32784 cls += ' masonry-center-title';
32787 if(!this.html.length && this.bgimage.length){
32788 cls += ' masonry-bottom-title';
32793 cls += ' ' + this.cls;
32797 tag: (this.href.length) ? 'a' : 'div',
32802 cls: 'masonry-brick-mask'
32806 cls: 'masonry-brick-paragraph',
32812 if(this.href.length){
32813 cfg.href = this.href;
32816 var cn = cfg.cn[1].cn;
32818 if(this.title.length){
32821 cls: 'masonry-brick-title',
32826 if(this.html.length){
32829 cls: 'masonry-brick-text',
32834 if (!this.title.length && !this.html.length) {
32835 cfg.cn[1].cls += ' hide';
32838 if(this.bgimage.length){
32841 cls: 'masonry-brick-image-view',
32846 if(this.videourl.length){
32847 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32848 // youtube support only?
32851 cls: 'masonry-brick-image-view',
32854 allowfullscreen : true
32862 getSplitAutoCreate : function()
32864 var cls = 'masonry-brick masonry-brick-split';
32866 if(this.href.length){
32867 cls += ' masonry-brick-link';
32870 if(this.bgimage.length){
32871 cls += ' masonry-brick-image';
32875 cls += ' masonry-' + this.size + '-brick';
32878 switch (this.placetitle) {
32880 cls += ' masonry-center-title';
32883 cls += ' masonry-bottom-title';
32886 if(!this.bgimage.length){
32887 cls += ' masonry-center-title';
32890 if(this.bgimage.length){
32891 cls += ' masonry-bottom-title';
32897 cls += ' ' + this.cls;
32901 tag: (this.href.length) ? 'a' : 'div',
32906 cls: 'masonry-brick-split-head',
32910 cls: 'masonry-brick-paragraph',
32917 cls: 'masonry-brick-split-body',
32923 if(this.href.length){
32924 cfg.href = this.href;
32927 if(this.title.length){
32928 cfg.cn[0].cn[0].cn.push({
32930 cls: 'masonry-brick-title',
32935 if(this.html.length){
32936 cfg.cn[1].cn.push({
32938 cls: 'masonry-brick-text',
32943 if(this.bgimage.length){
32944 cfg.cn[0].cn.push({
32946 cls: 'masonry-brick-image-view',
32951 if(this.videourl.length){
32952 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32953 // youtube support only?
32954 cfg.cn[0].cn.cn.push({
32956 cls: 'masonry-brick-image-view',
32959 allowfullscreen : true
32966 initEvents: function()
32968 switch (this.size) {
33001 this.el.on('touchstart', this.onTouchStart, this);
33002 this.el.on('touchmove', this.onTouchMove, this);
33003 this.el.on('touchend', this.onTouchEnd, this);
33004 this.el.on('contextmenu', this.onContextMenu, this);
33006 this.el.on('mouseenter' ,this.enter, this);
33007 this.el.on('mouseleave', this.leave, this);
33008 this.el.on('click', this.onClick, this);
33011 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33012 this.parent().bricks.push(this);
33017 onClick: function(e, el)
33019 var time = this.endTimer - this.startTimer;
33020 // Roo.log(e.preventDefault());
33023 e.preventDefault();
33028 if(!this.preventDefault){
33032 e.preventDefault();
33034 if (this.activeClass != '') {
33035 this.selectBrick();
33038 this.fireEvent('click', this, e);
33041 enter: function(e, el)
33043 e.preventDefault();
33045 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33049 if(this.bgimage.length && this.html.length){
33050 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33054 leave: function(e, el)
33056 e.preventDefault();
33058 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33062 if(this.bgimage.length && this.html.length){
33063 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33067 onTouchStart: function(e, el)
33069 // e.preventDefault();
33071 this.touchmoved = false;
33073 if(!this.isFitContainer){
33077 if(!this.bgimage.length || !this.html.length){
33081 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33083 this.timer = new Date().getTime();
33087 onTouchMove: function(e, el)
33089 this.touchmoved = true;
33092 onContextMenu : function(e,el)
33094 e.preventDefault();
33095 e.stopPropagation();
33099 onTouchEnd: function(e, el)
33101 // e.preventDefault();
33103 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33110 if(!this.bgimage.length || !this.html.length){
33112 if(this.href.length){
33113 window.location.href = this.href;
33119 if(!this.isFitContainer){
33123 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33125 window.location.href = this.href;
33128 //selection on single brick only
33129 selectBrick : function() {
33131 if (!this.parentId) {
33135 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33136 var index = m.selectedBrick.indexOf(this.id);
33139 m.selectedBrick.splice(index,1);
33140 this.el.removeClass(this.activeClass);
33144 for(var i = 0; i < m.selectedBrick.length; i++) {
33145 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33146 b.el.removeClass(b.activeClass);
33149 m.selectedBrick = [];
33151 m.selectedBrick.push(this.id);
33152 this.el.addClass(this.activeClass);
33156 isSelected : function(){
33157 return this.el.hasClass(this.activeClass);
33162 Roo.apply(Roo.bootstrap.MasonryBrick, {
33165 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33167 * register a Masonry Brick
33168 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33171 register : function(brick)
33173 //this.groups[brick.id] = brick;
33174 this.groups.add(brick.id, brick);
33177 * fetch a masonry brick based on the masonry brick ID
33178 * @param {string} the masonry brick to add
33179 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33182 get: function(brick_id)
33184 // if (typeof(this.groups[brick_id]) == 'undefined') {
33187 // return this.groups[brick_id] ;
33189 if(this.groups.key(brick_id)) {
33190 return this.groups.key(brick_id);
33208 * @class Roo.bootstrap.Brick
33209 * @extends Roo.bootstrap.Component
33210 * Bootstrap Brick class
33213 * Create a new Brick
33214 * @param {Object} config The config object
33217 Roo.bootstrap.Brick = function(config){
33218 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33224 * When a Brick is click
33225 * @param {Roo.bootstrap.Brick} this
33226 * @param {Roo.EventObject} e
33232 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33235 * @cfg {String} title
33239 * @cfg {String} html
33243 * @cfg {String} bgimage
33247 * @cfg {String} cls
33251 * @cfg {String} href
33255 * @cfg {String} video
33259 * @cfg {Boolean} square
33263 getAutoCreate : function()
33265 var cls = 'roo-brick';
33267 if(this.href.length){
33268 cls += ' roo-brick-link';
33271 if(this.bgimage.length){
33272 cls += ' roo-brick-image';
33275 if(!this.html.length && !this.bgimage.length){
33276 cls += ' roo-brick-center-title';
33279 if(!this.html.length && this.bgimage.length){
33280 cls += ' roo-brick-bottom-title';
33284 cls += ' ' + this.cls;
33288 tag: (this.href.length) ? 'a' : 'div',
33293 cls: 'roo-brick-paragraph',
33299 if(this.href.length){
33300 cfg.href = this.href;
33303 var cn = cfg.cn[0].cn;
33305 if(this.title.length){
33308 cls: 'roo-brick-title',
33313 if(this.html.length){
33316 cls: 'roo-brick-text',
33323 if(this.bgimage.length){
33326 cls: 'roo-brick-image-view',
33334 initEvents: function()
33336 if(this.title.length || this.html.length){
33337 this.el.on('mouseenter' ,this.enter, this);
33338 this.el.on('mouseleave', this.leave, this);
33341 Roo.EventManager.onWindowResize(this.resize, this);
33343 if(this.bgimage.length){
33344 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33345 this.imageEl.on('load', this.onImageLoad, this);
33352 onImageLoad : function()
33357 resize : function()
33359 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33361 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33363 if(this.bgimage.length){
33364 var image = this.el.select('.roo-brick-image-view', true).first();
33366 image.setWidth(paragraph.getWidth());
33369 image.setHeight(paragraph.getWidth());
33372 this.el.setHeight(image.getHeight());
33373 paragraph.setHeight(image.getHeight());
33379 enter: function(e, el)
33381 e.preventDefault();
33383 if(this.bgimage.length){
33384 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33385 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33389 leave: function(e, el)
33391 e.preventDefault();
33393 if(this.bgimage.length){
33394 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33395 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33410 * @class Roo.bootstrap.NumberField
33411 * @extends Roo.bootstrap.Input
33412 * Bootstrap NumberField class
33418 * Create a new NumberField
33419 * @param {Object} config The config object
33422 Roo.bootstrap.NumberField = function(config){
33423 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33426 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33429 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33431 allowDecimals : true,
33433 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33435 decimalSeparator : ".",
33437 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33439 decimalPrecision : 2,
33441 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33443 allowNegative : true,
33446 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33450 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33452 minValue : Number.NEGATIVE_INFINITY,
33454 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33456 maxValue : Number.MAX_VALUE,
33458 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33460 minText : "The minimum value for this field is {0}",
33462 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33464 maxText : "The maximum value for this field is {0}",
33466 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33467 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33469 nanText : "{0} is not a valid number",
33471 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33473 thousandsDelimiter : false,
33475 * @cfg {String} valueAlign alignment of value
33477 valueAlign : "left",
33479 getAutoCreate : function()
33481 var hiddenInput = {
33485 cls: 'hidden-number-input'
33489 hiddenInput.name = this.name;
33494 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33496 this.name = hiddenInput.name;
33498 if(cfg.cn.length > 0) {
33499 cfg.cn.push(hiddenInput);
33506 initEvents : function()
33508 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33510 var allowed = "0123456789";
33512 if(this.allowDecimals){
33513 allowed += this.decimalSeparator;
33516 if(this.allowNegative){
33520 if(this.thousandsDelimiter) {
33524 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33526 var keyPress = function(e){
33528 var k = e.getKey();
33530 var c = e.getCharCode();
33533 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33534 allowed.indexOf(String.fromCharCode(c)) === -1
33540 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33544 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33549 this.el.on("keypress", keyPress, this);
33552 validateValue : function(value)
33555 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33559 var num = this.parseValue(value);
33562 this.markInvalid(String.format(this.nanText, value));
33566 if(num < this.minValue){
33567 this.markInvalid(String.format(this.minText, this.minValue));
33571 if(num > this.maxValue){
33572 this.markInvalid(String.format(this.maxText, this.maxValue));
33579 getValue : function()
33581 var v = this.hiddenEl().getValue();
33583 return this.fixPrecision(this.parseValue(v));
33586 parseValue : function(value)
33588 if(this.thousandsDelimiter) {
33590 r = new RegExp(",", "g");
33591 value = value.replace(r, "");
33594 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33595 return isNaN(value) ? '' : value;
33598 fixPrecision : function(value)
33600 if(this.thousandsDelimiter) {
33602 r = new RegExp(",", "g");
33603 value = value.replace(r, "");
33606 var nan = isNaN(value);
33608 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33609 return nan ? '' : value;
33611 return parseFloat(value).toFixed(this.decimalPrecision);
33614 setValue : function(v)
33616 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33622 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33624 this.inputEl().dom.value = (v == '') ? '' :
33625 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33627 if(!this.allowZero && v === '0') {
33628 this.hiddenEl().dom.value = '';
33629 this.inputEl().dom.value = '';
33636 decimalPrecisionFcn : function(v)
33638 return Math.floor(v);
33641 beforeBlur : function()
33643 var v = this.parseValue(this.getRawValue());
33645 if(v || v === 0 || v === ''){
33650 hiddenEl : function()
33652 return this.el.select('input.hidden-number-input',true).first();
33664 * @class Roo.bootstrap.DocumentSlider
33665 * @extends Roo.bootstrap.Component
33666 * Bootstrap DocumentSlider class
33669 * Create a new DocumentViewer
33670 * @param {Object} config The config object
33673 Roo.bootstrap.DocumentSlider = function(config){
33674 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33681 * Fire after initEvent
33682 * @param {Roo.bootstrap.DocumentSlider} this
33687 * Fire after update
33688 * @param {Roo.bootstrap.DocumentSlider} this
33694 * @param {Roo.bootstrap.DocumentSlider} this
33700 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33706 getAutoCreate : function()
33710 cls : 'roo-document-slider',
33714 cls : 'roo-document-slider-header',
33718 cls : 'roo-document-slider-header-title'
33724 cls : 'roo-document-slider-body',
33728 cls : 'roo-document-slider-prev',
33732 cls : 'fa fa-chevron-left'
33738 cls : 'roo-document-slider-thumb',
33742 cls : 'roo-document-slider-image'
33748 cls : 'roo-document-slider-next',
33752 cls : 'fa fa-chevron-right'
33764 initEvents : function()
33766 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33767 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33769 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33770 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33772 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33773 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33775 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33776 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33778 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33779 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33781 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33782 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33784 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33785 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33787 this.thumbEl.on('click', this.onClick, this);
33789 this.prevIndicator.on('click', this.prev, this);
33791 this.nextIndicator.on('click', this.next, this);
33795 initial : function()
33797 if(this.files.length){
33798 this.indicator = 1;
33802 this.fireEvent('initial', this);
33805 update : function()
33807 this.imageEl.attr('src', this.files[this.indicator - 1]);
33809 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33811 this.prevIndicator.show();
33813 if(this.indicator == 1){
33814 this.prevIndicator.hide();
33817 this.nextIndicator.show();
33819 if(this.indicator == this.files.length){
33820 this.nextIndicator.hide();
33823 this.thumbEl.scrollTo('top');
33825 this.fireEvent('update', this);
33828 onClick : function(e)
33830 e.preventDefault();
33832 this.fireEvent('click', this);
33837 e.preventDefault();
33839 this.indicator = Math.max(1, this.indicator - 1);
33846 e.preventDefault();
33848 this.indicator = Math.min(this.files.length, this.indicator + 1);
33862 * @class Roo.bootstrap.RadioSet
33863 * @extends Roo.bootstrap.Input
33864 * Bootstrap RadioSet class
33865 * @cfg {String} indicatorpos (left|right) default left
33866 * @cfg {Boolean} inline (true|false) inline the element (default true)
33867 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33869 * Create a new RadioSet
33870 * @param {Object} config The config object
33873 Roo.bootstrap.RadioSet = function(config){
33875 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33879 Roo.bootstrap.RadioSet.register(this);
33884 * Fires when the element is checked or unchecked.
33885 * @param {Roo.bootstrap.RadioSet} this This radio
33886 * @param {Roo.bootstrap.Radio} item The checked item
33891 * Fires when the element is click.
33892 * @param {Roo.bootstrap.RadioSet} this This radio set
33893 * @param {Roo.bootstrap.Radio} item The checked item
33894 * @param {Roo.EventObject} e The event object
33901 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33909 indicatorpos : 'left',
33911 getAutoCreate : function()
33915 cls : 'roo-radio-set-label',
33919 html : this.fieldLabel
33924 if(this.indicatorpos == 'left'){
33927 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33928 tooltip : 'This field is required'
33933 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33934 tooltip : 'This field is required'
33940 cls : 'roo-radio-set-items'
33943 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33945 if (align === 'left' && this.fieldLabel.length) {
33948 cls : "roo-radio-set-right",
33954 if(this.labelWidth > 12){
33955 label.style = "width: " + this.labelWidth + 'px';
33958 if(this.labelWidth < 13 && this.labelmd == 0){
33959 this.labelmd = this.labelWidth;
33962 if(this.labellg > 0){
33963 label.cls += ' col-lg-' + this.labellg;
33964 items.cls += ' col-lg-' + (12 - this.labellg);
33967 if(this.labelmd > 0){
33968 label.cls += ' col-md-' + this.labelmd;
33969 items.cls += ' col-md-' + (12 - this.labelmd);
33972 if(this.labelsm > 0){
33973 label.cls += ' col-sm-' + this.labelsm;
33974 items.cls += ' col-sm-' + (12 - this.labelsm);
33977 if(this.labelxs > 0){
33978 label.cls += ' col-xs-' + this.labelxs;
33979 items.cls += ' col-xs-' + (12 - this.labelxs);
33985 cls : 'roo-radio-set',
33989 cls : 'roo-radio-set-input',
33992 value : this.value ? this.value : ''
33999 if(this.weight.length){
34000 cfg.cls += ' roo-radio-' + this.weight;
34004 cfg.cls += ' roo-radio-set-inline';
34008 ['xs','sm','md','lg'].map(function(size){
34009 if (settings[size]) {
34010 cfg.cls += ' col-' + size + '-' + settings[size];
34018 initEvents : function()
34020 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34021 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34023 if(!this.fieldLabel.length){
34024 this.labelEl.hide();
34027 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34028 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34030 this.indicator = this.indicatorEl();
34032 if(this.indicator){
34033 this.indicator.addClass('invisible');
34036 this.originalValue = this.getValue();
34040 inputEl: function ()
34042 return this.el.select('.roo-radio-set-input', true).first();
34045 getChildContainer : function()
34047 return this.itemsEl;
34050 register : function(item)
34052 this.radioes.push(item);
34056 validate : function()
34058 if(this.getVisibilityEl().hasClass('hidden')){
34064 Roo.each(this.radioes, function(i){
34073 if(this.allowBlank) {
34077 if(this.disabled || valid){
34082 this.markInvalid();
34087 markValid : function()
34089 if(this.labelEl.isVisible(true)){
34090 this.indicatorEl().removeClass('visible');
34091 this.indicatorEl().addClass('invisible');
34094 this.el.removeClass([this.invalidClass, this.validClass]);
34095 this.el.addClass(this.validClass);
34097 this.fireEvent('valid', this);
34100 markInvalid : function(msg)
34102 if(this.allowBlank || this.disabled){
34106 if(this.labelEl.isVisible(true)){
34107 this.indicatorEl().removeClass('invisible');
34108 this.indicatorEl().addClass('visible');
34111 this.el.removeClass([this.invalidClass, this.validClass]);
34112 this.el.addClass(this.invalidClass);
34114 this.fireEvent('invalid', this, msg);
34118 setValue : function(v, suppressEvent)
34120 if(this.value === v){
34127 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34130 Roo.each(this.radioes, function(i){
34132 i.el.removeClass('checked');
34135 Roo.each(this.radioes, function(i){
34137 if(i.value === v || i.value.toString() === v.toString()){
34139 i.el.addClass('checked');
34141 if(suppressEvent !== true){
34142 this.fireEvent('check', this, i);
34153 clearInvalid : function(){
34155 if(!this.el || this.preventMark){
34159 this.el.removeClass([this.invalidClass]);
34161 this.fireEvent('valid', this);
34166 Roo.apply(Roo.bootstrap.RadioSet, {
34170 register : function(set)
34172 this.groups[set.name] = set;
34175 get: function(name)
34177 if (typeof(this.groups[name]) == 'undefined') {
34181 return this.groups[name] ;
34187 * Ext JS Library 1.1.1
34188 * Copyright(c) 2006-2007, Ext JS, LLC.
34190 * Originally Released Under LGPL - original licence link has changed is not relivant.
34193 * <script type="text/javascript">
34198 * @class Roo.bootstrap.SplitBar
34199 * @extends Roo.util.Observable
34200 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34204 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34205 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34206 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34207 split.minSize = 100;
34208 split.maxSize = 600;
34209 split.animate = true;
34210 split.on('moved', splitterMoved);
34213 * Create a new SplitBar
34214 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34215 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34216 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34217 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34218 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34219 position of the SplitBar).
34221 Roo.bootstrap.SplitBar = function(cfg){
34226 // dragElement : elm
34227 // resizingElement: el,
34229 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34230 // placement : Roo.bootstrap.SplitBar.LEFT ,
34231 // existingProxy ???
34234 this.el = Roo.get(cfg.dragElement, true);
34235 this.el.dom.unselectable = "on";
34237 this.resizingEl = Roo.get(cfg.resizingElement, true);
34241 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34242 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34245 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34248 * The minimum size of the resizing element. (Defaults to 0)
34254 * The maximum size of the resizing element. (Defaults to 2000)
34257 this.maxSize = 2000;
34260 * Whether to animate the transition to the new size
34263 this.animate = false;
34266 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34269 this.useShim = false;
34274 if(!cfg.existingProxy){
34276 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34278 this.proxy = Roo.get(cfg.existingProxy).dom;
34281 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34284 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34287 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34290 this.dragSpecs = {};
34293 * @private The adapter to use to positon and resize elements
34295 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34296 this.adapter.init(this);
34298 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34300 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34301 this.el.addClass("roo-splitbar-h");
34304 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34305 this.el.addClass("roo-splitbar-v");
34311 * Fires when the splitter is moved (alias for {@link #event-moved})
34312 * @param {Roo.bootstrap.SplitBar} this
34313 * @param {Number} newSize the new width or height
34318 * Fires when the splitter is moved
34319 * @param {Roo.bootstrap.SplitBar} this
34320 * @param {Number} newSize the new width or height
34324 * @event beforeresize
34325 * Fires before the splitter is dragged
34326 * @param {Roo.bootstrap.SplitBar} this
34328 "beforeresize" : true,
34330 "beforeapply" : true
34333 Roo.util.Observable.call(this);
34336 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34337 onStartProxyDrag : function(x, y){
34338 this.fireEvent("beforeresize", this);
34340 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34342 o.enableDisplayMode("block");
34343 // all splitbars share the same overlay
34344 Roo.bootstrap.SplitBar.prototype.overlay = o;
34346 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34347 this.overlay.show();
34348 Roo.get(this.proxy).setDisplayed("block");
34349 var size = this.adapter.getElementSize(this);
34350 this.activeMinSize = this.getMinimumSize();;
34351 this.activeMaxSize = this.getMaximumSize();;
34352 var c1 = size - this.activeMinSize;
34353 var c2 = Math.max(this.activeMaxSize - size, 0);
34354 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34355 this.dd.resetConstraints();
34356 this.dd.setXConstraint(
34357 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34358 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34360 this.dd.setYConstraint(0, 0);
34362 this.dd.resetConstraints();
34363 this.dd.setXConstraint(0, 0);
34364 this.dd.setYConstraint(
34365 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34366 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34369 this.dragSpecs.startSize = size;
34370 this.dragSpecs.startPoint = [x, y];
34371 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34375 * @private Called after the drag operation by the DDProxy
34377 onEndProxyDrag : function(e){
34378 Roo.get(this.proxy).setDisplayed(false);
34379 var endPoint = Roo.lib.Event.getXY(e);
34381 this.overlay.hide();
34384 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34385 newSize = this.dragSpecs.startSize +
34386 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34387 endPoint[0] - this.dragSpecs.startPoint[0] :
34388 this.dragSpecs.startPoint[0] - endPoint[0]
34391 newSize = this.dragSpecs.startSize +
34392 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34393 endPoint[1] - this.dragSpecs.startPoint[1] :
34394 this.dragSpecs.startPoint[1] - endPoint[1]
34397 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34398 if(newSize != this.dragSpecs.startSize){
34399 if(this.fireEvent('beforeapply', this, newSize) !== false){
34400 this.adapter.setElementSize(this, newSize);
34401 this.fireEvent("moved", this, newSize);
34402 this.fireEvent("resize", this, newSize);
34408 * Get the adapter this SplitBar uses
34409 * @return The adapter object
34411 getAdapter : function(){
34412 return this.adapter;
34416 * Set the adapter this SplitBar uses
34417 * @param {Object} adapter A SplitBar adapter object
34419 setAdapter : function(adapter){
34420 this.adapter = adapter;
34421 this.adapter.init(this);
34425 * Gets the minimum size for the resizing element
34426 * @return {Number} The minimum size
34428 getMinimumSize : function(){
34429 return this.minSize;
34433 * Sets the minimum size for the resizing element
34434 * @param {Number} minSize The minimum size
34436 setMinimumSize : function(minSize){
34437 this.minSize = minSize;
34441 * Gets the maximum size for the resizing element
34442 * @return {Number} The maximum size
34444 getMaximumSize : function(){
34445 return this.maxSize;
34449 * Sets the maximum size for the resizing element
34450 * @param {Number} maxSize The maximum size
34452 setMaximumSize : function(maxSize){
34453 this.maxSize = maxSize;
34457 * Sets the initialize size for the resizing element
34458 * @param {Number} size The initial size
34460 setCurrentSize : function(size){
34461 var oldAnimate = this.animate;
34462 this.animate = false;
34463 this.adapter.setElementSize(this, size);
34464 this.animate = oldAnimate;
34468 * Destroy this splitbar.
34469 * @param {Boolean} removeEl True to remove the element
34471 destroy : function(removeEl){
34473 this.shim.remove();
34476 this.proxy.parentNode.removeChild(this.proxy);
34484 * @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.
34486 Roo.bootstrap.SplitBar.createProxy = function(dir){
34487 var proxy = new Roo.Element(document.createElement("div"));
34488 proxy.unselectable();
34489 var cls = 'roo-splitbar-proxy';
34490 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34491 document.body.appendChild(proxy.dom);
34496 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34497 * Default Adapter. It assumes the splitter and resizing element are not positioned
34498 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34500 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34503 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34504 // do nothing for now
34505 init : function(s){
34509 * Called before drag operations to get the current size of the resizing element.
34510 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34512 getElementSize : function(s){
34513 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34514 return s.resizingEl.getWidth();
34516 return s.resizingEl.getHeight();
34521 * Called after drag operations to set the size of the resizing element.
34522 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34523 * @param {Number} newSize The new size to set
34524 * @param {Function} onComplete A function to be invoked when resizing is complete
34526 setElementSize : function(s, newSize, onComplete){
34527 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34529 s.resizingEl.setWidth(newSize);
34531 onComplete(s, newSize);
34534 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34539 s.resizingEl.setHeight(newSize);
34541 onComplete(s, newSize);
34544 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34551 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34552 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34553 * Adapter that moves the splitter element to align with the resized sizing element.
34554 * Used with an absolute positioned SplitBar.
34555 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34556 * document.body, make sure you assign an id to the body element.
34558 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34559 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34560 this.container = Roo.get(container);
34563 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34564 init : function(s){
34565 this.basic.init(s);
34568 getElementSize : function(s){
34569 return this.basic.getElementSize(s);
34572 setElementSize : function(s, newSize, onComplete){
34573 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34576 moveSplitter : function(s){
34577 var yes = Roo.bootstrap.SplitBar;
34578 switch(s.placement){
34580 s.el.setX(s.resizingEl.getRight());
34583 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34586 s.el.setY(s.resizingEl.getBottom());
34589 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34596 * Orientation constant - Create a vertical SplitBar
34600 Roo.bootstrap.SplitBar.VERTICAL = 1;
34603 * Orientation constant - Create a horizontal SplitBar
34607 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34610 * Placement constant - The resizing element is to the left of the splitter element
34614 Roo.bootstrap.SplitBar.LEFT = 1;
34617 * Placement constant - The resizing element is to the right of the splitter element
34621 Roo.bootstrap.SplitBar.RIGHT = 2;
34624 * Placement constant - The resizing element is positioned above the splitter element
34628 Roo.bootstrap.SplitBar.TOP = 3;
34631 * Placement constant - The resizing element is positioned under splitter element
34635 Roo.bootstrap.SplitBar.BOTTOM = 4;
34636 Roo.namespace("Roo.bootstrap.layout");/*
34638 * Ext JS Library 1.1.1
34639 * Copyright(c) 2006-2007, Ext JS, LLC.
34641 * Originally Released Under LGPL - original licence link has changed is not relivant.
34644 * <script type="text/javascript">
34648 * @class Roo.bootstrap.layout.Manager
34649 * @extends Roo.bootstrap.Component
34650 * Base class for layout managers.
34652 Roo.bootstrap.layout.Manager = function(config)
34654 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34660 /** false to disable window resize monitoring @type Boolean */
34661 this.monitorWindowResize = true;
34666 * Fires when a layout is performed.
34667 * @param {Roo.LayoutManager} this
34671 * @event regionresized
34672 * Fires when the user resizes a region.
34673 * @param {Roo.LayoutRegion} region The resized region
34674 * @param {Number} newSize The new size (width for east/west, height for north/south)
34676 "regionresized" : true,
34678 * @event regioncollapsed
34679 * Fires when a region is collapsed.
34680 * @param {Roo.LayoutRegion} region The collapsed region
34682 "regioncollapsed" : true,
34684 * @event regionexpanded
34685 * Fires when a region is expanded.
34686 * @param {Roo.LayoutRegion} region The expanded region
34688 "regionexpanded" : true
34690 this.updating = false;
34693 this.el = Roo.get(config.el);
34699 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34704 monitorWindowResize : true,
34710 onRender : function(ct, position)
34713 this.el = Roo.get(ct);
34716 //this.fireEvent('render',this);
34720 initEvents: function()
34724 // ie scrollbar fix
34725 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34726 document.body.scroll = "no";
34727 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34728 this.el.position('relative');
34730 this.id = this.el.id;
34731 this.el.addClass("roo-layout-container");
34732 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34733 if(this.el.dom != document.body ) {
34734 this.el.on('resize', this.layout,this);
34735 this.el.on('show', this.layout,this);
34741 * Returns true if this layout is currently being updated
34742 * @return {Boolean}
34744 isUpdating : function(){
34745 return this.updating;
34749 * Suspend the LayoutManager from doing auto-layouts while
34750 * making multiple add or remove calls
34752 beginUpdate : function(){
34753 this.updating = true;
34757 * Restore auto-layouts and optionally disable the manager from performing a layout
34758 * @param {Boolean} noLayout true to disable a layout update
34760 endUpdate : function(noLayout){
34761 this.updating = false;
34767 layout: function(){
34771 onRegionResized : function(region, newSize){
34772 this.fireEvent("regionresized", region, newSize);
34776 onRegionCollapsed : function(region){
34777 this.fireEvent("regioncollapsed", region);
34780 onRegionExpanded : function(region){
34781 this.fireEvent("regionexpanded", region);
34785 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34786 * performs box-model adjustments.
34787 * @return {Object} The size as an object {width: (the width), height: (the height)}
34789 getViewSize : function()
34792 if(this.el.dom != document.body){
34793 size = this.el.getSize();
34795 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34797 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34798 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34803 * Returns the Element this layout is bound to.
34804 * @return {Roo.Element}
34806 getEl : function(){
34811 * Returns the specified region.
34812 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34813 * @return {Roo.LayoutRegion}
34815 getRegion : function(target){
34816 return this.regions[target.toLowerCase()];
34819 onWindowResize : function(){
34820 if(this.monitorWindowResize){
34827 * Ext JS Library 1.1.1
34828 * Copyright(c) 2006-2007, Ext JS, LLC.
34830 * Originally Released Under LGPL - original licence link has changed is not relivant.
34833 * <script type="text/javascript">
34836 * @class Roo.bootstrap.layout.Border
34837 * @extends Roo.bootstrap.layout.Manager
34838 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34839 * please see: examples/bootstrap/nested.html<br><br>
34841 <b>The container the layout is rendered into can be either the body element or any other element.
34842 If it is not the body element, the container needs to either be an absolute positioned element,
34843 or you will need to add "position:relative" to the css of the container. You will also need to specify
34844 the container size if it is not the body element.</b>
34847 * Create a new Border
34848 * @param {Object} config Configuration options
34850 Roo.bootstrap.layout.Border = function(config){
34851 config = config || {};
34852 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34856 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34857 if(config[region]){
34858 config[region].region = region;
34859 this.addRegion(config[region]);
34865 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34867 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34869 * Creates and adds a new region if it doesn't already exist.
34870 * @param {String} target The target region key (north, south, east, west or center).
34871 * @param {Object} config The regions config object
34872 * @return {BorderLayoutRegion} The new region
34874 addRegion : function(config)
34876 if(!this.regions[config.region]){
34877 var r = this.factory(config);
34878 this.bindRegion(r);
34880 return this.regions[config.region];
34884 bindRegion : function(r){
34885 this.regions[r.config.region] = r;
34887 r.on("visibilitychange", this.layout, this);
34888 r.on("paneladded", this.layout, this);
34889 r.on("panelremoved", this.layout, this);
34890 r.on("invalidated", this.layout, this);
34891 r.on("resized", this.onRegionResized, this);
34892 r.on("collapsed", this.onRegionCollapsed, this);
34893 r.on("expanded", this.onRegionExpanded, this);
34897 * Performs a layout update.
34899 layout : function()
34901 if(this.updating) {
34905 // render all the rebions if they have not been done alreayd?
34906 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34907 if(this.regions[region] && !this.regions[region].bodyEl){
34908 this.regions[region].onRender(this.el)
34912 var size = this.getViewSize();
34913 var w = size.width;
34914 var h = size.height;
34919 //var x = 0, y = 0;
34921 var rs = this.regions;
34922 var north = rs["north"];
34923 var south = rs["south"];
34924 var west = rs["west"];
34925 var east = rs["east"];
34926 var center = rs["center"];
34927 //if(this.hideOnLayout){ // not supported anymore
34928 //c.el.setStyle("display", "none");
34930 if(north && north.isVisible()){
34931 var b = north.getBox();
34932 var m = north.getMargins();
34933 b.width = w - (m.left+m.right);
34936 centerY = b.height + b.y + m.bottom;
34937 centerH -= centerY;
34938 north.updateBox(this.safeBox(b));
34940 if(south && south.isVisible()){
34941 var b = south.getBox();
34942 var m = south.getMargins();
34943 b.width = w - (m.left+m.right);
34945 var totalHeight = (b.height + m.top + m.bottom);
34946 b.y = h - totalHeight + m.top;
34947 centerH -= totalHeight;
34948 south.updateBox(this.safeBox(b));
34950 if(west && west.isVisible()){
34951 var b = west.getBox();
34952 var m = west.getMargins();
34953 b.height = centerH - (m.top+m.bottom);
34955 b.y = centerY + m.top;
34956 var totalWidth = (b.width + m.left + m.right);
34957 centerX += totalWidth;
34958 centerW -= totalWidth;
34959 west.updateBox(this.safeBox(b));
34961 if(east && east.isVisible()){
34962 var b = east.getBox();
34963 var m = east.getMargins();
34964 b.height = centerH - (m.top+m.bottom);
34965 var totalWidth = (b.width + m.left + m.right);
34966 b.x = w - totalWidth + m.left;
34967 b.y = centerY + m.top;
34968 centerW -= totalWidth;
34969 east.updateBox(this.safeBox(b));
34972 var m = center.getMargins();
34974 x: centerX + m.left,
34975 y: centerY + m.top,
34976 width: centerW - (m.left+m.right),
34977 height: centerH - (m.top+m.bottom)
34979 //if(this.hideOnLayout){
34980 //center.el.setStyle("display", "block");
34982 center.updateBox(this.safeBox(centerBox));
34985 this.fireEvent("layout", this);
34989 safeBox : function(box){
34990 box.width = Math.max(0, box.width);
34991 box.height = Math.max(0, box.height);
34996 * Adds a ContentPanel (or subclass) to this layout.
34997 * @param {String} target The target region key (north, south, east, west or center).
34998 * @param {Roo.ContentPanel} panel The panel to add
34999 * @return {Roo.ContentPanel} The added panel
35001 add : function(target, panel){
35003 target = target.toLowerCase();
35004 return this.regions[target].add(panel);
35008 * Remove a ContentPanel (or subclass) to this layout.
35009 * @param {String} target The target region key (north, south, east, west or center).
35010 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35011 * @return {Roo.ContentPanel} The removed panel
35013 remove : function(target, panel){
35014 target = target.toLowerCase();
35015 return this.regions[target].remove(panel);
35019 * Searches all regions for a panel with the specified id
35020 * @param {String} panelId
35021 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35023 findPanel : function(panelId){
35024 var rs = this.regions;
35025 for(var target in rs){
35026 if(typeof rs[target] != "function"){
35027 var p = rs[target].getPanel(panelId);
35037 * Searches all regions for a panel with the specified id and activates (shows) it.
35038 * @param {String/ContentPanel} panelId The panels id or the panel itself
35039 * @return {Roo.ContentPanel} The shown panel or null
35041 showPanel : function(panelId) {
35042 var rs = this.regions;
35043 for(var target in rs){
35044 var r = rs[target];
35045 if(typeof r != "function"){
35046 if(r.hasPanel(panelId)){
35047 return r.showPanel(panelId);
35055 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35056 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35059 restoreState : function(provider){
35061 provider = Roo.state.Manager;
35063 var sm = new Roo.LayoutStateManager();
35064 sm.init(this, provider);
35070 * Adds a xtype elements to the layout.
35074 xtype : 'ContentPanel',
35081 xtype : 'NestedLayoutPanel',
35087 items : [ ... list of content panels or nested layout panels.. ]
35091 * @param {Object} cfg Xtype definition of item to add.
35093 addxtype : function(cfg)
35095 // basically accepts a pannel...
35096 // can accept a layout region..!?!?
35097 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35100 // theory? children can only be panels??
35102 //if (!cfg.xtype.match(/Panel$/)) {
35107 if (typeof(cfg.region) == 'undefined') {
35108 Roo.log("Failed to add Panel, region was not set");
35112 var region = cfg.region;
35118 xitems = cfg.items;
35125 case 'Content': // ContentPanel (el, cfg)
35126 case 'Scroll': // ContentPanel (el, cfg)
35128 cfg.autoCreate = true;
35129 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35131 // var el = this.el.createChild();
35132 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35135 this.add(region, ret);
35139 case 'TreePanel': // our new panel!
35140 cfg.el = this.el.createChild();
35141 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35142 this.add(region, ret);
35147 // create a new Layout (which is a Border Layout...
35149 var clayout = cfg.layout;
35150 clayout.el = this.el.createChild();
35151 clayout.items = clayout.items || [];
35155 // replace this exitems with the clayout ones..
35156 xitems = clayout.items;
35158 // force background off if it's in center...
35159 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35160 cfg.background = false;
35162 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35165 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35166 //console.log('adding nested layout panel ' + cfg.toSource());
35167 this.add(region, ret);
35168 nb = {}; /// find first...
35173 // needs grid and region
35175 //var el = this.getRegion(region).el.createChild();
35177 *var el = this.el.createChild();
35178 // create the grid first...
35179 cfg.grid.container = el;
35180 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35183 if (region == 'center' && this.active ) {
35184 cfg.background = false;
35187 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35189 this.add(region, ret);
35191 if (cfg.background) {
35192 // render grid on panel activation (if panel background)
35193 ret.on('activate', function(gp) {
35194 if (!gp.grid.rendered) {
35195 // gp.grid.render(el);
35199 // cfg.grid.render(el);
35205 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35206 // it was the old xcomponent building that caused this before.
35207 // espeically if border is the top element in the tree.
35217 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35219 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35220 this.add(region, ret);
35224 throw "Can not add '" + cfg.xtype + "' to Border";
35230 this.beginUpdate();
35234 Roo.each(xitems, function(i) {
35235 region = nb && i.region ? i.region : false;
35237 var add = ret.addxtype(i);
35240 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35241 if (!i.background) {
35242 abn[region] = nb[region] ;
35249 // make the last non-background panel active..
35250 //if (nb) { Roo.log(abn); }
35253 for(var r in abn) {
35254 region = this.getRegion(r);
35256 // tried using nb[r], but it does not work..
35258 region.showPanel(abn[r]);
35269 factory : function(cfg)
35272 var validRegions = Roo.bootstrap.layout.Border.regions;
35274 var target = cfg.region;
35277 var r = Roo.bootstrap.layout;
35281 return new r.North(cfg);
35283 return new r.South(cfg);
35285 return new r.East(cfg);
35287 return new r.West(cfg);
35289 return new r.Center(cfg);
35291 throw 'Layout region "'+target+'" not supported.';
35298 * Ext JS Library 1.1.1
35299 * Copyright(c) 2006-2007, Ext JS, LLC.
35301 * Originally Released Under LGPL - original licence link has changed is not relivant.
35304 * <script type="text/javascript">
35308 * @class Roo.bootstrap.layout.Basic
35309 * @extends Roo.util.Observable
35310 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35311 * and does not have a titlebar, tabs or any other features. All it does is size and position
35312 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35313 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35314 * @cfg {string} region the region that it inhabits..
35315 * @cfg {bool} skipConfig skip config?
35319 Roo.bootstrap.layout.Basic = function(config){
35321 this.mgr = config.mgr;
35323 this.position = config.region;
35325 var skipConfig = config.skipConfig;
35329 * @scope Roo.BasicLayoutRegion
35333 * @event beforeremove
35334 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35335 * @param {Roo.LayoutRegion} this
35336 * @param {Roo.ContentPanel} panel The panel
35337 * @param {Object} e The cancel event object
35339 "beforeremove" : true,
35341 * @event invalidated
35342 * Fires when the layout for this region is changed.
35343 * @param {Roo.LayoutRegion} this
35345 "invalidated" : true,
35347 * @event visibilitychange
35348 * Fires when this region is shown or hidden
35349 * @param {Roo.LayoutRegion} this
35350 * @param {Boolean} visibility true or false
35352 "visibilitychange" : true,
35354 * @event paneladded
35355 * Fires when a panel is added.
35356 * @param {Roo.LayoutRegion} this
35357 * @param {Roo.ContentPanel} panel The panel
35359 "paneladded" : true,
35361 * @event panelremoved
35362 * Fires when a panel is removed.
35363 * @param {Roo.LayoutRegion} this
35364 * @param {Roo.ContentPanel} panel The panel
35366 "panelremoved" : true,
35368 * @event beforecollapse
35369 * Fires when this region before collapse.
35370 * @param {Roo.LayoutRegion} this
35372 "beforecollapse" : true,
35375 * Fires when this region is collapsed.
35376 * @param {Roo.LayoutRegion} this
35378 "collapsed" : true,
35381 * Fires when this region is expanded.
35382 * @param {Roo.LayoutRegion} this
35387 * Fires when this region is slid into view.
35388 * @param {Roo.LayoutRegion} this
35390 "slideshow" : true,
35393 * Fires when this region slides out of view.
35394 * @param {Roo.LayoutRegion} this
35396 "slidehide" : true,
35398 * @event panelactivated
35399 * Fires when a panel is activated.
35400 * @param {Roo.LayoutRegion} this
35401 * @param {Roo.ContentPanel} panel The activated panel
35403 "panelactivated" : true,
35406 * Fires when the user resizes this region.
35407 * @param {Roo.LayoutRegion} this
35408 * @param {Number} newSize The new size (width for east/west, height for north/south)
35412 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35413 this.panels = new Roo.util.MixedCollection();
35414 this.panels.getKey = this.getPanelId.createDelegate(this);
35416 this.activePanel = null;
35417 // ensure listeners are added...
35419 if (config.listeners || config.events) {
35420 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35421 listeners : config.listeners || {},
35422 events : config.events || {}
35426 if(skipConfig !== true){
35427 this.applyConfig(config);
35431 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35433 getPanelId : function(p){
35437 applyConfig : function(config){
35438 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35439 this.config = config;
35444 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35445 * the width, for horizontal (north, south) the height.
35446 * @param {Number} newSize The new width or height
35448 resizeTo : function(newSize){
35449 var el = this.el ? this.el :
35450 (this.activePanel ? this.activePanel.getEl() : null);
35452 switch(this.position){
35455 el.setWidth(newSize);
35456 this.fireEvent("resized", this, newSize);
35460 el.setHeight(newSize);
35461 this.fireEvent("resized", this, newSize);
35467 getBox : function(){
35468 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35471 getMargins : function(){
35472 return this.margins;
35475 updateBox : function(box){
35477 var el = this.activePanel.getEl();
35478 el.dom.style.left = box.x + "px";
35479 el.dom.style.top = box.y + "px";
35480 this.activePanel.setSize(box.width, box.height);
35484 * Returns the container element for this region.
35485 * @return {Roo.Element}
35487 getEl : function(){
35488 return this.activePanel;
35492 * Returns true if this region is currently visible.
35493 * @return {Boolean}
35495 isVisible : function(){
35496 return this.activePanel ? true : false;
35499 setActivePanel : function(panel){
35500 panel = this.getPanel(panel);
35501 if(this.activePanel && this.activePanel != panel){
35502 this.activePanel.setActiveState(false);
35503 this.activePanel.getEl().setLeftTop(-10000,-10000);
35505 this.activePanel = panel;
35506 panel.setActiveState(true);
35508 panel.setSize(this.box.width, this.box.height);
35510 this.fireEvent("panelactivated", this, panel);
35511 this.fireEvent("invalidated");
35515 * Show the specified panel.
35516 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35517 * @return {Roo.ContentPanel} The shown panel or null
35519 showPanel : function(panel){
35520 panel = this.getPanel(panel);
35522 this.setActivePanel(panel);
35528 * Get the active panel for this region.
35529 * @return {Roo.ContentPanel} The active panel or null
35531 getActivePanel : function(){
35532 return this.activePanel;
35536 * Add the passed ContentPanel(s)
35537 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35538 * @return {Roo.ContentPanel} The panel added (if only one was added)
35540 add : function(panel){
35541 if(arguments.length > 1){
35542 for(var i = 0, len = arguments.length; i < len; i++) {
35543 this.add(arguments[i]);
35547 if(this.hasPanel(panel)){
35548 this.showPanel(panel);
35551 var el = panel.getEl();
35552 if(el.dom.parentNode != this.mgr.el.dom){
35553 this.mgr.el.dom.appendChild(el.dom);
35555 if(panel.setRegion){
35556 panel.setRegion(this);
35558 this.panels.add(panel);
35559 el.setStyle("position", "absolute");
35560 if(!panel.background){
35561 this.setActivePanel(panel);
35562 if(this.config.initialSize && this.panels.getCount()==1){
35563 this.resizeTo(this.config.initialSize);
35566 this.fireEvent("paneladded", this, panel);
35571 * Returns true if the panel is in this region.
35572 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35573 * @return {Boolean}
35575 hasPanel : function(panel){
35576 if(typeof panel == "object"){ // must be panel obj
35577 panel = panel.getId();
35579 return this.getPanel(panel) ? true : false;
35583 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35584 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35585 * @param {Boolean} preservePanel Overrides the config preservePanel option
35586 * @return {Roo.ContentPanel} The panel that was removed
35588 remove : function(panel, preservePanel){
35589 panel = this.getPanel(panel);
35594 this.fireEvent("beforeremove", this, panel, e);
35595 if(e.cancel === true){
35598 var panelId = panel.getId();
35599 this.panels.removeKey(panelId);
35604 * Returns the panel specified or null if it's not in this region.
35605 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35606 * @return {Roo.ContentPanel}
35608 getPanel : function(id){
35609 if(typeof id == "object"){ // must be panel obj
35612 return this.panels.get(id);
35616 * Returns this regions position (north/south/east/west/center).
35619 getPosition: function(){
35620 return this.position;
35624 * Ext JS Library 1.1.1
35625 * Copyright(c) 2006-2007, Ext JS, LLC.
35627 * Originally Released Under LGPL - original licence link has changed is not relivant.
35630 * <script type="text/javascript">
35634 * @class Roo.bootstrap.layout.Region
35635 * @extends Roo.bootstrap.layout.Basic
35636 * This class represents a region in a layout manager.
35638 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35639 * @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})
35640 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35641 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35642 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35643 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35644 * @cfg {String} title The title for the region (overrides panel titles)
35645 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35646 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35647 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35648 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35649 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35650 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35651 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35652 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35653 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35654 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35656 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35657 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35658 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35659 * @cfg {Number} width For East/West panels
35660 * @cfg {Number} height For North/South panels
35661 * @cfg {Boolean} split To show the splitter
35662 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35664 * @cfg {string} cls Extra CSS classes to add to region
35666 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35667 * @cfg {string} region the region that it inhabits..
35670 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35671 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35673 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35674 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35675 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35677 Roo.bootstrap.layout.Region = function(config)
35679 this.applyConfig(config);
35681 var mgr = config.mgr;
35682 var pos = config.region;
35683 config.skipConfig = true;
35684 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35687 this.onRender(mgr.el);
35690 this.visible = true;
35691 this.collapsed = false;
35692 this.unrendered_panels = [];
35695 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35697 position: '', // set by wrapper (eg. north/south etc..)
35698 unrendered_panels : null, // unrendered panels.
35699 createBody : function(){
35700 /** This region's body element
35701 * @type Roo.Element */
35702 this.bodyEl = this.el.createChild({
35704 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35708 onRender: function(ctr, pos)
35710 var dh = Roo.DomHelper;
35711 /** This region's container element
35712 * @type Roo.Element */
35713 this.el = dh.append(ctr.dom, {
35715 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35717 /** This region's title element
35718 * @type Roo.Element */
35720 this.titleEl = dh.append(this.el.dom,
35723 unselectable: "on",
35724 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35726 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35727 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35730 this.titleEl.enableDisplayMode();
35731 /** This region's title text element
35732 * @type HTMLElement */
35733 this.titleTextEl = this.titleEl.dom.firstChild;
35734 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35736 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35737 this.closeBtn.enableDisplayMode();
35738 this.closeBtn.on("click", this.closeClicked, this);
35739 this.closeBtn.hide();
35741 this.createBody(this.config);
35742 if(this.config.hideWhenEmpty){
35744 this.on("paneladded", this.validateVisibility, this);
35745 this.on("panelremoved", this.validateVisibility, this);
35747 if(this.autoScroll){
35748 this.bodyEl.setStyle("overflow", "auto");
35750 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35752 //if(c.titlebar !== false){
35753 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35754 this.titleEl.hide();
35756 this.titleEl.show();
35757 if(this.config.title){
35758 this.titleTextEl.innerHTML = this.config.title;
35762 if(this.config.collapsed){
35763 this.collapse(true);
35765 if(this.config.hidden){
35769 if (this.unrendered_panels && this.unrendered_panels.length) {
35770 for (var i =0;i< this.unrendered_panels.length; i++) {
35771 this.add(this.unrendered_panels[i]);
35773 this.unrendered_panels = null;
35779 applyConfig : function(c)
35782 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35783 var dh = Roo.DomHelper;
35784 if(c.titlebar !== false){
35785 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35786 this.collapseBtn.on("click", this.collapse, this);
35787 this.collapseBtn.enableDisplayMode();
35789 if(c.showPin === true || this.showPin){
35790 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35791 this.stickBtn.enableDisplayMode();
35792 this.stickBtn.on("click", this.expand, this);
35793 this.stickBtn.hide();
35798 /** This region's collapsed element
35799 * @type Roo.Element */
35802 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35803 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35806 if(c.floatable !== false){
35807 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35808 this.collapsedEl.on("click", this.collapseClick, this);
35811 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35812 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35813 id: "message", unselectable: "on", style:{"float":"left"}});
35814 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35816 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35817 this.expandBtn.on("click", this.expand, this);
35821 if(this.collapseBtn){
35822 this.collapseBtn.setVisible(c.collapsible == true);
35825 this.cmargins = c.cmargins || this.cmargins ||
35826 (this.position == "west" || this.position == "east" ?
35827 {top: 0, left: 2, right:2, bottom: 0} :
35828 {top: 2, left: 0, right:0, bottom: 2});
35830 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35833 this.bottomTabs = c.tabPosition != "top";
35835 this.autoScroll = c.autoScroll || false;
35840 this.duration = c.duration || .30;
35841 this.slideDuration = c.slideDuration || .45;
35846 * Returns true if this region is currently visible.
35847 * @return {Boolean}
35849 isVisible : function(){
35850 return this.visible;
35854 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35855 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35857 //setCollapsedTitle : function(title){
35858 // title = title || " ";
35859 // if(this.collapsedTitleTextEl){
35860 // this.collapsedTitleTextEl.innerHTML = title;
35864 getBox : function(){
35866 // if(!this.collapsed){
35867 b = this.el.getBox(false, true);
35869 // b = this.collapsedEl.getBox(false, true);
35874 getMargins : function(){
35875 return this.margins;
35876 //return this.collapsed ? this.cmargins : this.margins;
35879 highlight : function(){
35880 this.el.addClass("x-layout-panel-dragover");
35883 unhighlight : function(){
35884 this.el.removeClass("x-layout-panel-dragover");
35887 updateBox : function(box)
35889 if (!this.bodyEl) {
35890 return; // not rendered yet..
35894 if(!this.collapsed){
35895 this.el.dom.style.left = box.x + "px";
35896 this.el.dom.style.top = box.y + "px";
35897 this.updateBody(box.width, box.height);
35899 this.collapsedEl.dom.style.left = box.x + "px";
35900 this.collapsedEl.dom.style.top = box.y + "px";
35901 this.collapsedEl.setSize(box.width, box.height);
35904 this.tabs.autoSizeTabs();
35908 updateBody : function(w, h)
35911 this.el.setWidth(w);
35912 w -= this.el.getBorderWidth("rl");
35913 if(this.config.adjustments){
35914 w += this.config.adjustments[0];
35917 if(h !== null && h > 0){
35918 this.el.setHeight(h);
35919 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35920 h -= this.el.getBorderWidth("tb");
35921 if(this.config.adjustments){
35922 h += this.config.adjustments[1];
35924 this.bodyEl.setHeight(h);
35926 h = this.tabs.syncHeight(h);
35929 if(this.panelSize){
35930 w = w !== null ? w : this.panelSize.width;
35931 h = h !== null ? h : this.panelSize.height;
35933 if(this.activePanel){
35934 var el = this.activePanel.getEl();
35935 w = w !== null ? w : el.getWidth();
35936 h = h !== null ? h : el.getHeight();
35937 this.panelSize = {width: w, height: h};
35938 this.activePanel.setSize(w, h);
35940 if(Roo.isIE && this.tabs){
35941 this.tabs.el.repaint();
35946 * Returns the container element for this region.
35947 * @return {Roo.Element}
35949 getEl : function(){
35954 * Hides this region.
35957 //if(!this.collapsed){
35958 this.el.dom.style.left = "-2000px";
35961 // this.collapsedEl.dom.style.left = "-2000px";
35962 // this.collapsedEl.hide();
35964 this.visible = false;
35965 this.fireEvent("visibilitychange", this, false);
35969 * Shows this region if it was previously hidden.
35972 //if(!this.collapsed){
35975 // this.collapsedEl.show();
35977 this.visible = true;
35978 this.fireEvent("visibilitychange", this, true);
35981 closeClicked : function(){
35982 if(this.activePanel){
35983 this.remove(this.activePanel);
35987 collapseClick : function(e){
35989 e.stopPropagation();
35992 e.stopPropagation();
35998 * Collapses this region.
35999 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36002 collapse : function(skipAnim, skipCheck = false){
36003 if(this.collapsed) {
36007 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36009 this.collapsed = true;
36011 this.split.el.hide();
36013 if(this.config.animate && skipAnim !== true){
36014 this.fireEvent("invalidated", this);
36015 this.animateCollapse();
36017 this.el.setLocation(-20000,-20000);
36019 this.collapsedEl.show();
36020 this.fireEvent("collapsed", this);
36021 this.fireEvent("invalidated", this);
36027 animateCollapse : function(){
36032 * Expands this region if it was previously collapsed.
36033 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36034 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36037 expand : function(e, skipAnim){
36039 e.stopPropagation();
36041 if(!this.collapsed || this.el.hasActiveFx()) {
36045 this.afterSlideIn();
36048 this.collapsed = false;
36049 if(this.config.animate && skipAnim !== true){
36050 this.animateExpand();
36054 this.split.el.show();
36056 this.collapsedEl.setLocation(-2000,-2000);
36057 this.collapsedEl.hide();
36058 this.fireEvent("invalidated", this);
36059 this.fireEvent("expanded", this);
36063 animateExpand : function(){
36067 initTabs : function()
36069 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36071 var ts = new Roo.bootstrap.panel.Tabs({
36072 el: this.bodyEl.dom,
36073 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36074 disableTooltips: this.config.disableTabTips,
36075 toolbar : this.config.toolbar
36078 if(this.config.hideTabs){
36079 ts.stripWrap.setDisplayed(false);
36082 ts.resizeTabs = this.config.resizeTabs === true;
36083 ts.minTabWidth = this.config.minTabWidth || 40;
36084 ts.maxTabWidth = this.config.maxTabWidth || 250;
36085 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36086 ts.monitorResize = false;
36087 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36088 ts.bodyEl.addClass('roo-layout-tabs-body');
36089 this.panels.each(this.initPanelAsTab, this);
36092 initPanelAsTab : function(panel){
36093 var ti = this.tabs.addTab(
36097 this.config.closeOnTab && panel.isClosable(),
36100 if(panel.tabTip !== undefined){
36101 ti.setTooltip(panel.tabTip);
36103 ti.on("activate", function(){
36104 this.setActivePanel(panel);
36107 if(this.config.closeOnTab){
36108 ti.on("beforeclose", function(t, e){
36110 this.remove(panel);
36114 panel.tabItem = ti;
36119 updatePanelTitle : function(panel, title)
36121 if(this.activePanel == panel){
36122 this.updateTitle(title);
36125 var ti = this.tabs.getTab(panel.getEl().id);
36127 if(panel.tabTip !== undefined){
36128 ti.setTooltip(panel.tabTip);
36133 updateTitle : function(title){
36134 if(this.titleTextEl && !this.config.title){
36135 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36139 setActivePanel : function(panel)
36141 panel = this.getPanel(panel);
36142 if(this.activePanel && this.activePanel != panel){
36143 if(this.activePanel.setActiveState(false) === false){
36147 this.activePanel = panel;
36148 panel.setActiveState(true);
36149 if(this.panelSize){
36150 panel.setSize(this.panelSize.width, this.panelSize.height);
36153 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36155 this.updateTitle(panel.getTitle());
36157 this.fireEvent("invalidated", this);
36159 this.fireEvent("panelactivated", this, panel);
36163 * Shows the specified panel.
36164 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36165 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36167 showPanel : function(panel)
36169 panel = this.getPanel(panel);
36172 var tab = this.tabs.getTab(panel.getEl().id);
36173 if(tab.isHidden()){
36174 this.tabs.unhideTab(tab.id);
36178 this.setActivePanel(panel);
36185 * Get the active panel for this region.
36186 * @return {Roo.ContentPanel} The active panel or null
36188 getActivePanel : function(){
36189 return this.activePanel;
36192 validateVisibility : function(){
36193 if(this.panels.getCount() < 1){
36194 this.updateTitle(" ");
36195 this.closeBtn.hide();
36198 if(!this.isVisible()){
36205 * Adds the passed ContentPanel(s) to this region.
36206 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36207 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36209 add : function(panel)
36211 if(arguments.length > 1){
36212 for(var i = 0, len = arguments.length; i < len; i++) {
36213 this.add(arguments[i]);
36218 // if we have not been rendered yet, then we can not really do much of this..
36219 if (!this.bodyEl) {
36220 this.unrendered_panels.push(panel);
36227 if(this.hasPanel(panel)){
36228 this.showPanel(panel);
36231 panel.setRegion(this);
36232 this.panels.add(panel);
36233 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36234 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36235 // and hide them... ???
36236 this.bodyEl.dom.appendChild(panel.getEl().dom);
36237 if(panel.background !== true){
36238 this.setActivePanel(panel);
36240 this.fireEvent("paneladded", this, panel);
36247 this.initPanelAsTab(panel);
36251 if(panel.background !== true){
36252 this.tabs.activate(panel.getEl().id);
36254 this.fireEvent("paneladded", this, panel);
36259 * Hides the tab for the specified panel.
36260 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36262 hidePanel : function(panel){
36263 if(this.tabs && (panel = this.getPanel(panel))){
36264 this.tabs.hideTab(panel.getEl().id);
36269 * Unhides the tab for a previously hidden panel.
36270 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36272 unhidePanel : function(panel){
36273 if(this.tabs && (panel = this.getPanel(panel))){
36274 this.tabs.unhideTab(panel.getEl().id);
36278 clearPanels : function(){
36279 while(this.panels.getCount() > 0){
36280 this.remove(this.panels.first());
36285 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36286 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36287 * @param {Boolean} preservePanel Overrides the config preservePanel option
36288 * @return {Roo.ContentPanel} The panel that was removed
36290 remove : function(panel, preservePanel)
36292 panel = this.getPanel(panel);
36297 this.fireEvent("beforeremove", this, panel, e);
36298 if(e.cancel === true){
36301 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36302 var panelId = panel.getId();
36303 this.panels.removeKey(panelId);
36305 document.body.appendChild(panel.getEl().dom);
36308 this.tabs.removeTab(panel.getEl().id);
36309 }else if (!preservePanel){
36310 this.bodyEl.dom.removeChild(panel.getEl().dom);
36312 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36313 var p = this.panels.first();
36314 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36315 tempEl.appendChild(p.getEl().dom);
36316 this.bodyEl.update("");
36317 this.bodyEl.dom.appendChild(p.getEl().dom);
36319 this.updateTitle(p.getTitle());
36321 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36322 this.setActivePanel(p);
36324 panel.setRegion(null);
36325 if(this.activePanel == panel){
36326 this.activePanel = null;
36328 if(this.config.autoDestroy !== false && preservePanel !== true){
36329 try{panel.destroy();}catch(e){}
36331 this.fireEvent("panelremoved", this, panel);
36336 * Returns the TabPanel component used by this region
36337 * @return {Roo.TabPanel}
36339 getTabs : function(){
36343 createTool : function(parentEl, className){
36344 var btn = Roo.DomHelper.append(parentEl, {
36346 cls: "x-layout-tools-button",
36349 cls: "roo-layout-tools-button-inner " + className,
36353 btn.addClassOnOver("roo-layout-tools-button-over");
36358 * Ext JS Library 1.1.1
36359 * Copyright(c) 2006-2007, Ext JS, LLC.
36361 * Originally Released Under LGPL - original licence link has changed is not relivant.
36364 * <script type="text/javascript">
36370 * @class Roo.SplitLayoutRegion
36371 * @extends Roo.LayoutRegion
36372 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36374 Roo.bootstrap.layout.Split = function(config){
36375 this.cursor = config.cursor;
36376 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36379 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36381 splitTip : "Drag to resize.",
36382 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36383 useSplitTips : false,
36385 applyConfig : function(config){
36386 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36389 onRender : function(ctr,pos) {
36391 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36392 if(!this.config.split){
36397 var splitEl = Roo.DomHelper.append(ctr.dom, {
36399 id: this.el.id + "-split",
36400 cls: "roo-layout-split roo-layout-split-"+this.position,
36403 /** The SplitBar for this region
36404 * @type Roo.SplitBar */
36405 // does not exist yet...
36406 Roo.log([this.position, this.orientation]);
36408 this.split = new Roo.bootstrap.SplitBar({
36409 dragElement : splitEl,
36410 resizingElement: this.el,
36411 orientation : this.orientation
36414 this.split.on("moved", this.onSplitMove, this);
36415 this.split.useShim = this.config.useShim === true;
36416 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36417 if(this.useSplitTips){
36418 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36420 //if(config.collapsible){
36421 // this.split.el.on("dblclick", this.collapse, this);
36424 if(typeof this.config.minSize != "undefined"){
36425 this.split.minSize = this.config.minSize;
36427 if(typeof this.config.maxSize != "undefined"){
36428 this.split.maxSize = this.config.maxSize;
36430 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36431 this.hideSplitter();
36436 getHMaxSize : function(){
36437 var cmax = this.config.maxSize || 10000;
36438 var center = this.mgr.getRegion("center");
36439 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36442 getVMaxSize : function(){
36443 var cmax = this.config.maxSize || 10000;
36444 var center = this.mgr.getRegion("center");
36445 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36448 onSplitMove : function(split, newSize){
36449 this.fireEvent("resized", this, newSize);
36453 * Returns the {@link Roo.SplitBar} for this region.
36454 * @return {Roo.SplitBar}
36456 getSplitBar : function(){
36461 this.hideSplitter();
36462 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36465 hideSplitter : function(){
36467 this.split.el.setLocation(-2000,-2000);
36468 this.split.el.hide();
36474 this.split.el.show();
36476 Roo.bootstrap.layout.Split.superclass.show.call(this);
36479 beforeSlide: function(){
36480 if(Roo.isGecko){// firefox overflow auto bug workaround
36481 this.bodyEl.clip();
36483 this.tabs.bodyEl.clip();
36485 if(this.activePanel){
36486 this.activePanel.getEl().clip();
36488 if(this.activePanel.beforeSlide){
36489 this.activePanel.beforeSlide();
36495 afterSlide : function(){
36496 if(Roo.isGecko){// firefox overflow auto bug workaround
36497 this.bodyEl.unclip();
36499 this.tabs.bodyEl.unclip();
36501 if(this.activePanel){
36502 this.activePanel.getEl().unclip();
36503 if(this.activePanel.afterSlide){
36504 this.activePanel.afterSlide();
36510 initAutoHide : function(){
36511 if(this.autoHide !== false){
36512 if(!this.autoHideHd){
36513 var st = new Roo.util.DelayedTask(this.slideIn, this);
36514 this.autoHideHd = {
36515 "mouseout": function(e){
36516 if(!e.within(this.el, true)){
36520 "mouseover" : function(e){
36526 this.el.on(this.autoHideHd);
36530 clearAutoHide : function(){
36531 if(this.autoHide !== false){
36532 this.el.un("mouseout", this.autoHideHd.mouseout);
36533 this.el.un("mouseover", this.autoHideHd.mouseover);
36537 clearMonitor : function(){
36538 Roo.get(document).un("click", this.slideInIf, this);
36541 // these names are backwards but not changed for compat
36542 slideOut : function(){
36543 if(this.isSlid || this.el.hasActiveFx()){
36546 this.isSlid = true;
36547 if(this.collapseBtn){
36548 this.collapseBtn.hide();
36550 this.closeBtnState = this.closeBtn.getStyle('display');
36551 this.closeBtn.hide();
36553 this.stickBtn.show();
36556 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36557 this.beforeSlide();
36558 this.el.setStyle("z-index", 10001);
36559 this.el.slideIn(this.getSlideAnchor(), {
36560 callback: function(){
36562 this.initAutoHide();
36563 Roo.get(document).on("click", this.slideInIf, this);
36564 this.fireEvent("slideshow", this);
36571 afterSlideIn : function(){
36572 this.clearAutoHide();
36573 this.isSlid = false;
36574 this.clearMonitor();
36575 this.el.setStyle("z-index", "");
36576 if(this.collapseBtn){
36577 this.collapseBtn.show();
36579 this.closeBtn.setStyle('display', this.closeBtnState);
36581 this.stickBtn.hide();
36583 this.fireEvent("slidehide", this);
36586 slideIn : function(cb){
36587 if(!this.isSlid || this.el.hasActiveFx()){
36591 this.isSlid = false;
36592 this.beforeSlide();
36593 this.el.slideOut(this.getSlideAnchor(), {
36594 callback: function(){
36595 this.el.setLeftTop(-10000, -10000);
36597 this.afterSlideIn();
36605 slideInIf : function(e){
36606 if(!e.within(this.el)){
36611 animateCollapse : function(){
36612 this.beforeSlide();
36613 this.el.setStyle("z-index", 20000);
36614 var anchor = this.getSlideAnchor();
36615 this.el.slideOut(anchor, {
36616 callback : function(){
36617 this.el.setStyle("z-index", "");
36618 this.collapsedEl.slideIn(anchor, {duration:.3});
36620 this.el.setLocation(-10000,-10000);
36622 this.fireEvent("collapsed", this);
36629 animateExpand : function(){
36630 this.beforeSlide();
36631 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36632 this.el.setStyle("z-index", 20000);
36633 this.collapsedEl.hide({
36636 this.el.slideIn(this.getSlideAnchor(), {
36637 callback : function(){
36638 this.el.setStyle("z-index", "");
36641 this.split.el.show();
36643 this.fireEvent("invalidated", this);
36644 this.fireEvent("expanded", this);
36672 getAnchor : function(){
36673 return this.anchors[this.position];
36676 getCollapseAnchor : function(){
36677 return this.canchors[this.position];
36680 getSlideAnchor : function(){
36681 return this.sanchors[this.position];
36684 getAlignAdj : function(){
36685 var cm = this.cmargins;
36686 switch(this.position){
36702 getExpandAdj : function(){
36703 var c = this.collapsedEl, cm = this.cmargins;
36704 switch(this.position){
36706 return [-(cm.right+c.getWidth()+cm.left), 0];
36709 return [cm.right+c.getWidth()+cm.left, 0];
36712 return [0, -(cm.top+cm.bottom+c.getHeight())];
36715 return [0, cm.top+cm.bottom+c.getHeight()];
36721 * Ext JS Library 1.1.1
36722 * Copyright(c) 2006-2007, Ext JS, LLC.
36724 * Originally Released Under LGPL - original licence link has changed is not relivant.
36727 * <script type="text/javascript">
36730 * These classes are private internal classes
36732 Roo.bootstrap.layout.Center = function(config){
36733 config.region = "center";
36734 Roo.bootstrap.layout.Region.call(this, config);
36735 this.visible = true;
36736 this.minWidth = config.minWidth || 20;
36737 this.minHeight = config.minHeight || 20;
36740 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36742 // center panel can't be hidden
36746 // center panel can't be hidden
36749 getMinWidth: function(){
36750 return this.minWidth;
36753 getMinHeight: function(){
36754 return this.minHeight;
36767 Roo.bootstrap.layout.North = function(config)
36769 config.region = 'north';
36770 config.cursor = 'n-resize';
36772 Roo.bootstrap.layout.Split.call(this, config);
36776 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36777 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36778 this.split.el.addClass("roo-layout-split-v");
36780 var size = config.initialSize || config.height;
36781 if(typeof size != "undefined"){
36782 this.el.setHeight(size);
36785 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36787 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36791 getBox : function(){
36792 if(this.collapsed){
36793 return this.collapsedEl.getBox();
36795 var box = this.el.getBox();
36797 box.height += this.split.el.getHeight();
36802 updateBox : function(box){
36803 if(this.split && !this.collapsed){
36804 box.height -= this.split.el.getHeight();
36805 this.split.el.setLeft(box.x);
36806 this.split.el.setTop(box.y+box.height);
36807 this.split.el.setWidth(box.width);
36809 if(this.collapsed){
36810 this.updateBody(box.width, null);
36812 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36820 Roo.bootstrap.layout.South = function(config){
36821 config.region = 'south';
36822 config.cursor = 's-resize';
36823 Roo.bootstrap.layout.Split.call(this, config);
36825 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36826 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36827 this.split.el.addClass("roo-layout-split-v");
36829 var size = config.initialSize || config.height;
36830 if(typeof size != "undefined"){
36831 this.el.setHeight(size);
36835 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36836 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36837 getBox : function(){
36838 if(this.collapsed){
36839 return this.collapsedEl.getBox();
36841 var box = this.el.getBox();
36843 var sh = this.split.el.getHeight();
36850 updateBox : function(box){
36851 if(this.split && !this.collapsed){
36852 var sh = this.split.el.getHeight();
36855 this.split.el.setLeft(box.x);
36856 this.split.el.setTop(box.y-sh);
36857 this.split.el.setWidth(box.width);
36859 if(this.collapsed){
36860 this.updateBody(box.width, null);
36862 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36866 Roo.bootstrap.layout.East = function(config){
36867 config.region = "east";
36868 config.cursor = "e-resize";
36869 Roo.bootstrap.layout.Split.call(this, config);
36871 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36872 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36873 this.split.el.addClass("roo-layout-split-h");
36875 var size = config.initialSize || config.width;
36876 if(typeof size != "undefined"){
36877 this.el.setWidth(size);
36880 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36881 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36882 getBox : function(){
36883 if(this.collapsed){
36884 return this.collapsedEl.getBox();
36886 var box = this.el.getBox();
36888 var sw = this.split.el.getWidth();
36895 updateBox : function(box){
36896 if(this.split && !this.collapsed){
36897 var sw = this.split.el.getWidth();
36899 this.split.el.setLeft(box.x);
36900 this.split.el.setTop(box.y);
36901 this.split.el.setHeight(box.height);
36904 if(this.collapsed){
36905 this.updateBody(null, box.height);
36907 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36911 Roo.bootstrap.layout.West = function(config){
36912 config.region = "west";
36913 config.cursor = "w-resize";
36915 Roo.bootstrap.layout.Split.call(this, config);
36917 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36918 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36919 this.split.el.addClass("roo-layout-split-h");
36923 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36924 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36926 onRender: function(ctr, pos)
36928 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36929 var size = this.config.initialSize || this.config.width;
36930 if(typeof size != "undefined"){
36931 this.el.setWidth(size);
36935 getBox : function(){
36936 if(this.collapsed){
36937 return this.collapsedEl.getBox();
36939 var box = this.el.getBox();
36941 box.width += this.split.el.getWidth();
36946 updateBox : function(box){
36947 if(this.split && !this.collapsed){
36948 var sw = this.split.el.getWidth();
36950 this.split.el.setLeft(box.x+box.width);
36951 this.split.el.setTop(box.y);
36952 this.split.el.setHeight(box.height);
36954 if(this.collapsed){
36955 this.updateBody(null, box.height);
36957 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36960 Roo.namespace("Roo.bootstrap.panel");/*
36962 * Ext JS Library 1.1.1
36963 * Copyright(c) 2006-2007, Ext JS, LLC.
36965 * Originally Released Under LGPL - original licence link has changed is not relivant.
36968 * <script type="text/javascript">
36971 * @class Roo.ContentPanel
36972 * @extends Roo.util.Observable
36973 * A basic ContentPanel element.
36974 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36975 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36976 * @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
36977 * @cfg {Boolean} closable True if the panel can be closed/removed
36978 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36979 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36980 * @cfg {Toolbar} toolbar A toolbar for this panel
36981 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36982 * @cfg {String} title The title for this panel
36983 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36984 * @cfg {String} url Calls {@link #setUrl} with this value
36985 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36986 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36987 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36988 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36989 * @cfg {Boolean} badges render the badges
36992 * Create a new ContentPanel.
36993 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36994 * @param {String/Object} config A string to set only the title or a config object
36995 * @param {String} content (optional) Set the HTML content for this panel
36996 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36998 Roo.bootstrap.panel.Content = function( config){
37000 this.tpl = config.tpl || false;
37002 var el = config.el;
37003 var content = config.content;
37005 if(config.autoCreate){ // xtype is available if this is called from factory
37008 this.el = Roo.get(el);
37009 if(!this.el && config && config.autoCreate){
37010 if(typeof config.autoCreate == "object"){
37011 if(!config.autoCreate.id){
37012 config.autoCreate.id = config.id||el;
37014 this.el = Roo.DomHelper.append(document.body,
37015 config.autoCreate, true);
37017 var elcfg = { tag: "div",
37018 cls: "roo-layout-inactive-content",
37022 elcfg.html = config.html;
37026 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37029 this.closable = false;
37030 this.loaded = false;
37031 this.active = false;
37034 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37036 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37038 this.wrapEl = this.el; //this.el.wrap();
37040 if (config.toolbar.items) {
37041 ti = config.toolbar.items ;
37042 delete config.toolbar.items ;
37046 this.toolbar.render(this.wrapEl, 'before');
37047 for(var i =0;i < ti.length;i++) {
37048 // Roo.log(['add child', items[i]]);
37049 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37051 this.toolbar.items = nitems;
37052 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37053 delete config.toolbar;
37057 // xtype created footer. - not sure if will work as we normally have to render first..
37058 if (this.footer && !this.footer.el && this.footer.xtype) {
37059 if (!this.wrapEl) {
37060 this.wrapEl = this.el.wrap();
37063 this.footer.container = this.wrapEl.createChild();
37065 this.footer = Roo.factory(this.footer, Roo);
37070 if(typeof config == "string"){
37071 this.title = config;
37073 Roo.apply(this, config);
37077 this.resizeEl = Roo.get(this.resizeEl, true);
37079 this.resizeEl = this.el;
37081 // handle view.xtype
37089 * Fires when this panel is activated.
37090 * @param {Roo.ContentPanel} this
37094 * @event deactivate
37095 * Fires when this panel is activated.
37096 * @param {Roo.ContentPanel} this
37098 "deactivate" : true,
37102 * Fires when this panel is resized if fitToFrame is true.
37103 * @param {Roo.ContentPanel} this
37104 * @param {Number} width The width after any component adjustments
37105 * @param {Number} height The height after any component adjustments
37111 * Fires when this tab is created
37112 * @param {Roo.ContentPanel} this
37123 if(this.autoScroll){
37124 this.resizeEl.setStyle("overflow", "auto");
37126 // fix randome scrolling
37127 //this.el.on('scroll', function() {
37128 // Roo.log('fix random scolling');
37129 // this.scrollTo('top',0);
37132 content = content || this.content;
37134 this.setContent(content);
37136 if(config && config.url){
37137 this.setUrl(this.url, this.params, this.loadOnce);
37142 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37144 if (this.view && typeof(this.view.xtype) != 'undefined') {
37145 this.view.el = this.el.appendChild(document.createElement("div"));
37146 this.view = Roo.factory(this.view);
37147 this.view.render && this.view.render(false, '');
37151 this.fireEvent('render', this);
37154 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37158 setRegion : function(region){
37159 this.region = region;
37160 this.setActiveClass(region && !this.background);
37164 setActiveClass: function(state)
37167 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37168 this.el.setStyle('position','relative');
37170 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37171 this.el.setStyle('position', 'absolute');
37176 * Returns the toolbar for this Panel if one was configured.
37177 * @return {Roo.Toolbar}
37179 getToolbar : function(){
37180 return this.toolbar;
37183 setActiveState : function(active)
37185 this.active = active;
37186 this.setActiveClass(active);
37188 if(this.fireEvent("deactivate", this) === false){
37193 this.fireEvent("activate", this);
37197 * Updates this panel's element
37198 * @param {String} content The new content
37199 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37201 setContent : function(content, loadScripts){
37202 this.el.update(content, loadScripts);
37205 ignoreResize : function(w, h){
37206 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37209 this.lastSize = {width: w, height: h};
37214 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37215 * @return {Roo.UpdateManager} The UpdateManager
37217 getUpdateManager : function(){
37218 return this.el.getUpdateManager();
37221 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37222 * @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:
37225 url: "your-url.php",
37226 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37227 callback: yourFunction,
37228 scope: yourObject, //(optional scope)
37231 text: "Loading...",
37236 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37237 * 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.
37238 * @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}
37239 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37240 * @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.
37241 * @return {Roo.ContentPanel} this
37244 var um = this.el.getUpdateManager();
37245 um.update.apply(um, arguments);
37251 * 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.
37252 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37253 * @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)
37254 * @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)
37255 * @return {Roo.UpdateManager} The UpdateManager
37257 setUrl : function(url, params, loadOnce){
37258 if(this.refreshDelegate){
37259 this.removeListener("activate", this.refreshDelegate);
37261 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37262 this.on("activate", this.refreshDelegate);
37263 return this.el.getUpdateManager();
37266 _handleRefresh : function(url, params, loadOnce){
37267 if(!loadOnce || !this.loaded){
37268 var updater = this.el.getUpdateManager();
37269 updater.update(url, params, this._setLoaded.createDelegate(this));
37273 _setLoaded : function(){
37274 this.loaded = true;
37278 * Returns this panel's id
37281 getId : function(){
37286 * Returns this panel's element - used by regiosn to add.
37287 * @return {Roo.Element}
37289 getEl : function(){
37290 return this.wrapEl || this.el;
37295 adjustForComponents : function(width, height)
37297 //Roo.log('adjustForComponents ');
37298 if(this.resizeEl != this.el){
37299 width -= this.el.getFrameWidth('lr');
37300 height -= this.el.getFrameWidth('tb');
37303 var te = this.toolbar.getEl();
37304 te.setWidth(width);
37305 height -= te.getHeight();
37308 var te = this.footer.getEl();
37309 te.setWidth(width);
37310 height -= te.getHeight();
37314 if(this.adjustments){
37315 width += this.adjustments[0];
37316 height += this.adjustments[1];
37318 return {"width": width, "height": height};
37321 setSize : function(width, height){
37322 if(this.fitToFrame && !this.ignoreResize(width, height)){
37323 if(this.fitContainer && this.resizeEl != this.el){
37324 this.el.setSize(width, height);
37326 var size = this.adjustForComponents(width, height);
37327 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37328 this.fireEvent('resize', this, size.width, size.height);
37333 * Returns this panel's title
37336 getTitle : function(){
37338 if (typeof(this.title) != 'object') {
37343 for (var k in this.title) {
37344 if (!this.title.hasOwnProperty(k)) {
37348 if (k.indexOf('-') >= 0) {
37349 var s = k.split('-');
37350 for (var i = 0; i<s.length; i++) {
37351 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37354 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37361 * Set this panel's title
37362 * @param {String} title
37364 setTitle : function(title){
37365 this.title = title;
37367 this.region.updatePanelTitle(this, title);
37372 * Returns true is this panel was configured to be closable
37373 * @return {Boolean}
37375 isClosable : function(){
37376 return this.closable;
37379 beforeSlide : function(){
37381 this.resizeEl.clip();
37384 afterSlide : function(){
37386 this.resizeEl.unclip();
37390 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37391 * Will fail silently if the {@link #setUrl} method has not been called.
37392 * This does not activate the panel, just updates its content.
37394 refresh : function(){
37395 if(this.refreshDelegate){
37396 this.loaded = false;
37397 this.refreshDelegate();
37402 * Destroys this panel
37404 destroy : function(){
37405 this.el.removeAllListeners();
37406 var tempEl = document.createElement("span");
37407 tempEl.appendChild(this.el.dom);
37408 tempEl.innerHTML = "";
37414 * form - if the content panel contains a form - this is a reference to it.
37415 * @type {Roo.form.Form}
37419 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37420 * This contains a reference to it.
37426 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37436 * @param {Object} cfg Xtype definition of item to add.
37440 getChildContainer: function () {
37441 return this.getEl();
37446 var ret = new Roo.factory(cfg);
37451 if (cfg.xtype.match(/^Form$/)) {
37454 //if (this.footer) {
37455 // el = this.footer.container.insertSibling(false, 'before');
37457 el = this.el.createChild();
37460 this.form = new Roo.form.Form(cfg);
37463 if ( this.form.allItems.length) {
37464 this.form.render(el.dom);
37468 // should only have one of theses..
37469 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37470 // views.. should not be just added - used named prop 'view''
37472 cfg.el = this.el.appendChild(document.createElement("div"));
37475 var ret = new Roo.factory(cfg);
37477 ret.render && ret.render(false, ''); // render blank..
37487 * @class Roo.bootstrap.panel.Grid
37488 * @extends Roo.bootstrap.panel.Content
37490 * Create a new GridPanel.
37491 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37492 * @param {Object} config A the config object
37498 Roo.bootstrap.panel.Grid = function(config)
37502 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37503 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37505 config.el = this.wrapper;
37506 //this.el = this.wrapper;
37508 if (config.container) {
37509 // ctor'ed from a Border/panel.grid
37512 this.wrapper.setStyle("overflow", "hidden");
37513 this.wrapper.addClass('roo-grid-container');
37518 if(config.toolbar){
37519 var tool_el = this.wrapper.createChild();
37520 this.toolbar = Roo.factory(config.toolbar);
37522 if (config.toolbar.items) {
37523 ti = config.toolbar.items ;
37524 delete config.toolbar.items ;
37528 this.toolbar.render(tool_el);
37529 for(var i =0;i < ti.length;i++) {
37530 // Roo.log(['add child', items[i]]);
37531 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37533 this.toolbar.items = nitems;
37535 delete config.toolbar;
37538 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37539 config.grid.scrollBody = true;;
37540 config.grid.monitorWindowResize = false; // turn off autosizing
37541 config.grid.autoHeight = false;
37542 config.grid.autoWidth = false;
37544 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37546 if (config.background) {
37547 // render grid on panel activation (if panel background)
37548 this.on('activate', function(gp) {
37549 if (!gp.grid.rendered) {
37550 gp.grid.render(this.wrapper);
37551 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37556 this.grid.render(this.wrapper);
37557 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37560 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37561 // ??? needed ??? config.el = this.wrapper;
37566 // xtype created footer. - not sure if will work as we normally have to render first..
37567 if (this.footer && !this.footer.el && this.footer.xtype) {
37569 var ctr = this.grid.getView().getFooterPanel(true);
37570 this.footer.dataSource = this.grid.dataSource;
37571 this.footer = Roo.factory(this.footer, Roo);
37572 this.footer.render(ctr);
37582 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37583 getId : function(){
37584 return this.grid.id;
37588 * Returns the grid for this panel
37589 * @return {Roo.bootstrap.Table}
37591 getGrid : function(){
37595 setSize : function(width, height){
37596 if(!this.ignoreResize(width, height)){
37597 var grid = this.grid;
37598 var size = this.adjustForComponents(width, height);
37599 var gridel = grid.getGridEl();
37600 gridel.setSize(size.width, size.height);
37602 var thd = grid.getGridEl().select('thead',true).first();
37603 var tbd = grid.getGridEl().select('tbody', true).first();
37605 tbd.setSize(width, height - thd.getHeight());
37614 beforeSlide : function(){
37615 this.grid.getView().scroller.clip();
37618 afterSlide : function(){
37619 this.grid.getView().scroller.unclip();
37622 destroy : function(){
37623 this.grid.destroy();
37625 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37630 * @class Roo.bootstrap.panel.Nest
37631 * @extends Roo.bootstrap.panel.Content
37633 * Create a new Panel, that can contain a layout.Border.
37636 * @param {Roo.BorderLayout} layout The layout for this panel
37637 * @param {String/Object} config A string to set only the title or a config object
37639 Roo.bootstrap.panel.Nest = function(config)
37641 // construct with only one argument..
37642 /* FIXME - implement nicer consturctors
37643 if (layout.layout) {
37645 layout = config.layout;
37646 delete config.layout;
37648 if (layout.xtype && !layout.getEl) {
37649 // then layout needs constructing..
37650 layout = Roo.factory(layout, Roo);
37654 config.el = config.layout.getEl();
37656 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37658 config.layout.monitorWindowResize = false; // turn off autosizing
37659 this.layout = config.layout;
37660 this.layout.getEl().addClass("roo-layout-nested-layout");
37667 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37669 setSize : function(width, height){
37670 if(!this.ignoreResize(width, height)){
37671 var size = this.adjustForComponents(width, height);
37672 var el = this.layout.getEl();
37673 if (size.height < 1) {
37674 el.setWidth(size.width);
37676 el.setSize(size.width, size.height);
37678 var touch = el.dom.offsetWidth;
37679 this.layout.layout();
37680 // ie requires a double layout on the first pass
37681 if(Roo.isIE && !this.initialized){
37682 this.initialized = true;
37683 this.layout.layout();
37688 // activate all subpanels if not currently active..
37690 setActiveState : function(active){
37691 this.active = active;
37692 this.setActiveClass(active);
37695 this.fireEvent("deactivate", this);
37699 this.fireEvent("activate", this);
37700 // not sure if this should happen before or after..
37701 if (!this.layout) {
37702 return; // should not happen..
37705 for (var r in this.layout.regions) {
37706 reg = this.layout.getRegion(r);
37707 if (reg.getActivePanel()) {
37708 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37709 reg.setActivePanel(reg.getActivePanel());
37712 if (!reg.panels.length) {
37715 reg.showPanel(reg.getPanel(0));
37724 * Returns the nested BorderLayout for this panel
37725 * @return {Roo.BorderLayout}
37727 getLayout : function(){
37728 return this.layout;
37732 * Adds a xtype elements to the layout of the nested panel
37736 xtype : 'ContentPanel',
37743 xtype : 'NestedLayoutPanel',
37749 items : [ ... list of content panels or nested layout panels.. ]
37753 * @param {Object} cfg Xtype definition of item to add.
37755 addxtype : function(cfg) {
37756 return this.layout.addxtype(cfg);
37761 * Ext JS Library 1.1.1
37762 * Copyright(c) 2006-2007, Ext JS, LLC.
37764 * Originally Released Under LGPL - original licence link has changed is not relivant.
37767 * <script type="text/javascript">
37770 * @class Roo.TabPanel
37771 * @extends Roo.util.Observable
37772 * A lightweight tab container.
37776 // basic tabs 1, built from existing content
37777 var tabs = new Roo.TabPanel("tabs1");
37778 tabs.addTab("script", "View Script");
37779 tabs.addTab("markup", "View Markup");
37780 tabs.activate("script");
37782 // more advanced tabs, built from javascript
37783 var jtabs = new Roo.TabPanel("jtabs");
37784 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37786 // set up the UpdateManager
37787 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37788 var updater = tab2.getUpdateManager();
37789 updater.setDefaultUrl("ajax1.htm");
37790 tab2.on('activate', updater.refresh, updater, true);
37792 // Use setUrl for Ajax loading
37793 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37794 tab3.setUrl("ajax2.htm", null, true);
37797 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37800 jtabs.activate("jtabs-1");
37803 * Create a new TabPanel.
37804 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37805 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37807 Roo.bootstrap.panel.Tabs = function(config){
37809 * The container element for this TabPanel.
37810 * @type Roo.Element
37812 this.el = Roo.get(config.el);
37815 if(typeof config == "boolean"){
37816 this.tabPosition = config ? "bottom" : "top";
37818 Roo.apply(this, config);
37822 if(this.tabPosition == "bottom"){
37823 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37824 this.el.addClass("roo-tabs-bottom");
37826 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37827 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37828 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37830 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37832 if(this.tabPosition != "bottom"){
37833 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37834 * @type Roo.Element
37836 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37837 this.el.addClass("roo-tabs-top");
37841 this.bodyEl.setStyle("position", "relative");
37843 this.active = null;
37844 this.activateDelegate = this.activate.createDelegate(this);
37849 * Fires when the active tab changes
37850 * @param {Roo.TabPanel} this
37851 * @param {Roo.TabPanelItem} activePanel The new active tab
37855 * @event beforetabchange
37856 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37857 * @param {Roo.TabPanel} this
37858 * @param {Object} e Set cancel to true on this object to cancel the tab change
37859 * @param {Roo.TabPanelItem} tab The tab being changed to
37861 "beforetabchange" : true
37864 Roo.EventManager.onWindowResize(this.onResize, this);
37865 this.cpad = this.el.getPadding("lr");
37866 this.hiddenCount = 0;
37869 // toolbar on the tabbar support...
37870 if (this.toolbar) {
37871 alert("no toolbar support yet");
37872 this.toolbar = false;
37874 var tcfg = this.toolbar;
37875 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37876 this.toolbar = new Roo.Toolbar(tcfg);
37877 if (Roo.isSafari) {
37878 var tbl = tcfg.container.child('table', true);
37879 tbl.setAttribute('width', '100%');
37887 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37890 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37892 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37894 tabPosition : "top",
37896 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37898 currentTabWidth : 0,
37900 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37904 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37908 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37910 preferredTabWidth : 175,
37912 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37914 resizeTabs : false,
37916 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37918 monitorResize : true,
37920 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37925 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37926 * @param {String} id The id of the div to use <b>or create</b>
37927 * @param {String} text The text for the tab
37928 * @param {String} content (optional) Content to put in the TabPanelItem body
37929 * @param {Boolean} closable (optional) True to create a close icon on the tab
37930 * @return {Roo.TabPanelItem} The created TabPanelItem
37932 addTab : function(id, text, content, closable, tpl)
37934 var item = new Roo.bootstrap.panel.TabItem({
37938 closable : closable,
37941 this.addTabItem(item);
37943 item.setContent(content);
37949 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37950 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37951 * @return {Roo.TabPanelItem}
37953 getTab : function(id){
37954 return this.items[id];
37958 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37959 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37961 hideTab : function(id){
37962 var t = this.items[id];
37965 this.hiddenCount++;
37966 this.autoSizeTabs();
37971 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37972 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37974 unhideTab : function(id){
37975 var t = this.items[id];
37977 t.setHidden(false);
37978 this.hiddenCount--;
37979 this.autoSizeTabs();
37984 * Adds an existing {@link Roo.TabPanelItem}.
37985 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37987 addTabItem : function(item){
37988 this.items[item.id] = item;
37989 this.items.push(item);
37990 // if(this.resizeTabs){
37991 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37992 // this.autoSizeTabs();
37994 // item.autoSize();
37999 * Removes a {@link Roo.TabPanelItem}.
38000 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38002 removeTab : function(id){
38003 var items = this.items;
38004 var tab = items[id];
38005 if(!tab) { return; }
38006 var index = items.indexOf(tab);
38007 if(this.active == tab && items.length > 1){
38008 var newTab = this.getNextAvailable(index);
38013 this.stripEl.dom.removeChild(tab.pnode.dom);
38014 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38015 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38017 items.splice(index, 1);
38018 delete this.items[tab.id];
38019 tab.fireEvent("close", tab);
38020 tab.purgeListeners();
38021 this.autoSizeTabs();
38024 getNextAvailable : function(start){
38025 var items = this.items;
38027 // look for a next tab that will slide over to
38028 // replace the one being removed
38029 while(index < items.length){
38030 var item = items[++index];
38031 if(item && !item.isHidden()){
38035 // if one isn't found select the previous tab (on the left)
38038 var item = items[--index];
38039 if(item && !item.isHidden()){
38047 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38048 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38050 disableTab : function(id){
38051 var tab = this.items[id];
38052 if(tab && this.active != tab){
38058 * Enables a {@link Roo.TabPanelItem} that is disabled.
38059 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38061 enableTab : function(id){
38062 var tab = this.items[id];
38067 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38068 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38069 * @return {Roo.TabPanelItem} The TabPanelItem.
38071 activate : function(id){
38072 var tab = this.items[id];
38076 if(tab == this.active || tab.disabled){
38080 this.fireEvent("beforetabchange", this, e, tab);
38081 if(e.cancel !== true && !tab.disabled){
38083 this.active.hide();
38085 this.active = this.items[id];
38086 this.active.show();
38087 this.fireEvent("tabchange", this, this.active);
38093 * Gets the active {@link Roo.TabPanelItem}.
38094 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38096 getActiveTab : function(){
38097 return this.active;
38101 * Updates the tab body element to fit the height of the container element
38102 * for overflow scrolling
38103 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38105 syncHeight : function(targetHeight){
38106 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38107 var bm = this.bodyEl.getMargins();
38108 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38109 this.bodyEl.setHeight(newHeight);
38113 onResize : function(){
38114 if(this.monitorResize){
38115 this.autoSizeTabs();
38120 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38122 beginUpdate : function(){
38123 this.updating = true;
38127 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38129 endUpdate : function(){
38130 this.updating = false;
38131 this.autoSizeTabs();
38135 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38137 autoSizeTabs : function(){
38138 var count = this.items.length;
38139 var vcount = count - this.hiddenCount;
38140 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38143 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38144 var availWidth = Math.floor(w / vcount);
38145 var b = this.stripBody;
38146 if(b.getWidth() > w){
38147 var tabs = this.items;
38148 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38149 if(availWidth < this.minTabWidth){
38150 /*if(!this.sleft){ // incomplete scrolling code
38151 this.createScrollButtons();
38154 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38157 if(this.currentTabWidth < this.preferredTabWidth){
38158 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38164 * Returns the number of tabs in this TabPanel.
38167 getCount : function(){
38168 return this.items.length;
38172 * Resizes all the tabs to the passed width
38173 * @param {Number} The new width
38175 setTabWidth : function(width){
38176 this.currentTabWidth = width;
38177 for(var i = 0, len = this.items.length; i < len; i++) {
38178 if(!this.items[i].isHidden()) {
38179 this.items[i].setWidth(width);
38185 * Destroys this TabPanel
38186 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38188 destroy : function(removeEl){
38189 Roo.EventManager.removeResizeListener(this.onResize, this);
38190 for(var i = 0, len = this.items.length; i < len; i++){
38191 this.items[i].purgeListeners();
38193 if(removeEl === true){
38194 this.el.update("");
38199 createStrip : function(container)
38201 var strip = document.createElement("nav");
38202 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38203 container.appendChild(strip);
38207 createStripList : function(strip)
38209 // div wrapper for retard IE
38210 // returns the "tr" element.
38211 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38212 //'<div class="x-tabs-strip-wrap">'+
38213 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38214 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38215 return strip.firstChild; //.firstChild.firstChild.firstChild;
38217 createBody : function(container)
38219 var body = document.createElement("div");
38220 Roo.id(body, "tab-body");
38221 //Roo.fly(body).addClass("x-tabs-body");
38222 Roo.fly(body).addClass("tab-content");
38223 container.appendChild(body);
38226 createItemBody :function(bodyEl, id){
38227 var body = Roo.getDom(id);
38229 body = document.createElement("div");
38232 //Roo.fly(body).addClass("x-tabs-item-body");
38233 Roo.fly(body).addClass("tab-pane");
38234 bodyEl.insertBefore(body, bodyEl.firstChild);
38238 createStripElements : function(stripEl, text, closable, tpl)
38240 var td = document.createElement("li"); // was td..
38243 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38246 stripEl.appendChild(td);
38248 td.className = "x-tabs-closable";
38249 if(!this.closeTpl){
38250 this.closeTpl = new Roo.Template(
38251 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38252 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38253 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38256 var el = this.closeTpl.overwrite(td, {"text": text});
38257 var close = el.getElementsByTagName("div")[0];
38258 var inner = el.getElementsByTagName("em")[0];
38259 return {"el": el, "close": close, "inner": inner};
38262 // not sure what this is..
38263 // if(!this.tabTpl){
38264 //this.tabTpl = new Roo.Template(
38265 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38266 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38268 // this.tabTpl = new Roo.Template(
38269 // '<a href="#">' +
38270 // '<span unselectable="on"' +
38271 // (this.disableTooltips ? '' : ' title="{text}"') +
38272 // ' >{text}</span></a>'
38278 var template = tpl || this.tabTpl || false;
38282 template = new Roo.Template(
38284 '<span unselectable="on"' +
38285 (this.disableTooltips ? '' : ' title="{text}"') +
38286 ' >{text}</span></a>'
38290 switch (typeof(template)) {
38294 template = new Roo.Template(template);
38300 var el = template.overwrite(td, {"text": text});
38302 var inner = el.getElementsByTagName("span")[0];
38304 return {"el": el, "inner": inner};
38312 * @class Roo.TabPanelItem
38313 * @extends Roo.util.Observable
38314 * Represents an individual item (tab plus body) in a TabPanel.
38315 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38316 * @param {String} id The id of this TabPanelItem
38317 * @param {String} text The text for the tab of this TabPanelItem
38318 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38320 Roo.bootstrap.panel.TabItem = function(config){
38322 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38323 * @type Roo.TabPanel
38325 this.tabPanel = config.panel;
38327 * The id for this TabPanelItem
38330 this.id = config.id;
38332 this.disabled = false;
38334 this.text = config.text;
38336 this.loaded = false;
38337 this.closable = config.closable;
38340 * The body element for this TabPanelItem.
38341 * @type Roo.Element
38343 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38344 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38345 this.bodyEl.setStyle("display", "block");
38346 this.bodyEl.setStyle("zoom", "1");
38347 //this.hideAction();
38349 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38351 this.el = Roo.get(els.el);
38352 this.inner = Roo.get(els.inner, true);
38353 this.textEl = Roo.get(this.el.dom.firstChild, true);
38354 this.pnode = Roo.get(els.el.parentNode, true);
38355 // this.el.on("mousedown", this.onTabMouseDown, this);
38356 this.el.on("click", this.onTabClick, this);
38358 if(config.closable){
38359 var c = Roo.get(els.close, true);
38360 c.dom.title = this.closeText;
38361 c.addClassOnOver("close-over");
38362 c.on("click", this.closeClick, this);
38368 * Fires when this tab becomes the active tab.
38369 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38370 * @param {Roo.TabPanelItem} this
38374 * @event beforeclose
38375 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38376 * @param {Roo.TabPanelItem} this
38377 * @param {Object} e Set cancel to true on this object to cancel the close.
38379 "beforeclose": true,
38382 * Fires when this tab is closed.
38383 * @param {Roo.TabPanelItem} this
38387 * @event deactivate
38388 * Fires when this tab is no longer the active tab.
38389 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38390 * @param {Roo.TabPanelItem} this
38392 "deactivate" : true
38394 this.hidden = false;
38396 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38399 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38401 purgeListeners : function(){
38402 Roo.util.Observable.prototype.purgeListeners.call(this);
38403 this.el.removeAllListeners();
38406 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38409 this.pnode.addClass("active");
38412 this.tabPanel.stripWrap.repaint();
38414 this.fireEvent("activate", this.tabPanel, this);
38418 * Returns true if this tab is the active tab.
38419 * @return {Boolean}
38421 isActive : function(){
38422 return this.tabPanel.getActiveTab() == this;
38426 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38429 this.pnode.removeClass("active");
38431 this.fireEvent("deactivate", this.tabPanel, this);
38434 hideAction : function(){
38435 this.bodyEl.hide();
38436 this.bodyEl.setStyle("position", "absolute");
38437 this.bodyEl.setLeft("-20000px");
38438 this.bodyEl.setTop("-20000px");
38441 showAction : function(){
38442 this.bodyEl.setStyle("position", "relative");
38443 this.bodyEl.setTop("");
38444 this.bodyEl.setLeft("");
38445 this.bodyEl.show();
38449 * Set the tooltip for the tab.
38450 * @param {String} tooltip The tab's tooltip
38452 setTooltip : function(text){
38453 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38454 this.textEl.dom.qtip = text;
38455 this.textEl.dom.removeAttribute('title');
38457 this.textEl.dom.title = text;
38461 onTabClick : function(e){
38462 e.preventDefault();
38463 this.tabPanel.activate(this.id);
38466 onTabMouseDown : function(e){
38467 e.preventDefault();
38468 this.tabPanel.activate(this.id);
38471 getWidth : function(){
38472 return this.inner.getWidth();
38475 setWidth : function(width){
38476 var iwidth = width - this.pnode.getPadding("lr");
38477 this.inner.setWidth(iwidth);
38478 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38479 this.pnode.setWidth(width);
38483 * Show or hide the tab
38484 * @param {Boolean} hidden True to hide or false to show.
38486 setHidden : function(hidden){
38487 this.hidden = hidden;
38488 this.pnode.setStyle("display", hidden ? "none" : "");
38492 * Returns true if this tab is "hidden"
38493 * @return {Boolean}
38495 isHidden : function(){
38496 return this.hidden;
38500 * Returns the text for this tab
38503 getText : function(){
38507 autoSize : function(){
38508 //this.el.beginMeasure();
38509 this.textEl.setWidth(1);
38511 * #2804 [new] Tabs in Roojs
38512 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38514 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38515 //this.el.endMeasure();
38519 * Sets the text for the tab (Note: this also sets the tooltip text)
38520 * @param {String} text The tab's text and tooltip
38522 setText : function(text){
38524 this.textEl.update(text);
38525 this.setTooltip(text);
38526 //if(!this.tabPanel.resizeTabs){
38527 // this.autoSize();
38531 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38533 activate : function(){
38534 this.tabPanel.activate(this.id);
38538 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38540 disable : function(){
38541 if(this.tabPanel.active != this){
38542 this.disabled = true;
38543 this.pnode.addClass("disabled");
38548 * Enables this TabPanelItem if it was previously disabled.
38550 enable : function(){
38551 this.disabled = false;
38552 this.pnode.removeClass("disabled");
38556 * Sets the content for this TabPanelItem.
38557 * @param {String} content The content
38558 * @param {Boolean} loadScripts true to look for and load scripts
38560 setContent : function(content, loadScripts){
38561 this.bodyEl.update(content, loadScripts);
38565 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38566 * @return {Roo.UpdateManager} The UpdateManager
38568 getUpdateManager : function(){
38569 return this.bodyEl.getUpdateManager();
38573 * Set a URL to be used to load the content for this TabPanelItem.
38574 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38575 * @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)
38576 * @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)
38577 * @return {Roo.UpdateManager} The UpdateManager
38579 setUrl : function(url, params, loadOnce){
38580 if(this.refreshDelegate){
38581 this.un('activate', this.refreshDelegate);
38583 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38584 this.on("activate", this.refreshDelegate);
38585 return this.bodyEl.getUpdateManager();
38589 _handleRefresh : function(url, params, loadOnce){
38590 if(!loadOnce || !this.loaded){
38591 var updater = this.bodyEl.getUpdateManager();
38592 updater.update(url, params, this._setLoaded.createDelegate(this));
38597 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38598 * Will fail silently if the setUrl method has not been called.
38599 * This does not activate the panel, just updates its content.
38601 refresh : function(){
38602 if(this.refreshDelegate){
38603 this.loaded = false;
38604 this.refreshDelegate();
38609 _setLoaded : function(){
38610 this.loaded = true;
38614 closeClick : function(e){
38617 this.fireEvent("beforeclose", this, o);
38618 if(o.cancel !== true){
38619 this.tabPanel.removeTab(this.id);
38623 * The text displayed in the tooltip for the close icon.
38626 closeText : "Close this tab"
38629 * This script refer to:
38630 * Title: International Telephone Input
38631 * Author: Jack O'Connor
38632 * Code version: v12.1.12
38633 * Availability: https://github.com/jackocnr/intl-tel-input.git
38636 Roo.bootstrap.PhoneInputData = function() {
38639 "Afghanistan (افغانستان)",
38644 "Albania (Shqipëri)",
38649 "Algeria (الجزائر)",
38674 "Antigua and Barbuda",
38684 "Armenia (Հայաստան)",
38700 "Austria (Österreich)",
38705 "Azerbaijan (Azərbaycan)",
38715 "Bahrain (البحرين)",
38720 "Bangladesh (বাংলাদেশ)",
38730 "Belarus (Беларусь)",
38735 "Belgium (België)",
38765 "Bosnia and Herzegovina (Босна и Херцеговина)",
38780 "British Indian Ocean Territory",
38785 "British Virgin Islands",
38795 "Bulgaria (България)",
38805 "Burundi (Uburundi)",
38810 "Cambodia (កម្ពុជា)",
38815 "Cameroon (Cameroun)",
38824 ["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"]
38827 "Cape Verde (Kabu Verdi)",
38832 "Caribbean Netherlands",
38843 "Central African Republic (République centrafricaine)",
38863 "Christmas Island",
38869 "Cocos (Keeling) Islands",
38880 "Comoros (جزر القمر)",
38885 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38890 "Congo (Republic) (Congo-Brazzaville)",
38910 "Croatia (Hrvatska)",
38931 "Czech Republic (Česká republika)",
38936 "Denmark (Danmark)",
38951 "Dominican Republic (República Dominicana)",
38955 ["809", "829", "849"]
38973 "Equatorial Guinea (Guinea Ecuatorial)",
38993 "Falkland Islands (Islas Malvinas)",
38998 "Faroe Islands (Føroyar)",
39019 "French Guiana (Guyane française)",
39024 "French Polynesia (Polynésie française)",
39039 "Georgia (საქართველო)",
39044 "Germany (Deutschland)",
39064 "Greenland (Kalaallit Nunaat)",
39101 "Guinea-Bissau (Guiné Bissau)",
39126 "Hungary (Magyarország)",
39131 "Iceland (Ísland)",
39151 "Iraq (العراق)",
39167 "Israel (ישראל)",
39194 "Jordan (الأردن)",
39199 "Kazakhstan (Казахстан)",
39220 "Kuwait (الكويت)",
39225 "Kyrgyzstan (Кыргызстан)",
39235 "Latvia (Latvija)",
39240 "Lebanon (لبنان)",
39255 "Libya (ليبيا)",
39265 "Lithuania (Lietuva)",
39280 "Macedonia (FYROM) (Македонија)",
39285 "Madagascar (Madagasikara)",
39315 "Marshall Islands",
39325 "Mauritania (موريتانيا)",
39330 "Mauritius (Moris)",
39351 "Moldova (Republica Moldova)",
39361 "Mongolia (Монгол)",
39366 "Montenegro (Crna Gora)",
39376 "Morocco (المغرب)",
39382 "Mozambique (Moçambique)",
39387 "Myanmar (Burma) (မြန်မာ)",
39392 "Namibia (Namibië)",
39407 "Netherlands (Nederland)",
39412 "New Caledonia (Nouvelle-Calédonie)",
39447 "North Korea (조선 민주주의 인민 공화국)",
39452 "Northern Mariana Islands",
39468 "Pakistan (پاکستان)",
39478 "Palestine (فلسطين)",
39488 "Papua New Guinea",
39530 "Réunion (La Réunion)",
39536 "Romania (România)",
39552 "Saint Barthélemy",
39563 "Saint Kitts and Nevis",
39573 "Saint Martin (Saint-Martin (partie française))",
39579 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39584 "Saint Vincent and the Grenadines",
39599 "São Tomé and Príncipe (São Tomé e Príncipe)",
39604 "Saudi Arabia (المملكة العربية السعودية)",
39609 "Senegal (Sénégal)",
39639 "Slovakia (Slovensko)",
39644 "Slovenia (Slovenija)",
39654 "Somalia (Soomaaliya)",
39664 "South Korea (대한민국)",
39669 "South Sudan (جنوب السودان)",
39679 "Sri Lanka (ශ්රී ලංකාව)",
39684 "Sudan (السودان)",
39694 "Svalbard and Jan Mayen",
39705 "Sweden (Sverige)",
39710 "Switzerland (Schweiz)",
39715 "Syria (سوريا)",
39760 "Trinidad and Tobago",
39765 "Tunisia (تونس)",
39770 "Turkey (Türkiye)",
39780 "Turks and Caicos Islands",
39790 "U.S. Virgin Islands",
39800 "Ukraine (Україна)",
39805 "United Arab Emirates (الإمارات العربية المتحدة)",
39827 "Uzbekistan (Oʻzbekiston)",
39837 "Vatican City (Città del Vaticano)",
39848 "Vietnam (Việt Nam)",
39853 "Wallis and Futuna (Wallis-et-Futuna)",
39858 "Western Sahara (الصحراء الغربية)",
39864 "Yemen (اليمن)",
39888 * This script refer to:
39889 * Title: International Telephone Input
39890 * Author: Jack O'Connor
39891 * Code version: v12.1.12
39892 * Availability: https://github.com/jackocnr/intl-tel-input.git
39896 * @class Roo.bootstrap.PhoneInput
39897 * @extends Roo.bootstrap.TriggerField
39898 * An input with International dial-code selection
39900 * @cfg {String} defaultDialCode default '+852'
39901 * @cfg {Array} preferedCountries default []
39904 * Create a new PhoneInput.
39905 * @param {Object} config Configuration options
39908 Roo.bootstrap.PhoneInput = function(config) {
39909 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39912 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39914 listWidth: undefined,
39916 selectedClass: 'active',
39918 invalidClass : "has-warning",
39920 validClass: 'has-success',
39922 allowed: '0123456789',
39927 * @cfg {String} defaultDialCode The default dial code when initializing the input
39929 defaultDialCode: '+852',
39932 * @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
39934 preferedCountries: false,
39936 getAutoCreate : function()
39938 var data = Roo.bootstrap.PhoneInputData();
39939 var align = this.labelAlign || this.parentLabelAlign();
39942 this.allCountries = [];
39943 this.dialCodeMapping = [];
39945 for (var i = 0; i < data.length; i++) {
39947 this.allCountries[i] = {
39951 priority: c[3] || 0,
39952 areaCodes: c[4] || null
39954 this.dialCodeMapping[c[2]] = {
39957 priority: c[3] || 0,
39958 areaCodes: c[4] || null
39970 // type: 'number', -- do not use number - we get the flaky up/down arrows.
39971 maxlength: this.max_length,
39972 cls : 'form-control tel-input',
39973 autocomplete: 'new-password'
39976 var hiddenInput = {
39979 cls: 'hidden-tel-input'
39983 hiddenInput.name = this.name;
39986 if (this.disabled) {
39987 input.disabled = true;
39990 var flag_container = {
40007 cls: this.hasFeedback ? 'has-feedback' : '',
40013 cls: 'dial-code-holder',
40020 cls: 'roo-select2-container input-group',
40027 if (this.fieldLabel.length) {
40030 tooltip: 'This field is required'
40036 cls: 'control-label',
40042 html: this.fieldLabel
40045 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40051 if(this.indicatorpos == 'right') {
40052 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40059 if(align == 'left') {
40067 if(this.labelWidth > 12){
40068 label.style = "width: " + this.labelWidth + 'px';
40070 if(this.labelWidth < 13 && this.labelmd == 0){
40071 this.labelmd = this.labelWidth;
40073 if(this.labellg > 0){
40074 label.cls += ' col-lg-' + this.labellg;
40075 input.cls += ' col-lg-' + (12 - this.labellg);
40077 if(this.labelmd > 0){
40078 label.cls += ' col-md-' + this.labelmd;
40079 container.cls += ' col-md-' + (12 - this.labelmd);
40081 if(this.labelsm > 0){
40082 label.cls += ' col-sm-' + this.labelsm;
40083 container.cls += ' col-sm-' + (12 - this.labelsm);
40085 if(this.labelxs > 0){
40086 label.cls += ' col-xs-' + this.labelxs;
40087 container.cls += ' col-xs-' + (12 - this.labelxs);
40097 var settings = this;
40099 ['xs','sm','md','lg'].map(function(size){
40100 if (settings[size]) {
40101 cfg.cls += ' col-' + size + '-' + settings[size];
40105 this.store = new Roo.data.Store({
40106 proxy : new Roo.data.MemoryProxy({}),
40107 reader : new Roo.data.JsonReader({
40118 'name' : 'dialCode',
40122 'name' : 'priority',
40126 'name' : 'areaCodes',
40133 if(!this.preferedCountries) {
40134 this.preferedCountries = [
40141 var p = this.preferedCountries.reverse();
40144 for (var i = 0; i < p.length; i++) {
40145 for (var j = 0; j < this.allCountries.length; j++) {
40146 if(this.allCountries[j].iso2 == p[i]) {
40147 var t = this.allCountries[j];
40148 this.allCountries.splice(j,1);
40149 this.allCountries.unshift(t);
40155 this.store.proxy.data = {
40157 data: this.allCountries
40163 initEvents : function()
40166 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40168 this.indicator = this.indicatorEl();
40169 this.flag = this.flagEl();
40170 this.dialCodeHolder = this.dialCodeHolderEl();
40172 this.trigger = this.el.select('div.flag-box',true).first();
40173 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40178 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40179 _this.list.setWidth(lw);
40182 this.list.on('mouseover', this.onViewOver, this);
40183 this.list.on('mousemove', this.onViewMove, this);
40184 this.inputEl().on("keyup", this.onKeyUp, this);
40185 this.inputEl().on("keypress", this.onKeyPress, this);
40187 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40189 this.view = new Roo.View(this.list, this.tpl, {
40190 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40193 this.view.on('click', this.onViewClick, this);
40194 this.setValue(this.defaultDialCode);
40197 onTriggerClick : function(e)
40199 Roo.log('trigger click');
40204 if(this.isExpanded()){
40206 this.hasFocus = false;
40208 this.store.load({});
40209 this.hasFocus = true;
40214 isExpanded : function()
40216 return this.list.isVisible();
40219 collapse : function()
40221 if(!this.isExpanded()){
40225 Roo.get(document).un('mousedown', this.collapseIf, this);
40226 Roo.get(document).un('mousewheel', this.collapseIf, this);
40227 this.fireEvent('collapse', this);
40231 expand : function()
40235 if(this.isExpanded() || !this.hasFocus){
40239 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40240 this.list.setWidth(lw);
40243 this.restrictHeight();
40245 Roo.get(document).on('mousedown', this.collapseIf, this);
40246 Roo.get(document).on('mousewheel', this.collapseIf, this);
40248 this.fireEvent('expand', this);
40251 restrictHeight : function()
40253 this.list.alignTo(this.inputEl(), this.listAlign);
40254 this.list.alignTo(this.inputEl(), this.listAlign);
40257 onViewOver : function(e, t)
40259 if(this.inKeyMode){
40262 var item = this.view.findItemFromChild(t);
40265 var index = this.view.indexOf(item);
40266 this.select(index, false);
40271 onViewClick : function(view, doFocus, el, e)
40273 var index = this.view.getSelectedIndexes()[0];
40275 var r = this.store.getAt(index);
40278 this.onSelect(r, index);
40280 if(doFocus !== false && !this.blockFocus){
40281 this.inputEl().focus();
40285 onViewMove : function(e, t)
40287 this.inKeyMode = false;
40290 select : function(index, scrollIntoView)
40292 this.selectedIndex = index;
40293 this.view.select(index);
40294 if(scrollIntoView !== false){
40295 var el = this.view.getNode(index);
40297 this.list.scrollChildIntoView(el, false);
40302 createList : function()
40304 this.list = Roo.get(document.body).createChild({
40306 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40307 style: 'display:none'
40310 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40313 collapseIf : function(e)
40315 var in_combo = e.within(this.el);
40316 var in_list = e.within(this.list);
40317 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40319 if (in_combo || in_list || is_list) {
40325 onSelect : function(record, index)
40327 if(this.fireEvent('beforeselect', this, record, index) !== false){
40329 this.setFlagClass(record.data.iso2);
40330 this.setDialCode(record.data.dialCode);
40331 this.hasFocus = false;
40333 this.fireEvent('select', this, record, index);
40337 flagEl : function()
40339 var flag = this.el.select('div.flag',true).first();
40346 dialCodeHolderEl : function()
40348 var d = this.el.select('input.dial-code-holder',true).first();
40355 setDialCode : function(v)
40357 this.dialCodeHolder.dom.value = '+'+v;
40360 setFlagClass : function(n)
40362 this.flag.dom.className = 'flag '+n;
40365 getValue : function()
40367 var v = this.inputEl().getValue();
40368 if(this.dialCodeHolder) {
40369 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40374 setValue : function(v)
40376 var d = this.getDialCode(v);
40378 //invalid dial code
40379 if(v.length == 0 || !d || d.length == 0) {
40381 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40382 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40388 this.setFlagClass(this.dialCodeMapping[d].iso2);
40389 this.setDialCode(d);
40390 this.inputEl().dom.value = v.replace('+'+d,'');
40391 this.hiddenEl().dom.value = this.getValue();
40396 getDialCode : function(v)
40400 if (v.length == 0) {
40401 return this.dialCodeHolder.dom.value;
40405 if (v.charAt(0) != "+") {
40408 var numericChars = "";
40409 for (var i = 1; i < v.length; i++) {
40410 var c = v.charAt(i);
40413 if (this.dialCodeMapping[numericChars]) {
40414 dialCode = v.substr(1, i);
40416 if (numericChars.length == 4) {
40426 this.setValue(this.defaultDialCode);
40430 hiddenEl : function()
40432 return this.el.select('input.hidden-tel-input',true).first();
40435 // after setting val
40436 onKeyUp : function(e){
40437 this.setValue(this.getValue());
40440 onKeyPress : function(e){
40441 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40448 * @class Roo.bootstrap.MoneyField
40449 * @extends Roo.bootstrap.ComboBox
40450 * Bootstrap MoneyField class
40453 * Create a new MoneyField.
40454 * @param {Object} config Configuration options
40457 Roo.bootstrap.MoneyField = function(config) {
40459 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40463 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40466 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40468 allowDecimals : true,
40470 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40472 decimalSeparator : ".",
40474 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40476 decimalPrecision : 0,
40478 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40480 allowNegative : true,
40482 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40486 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40488 minValue : Number.NEGATIVE_INFINITY,
40490 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40492 maxValue : Number.MAX_VALUE,
40494 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40496 minText : "The minimum value for this field is {0}",
40498 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40500 maxText : "The maximum value for this field is {0}",
40502 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40503 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40505 nanText : "{0} is not a valid number",
40507 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40511 * @cfg {String} defaults currency of the MoneyField
40512 * value should be in lkey
40514 defaultCurrency : false,
40516 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40518 thousandsDelimiter : false,
40520 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40531 getAutoCreate : function()
40533 var align = this.labelAlign || this.parentLabelAlign();
40545 cls : 'form-control roo-money-amount-input',
40546 autocomplete: 'new-password'
40549 var hiddenInput = {
40553 cls: 'hidden-number-input'
40556 if(this.max_length) {
40557 input.maxlength = this.max_length;
40561 hiddenInput.name = this.name;
40564 if (this.disabled) {
40565 input.disabled = true;
40568 var clg = 12 - this.inputlg;
40569 var cmd = 12 - this.inputmd;
40570 var csm = 12 - this.inputsm;
40571 var cxs = 12 - this.inputxs;
40575 cls : 'row roo-money-field',
40579 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40583 cls: 'roo-select2-container input-group',
40587 cls : 'form-control roo-money-currency-input',
40588 autocomplete: 'new-password',
40590 name : this.currencyName
40594 cls : 'input-group-addon',
40608 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40612 cls: this.hasFeedback ? 'has-feedback' : '',
40623 if (this.fieldLabel.length) {
40626 tooltip: 'This field is required'
40632 cls: 'control-label',
40638 html: this.fieldLabel
40641 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40647 if(this.indicatorpos == 'right') {
40648 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40655 if(align == 'left') {
40663 if(this.labelWidth > 12){
40664 label.style = "width: " + this.labelWidth + 'px';
40666 if(this.labelWidth < 13 && this.labelmd == 0){
40667 this.labelmd = this.labelWidth;
40669 if(this.labellg > 0){
40670 label.cls += ' col-lg-' + this.labellg;
40671 input.cls += ' col-lg-' + (12 - this.labellg);
40673 if(this.labelmd > 0){
40674 label.cls += ' col-md-' + this.labelmd;
40675 container.cls += ' col-md-' + (12 - this.labelmd);
40677 if(this.labelsm > 0){
40678 label.cls += ' col-sm-' + this.labelsm;
40679 container.cls += ' col-sm-' + (12 - this.labelsm);
40681 if(this.labelxs > 0){
40682 label.cls += ' col-xs-' + this.labelxs;
40683 container.cls += ' col-xs-' + (12 - this.labelxs);
40694 var settings = this;
40696 ['xs','sm','md','lg'].map(function(size){
40697 if (settings[size]) {
40698 cfg.cls += ' col-' + size + '-' + settings[size];
40705 initEvents : function()
40707 this.indicator = this.indicatorEl();
40709 this.initCurrencyEvent();
40711 this.initNumberEvent();
40714 initCurrencyEvent : function()
40717 throw "can not find store for combo";
40720 this.store = Roo.factory(this.store, Roo.data);
40721 this.store.parent = this;
40725 this.triggerEl = this.el.select('.input-group-addon', true).first();
40727 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40732 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40733 _this.list.setWidth(lw);
40736 this.list.on('mouseover', this.onViewOver, this);
40737 this.list.on('mousemove', this.onViewMove, this);
40738 this.list.on('scroll', this.onViewScroll, this);
40741 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40744 this.view = new Roo.View(this.list, this.tpl, {
40745 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40748 this.view.on('click', this.onViewClick, this);
40750 this.store.on('beforeload', this.onBeforeLoad, this);
40751 this.store.on('load', this.onLoad, this);
40752 this.store.on('loadexception', this.onLoadException, this);
40754 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40755 "up" : function(e){
40756 this.inKeyMode = true;
40760 "down" : function(e){
40761 if(!this.isExpanded()){
40762 this.onTriggerClick();
40764 this.inKeyMode = true;
40769 "enter" : function(e){
40772 if(this.fireEvent("specialkey", this, e)){
40773 this.onViewClick(false);
40779 "esc" : function(e){
40783 "tab" : function(e){
40786 if(this.fireEvent("specialkey", this, e)){
40787 this.onViewClick(false);
40795 doRelay : function(foo, bar, hname){
40796 if(hname == 'down' || this.scope.isExpanded()){
40797 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40805 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40809 initNumberEvent : function(e)
40811 this.inputEl().on("keydown" , this.fireKey, this);
40812 this.inputEl().on("focus", this.onFocus, this);
40813 this.inputEl().on("blur", this.onBlur, this);
40815 this.inputEl().relayEvent('keyup', this);
40817 if(this.indicator){
40818 this.indicator.addClass('invisible');
40821 this.originalValue = this.getValue();
40823 if(this.validationEvent == 'keyup'){
40824 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40825 this.inputEl().on('keyup', this.filterValidation, this);
40827 else if(this.validationEvent !== false){
40828 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40831 if(this.selectOnFocus){
40832 this.on("focus", this.preFocus, this);
40835 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40836 this.inputEl().on("keypress", this.filterKeys, this);
40838 this.inputEl().relayEvent('keypress', this);
40841 var allowed = "0123456789";
40843 if(this.allowDecimals){
40844 allowed += this.decimalSeparator;
40847 if(this.allowNegative){
40851 if(this.thousandsDelimiter) {
40855 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40857 var keyPress = function(e){
40859 var k = e.getKey();
40861 var c = e.getCharCode();
40864 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40865 allowed.indexOf(String.fromCharCode(c)) === -1
40871 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40875 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40880 this.inputEl().on("keypress", keyPress, this);
40884 onTriggerClick : function(e)
40891 this.loadNext = false;
40893 if(this.isExpanded()){
40898 this.hasFocus = true;
40900 if(this.triggerAction == 'all') {
40901 this.doQuery(this.allQuery, true);
40905 this.doQuery(this.getRawValue());
40908 getCurrency : function()
40910 var v = this.currencyEl().getValue();
40915 restrictHeight : function()
40917 this.list.alignTo(this.currencyEl(), this.listAlign);
40918 this.list.alignTo(this.currencyEl(), this.listAlign);
40921 onViewClick : function(view, doFocus, el, e)
40923 var index = this.view.getSelectedIndexes()[0];
40925 var r = this.store.getAt(index);
40928 this.onSelect(r, index);
40932 onSelect : function(record, index){
40934 if(this.fireEvent('beforeselect', this, record, index) !== false){
40936 this.setFromCurrencyData(index > -1 ? record.data : false);
40940 this.fireEvent('select', this, record, index);
40944 setFromCurrencyData : function(o)
40948 this.lastCurrency = o;
40950 if (this.currencyField) {
40951 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40953 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40956 this.lastSelectionText = currency;
40958 //setting default currency
40959 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40960 this.setCurrency(this.defaultCurrency);
40964 this.setCurrency(currency);
40967 setFromData : function(o)
40971 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40973 this.setFromCurrencyData(c);
40978 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40980 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40983 this.setValue(value);
40987 setCurrency : function(v)
40989 this.currencyValue = v;
40992 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40997 setValue : function(v)
40999 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41005 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41007 this.inputEl().dom.value = (v == '') ? '' :
41008 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41010 if(!this.allowZero && v === '0') {
41011 this.hiddenEl().dom.value = '';
41012 this.inputEl().dom.value = '';
41019 getRawValue : function()
41021 var v = this.inputEl().getValue();
41026 getValue : function()
41028 return this.fixPrecision(this.parseValue(this.getRawValue()));
41031 parseValue : function(value)
41033 if(this.thousandsDelimiter) {
41035 r = new RegExp(",", "g");
41036 value = value.replace(r, "");
41039 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41040 return isNaN(value) ? '' : value;
41044 fixPrecision : function(value)
41046 if(this.thousandsDelimiter) {
41048 r = new RegExp(",", "g");
41049 value = value.replace(r, "");
41052 var nan = isNaN(value);
41054 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41055 return nan ? '' : value;
41057 return parseFloat(value).toFixed(this.decimalPrecision);
41060 decimalPrecisionFcn : function(v)
41062 return Math.floor(v);
41065 validateValue : function(value)
41067 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41071 var num = this.parseValue(value);
41074 this.markInvalid(String.format(this.nanText, value));
41078 if(num < this.minValue){
41079 this.markInvalid(String.format(this.minText, this.minValue));
41083 if(num > this.maxValue){
41084 this.markInvalid(String.format(this.maxText, this.maxValue));
41091 validate : function()
41093 if(this.disabled || this.allowBlank){
41098 var currency = this.getCurrency();
41100 if(this.validateValue(this.getRawValue()) && currency.length){
41105 this.markInvalid();
41109 getName: function()
41114 beforeBlur : function()
41120 var v = this.parseValue(this.getRawValue());
41127 onBlur : function()
41131 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41132 //this.el.removeClass(this.focusClass);
41135 this.hasFocus = false;
41137 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41141 var v = this.getValue();
41143 if(String(v) !== String(this.startValue)){
41144 this.fireEvent('change', this, v, this.startValue);
41147 this.fireEvent("blur", this);
41150 inputEl : function()
41152 return this.el.select('.roo-money-amount-input', true).first();
41155 currencyEl : function()
41157 return this.el.select('.roo-money-currency-input', true).first();
41160 hiddenEl : function()
41162 return this.el.select('input.hidden-number-input',true).first();