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 | secondary | success | info | warning | danger | link ) default
585 * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587 * @cfg {String} size ( lg | sm | xs)
588 * @cfg {String} tag ( a | input | submit)
589 * @cfg {String} href empty or href
590 * @cfg {Boolean} disabled default false;
591 * @cfg {Boolean} isClose default false;
592 * @cfg {String} glyphicon (| 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)
593 * @cfg {String} badge text for badge
594 * @cfg {String} theme (default|glow)
595 * @cfg {Boolean} inverse dark themed version
596 * @cfg {Boolean} toggle is it a slidy toggle button
597 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
598 * @cfg {String} ontext text for on slidy toggle state
599 * @cfg {String} offtext text for off slidy toggle state
600 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
601 * @cfg {Boolean} removeClass remove the standard class..
602 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
605 * Create a new button
606 * @param {Object} config The config object
610 Roo.bootstrap.Button = function(config){
611 Roo.bootstrap.Button.superclass.constructor.call(this, config);
612 this.weightClass = ["btn-default btn-outline-secondary",
624 * When a butotn is pressed
625 * @param {Roo.bootstrap.Button} btn
626 * @param {Roo.EventObject} e
631 * After the button has been toggles
632 * @param {Roo.bootstrap.Button} btn
633 * @param {Roo.EventObject} e
634 * @param {boolean} pressed (also available as button.pressed)
640 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
660 preventDefault: true,
668 getAutoCreate : function(){
676 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
677 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
682 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
684 if (this.toggle == true) {
687 cls: 'slider-frame roo-button',
692 'data-off-text':'OFF',
693 cls: 'slider-button',
699 if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
700 cfg.cls += ' '+this.weight;
709 cfg["aria-hidden"] = true;
711 cfg.html = "×";
717 if (this.theme==='default') {
718 cfg.cls = 'btn roo-button';
720 //if (this.parentType != 'Navbar') {
721 this.weight = this.weight.length ? this.weight : 'default';
723 if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
725 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
726 var weight = this.weight == 'default' ? 'secondary' : this.weight;
727 cfg.cls += ' btn-' + outline + weight;
728 if (this.weight == 'default') {
730 cfg.cls += ' btn-' + this.weight;
733 } else if (this.theme==='glow') {
736 cfg.cls = 'btn-glow roo-button';
738 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
740 cfg.cls += ' ' + this.weight;
746 this.cls += ' inverse';
750 if (this.active || this.pressed === true) {
751 cfg.cls += ' active';
755 cfg.disabled = 'disabled';
759 Roo.log('changing to ul' );
761 this.glyphicon = 'caret';
764 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
766 //gsRoo.log(this.parentType);
767 if (this.parentType === 'Navbar' && !this.parent().bar) {
768 Roo.log('changing to li?');
777 href : this.href || '#'
780 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
781 cfg.cls += ' dropdown';
788 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
790 if (this.glyphicon) {
791 cfg.html = ' ' + cfg.html;
796 cls: 'glyphicon glyphicon-' + this.glyphicon
806 // cfg.cls='btn roo-button';
810 var value = cfg.html;
815 cls: 'glyphicon glyphicon-' + this.glyphicon,
820 var bw = this.badge_weight.length ? this.badge_weight :
821 (this.weight.length ? this.weight : 'secondary');
822 bw = bw == 'default' ? 'secondary' : bw;
828 cls: 'badge badge-' + bw,
837 cfg.cls += ' dropdown';
838 cfg.html = typeof(cfg.html) != 'undefined' ?
839 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
842 if (cfg.tag !== 'a' && this.href !== '') {
843 throw "Tag must be a to set href.";
844 } else if (this.href.length > 0) {
845 cfg.href = this.href;
848 if(this.removeClass){
853 cfg.target = this.target;
858 initEvents: function() {
859 // Roo.log('init events?');
860 // Roo.log(this.el.dom);
863 if (typeof (this.menu) != 'undefined') {
864 this.menu.parentType = this.xtype;
865 this.menu.triggerEl = this.el;
866 this.addxtype(Roo.apply({}, this.menu));
870 if (this.el.hasClass('roo-button')) {
871 this.el.on('click', this.onClick, this);
873 this.el.select('.roo-button').on('click', this.onClick, this);
876 if(this.removeClass){
877 this.el.on('click', this.onClick, this);
880 this.el.enableDisplayMode();
883 onClick : function(e)
889 Roo.log('button on click ');
890 if(this.preventDefault){
894 if (this.pressed === true || this.pressed === false) {
895 this.toggleActive(e);
899 this.fireEvent('click', this, e);
903 * Enables this button
907 this.disabled = false;
908 this.el.removeClass('disabled');
912 * Disable this button
916 this.disabled = true;
917 this.el.addClass('disabled');
920 * sets the active state on/off,
921 * @param {Boolean} state (optional) Force a particular state
923 setActive : function(v) {
925 this.el[v ? 'addClass' : 'removeClass']('active');
929 * toggles the current active state
931 toggleActive : function(e)
933 this.setActive(!this.pressed);
934 this.fireEvent('toggle', this, e, !this.pressed);
937 * get the current active state
938 * @return {boolean} true if it's active
940 isActive : function()
942 return this.el.hasClass('active');
945 * set the text of the first selected button
947 setText : function(str)
949 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
952 * get the text of the first selected button
956 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
959 setWeight : function(str)
961 this.el.removeClass(this.weightClass);
963 var outline = this.outline ? 'outline-' : '';
964 if (str == 'default') {
965 this.el.addClass('btn-default btn-outline-secondary');
968 this.el.addClass('btn-' + outline + str);
982 * @class Roo.bootstrap.Column
983 * @extends Roo.bootstrap.Component
984 * Bootstrap Column class
985 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
986 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
987 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
988 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
989 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
990 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
991 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
992 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
995 * @cfg {Boolean} hidden (true|false) hide the element
996 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
997 * @cfg {String} fa (ban|check|...) font awesome icon
998 * @cfg {Number} fasize (1|2|....) font awsome size
1000 * @cfg {String} icon (info-sign|check|...) glyphicon name
1002 * @cfg {String} html content of column.
1005 * Create a new Column
1006 * @param {Object} config The config object
1009 Roo.bootstrap.Column = function(config){
1010 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1013 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1031 getAutoCreate : function(){
1032 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1040 ['xs','sm','md','lg'].map(function(size){
1041 //Roo.log( size + ':' + settings[size]);
1043 if (settings[size+'off'] !== false) {
1044 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1047 if (settings[size] === false) {
1051 if (!settings[size]) { // 0 = hidden
1052 cfg.cls += ' hidden-' + size;
1055 cfg.cls += ' col-' + size + '-' + settings[size];
1060 cfg.cls += ' hidden';
1063 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1064 cfg.cls +=' alert alert-' + this.alert;
1068 if (this.html.length) {
1069 cfg.html = this.html;
1073 if (this.fasize > 1) {
1074 fasize = ' fa-' + this.fasize + 'x';
1076 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1081 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1100 * @class Roo.bootstrap.Container
1101 * @extends Roo.bootstrap.Component
1102 * Bootstrap Container class
1103 * @cfg {Boolean} jumbotron is it a jumbotron element
1104 * @cfg {String} html content of element
1105 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1106 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1107 * @cfg {String} header content of header (for panel)
1108 * @cfg {String} footer content of footer (for panel)
1109 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1110 * @cfg {String} tag (header|aside|section) type of HTML tag.
1111 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1112 * @cfg {String} fa font awesome icon
1113 * @cfg {String} icon (info-sign|check|...) glyphicon name
1114 * @cfg {Boolean} hidden (true|false) hide the element
1115 * @cfg {Boolean} expandable (true|false) default false
1116 * @cfg {Boolean} expanded (true|false) default true
1117 * @cfg {String} rheader contet on the right of header
1118 * @cfg {Boolean} clickable (true|false) default false
1122 * Create a new Container
1123 * @param {Object} config The config object
1126 Roo.bootstrap.Container = function(config){
1127 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1133 * After the panel has been expand
1135 * @param {Roo.bootstrap.Container} this
1140 * After the panel has been collapsed
1142 * @param {Roo.bootstrap.Container} this
1147 * When a element is chick
1148 * @param {Roo.bootstrap.Container} this
1149 * @param {Roo.EventObject} e
1155 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1173 getChildContainer : function() {
1179 if (this.panel.length) {
1180 return this.el.select('.panel-body',true).first();
1187 getAutoCreate : function(){
1190 tag : this.tag || 'div',
1194 if (this.jumbotron) {
1195 cfg.cls = 'jumbotron';
1200 // - this is applied by the parent..
1202 // cfg.cls = this.cls + '';
1205 if (this.sticky.length) {
1207 var bd = Roo.get(document.body);
1208 if (!bd.hasClass('bootstrap-sticky')) {
1209 bd.addClass('bootstrap-sticky');
1210 Roo.select('html',true).setStyle('height', '100%');
1213 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1217 if (this.well.length) {
1218 switch (this.well) {
1221 cfg.cls +=' well well-' +this.well;
1230 cfg.cls += ' hidden';
1234 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1235 cfg.cls +=' alert alert-' + this.alert;
1240 if (this.panel.length) {
1241 cfg.cls += ' panel panel-' + this.panel;
1243 if (this.header.length) {
1247 if(this.expandable){
1249 cfg.cls = cfg.cls + ' expandable';
1253 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1261 cls : 'panel-title',
1262 html : (this.expandable ? ' ' : '') + this.header
1266 cls: 'panel-header-right',
1272 cls : 'panel-heading',
1273 style : this.expandable ? 'cursor: pointer' : '',
1281 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1286 if (this.footer.length) {
1288 cls : 'panel-footer',
1297 body.html = this.html || cfg.html;
1298 // prefix with the icons..
1300 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1303 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1308 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1309 cfg.cls = 'container';
1315 initEvents: function()
1317 if(this.expandable){
1318 var headerEl = this.headerEl();
1321 headerEl.on('click', this.onToggleClick, this);
1326 this.el.on('click', this.onClick, this);
1331 onToggleClick : function()
1333 var headerEl = this.headerEl();
1349 if(this.fireEvent('expand', this)) {
1351 this.expanded = true;
1353 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1355 this.el.select('.panel-body',true).first().removeClass('hide');
1357 var toggleEl = this.toggleEl();
1363 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1368 collapse : function()
1370 if(this.fireEvent('collapse', this)) {
1372 this.expanded = false;
1374 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1375 this.el.select('.panel-body',true).first().addClass('hide');
1377 var toggleEl = this.toggleEl();
1383 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1387 toggleEl : function()
1389 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1393 return this.el.select('.panel-heading .fa',true).first();
1396 headerEl : function()
1398 if(!this.el || !this.panel.length || !this.header.length){
1402 return this.el.select('.panel-heading',true).first()
1407 if(!this.el || !this.panel.length){
1411 return this.el.select('.panel-body',true).first()
1414 titleEl : function()
1416 if(!this.el || !this.panel.length || !this.header.length){
1420 return this.el.select('.panel-title',true).first();
1423 setTitle : function(v)
1425 var titleEl = this.titleEl();
1431 titleEl.dom.innerHTML = v;
1434 getTitle : function()
1437 var titleEl = this.titleEl();
1443 return titleEl.dom.innerHTML;
1446 setRightTitle : function(v)
1448 var t = this.el.select('.panel-header-right',true).first();
1454 t.dom.innerHTML = v;
1457 onClick : function(e)
1461 this.fireEvent('click', this, e);
1474 * @class Roo.bootstrap.Img
1475 * @extends Roo.bootstrap.Component
1476 * Bootstrap Img class
1477 * @cfg {Boolean} imgResponsive false | true
1478 * @cfg {String} border rounded | circle | thumbnail
1479 * @cfg {String} src image source
1480 * @cfg {String} alt image alternative text
1481 * @cfg {String} href a tag href
1482 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1483 * @cfg {String} xsUrl xs image source
1484 * @cfg {String} smUrl sm image source
1485 * @cfg {String} mdUrl md image source
1486 * @cfg {String} lgUrl lg image source
1489 * Create a new Input
1490 * @param {Object} config The config object
1493 Roo.bootstrap.Img = function(config){
1494 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1500 * The img click event for the img.
1501 * @param {Roo.EventObject} e
1507 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1509 imgResponsive: true,
1519 getAutoCreate : function()
1521 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1522 return this.createSingleImg();
1527 cls: 'roo-image-responsive-group',
1532 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1534 if(!_this[size + 'Url']){
1540 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1541 html: _this.html || cfg.html,
1542 src: _this[size + 'Url']
1545 img.cls += ' roo-image-responsive-' + size;
1547 var s = ['xs', 'sm', 'md', 'lg'];
1549 s.splice(s.indexOf(size), 1);
1551 Roo.each(s, function(ss){
1552 img.cls += ' hidden-' + ss;
1555 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1556 cfg.cls += ' img-' + _this.border;
1560 cfg.alt = _this.alt;
1573 a.target = _this.target;
1577 cfg.cn.push((_this.href) ? a : img);
1584 createSingleImg : function()
1588 cls: (this.imgResponsive) ? 'img-responsive' : '',
1590 src : 'about:blank' // just incase src get's set to undefined?!?
1593 cfg.html = this.html || cfg.html;
1595 cfg.src = this.src || cfg.src;
1597 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1598 cfg.cls += ' img-' + this.border;
1615 a.target = this.target;
1620 return (this.href) ? a : cfg;
1623 initEvents: function()
1626 this.el.on('click', this.onClick, this);
1631 onClick : function(e)
1633 Roo.log('img onclick');
1634 this.fireEvent('click', this, e);
1637 * Sets the url of the image - used to update it
1638 * @param {String} url the url of the image
1641 setSrc : function(url)
1645 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1646 this.el.dom.src = url;
1650 this.el.select('img', true).first().dom.src = url;
1666 * @class Roo.bootstrap.Link
1667 * @extends Roo.bootstrap.Component
1668 * Bootstrap Link Class
1669 * @cfg {String} alt image alternative text
1670 * @cfg {String} href a tag href
1671 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1672 * @cfg {String} html the content of the link.
1673 * @cfg {String} anchor name for the anchor link
1674 * @cfg {String} fa - favicon
1676 * @cfg {Boolean} preventDefault (true | false) default false
1680 * Create a new Input
1681 * @param {Object} config The config object
1684 Roo.bootstrap.Link = function(config){
1685 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1691 * The img click event for the img.
1692 * @param {Roo.EventObject} e
1698 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1702 preventDefault: false,
1708 getAutoCreate : function()
1710 var html = this.html || '';
1712 if (this.fa !== false) {
1713 html = '<i class="fa fa-' + this.fa + '"></i>';
1718 // anchor's do not require html/href...
1719 if (this.anchor === false) {
1721 cfg.href = this.href || '#';
1723 cfg.name = this.anchor;
1724 if (this.html !== false || this.fa !== false) {
1727 if (this.href !== false) {
1728 cfg.href = this.href;
1732 if(this.alt !== false){
1737 if(this.target !== false) {
1738 cfg.target = this.target;
1744 initEvents: function() {
1746 if(!this.href || this.preventDefault){
1747 this.el.on('click', this.onClick, this);
1751 onClick : function(e)
1753 if(this.preventDefault){
1756 //Roo.log('img onclick');
1757 this.fireEvent('click', this, e);
1770 * @class Roo.bootstrap.Header
1771 * @extends Roo.bootstrap.Component
1772 * Bootstrap Header class
1773 * @cfg {String} html content of header
1774 * @cfg {Number} level (1|2|3|4|5|6) default 1
1777 * Create a new Header
1778 * @param {Object} config The config object
1782 Roo.bootstrap.Header = function(config){
1783 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1786 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1794 getAutoCreate : function(){
1799 tag: 'h' + (1 *this.level),
1800 html: this.html || ''
1812 * Ext JS Library 1.1.1
1813 * Copyright(c) 2006-2007, Ext JS, LLC.
1815 * Originally Released Under LGPL - original licence link has changed is not relivant.
1818 * <script type="text/javascript">
1822 * @class Roo.bootstrap.MenuMgr
1823 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1826 Roo.bootstrap.MenuMgr = function(){
1827 var menus, active, groups = {}, attached = false, lastShow = new Date();
1829 // private - called when first menu is created
1832 active = new Roo.util.MixedCollection();
1833 Roo.get(document).addKeyListener(27, function(){
1834 if(active.length > 0){
1842 if(active && active.length > 0){
1843 var c = active.clone();
1853 if(active.length < 1){
1854 Roo.get(document).un("mouseup", onMouseDown);
1862 var last = active.last();
1863 lastShow = new Date();
1866 Roo.get(document).on("mouseup", onMouseDown);
1871 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1872 m.parentMenu.activeChild = m;
1873 }else if(last && last.isVisible()){
1874 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1879 function onBeforeHide(m){
1881 m.activeChild.hide();
1883 if(m.autoHideTimer){
1884 clearTimeout(m.autoHideTimer);
1885 delete m.autoHideTimer;
1890 function onBeforeShow(m){
1891 var pm = m.parentMenu;
1892 if(!pm && !m.allowOtherMenus){
1894 }else if(pm && pm.activeChild && active != m){
1895 pm.activeChild.hide();
1899 // private this should really trigger on mouseup..
1900 function onMouseDown(e){
1901 Roo.log("on Mouse Up");
1903 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1904 Roo.log("MenuManager hideAll");
1913 function onBeforeCheck(mi, state){
1915 var g = groups[mi.group];
1916 for(var i = 0, l = g.length; i < l; i++){
1918 g[i].setChecked(false);
1927 * Hides all menus that are currently visible
1929 hideAll : function(){
1934 register : function(menu){
1938 menus[menu.id] = menu;
1939 menu.on("beforehide", onBeforeHide);
1940 menu.on("hide", onHide);
1941 menu.on("beforeshow", onBeforeShow);
1942 menu.on("show", onShow);
1944 if(g && menu.events["checkchange"]){
1948 groups[g].push(menu);
1949 menu.on("checkchange", onCheck);
1954 * Returns a {@link Roo.menu.Menu} object
1955 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1956 * be used to generate and return a new Menu instance.
1958 get : function(menu){
1959 if(typeof menu == "string"){ // menu id
1961 }else if(menu.events){ // menu instance
1964 /*else if(typeof menu.length == 'number'){ // array of menu items?
1965 return new Roo.bootstrap.Menu({items:menu});
1966 }else{ // otherwise, must be a config
1967 return new Roo.bootstrap.Menu(menu);
1974 unregister : function(menu){
1975 delete menus[menu.id];
1976 menu.un("beforehide", onBeforeHide);
1977 menu.un("hide", onHide);
1978 menu.un("beforeshow", onBeforeShow);
1979 menu.un("show", onShow);
1981 if(g && menu.events["checkchange"]){
1982 groups[g].remove(menu);
1983 menu.un("checkchange", onCheck);
1988 registerCheckable : function(menuItem){
1989 var g = menuItem.group;
1994 groups[g].push(menuItem);
1995 menuItem.on("beforecheckchange", onBeforeCheck);
2000 unregisterCheckable : function(menuItem){
2001 var g = menuItem.group;
2003 groups[g].remove(menuItem);
2004 menuItem.un("beforecheckchange", onBeforeCheck);
2016 * @class Roo.bootstrap.Menu
2017 * @extends Roo.bootstrap.Component
2018 * Bootstrap Menu class - container for MenuItems
2019 * @cfg {String} type (dropdown|treeview|submenu) type of menu
2020 * @cfg {bool} hidden if the menu should be hidden when rendered.
2021 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
2022 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
2026 * @param {Object} config The config object
2030 Roo.bootstrap.Menu = function(config){
2031 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2032 if (this.registerMenu && this.type != 'treeview') {
2033 Roo.bootstrap.MenuMgr.register(this);
2040 * Fires before this menu is displayed
2041 * @param {Roo.menu.Menu} this
2046 * Fires before this menu is hidden
2047 * @param {Roo.menu.Menu} this
2052 * Fires after this menu is displayed
2053 * @param {Roo.menu.Menu} this
2058 * Fires after this menu is hidden
2059 * @param {Roo.menu.Menu} this
2064 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2065 * @param {Roo.menu.Menu} this
2066 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2067 * @param {Roo.EventObject} e
2072 * Fires when the mouse is hovering over this menu
2073 * @param {Roo.menu.Menu} this
2074 * @param {Roo.EventObject} e
2075 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2080 * Fires when the mouse exits this menu
2081 * @param {Roo.menu.Menu} this
2082 * @param {Roo.EventObject} e
2083 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2088 * Fires when a menu item contained in this menu is clicked
2089 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2090 * @param {Roo.EventObject} e
2094 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2097 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2101 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2104 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2106 registerMenu : true,
2108 menuItems :false, // stores the menu items..
2118 getChildContainer : function() {
2122 getAutoCreate : function(){
2124 //if (['right'].indexOf(this.align)!==-1) {
2125 // cfg.cn[1].cls += ' pull-right'
2131 cls : 'dropdown-menu' ,
2132 style : 'z-index:1000'
2136 if (this.type === 'submenu') {
2137 cfg.cls = 'submenu active';
2139 if (this.type === 'treeview') {
2140 cfg.cls = 'treeview-menu';
2145 initEvents : function() {
2147 // Roo.log("ADD event");
2148 // Roo.log(this.triggerEl.dom);
2150 this.triggerEl.on('click', this.onTriggerClick, this);
2152 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2155 if (this.triggerEl.hasClass('nav-item')) {
2156 // dropdown toggle on the 'a' in BS4?
2157 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2159 this.triggerEl.addClass('dropdown-toggle');
2162 this.el.on('touchstart' , this.onTouch, this);
2164 this.el.on('click' , this.onClick, this);
2166 this.el.on("mouseover", this.onMouseOver, this);
2167 this.el.on("mouseout", this.onMouseOut, this);
2171 findTargetItem : function(e)
2173 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2177 //Roo.log(t); Roo.log(t.id);
2179 //Roo.log(this.menuitems);
2180 return this.menuitems.get(t.id);
2182 //return this.items.get(t.menuItemId);
2188 onTouch : function(e)
2190 Roo.log("menu.onTouch");
2191 //e.stopEvent(); this make the user popdown broken
2195 onClick : function(e)
2197 Roo.log("menu.onClick");
2199 var t = this.findTargetItem(e);
2200 if(!t || t.isContainer){
2205 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2206 if(t == this.activeItem && t.shouldDeactivate(e)){
2207 this.activeItem.deactivate();
2208 delete this.activeItem;
2212 this.setActiveItem(t, true);
2220 Roo.log('pass click event');
2224 this.fireEvent("click", this, t, e);
2228 if(!t.href.length || t.href == '#'){
2229 (function() { _this.hide(); }).defer(100);
2234 onMouseOver : function(e){
2235 var t = this.findTargetItem(e);
2238 // if(t.canActivate && !t.disabled){
2239 // this.setActiveItem(t, true);
2243 this.fireEvent("mouseover", this, e, t);
2245 isVisible : function(){
2246 return !this.hidden;
2248 onMouseOut : function(e){
2249 var t = this.findTargetItem(e);
2252 // if(t == this.activeItem && t.shouldDeactivate(e)){
2253 // this.activeItem.deactivate();
2254 // delete this.activeItem;
2257 this.fireEvent("mouseout", this, e, t);
2262 * Displays this menu relative to another element
2263 * @param {String/HTMLElement/Roo.Element} element The element to align to
2264 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2265 * the element (defaults to this.defaultAlign)
2266 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2268 show : function(el, pos, parentMenu){
2269 this.parentMenu = parentMenu;
2273 this.fireEvent("beforeshow", this);
2274 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2277 * Displays this menu at a specific xy position
2278 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2279 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2281 showAt : function(xy, parentMenu, /* private: */_e){
2282 this.parentMenu = parentMenu;
2287 this.fireEvent("beforeshow", this);
2288 //xy = this.el.adjustForConstraints(xy);
2292 this.hideMenuItems();
2293 this.hidden = false;
2294 this.triggerEl.addClass('open');
2295 this.el.addClass('show');
2297 // reassign x when hitting right
2298 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2299 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2302 // reassign y when hitting bottom
2303 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2304 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2307 // but the list may align on trigger left or trigger top... should it be a properity?
2309 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2314 this.fireEvent("show", this);
2320 this.doFocus.defer(50, this);
2324 doFocus : function(){
2326 this.focusEl.focus();
2331 * Hides this menu and optionally all parent menus
2332 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2334 hide : function(deep)
2337 this.hideMenuItems();
2338 if(this.el && this.isVisible()){
2339 this.fireEvent("beforehide", this);
2340 if(this.activeItem){
2341 this.activeItem.deactivate();
2342 this.activeItem = null;
2344 this.triggerEl.removeClass('open');;
2345 this.el.removeClass('show');
2347 this.fireEvent("hide", this);
2349 if(deep === true && this.parentMenu){
2350 this.parentMenu.hide(true);
2354 onTriggerClick : function(e)
2356 Roo.log('trigger click');
2358 var target = e.getTarget();
2360 Roo.log(target.nodeName.toLowerCase());
2362 if(target.nodeName.toLowerCase() === 'i'){
2368 onTriggerPress : function(e)
2370 Roo.log('trigger press');
2371 //Roo.log(e.getTarget());
2372 // Roo.log(this.triggerEl.dom);
2374 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2375 var pel = Roo.get(e.getTarget());
2376 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2377 Roo.log('is treeview or dropdown?');
2381 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2385 if (this.isVisible()) {
2390 this.show(this.triggerEl, false, false);
2393 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2400 hideMenuItems : function()
2402 Roo.log("hide Menu Items");
2406 //$(backdrop).remove()
2407 this.el.select('.open',true).each(function(aa) {
2409 aa.removeClass('open');
2410 //var parent = getParent($(this))
2411 //var relatedTarget = { relatedTarget: this }
2413 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2414 //if (e.isDefaultPrevented()) return
2415 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2418 addxtypeChild : function (tree, cntr) {
2419 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2421 this.menuitems.add(comp);
2433 this.getEl().dom.innerHTML = '';
2434 this.menuitems.clear();
2448 * @class Roo.bootstrap.MenuItem
2449 * @extends Roo.bootstrap.Component
2450 * Bootstrap MenuItem class
2451 * @cfg {String} html the menu label
2452 * @cfg {String} href the link
2453 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2454 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2455 * @cfg {Boolean} active used on sidebars to highlight active itesm
2456 * @cfg {String} fa favicon to show on left of menu item.
2457 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2461 * Create a new MenuItem
2462 * @param {Object} config The config object
2466 Roo.bootstrap.MenuItem = function(config){
2467 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2472 * The raw click event for the entire grid.
2473 * @param {Roo.bootstrap.MenuItem} this
2474 * @param {Roo.EventObject} e
2480 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2484 preventDefault: false,
2485 isContainer : false,
2489 getAutoCreate : function(){
2491 if(this.isContainer){
2494 cls: 'dropdown-menu-item dropdown-item'
2508 if (this.fa !== false) {
2511 cls : 'fa fa-' + this.fa
2520 cls: 'dropdown-menu-item dropdown-item',
2523 if (this.parent().type == 'treeview') {
2524 cfg.cls = 'treeview-menu';
2527 cfg.cls += ' active';
2532 anc.href = this.href || cfg.cn[0].href ;
2533 ctag.html = this.html || cfg.cn[0].html ;
2537 initEvents: function()
2539 if (this.parent().type == 'treeview') {
2540 this.el.select('a').on('click', this.onClick, this);
2544 this.menu.parentType = this.xtype;
2545 this.menu.triggerEl = this.el;
2546 this.menu = this.addxtype(Roo.apply({}, this.menu));
2550 onClick : function(e)
2552 Roo.log('item on click ');
2554 if(this.preventDefault){
2557 //this.parent().hideMenuItems();
2559 this.fireEvent('click', this, e);
2578 * @class Roo.bootstrap.MenuSeparator
2579 * @extends Roo.bootstrap.Component
2580 * Bootstrap MenuSeparator class
2583 * Create a new MenuItem
2584 * @param {Object} config The config object
2588 Roo.bootstrap.MenuSeparator = function(config){
2589 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2592 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2594 getAutoCreate : function(){
2613 * @class Roo.bootstrap.Modal
2614 * @extends Roo.bootstrap.Component
2615 * Bootstrap Modal class
2616 * @cfg {String} title Title of dialog
2617 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2618 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2619 * @cfg {Boolean} specificTitle default false
2620 * @cfg {Array} buttons Array of buttons or standard button set..
2621 * @cfg {String} buttonPosition (left|right|center) default right
2622 * @cfg {Boolean} animate default true
2623 * @cfg {Boolean} allow_close default true
2624 * @cfg {Boolean} fitwindow default false
2625 * @cfg {String} size (sm|lg) default empty
2626 * @cfg {Number} max_width set the max width of modal
2630 * Create a new Modal Dialog
2631 * @param {Object} config The config object
2634 Roo.bootstrap.Modal = function(config){
2635 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2640 * The raw btnclick event for the button
2641 * @param {Roo.EventObject} e
2646 * Fire when dialog resize
2647 * @param {Roo.bootstrap.Modal} this
2648 * @param {Roo.EventObject} e
2652 this.buttons = this.buttons || [];
2655 this.tmpl = Roo.factory(this.tmpl);
2660 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2662 title : 'test dialog',
2672 specificTitle: false,
2674 buttonPosition: 'right',
2697 onRender : function(ct, position)
2699 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2702 var cfg = Roo.apply({}, this.getAutoCreate());
2705 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2707 //if (!cfg.name.length) {
2711 cfg.cls += ' ' + this.cls;
2714 cfg.style = this.style;
2716 this.el = Roo.get(document.body).createChild(cfg, position);
2718 //var type = this.el.dom.type;
2721 if(this.tabIndex !== undefined){
2722 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2725 this.dialogEl = this.el.select('.modal-dialog',true).first();
2726 this.bodyEl = this.el.select('.modal-body',true).first();
2727 this.closeEl = this.el.select('.modal-header .close', true).first();
2728 this.headerEl = this.el.select('.modal-header',true).first();
2729 this.titleEl = this.el.select('.modal-title',true).first();
2730 this.footerEl = this.el.select('.modal-footer',true).first();
2732 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2734 //this.el.addClass("x-dlg-modal");
2736 if (this.buttons.length) {
2737 Roo.each(this.buttons, function(bb) {
2738 var b = Roo.apply({}, bb);
2739 b.xns = b.xns || Roo.bootstrap;
2740 b.xtype = b.xtype || 'Button';
2741 if (typeof(b.listeners) == 'undefined') {
2742 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2745 var btn = Roo.factory(b);
2747 btn.render(this.el.select('.modal-footer div').first());
2751 // render the children.
2754 if(typeof(this.items) != 'undefined'){
2755 var items = this.items;
2758 for(var i =0;i < items.length;i++) {
2759 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2763 this.items = nitems;
2765 // where are these used - they used to be body/close/footer
2769 //this.el.addClass([this.fieldClass, this.cls]);
2773 getAutoCreate : function()
2777 html : this.html || ''
2782 cls : 'modal-title',
2786 if(this.specificTitle){
2792 if (this.allow_close && Roo.bootstrap.version == 3) {
2802 if (this.allow_close && Roo.bootstrap.version == 4) {
2812 if(this.size.length){
2813 size = 'modal-' + this.size;
2820 cls: "modal-dialog " + size,
2823 cls : "modal-content",
2826 cls : 'modal-header',
2831 cls : 'modal-footer',
2835 cls: 'btn-' + this.buttonPosition
2852 modal.cls += ' fade';
2858 getChildContainer : function() {
2863 getButtonContainer : function() {
2864 return this.el.select('.modal-footer div',true).first();
2867 initEvents : function()
2869 if (this.allow_close) {
2870 this.closeEl.on('click', this.hide, this);
2872 Roo.EventManager.onWindowResize(this.resize, this, true);
2879 this.maskEl.setSize(
2880 Roo.lib.Dom.getViewWidth(true),
2881 Roo.lib.Dom.getViewHeight(true)
2884 if (this.fitwindow) {
2886 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2887 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2892 if(this.max_width !== 0) {
2894 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2897 this.setSize(w, this.height);
2901 if(this.max_height) {
2902 this.setSize(w,Math.min(
2904 Roo.lib.Dom.getViewportHeight(true) - 60
2910 if(!this.fit_content) {
2911 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2915 this.setSize(w, Math.min(
2917 this.headerEl.getHeight() +
2918 this.footerEl.getHeight() +
2919 this.getChildHeight(this.bodyEl.dom.childNodes),
2920 Roo.lib.Dom.getViewportHeight(true) - 60)
2926 setSize : function(w,h)
2937 if (!this.rendered) {
2941 //this.el.setStyle('display', 'block');
2942 this.el.removeClass('hideing');
2943 this.el.dom.style.display='block';
2945 Roo.get(document.body).addClass('modal-open');
2947 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2950 this.el.addClass('show');
2951 this.el.addClass('in');
2954 this.el.addClass('show');
2955 this.el.addClass('in');
2958 // not sure how we can show data in here..
2960 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2963 Roo.get(document.body).addClass("x-body-masked");
2965 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2966 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2967 this.maskEl.dom.style.display = 'block';
2968 this.maskEl.addClass('show');
2973 this.fireEvent('show', this);
2975 // set zindex here - otherwise it appears to be ignored...
2976 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2979 this.items.forEach( function(e) {
2980 e.layout ? e.layout() : false;
2988 if(this.fireEvent("beforehide", this) !== false){
2990 this.maskEl.removeClass('show');
2992 this.maskEl.dom.style.display = '';
2993 Roo.get(document.body).removeClass("x-body-masked");
2994 this.el.removeClass('in');
2995 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2997 if(this.animate){ // why
2998 this.el.addClass('hideing');
2999 this.el.removeClass('show');
3001 if (!this.el.hasClass('hideing')) {
3002 return; // it's been shown again...
3005 this.el.dom.style.display='';
3007 Roo.get(document.body).removeClass('modal-open');
3008 this.el.removeClass('hideing');
3012 this.el.removeClass('show');
3013 this.el.dom.style.display='';
3014 Roo.get(document.body).removeClass('modal-open');
3017 this.fireEvent('hide', this);
3020 isVisible : function()
3023 return this.el.hasClass('show') && !this.el.hasClass('hideing');
3027 addButton : function(str, cb)
3031 var b = Roo.apply({}, { html : str } );
3032 b.xns = b.xns || Roo.bootstrap;
3033 b.xtype = b.xtype || 'Button';
3034 if (typeof(b.listeners) == 'undefined') {
3035 b.listeners = { click : cb.createDelegate(this) };
3038 var btn = Roo.factory(b);
3040 btn.render(this.el.select('.modal-footer div').first());
3046 setDefaultButton : function(btn)
3048 //this.el.select('.modal-footer').()
3052 resizeTo: function(w,h)
3056 this.dialogEl.setWidth(w);
3057 if (this.diff === false) {
3058 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3061 this.bodyEl.setHeight(h - this.diff);
3063 this.fireEvent('resize', this);
3066 setContentSize : function(w, h)
3070 onButtonClick: function(btn,e)
3073 this.fireEvent('btnclick', btn.name, e);
3076 * Set the title of the Dialog
3077 * @param {String} str new Title
3079 setTitle: function(str) {
3080 this.titleEl.dom.innerHTML = str;
3083 * Set the body of the Dialog
3084 * @param {String} str new Title
3086 setBody: function(str) {
3087 this.bodyEl.dom.innerHTML = str;
3090 * Set the body of the Dialog using the template
3091 * @param {Obj} data - apply this data to the template and replace the body contents.
3093 applyBody: function(obj)
3096 Roo.log("Error - using apply Body without a template");
3099 this.tmpl.overwrite(this.bodyEl, obj);
3102 getChildHeight : function(child_nodes)
3106 child_nodes.length == 0
3111 var child_height = 0;
3113 for(var i = 0; i < child_nodes.length; i++) {
3116 * for modal with tabs...
3117 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3119 var layout_childs = child_nodes[i].childNodes;
3121 for(var j = 0; j < layout_childs.length; j++) {
3123 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3125 var layout_body_childs = layout_childs[j].childNodes;
3127 for(var k = 0; k < layout_body_childs.length; k++) {
3129 if(layout_body_childs[k].classList.contains('navbar')) {
3130 child_height += layout_body_childs[k].offsetHeight;
3134 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3136 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3138 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3140 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3141 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3156 child_height += child_nodes[i].offsetHeight;
3157 // Roo.log(child_nodes[i].offsetHeight);
3160 return child_height;
3166 Roo.apply(Roo.bootstrap.Modal, {
3168 * Button config that displays a single OK button
3177 * Button config that displays Yes and No buttons
3193 * Button config that displays OK and Cancel buttons
3208 * Button config that displays Yes, No and Cancel buttons
3232 * messagebox - can be used as a replace
3236 * @class Roo.MessageBox
3237 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3241 Roo.Msg.alert('Status', 'Changes saved successfully.');
3243 // Prompt for user data:
3244 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3246 // process text value...
3250 // Show a dialog using config options:
3252 title:'Save Changes?',
3253 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3254 buttons: Roo.Msg.YESNOCANCEL,
3261 Roo.bootstrap.MessageBox = function(){
3262 var dlg, opt, mask, waitTimer;
3263 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3264 var buttons, activeTextEl, bwidth;
3268 var handleButton = function(button){
3270 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3274 var handleHide = function(){
3276 dlg.el.removeClass(opt.cls);
3279 // Roo.TaskMgr.stop(waitTimer);
3280 // waitTimer = null;
3285 var updateButtons = function(b){
3288 buttons["ok"].hide();
3289 buttons["cancel"].hide();
3290 buttons["yes"].hide();
3291 buttons["no"].hide();
3292 //dlg.footer.dom.style.display = 'none';
3295 dlg.footerEl.dom.style.display = '';
3296 for(var k in buttons){
3297 if(typeof buttons[k] != "function"){
3300 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3301 width += buttons[k].el.getWidth()+15;
3311 var handleEsc = function(d, k, e){
3312 if(opt && opt.closable !== false){
3322 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3323 * @return {Roo.BasicDialog} The BasicDialog element
3325 getDialog : function(){
3327 dlg = new Roo.bootstrap.Modal( {
3330 //constraintoviewport:false,
3332 //collapsible : false,
3337 //buttonAlign:"center",
3338 closeClick : function(){
3339 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3342 handleButton("cancel");
3347 dlg.on("hide", handleHide);
3349 //dlg.addKeyListener(27, handleEsc);
3351 this.buttons = buttons;
3352 var bt = this.buttonText;
3353 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3354 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3355 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3356 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3358 bodyEl = dlg.bodyEl.createChild({
3360 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3361 '<textarea class="roo-mb-textarea"></textarea>' +
3362 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3364 msgEl = bodyEl.dom.firstChild;
3365 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3366 textboxEl.enableDisplayMode();
3367 textboxEl.addKeyListener([10,13], function(){
3368 if(dlg.isVisible() && opt && opt.buttons){
3371 }else if(opt.buttons.yes){
3372 handleButton("yes");
3376 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3377 textareaEl.enableDisplayMode();
3378 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3379 progressEl.enableDisplayMode();
3381 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3382 var pf = progressEl.dom.firstChild;
3384 pp = Roo.get(pf.firstChild);
3385 pp.setHeight(pf.offsetHeight);
3393 * Updates the message box body text
3394 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3395 * the XHTML-compliant non-breaking space character '&#160;')
3396 * @return {Roo.MessageBox} This message box
3398 updateText : function(text)
3400 if(!dlg.isVisible() && !opt.width){
3401 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3402 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3404 msgEl.innerHTML = text || ' ';
3406 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3407 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3409 Math.min(opt.width || cw , this.maxWidth),
3410 Math.max(opt.minWidth || this.minWidth, bwidth)
3413 activeTextEl.setWidth(w);
3415 if(dlg.isVisible()){
3416 dlg.fixedcenter = false;
3418 // to big, make it scroll. = But as usual stupid IE does not support
3421 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3422 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3423 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3425 bodyEl.dom.style.height = '';
3426 bodyEl.dom.style.overflowY = '';
3429 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3431 bodyEl.dom.style.overflowX = '';
3434 dlg.setContentSize(w, bodyEl.getHeight());
3435 if(dlg.isVisible()){
3436 dlg.fixedcenter = true;
3442 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3443 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3444 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3445 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3446 * @return {Roo.MessageBox} This message box
3448 updateProgress : function(value, text){
3450 this.updateText(text);
3453 if (pp) { // weird bug on my firefox - for some reason this is not defined
3454 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3455 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3461 * Returns true if the message box is currently displayed
3462 * @return {Boolean} True if the message box is visible, else false
3464 isVisible : function(){
3465 return dlg && dlg.isVisible();
3469 * Hides the message box if it is displayed
3472 if(this.isVisible()){
3478 * Displays a new message box, or reinitializes an existing message box, based on the config options
3479 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3480 * The following config object properties are supported:
3482 Property Type Description
3483 ---------- --------------- ------------------------------------------------------------------------------------
3484 animEl String/Element An id or Element from which the message box should animate as it opens and
3485 closes (defaults to undefined)
3486 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3487 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3488 closable Boolean False to hide the top-right close button (defaults to true). Note that
3489 progress and wait dialogs will ignore this property and always hide the
3490 close button as they can only be closed programmatically.
3491 cls String A custom CSS class to apply to the message box element
3492 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3493 displayed (defaults to 75)
3494 fn Function A callback function to execute after closing the dialog. The arguments to the
3495 function will be btn (the name of the button that was clicked, if applicable,
3496 e.g. "ok"), and text (the value of the active text field, if applicable).
3497 Progress and wait dialogs will ignore this option since they do not respond to
3498 user actions and can only be closed programmatically, so any required function
3499 should be called by the same code after it closes the dialog.
3500 icon String A CSS class that provides a background image to be used as an icon for
3501 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3502 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3503 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3504 modal Boolean False to allow user interaction with the page while the message box is
3505 displayed (defaults to true)
3506 msg String A string that will replace the existing message box body text (defaults
3507 to the XHTML-compliant non-breaking space character ' ')
3508 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3509 progress Boolean True to display a progress bar (defaults to false)
3510 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3511 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3512 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3513 title String The title text
3514 value String The string value to set into the active textbox element if displayed
3515 wait Boolean True to display a progress bar (defaults to false)
3516 width Number The width of the dialog in pixels
3523 msg: 'Please enter your address:',
3525 buttons: Roo.MessageBox.OKCANCEL,
3528 animEl: 'addAddressBtn'
3531 * @param {Object} config Configuration options
3532 * @return {Roo.MessageBox} This message box
3534 show : function(options)
3537 // this causes nightmares if you show one dialog after another
3538 // especially on callbacks..
3540 if(this.isVisible()){
3543 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3544 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3545 Roo.log("New Dialog Message:" + options.msg )
3546 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3547 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3550 var d = this.getDialog();
3552 d.setTitle(opt.title || " ");
3553 d.closeEl.setDisplayed(opt.closable !== false);
3554 activeTextEl = textboxEl;
3555 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3560 textareaEl.setHeight(typeof opt.multiline == "number" ?
3561 opt.multiline : this.defaultTextHeight);
3562 activeTextEl = textareaEl;
3571 progressEl.setDisplayed(opt.progress === true);
3572 this.updateProgress(0);
3573 activeTextEl.dom.value = opt.value || "";
3575 dlg.setDefaultButton(activeTextEl);
3577 var bs = opt.buttons;
3581 }else if(bs && bs.yes){
3582 db = buttons["yes"];
3584 dlg.setDefaultButton(db);
3586 bwidth = updateButtons(opt.buttons);
3587 this.updateText(opt.msg);
3589 d.el.addClass(opt.cls);
3591 d.proxyDrag = opt.proxyDrag === true;
3592 d.modal = opt.modal !== false;
3593 d.mask = opt.modal !== false ? mask : false;
3595 // force it to the end of the z-index stack so it gets a cursor in FF
3596 document.body.appendChild(dlg.el.dom);
3597 d.animateTarget = null;
3598 d.show(options.animEl);
3604 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3605 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3606 * and closing the message box when the process is complete.
3607 * @param {String} title The title bar text
3608 * @param {String} msg The message box body text
3609 * @return {Roo.MessageBox} This message box
3611 progress : function(title, msg){
3618 minWidth: this.minProgressWidth,
3625 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3626 * If a callback function is passed it will be called after the user clicks the button, and the
3627 * id of the button that was clicked will be passed as the only parameter to the callback
3628 * (could also be the top-right close button).
3629 * @param {String} title The title bar text
3630 * @param {String} msg The message box body text
3631 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3632 * @param {Object} scope (optional) The scope of the callback function
3633 * @return {Roo.MessageBox} This message box
3635 alert : function(title, msg, fn, scope)
3650 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3651 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3652 * You are responsible for closing the message box when the process is complete.
3653 * @param {String} msg The message box body text
3654 * @param {String} title (optional) The title bar text
3655 * @return {Roo.MessageBox} This message box
3657 wait : function(msg, title){
3668 waitTimer = Roo.TaskMgr.start({
3670 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3678 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3679 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3680 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3681 * @param {String} title The title bar text
3682 * @param {String} msg The message box body text
3683 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3684 * @param {Object} scope (optional) The scope of the callback function
3685 * @return {Roo.MessageBox} This message box
3687 confirm : function(title, msg, fn, scope){
3691 buttons: this.YESNO,
3700 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3701 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3702 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3703 * (could also be the top-right close button) and the text that was entered will be passed as the two
3704 * parameters to the callback.
3705 * @param {String} title The title bar text
3706 * @param {String} msg The message box body text
3707 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3708 * @param {Object} scope (optional) The scope of the callback function
3709 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3710 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3711 * @return {Roo.MessageBox} This message box
3713 prompt : function(title, msg, fn, scope, multiline){
3717 buttons: this.OKCANCEL,
3722 multiline: multiline,
3729 * Button config that displays a single OK button
3734 * Button config that displays Yes and No buttons
3737 YESNO : {yes:true, no:true},
3739 * Button config that displays OK and Cancel buttons
3742 OKCANCEL : {ok:true, cancel:true},
3744 * Button config that displays Yes, No and Cancel buttons
3747 YESNOCANCEL : {yes:true, no:true, cancel:true},
3750 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3753 defaultTextHeight : 75,
3755 * The maximum width in pixels of the message box (defaults to 600)
3760 * The minimum width in pixels of the message box (defaults to 100)
3765 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3766 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3769 minProgressWidth : 250,
3771 * An object containing the default button text strings that can be overriden for localized language support.
3772 * Supported properties are: ok, cancel, yes and no.
3773 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3786 * Shorthand for {@link Roo.MessageBox}
3788 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3789 Roo.Msg = Roo.Msg || Roo.MessageBox;
3798 * @class Roo.bootstrap.Navbar
3799 * @extends Roo.bootstrap.Component
3800 * Bootstrap Navbar class
3803 * Create a new Navbar
3804 * @param {Object} config The config object
3808 Roo.bootstrap.Navbar = function(config){
3809 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3813 * @event beforetoggle
3814 * Fire before toggle the menu
3815 * @param {Roo.EventObject} e
3817 "beforetoggle" : true
3821 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3830 getAutoCreate : function(){
3833 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3837 initEvents :function ()
3839 //Roo.log(this.el.select('.navbar-toggle',true));
3840 this.el.select('.navbar-toggle',true).on('click', function() {
3841 if(this.fireEvent('beforetoggle', this) !== false){
3842 this.el.select('.navbar-collapse',true).toggleClass('in');
3852 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3854 var size = this.el.getSize();
3855 this.maskEl.setSize(size.width, size.height);
3856 this.maskEl.enableDisplayMode("block");
3865 getChildContainer : function()
3867 if (this.el.select('.collapse').getCount()) {
3868 return this.el.select('.collapse',true).first();
3901 * @class Roo.bootstrap.NavSimplebar
3902 * @extends Roo.bootstrap.Navbar
3903 * Bootstrap Sidebar class
3905 * @cfg {Boolean} inverse is inverted color
3907 * @cfg {String} type (nav | pills | tabs)
3908 * @cfg {Boolean} arrangement stacked | justified
3909 * @cfg {String} align (left | right) alignment
3911 * @cfg {Boolean} main (true|false) main nav bar? default false
3912 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3914 * @cfg {String} tag (header|footer|nav|div) default is nav
3916 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3920 * Create a new Sidebar
3921 * @param {Object} config The config object
3925 Roo.bootstrap.NavSimplebar = function(config){
3926 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3929 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3945 getAutoCreate : function(){
3949 tag : this.tag || 'div',
3950 cls : 'navbar navbar-expand-lg'
3952 if (['light','white'].indexOf(this.weight) > -1) {
3953 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
3955 cfg.cls += ' bg-' + this.weight;
3967 this.type = this.type || 'nav';
3968 if (['tabs','pills'].indexOf(this.type)!==-1) {
3969 cfg.cn[0].cls += ' nav-' + this.type
3973 if (this.type!=='nav') {
3974 Roo.log('nav type must be nav/tabs/pills')
3976 cfg.cn[0].cls += ' navbar-nav'
3982 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3983 cfg.cn[0].cls += ' nav-' + this.arrangement;
3987 if (this.align === 'right') {
3988 cfg.cn[0].cls += ' navbar-right';
3992 cfg.cls += ' navbar-inverse';
4016 * navbar-expand-md fixed-top
4020 * @class Roo.bootstrap.NavHeaderbar
4021 * @extends Roo.bootstrap.NavSimplebar
4022 * Bootstrap Sidebar class
4024 * @cfg {String} brand what is brand
4025 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4026 * @cfg {String} brand_href href of the brand
4027 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4028 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4029 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4030 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4033 * Create a new Sidebar
4034 * @param {Object} config The config object
4038 Roo.bootstrap.NavHeaderbar = function(config){
4039 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4043 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4050 desktopCenter : false,
4053 getAutoCreate : function(){
4056 tag: this.nav || 'nav',
4057 cls: 'navbar navbar-expand-md',
4063 if (this.desktopCenter) {
4064 cn.push({cls : 'container', cn : []});
4071 cls: 'navbar-header',
4076 cls: 'navbar-toggle navbar-toggler',
4077 'data-toggle': 'collapse',
4082 html: 'Toggle navigation'
4086 cls: 'icon-bar navbar-toggler-icon'
4104 cls: 'collapse navbar-collapse',
4108 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4110 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4111 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4113 // tag can override this..
4115 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4118 if (this.brand !== '') {
4121 href: this.brand_href ? this.brand_href : '#',
4122 cls: 'navbar-brand',
4130 cfg.cls += ' main-nav';
4138 getHeaderChildContainer : function()
4140 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4141 return this.el.select('.navbar-header',true).first();
4144 return this.getChildContainer();
4148 initEvents : function()
4150 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4152 if (this.autohide) {
4157 Roo.get(document).on('scroll',function(e) {
4158 var ns = Roo.get(document).getScroll().top;
4159 var os = prevScroll;
4163 ft.removeClass('slideDown');
4164 ft.addClass('slideUp');
4167 ft.removeClass('slideUp');
4168 ft.addClass('slideDown');
4189 * @class Roo.bootstrap.NavSidebar
4190 * @extends Roo.bootstrap.Navbar
4191 * Bootstrap Sidebar class
4194 * Create a new Sidebar
4195 * @param {Object} config The config object
4199 Roo.bootstrap.NavSidebar = function(config){
4200 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4203 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4205 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4207 getAutoCreate : function(){
4212 cls: 'sidebar sidebar-nav'
4234 * @class Roo.bootstrap.NavGroup
4235 * @extends Roo.bootstrap.Component
4236 * Bootstrap NavGroup class
4237 * @cfg {String} align (left|right)
4238 * @cfg {Boolean} inverse
4239 * @cfg {String} type (nav|pills|tab) default nav
4240 * @cfg {String} navId - reference Id for navbar.
4244 * Create a new nav group
4245 * @param {Object} config The config object
4248 Roo.bootstrap.NavGroup = function(config){
4249 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4252 Roo.bootstrap.NavGroup.register(this);
4256 * Fires when the active item changes
4257 * @param {Roo.bootstrap.NavGroup} this
4258 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4259 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4266 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4277 getAutoCreate : function()
4279 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4286 if (['tabs','pills'].indexOf(this.type)!==-1) {
4287 cfg.cls += ' nav-' + this.type
4289 if (this.type!=='nav') {
4290 Roo.log('nav type must be nav/tabs/pills')
4292 cfg.cls += ' navbar-nav'
4295 if (this.parent() && this.parent().sidebar) {
4298 cls: 'dashboard-menu sidebar-menu'
4304 if (this.form === true) {
4310 if (this.align === 'right') {
4311 cfg.cls += ' navbar-right ml-md-auto';
4313 cfg.cls += ' navbar-left';
4317 if (this.align === 'right') {
4318 cfg.cls += ' navbar-right ml-md-auto';
4320 cfg.cls += ' mr-auto';
4324 cfg.cls += ' navbar-inverse';
4332 * sets the active Navigation item
4333 * @param {Roo.bootstrap.NavItem} the new current navitem
4335 setActiveItem : function(item)
4338 Roo.each(this.navItems, function(v){
4343 v.setActive(false, true);
4350 item.setActive(true, true);
4351 this.fireEvent('changed', this, item, prev);
4356 * gets the active Navigation item
4357 * @return {Roo.bootstrap.NavItem} the current navitem
4359 getActive : function()
4363 Roo.each(this.navItems, function(v){
4374 indexOfNav : function()
4378 Roo.each(this.navItems, function(v,i){
4389 * adds a Navigation item
4390 * @param {Roo.bootstrap.NavItem} the navitem to add
4392 addItem : function(cfg)
4394 var cn = new Roo.bootstrap.NavItem(cfg);
4396 cn.parentId = this.id;
4397 cn.onRender(this.el, null);
4401 * register a Navigation item
4402 * @param {Roo.bootstrap.NavItem} the navitem to add
4404 register : function(item)
4406 this.navItems.push( item);
4407 item.navId = this.navId;
4412 * clear all the Navigation item
4415 clearAll : function()
4418 this.el.dom.innerHTML = '';
4421 getNavItem: function(tabId)
4424 Roo.each(this.navItems, function(e) {
4425 if (e.tabId == tabId) {
4435 setActiveNext : function()
4437 var i = this.indexOfNav(this.getActive());
4438 if (i > this.navItems.length) {
4441 this.setActiveItem(this.navItems[i+1]);
4443 setActivePrev : function()
4445 var i = this.indexOfNav(this.getActive());
4449 this.setActiveItem(this.navItems[i-1]);
4451 clearWasActive : function(except) {
4452 Roo.each(this.navItems, function(e) {
4453 if (e.tabId != except.tabId && e.was_active) {
4454 e.was_active = false;
4461 getWasActive : function ()
4464 Roo.each(this.navItems, function(e) {
4479 Roo.apply(Roo.bootstrap.NavGroup, {
4483 * register a Navigation Group
4484 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4486 register : function(navgrp)
4488 this.groups[navgrp.navId] = navgrp;
4492 * fetch a Navigation Group based on the navigation ID
4493 * @param {string} the navgroup to add
4494 * @returns {Roo.bootstrap.NavGroup} the navgroup
4496 get: function(navId) {
4497 if (typeof(this.groups[navId]) == 'undefined') {
4499 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4501 return this.groups[navId] ;
4516 * @class Roo.bootstrap.NavItem
4517 * @extends Roo.bootstrap.Component
4518 * Bootstrap Navbar.NavItem class
4519 * @cfg {String} href link to
4520 * @cfg {String} html content of button
4521 * @cfg {String} badge text inside badge
4522 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4523 * @cfg {String} glyphicon name of glyphicon
4524 * @cfg {String} icon name of font awesome icon
4525 * @cfg {Boolean} active Is item active
4526 * @cfg {Boolean} disabled Is item disabled
4528 * @cfg {Boolean} preventDefault (true | false) default false
4529 * @cfg {String} tabId the tab that this item activates.
4530 * @cfg {String} tagtype (a|span) render as a href or span?
4531 * @cfg {Boolean} animateRef (true|false) link to element default false
4534 * Create a new Navbar Item
4535 * @param {Object} config The config object
4537 Roo.bootstrap.NavItem = function(config){
4538 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4543 * The raw click event for the entire grid.
4544 * @param {Roo.EventObject} e
4549 * Fires when the active item active state changes
4550 * @param {Roo.bootstrap.NavItem} this
4551 * @param {boolean} state the new state
4557 * Fires when scroll to element
4558 * @param {Roo.bootstrap.NavItem} this
4559 * @param {Object} options
4560 * @param {Roo.EventObject} e
4568 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4576 preventDefault : false,
4583 getAutoCreate : function(){
4592 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4594 if (this.disabled) {
4595 cfg.cls += ' disabled';
4598 if (this.href || this.html || this.glyphicon || this.icon) {
4602 href : this.href || "#",
4603 html: this.html || ''
4606 if (this.tagtype == 'a') {
4607 cfg.cn[0].cls = 'nav-link';
4610 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4613 if(this.glyphicon) {
4614 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4619 cfg.cn[0].html += " <span class='caret'></span>";
4623 if (this.badge !== '') {
4625 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4633 initEvents: function()
4635 if (typeof (this.menu) != 'undefined') {
4636 this.menu.parentType = this.xtype;
4637 this.menu.triggerEl = this.el;
4638 this.menu = this.addxtype(Roo.apply({}, this.menu));
4641 this.el.select('a',true).on('click', this.onClick, this);
4643 if(this.tagtype == 'span'){
4644 this.el.select('span',true).on('click', this.onClick, this);
4647 // at this point parent should be available..
4648 this.parent().register(this);
4651 onClick : function(e)
4653 if (e.getTarget('.dropdown-menu-item')) {
4654 // did you click on a menu itemm.... - then don't trigger onclick..
4659 this.preventDefault ||
4662 Roo.log("NavItem - prevent Default?");
4666 if (this.disabled) {
4670 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4671 if (tg && tg.transition) {
4672 Roo.log("waiting for the transitionend");
4678 //Roo.log("fire event clicked");
4679 if(this.fireEvent('click', this, e) === false){
4683 if(this.tagtype == 'span'){
4687 //Roo.log(this.href);
4688 var ael = this.el.select('a',true).first();
4691 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4692 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4693 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4694 return; // ignore... - it's a 'hash' to another page.
4696 Roo.log("NavItem - prevent Default?");
4698 this.scrollToElement(e);
4702 var p = this.parent();
4704 if (['tabs','pills'].indexOf(p.type)!==-1) {
4705 if (typeof(p.setActiveItem) !== 'undefined') {
4706 p.setActiveItem(this);
4710 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4711 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4712 // remove the collapsed menu expand...
4713 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4717 isActive: function () {
4720 setActive : function(state, fire, is_was_active)
4722 if (this.active && !state && this.navId) {
4723 this.was_active = true;
4724 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4726 nv.clearWasActive(this);
4730 this.active = state;
4733 this.el.removeClass('active');
4734 } else if (!this.el.hasClass('active')) {
4735 this.el.addClass('active');
4738 this.fireEvent('changed', this, state);
4741 // show a panel if it's registered and related..
4743 if (!this.navId || !this.tabId || !state || is_was_active) {
4747 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4751 var pan = tg.getPanelByName(this.tabId);
4755 // if we can not flip to new panel - go back to old nav highlight..
4756 if (false == tg.showPanel(pan)) {
4757 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4759 var onav = nv.getWasActive();
4761 onav.setActive(true, false, true);
4770 // this should not be here...
4771 setDisabled : function(state)
4773 this.disabled = state;
4775 this.el.removeClass('disabled');
4776 } else if (!this.el.hasClass('disabled')) {
4777 this.el.addClass('disabled');
4783 * Fetch the element to display the tooltip on.
4784 * @return {Roo.Element} defaults to this.el
4786 tooltipEl : function()
4788 return this.el.select('' + this.tagtype + '', true).first();
4791 scrollToElement : function(e)
4793 var c = document.body;
4796 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4798 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4799 c = document.documentElement;
4802 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4808 var o = target.calcOffsetsTo(c);
4815 this.fireEvent('scrollto', this, options, e);
4817 Roo.get(c).scrollTo('top', options.value, true);
4830 * <span> icon </span>
4831 * <span> text </span>
4832 * <span>badge </span>
4836 * @class Roo.bootstrap.NavSidebarItem
4837 * @extends Roo.bootstrap.NavItem
4838 * Bootstrap Navbar.NavSidebarItem class
4839 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4840 * {Boolean} open is the menu open
4841 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4842 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4843 * {String} buttonSize (sm|md|lg)the extra classes for the button
4844 * {Boolean} showArrow show arrow next to the text (default true)
4846 * Create a new Navbar Button
4847 * @param {Object} config The config object
4849 Roo.bootstrap.NavSidebarItem = function(config){
4850 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4855 * The raw click event for the entire grid.
4856 * @param {Roo.EventObject} e
4861 * Fires when the active item active state changes
4862 * @param {Roo.bootstrap.NavSidebarItem} this
4863 * @param {boolean} state the new state
4871 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4873 badgeWeight : 'default',
4879 buttonWeight : 'default',
4885 getAutoCreate : function(){
4890 href : this.href || '#',
4896 if(this.buttonView){
4899 href : this.href || '#',
4900 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4913 cfg.cls += ' active';
4916 if (this.disabled) {
4917 cfg.cls += ' disabled';
4920 cfg.cls += ' open x-open';
4923 if (this.glyphicon || this.icon) {
4924 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4925 a.cn.push({ tag : 'i', cls : c }) ;
4928 if(!this.buttonView){
4931 html : this.html || ''
4938 if (this.badge !== '') {
4939 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4945 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4948 a.cls += ' dropdown-toggle treeview' ;
4954 initEvents : function()
4956 if (typeof (this.menu) != 'undefined') {
4957 this.menu.parentType = this.xtype;
4958 this.menu.triggerEl = this.el;
4959 this.menu = this.addxtype(Roo.apply({}, this.menu));
4962 this.el.on('click', this.onClick, this);
4964 if(this.badge !== ''){
4965 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4970 onClick : function(e)
4977 if(this.preventDefault){
4981 this.fireEvent('click', this);
4984 disable : function()
4986 this.setDisabled(true);
4991 this.setDisabled(false);
4994 setDisabled : function(state)
4996 if(this.disabled == state){
5000 this.disabled = state;
5003 this.el.addClass('disabled');
5007 this.el.removeClass('disabled');
5012 setActive : function(state)
5014 if(this.active == state){
5018 this.active = state;
5021 this.el.addClass('active');
5025 this.el.removeClass('active');
5030 isActive: function ()
5035 setBadge : function(str)
5041 this.badgeEl.dom.innerHTML = str;
5058 * @class Roo.bootstrap.Row
5059 * @extends Roo.bootstrap.Component
5060 * Bootstrap Row class (contains columns...)
5064 * @param {Object} config The config object
5067 Roo.bootstrap.Row = function(config){
5068 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5071 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5073 getAutoCreate : function(){
5092 * @class Roo.bootstrap.Element
5093 * @extends Roo.bootstrap.Component
5094 * Bootstrap Element class
5095 * @cfg {String} html contents of the element
5096 * @cfg {String} tag tag of the element
5097 * @cfg {String} cls class of the element
5098 * @cfg {Boolean} preventDefault (true|false) default false
5099 * @cfg {Boolean} clickable (true|false) default false
5102 * Create a new Element
5103 * @param {Object} config The config object
5106 Roo.bootstrap.Element = function(config){
5107 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5113 * When a element is chick
5114 * @param {Roo.bootstrap.Element} this
5115 * @param {Roo.EventObject} e
5121 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5126 preventDefault: false,
5129 getAutoCreate : function(){
5133 // cls: this.cls, double assign in parent class Component.js :: onRender
5140 initEvents: function()
5142 Roo.bootstrap.Element.superclass.initEvents.call(this);
5145 this.el.on('click', this.onClick, this);
5150 onClick : function(e)
5152 if(this.preventDefault){
5156 this.fireEvent('click', this, e);
5159 getValue : function()
5161 return this.el.dom.innerHTML;
5164 setValue : function(value)
5166 this.el.dom.innerHTML = value;
5181 * @class Roo.bootstrap.Pagination
5182 * @extends Roo.bootstrap.Component
5183 * Bootstrap Pagination class
5184 * @cfg {String} size xs | sm | md | lg
5185 * @cfg {Boolean} inverse false | true
5188 * Create a new Pagination
5189 * @param {Object} config The config object
5192 Roo.bootstrap.Pagination = function(config){
5193 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5196 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5202 getAutoCreate : function(){
5208 cfg.cls += ' inverse';
5214 cfg.cls += " " + this.cls;
5232 * @class Roo.bootstrap.PaginationItem
5233 * @extends Roo.bootstrap.Component
5234 * Bootstrap PaginationItem class
5235 * @cfg {String} html text
5236 * @cfg {String} href the link
5237 * @cfg {Boolean} preventDefault (true | false) default true
5238 * @cfg {Boolean} active (true | false) default false
5239 * @cfg {Boolean} disabled default false
5243 * Create a new PaginationItem
5244 * @param {Object} config The config object
5248 Roo.bootstrap.PaginationItem = function(config){
5249 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5254 * The raw click event for the entire grid.
5255 * @param {Roo.EventObject} e
5261 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5265 preventDefault: true,
5270 getAutoCreate : function(){
5276 href : this.href ? this.href : '#',
5277 html : this.html ? this.html : ''
5287 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5291 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5297 initEvents: function() {
5299 this.el.on('click', this.onClick, this);
5302 onClick : function(e)
5304 Roo.log('PaginationItem on click ');
5305 if(this.preventDefault){
5313 this.fireEvent('click', this, e);
5329 * @class Roo.bootstrap.Slider
5330 * @extends Roo.bootstrap.Component
5331 * Bootstrap Slider class
5334 * Create a new Slider
5335 * @param {Object} config The config object
5338 Roo.bootstrap.Slider = function(config){
5339 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5342 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5344 getAutoCreate : function(){
5348 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5352 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5364 * Ext JS Library 1.1.1
5365 * Copyright(c) 2006-2007, Ext JS, LLC.
5367 * Originally Released Under LGPL - original licence link has changed is not relivant.
5370 * <script type="text/javascript">
5375 * @class Roo.grid.ColumnModel
5376 * @extends Roo.util.Observable
5377 * This is the default implementation of a ColumnModel used by the Grid. It defines
5378 * the columns in the grid.
5381 var colModel = new Roo.grid.ColumnModel([
5382 {header: "Ticker", width: 60, sortable: true, locked: true},
5383 {header: "Company Name", width: 150, sortable: true},
5384 {header: "Market Cap.", width: 100, sortable: true},
5385 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5386 {header: "Employees", width: 100, sortable: true, resizable: false}
5391 * The config options listed for this class are options which may appear in each
5392 * individual column definition.
5393 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5395 * @param {Object} config An Array of column config objects. See this class's
5396 * config objects for details.
5398 Roo.grid.ColumnModel = function(config){
5400 * The config passed into the constructor
5402 this.config = config;
5405 // if no id, create one
5406 // if the column does not have a dataIndex mapping,
5407 // map it to the order it is in the config
5408 for(var i = 0, len = config.length; i < len; i++){
5410 if(typeof c.dataIndex == "undefined"){
5413 if(typeof c.renderer == "string"){
5414 c.renderer = Roo.util.Format[c.renderer];
5416 if(typeof c.id == "undefined"){
5419 if(c.editor && c.editor.xtype){
5420 c.editor = Roo.factory(c.editor, Roo.grid);
5422 if(c.editor && c.editor.isFormField){
5423 c.editor = new Roo.grid.GridEditor(c.editor);
5425 this.lookup[c.id] = c;
5429 * The width of columns which have no width specified (defaults to 100)
5432 this.defaultWidth = 100;
5435 * Default sortable of columns which have no sortable specified (defaults to false)
5438 this.defaultSortable = false;
5442 * @event widthchange
5443 * Fires when the width of a column changes.
5444 * @param {ColumnModel} this
5445 * @param {Number} columnIndex The column index
5446 * @param {Number} newWidth The new width
5448 "widthchange": true,
5450 * @event headerchange
5451 * Fires when the text of a header changes.
5452 * @param {ColumnModel} this
5453 * @param {Number} columnIndex The column index
5454 * @param {Number} newText The new header text
5456 "headerchange": true,
5458 * @event hiddenchange
5459 * Fires when a column is hidden or "unhidden".
5460 * @param {ColumnModel} this
5461 * @param {Number} columnIndex The column index
5462 * @param {Boolean} hidden true if hidden, false otherwise
5464 "hiddenchange": true,
5466 * @event columnmoved
5467 * Fires when a column is moved.
5468 * @param {ColumnModel} this
5469 * @param {Number} oldIndex
5470 * @param {Number} newIndex
5472 "columnmoved" : true,
5474 * @event columlockchange
5475 * Fires when a column's locked state is changed
5476 * @param {ColumnModel} this
5477 * @param {Number} colIndex
5478 * @param {Boolean} locked true if locked
5480 "columnlockchange" : true
5482 Roo.grid.ColumnModel.superclass.constructor.call(this);
5484 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5486 * @cfg {String} header The header text to display in the Grid view.
5489 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5490 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5491 * specified, the column's index is used as an index into the Record's data Array.
5494 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5495 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5498 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5499 * Defaults to the value of the {@link #defaultSortable} property.
5500 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5503 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5506 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5509 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5512 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5515 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5516 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5517 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5518 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5521 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5524 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5527 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5530 * @cfg {String} cursor (Optional)
5533 * @cfg {String} tooltip (Optional)
5536 * @cfg {Number} xs (Optional)
5539 * @cfg {Number} sm (Optional)
5542 * @cfg {Number} md (Optional)
5545 * @cfg {Number} lg (Optional)
5548 * Returns the id of the column at the specified index.
5549 * @param {Number} index The column index
5550 * @return {String} the id
5552 getColumnId : function(index){
5553 return this.config[index].id;
5557 * Returns the column for a specified id.
5558 * @param {String} id The column id
5559 * @return {Object} the column
5561 getColumnById : function(id){
5562 return this.lookup[id];
5567 * Returns the column for a specified dataIndex.
5568 * @param {String} dataIndex The column dataIndex
5569 * @return {Object|Boolean} the column or false if not found
5571 getColumnByDataIndex: function(dataIndex){
5572 var index = this.findColumnIndex(dataIndex);
5573 return index > -1 ? this.config[index] : false;
5577 * Returns the index for a specified column id.
5578 * @param {String} id The column id
5579 * @return {Number} the index, or -1 if not found
5581 getIndexById : function(id){
5582 for(var i = 0, len = this.config.length; i < len; i++){
5583 if(this.config[i].id == id){
5591 * Returns the index for a specified column dataIndex.
5592 * @param {String} dataIndex The column dataIndex
5593 * @return {Number} the index, or -1 if not found
5596 findColumnIndex : function(dataIndex){
5597 for(var i = 0, len = this.config.length; i < len; i++){
5598 if(this.config[i].dataIndex == dataIndex){
5606 moveColumn : function(oldIndex, newIndex){
5607 var c = this.config[oldIndex];
5608 this.config.splice(oldIndex, 1);
5609 this.config.splice(newIndex, 0, c);
5610 this.dataMap = null;
5611 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5614 isLocked : function(colIndex){
5615 return this.config[colIndex].locked === true;
5618 setLocked : function(colIndex, value, suppressEvent){
5619 if(this.isLocked(colIndex) == value){
5622 this.config[colIndex].locked = value;
5624 this.fireEvent("columnlockchange", this, colIndex, value);
5628 getTotalLockedWidth : function(){
5630 for(var i = 0; i < this.config.length; i++){
5631 if(this.isLocked(i) && !this.isHidden(i)){
5632 this.totalWidth += this.getColumnWidth(i);
5638 getLockedCount : function(){
5639 for(var i = 0, len = this.config.length; i < len; i++){
5640 if(!this.isLocked(i)){
5645 return this.config.length;
5649 * Returns the number of columns.
5652 getColumnCount : function(visibleOnly){
5653 if(visibleOnly === true){
5655 for(var i = 0, len = this.config.length; i < len; i++){
5656 if(!this.isHidden(i)){
5662 return this.config.length;
5666 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5667 * @param {Function} fn
5668 * @param {Object} scope (optional)
5669 * @return {Array} result
5671 getColumnsBy : function(fn, scope){
5673 for(var i = 0, len = this.config.length; i < len; i++){
5674 var c = this.config[i];
5675 if(fn.call(scope||this, c, i) === true){
5683 * Returns true if the specified column is sortable.
5684 * @param {Number} col The column index
5687 isSortable : function(col){
5688 if(typeof this.config[col].sortable == "undefined"){
5689 return this.defaultSortable;
5691 return this.config[col].sortable;
5695 * Returns the rendering (formatting) function defined for the column.
5696 * @param {Number} col The column index.
5697 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5699 getRenderer : function(col){
5700 if(!this.config[col].renderer){
5701 return Roo.grid.ColumnModel.defaultRenderer;
5703 return this.config[col].renderer;
5707 * Sets the rendering (formatting) function for a column.
5708 * @param {Number} col The column index
5709 * @param {Function} fn The function to use to process the cell's raw data
5710 * to return HTML markup for the grid view. The render function is called with
5711 * the following parameters:<ul>
5712 * <li>Data value.</li>
5713 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5714 * <li>css A CSS style string to apply to the table cell.</li>
5715 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5716 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5717 * <li>Row index</li>
5718 * <li>Column index</li>
5719 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5721 setRenderer : function(col, fn){
5722 this.config[col].renderer = fn;
5726 * Returns the width for the specified column.
5727 * @param {Number} col The column index
5730 getColumnWidth : function(col){
5731 return this.config[col].width * 1 || this.defaultWidth;
5735 * Sets the width for a column.
5736 * @param {Number} col The column index
5737 * @param {Number} width The new width
5739 setColumnWidth : function(col, width, suppressEvent){
5740 this.config[col].width = width;
5741 this.totalWidth = null;
5743 this.fireEvent("widthchange", this, col, width);
5748 * Returns the total width of all columns.
5749 * @param {Boolean} includeHidden True to include hidden column widths
5752 getTotalWidth : function(includeHidden){
5753 if(!this.totalWidth){
5754 this.totalWidth = 0;
5755 for(var i = 0, len = this.config.length; i < len; i++){
5756 if(includeHidden || !this.isHidden(i)){
5757 this.totalWidth += this.getColumnWidth(i);
5761 return this.totalWidth;
5765 * Returns the header for the specified column.
5766 * @param {Number} col The column index
5769 getColumnHeader : function(col){
5770 return this.config[col].header;
5774 * Sets the header for a column.
5775 * @param {Number} col The column index
5776 * @param {String} header The new header
5778 setColumnHeader : function(col, header){
5779 this.config[col].header = header;
5780 this.fireEvent("headerchange", this, col, header);
5784 * Returns the tooltip for the specified column.
5785 * @param {Number} col The column index
5788 getColumnTooltip : function(col){
5789 return this.config[col].tooltip;
5792 * Sets the tooltip for a column.
5793 * @param {Number} col The column index
5794 * @param {String} tooltip The new tooltip
5796 setColumnTooltip : function(col, tooltip){
5797 this.config[col].tooltip = tooltip;
5801 * Returns the dataIndex for the specified column.
5802 * @param {Number} col The column index
5805 getDataIndex : function(col){
5806 return this.config[col].dataIndex;
5810 * Sets the dataIndex for a column.
5811 * @param {Number} col The column index
5812 * @param {Number} dataIndex The new dataIndex
5814 setDataIndex : function(col, dataIndex){
5815 this.config[col].dataIndex = dataIndex;
5821 * Returns true if the cell is editable.
5822 * @param {Number} colIndex The column index
5823 * @param {Number} rowIndex The row index - this is nto actually used..?
5826 isCellEditable : function(colIndex, rowIndex){
5827 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5831 * Returns the editor defined for the cell/column.
5832 * return false or null to disable editing.
5833 * @param {Number} colIndex The column index
5834 * @param {Number} rowIndex The row index
5837 getCellEditor : function(colIndex, rowIndex){
5838 return this.config[colIndex].editor;
5842 * Sets if a column is editable.
5843 * @param {Number} col The column index
5844 * @param {Boolean} editable True if the column is editable
5846 setEditable : function(col, editable){
5847 this.config[col].editable = editable;
5852 * Returns true if the column is hidden.
5853 * @param {Number} colIndex The column index
5856 isHidden : function(colIndex){
5857 return this.config[colIndex].hidden;
5862 * Returns true if the column width cannot be changed
5864 isFixed : function(colIndex){
5865 return this.config[colIndex].fixed;
5869 * Returns true if the column can be resized
5872 isResizable : function(colIndex){
5873 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5876 * Sets if a column is hidden.
5877 * @param {Number} colIndex The column index
5878 * @param {Boolean} hidden True if the column is hidden
5880 setHidden : function(colIndex, hidden){
5881 this.config[colIndex].hidden = hidden;
5882 this.totalWidth = null;
5883 this.fireEvent("hiddenchange", this, colIndex, hidden);
5887 * Sets the editor for a column.
5888 * @param {Number} col The column index
5889 * @param {Object} editor The editor object
5891 setEditor : function(col, editor){
5892 this.config[col].editor = editor;
5896 Roo.grid.ColumnModel.defaultRenderer = function(value)
5898 if(typeof value == "object") {
5901 if(typeof value == "string" && value.length < 1){
5905 return String.format("{0}", value);
5908 // Alias for backwards compatibility
5909 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5912 * Ext JS Library 1.1.1
5913 * Copyright(c) 2006-2007, Ext JS, LLC.
5915 * Originally Released Under LGPL - original licence link has changed is not relivant.
5918 * <script type="text/javascript">
5922 * @class Roo.LoadMask
5923 * A simple utility class for generically masking elements while loading data. If the element being masked has
5924 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5925 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5926 * element's UpdateManager load indicator and will be destroyed after the initial load.
5928 * Create a new LoadMask
5929 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5930 * @param {Object} config The config object
5932 Roo.LoadMask = function(el, config){
5933 this.el = Roo.get(el);
5934 Roo.apply(this, config);
5936 this.store.on('beforeload', this.onBeforeLoad, this);
5937 this.store.on('load', this.onLoad, this);
5938 this.store.on('loadexception', this.onLoadException, this);
5939 this.removeMask = false;
5941 var um = this.el.getUpdateManager();
5942 um.showLoadIndicator = false; // disable the default indicator
5943 um.on('beforeupdate', this.onBeforeLoad, this);
5944 um.on('update', this.onLoad, this);
5945 um.on('failure', this.onLoad, this);
5946 this.removeMask = true;
5950 Roo.LoadMask.prototype = {
5952 * @cfg {Boolean} removeMask
5953 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5954 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5958 * The text to display in a centered loading message box (defaults to 'Loading...')
5962 * @cfg {String} msgCls
5963 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5965 msgCls : 'x-mask-loading',
5968 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5974 * Disables the mask to prevent it from being displayed
5976 disable : function(){
5977 this.disabled = true;
5981 * Enables the mask so that it can be displayed
5983 enable : function(){
5984 this.disabled = false;
5987 onLoadException : function()
5991 if (typeof(arguments[3]) != 'undefined') {
5992 Roo.MessageBox.alert("Error loading",arguments[3]);
5996 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5997 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6004 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6009 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6013 onBeforeLoad : function(){
6015 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6020 destroy : function(){
6022 this.store.un('beforeload', this.onBeforeLoad, this);
6023 this.store.un('load', this.onLoad, this);
6024 this.store.un('loadexception', this.onLoadException, this);
6026 var um = this.el.getUpdateManager();
6027 um.un('beforeupdate', this.onBeforeLoad, this);
6028 um.un('update', this.onLoad, this);
6029 um.un('failure', this.onLoad, this);
6040 * @class Roo.bootstrap.Table
6041 * @extends Roo.bootstrap.Component
6042 * Bootstrap Table class
6043 * @cfg {String} cls table class
6044 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6045 * @cfg {String} bgcolor Specifies the background color for a table
6046 * @cfg {Number} border Specifies whether the table cells should have borders or not
6047 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6048 * @cfg {Number} cellspacing Specifies the space between cells
6049 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6050 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6051 * @cfg {String} sortable Specifies that the table should be sortable
6052 * @cfg {String} summary Specifies a summary of the content of a table
6053 * @cfg {Number} width Specifies the width of a table
6054 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6056 * @cfg {boolean} striped Should the rows be alternative striped
6057 * @cfg {boolean} bordered Add borders to the table
6058 * @cfg {boolean} hover Add hover highlighting
6059 * @cfg {boolean} condensed Format condensed
6060 * @cfg {boolean} responsive Format condensed
6061 * @cfg {Boolean} loadMask (true|false) default false
6062 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6063 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6064 * @cfg {Boolean} rowSelection (true|false) default false
6065 * @cfg {Boolean} cellSelection (true|false) default false
6066 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6067 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6068 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6069 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6073 * Create a new Table
6074 * @param {Object} config The config object
6077 Roo.bootstrap.Table = function(config){
6078 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6083 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6084 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6085 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6086 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6088 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6090 this.sm.grid = this;
6091 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6092 this.sm = this.selModel;
6093 this.sm.xmodule = this.xmodule || false;
6096 if (this.cm && typeof(this.cm.config) == 'undefined') {
6097 this.colModel = new Roo.grid.ColumnModel(this.cm);
6098 this.cm = this.colModel;
6099 this.cm.xmodule = this.xmodule || false;
6102 this.store= Roo.factory(this.store, Roo.data);
6103 this.ds = this.store;
6104 this.ds.xmodule = this.xmodule || false;
6107 if (this.footer && this.store) {
6108 this.footer.dataSource = this.ds;
6109 this.footer = Roo.factory(this.footer);
6116 * Fires when a cell is clicked
6117 * @param {Roo.bootstrap.Table} this
6118 * @param {Roo.Element} el
6119 * @param {Number} rowIndex
6120 * @param {Number} columnIndex
6121 * @param {Roo.EventObject} e
6125 * @event celldblclick
6126 * Fires when a cell is double clicked
6127 * @param {Roo.bootstrap.Table} this
6128 * @param {Roo.Element} el
6129 * @param {Number} rowIndex
6130 * @param {Number} columnIndex
6131 * @param {Roo.EventObject} e
6133 "celldblclick" : true,
6136 * Fires when a row is clicked
6137 * @param {Roo.bootstrap.Table} this
6138 * @param {Roo.Element} el
6139 * @param {Number} rowIndex
6140 * @param {Roo.EventObject} e
6144 * @event rowdblclick
6145 * Fires when a row is double clicked
6146 * @param {Roo.bootstrap.Table} this
6147 * @param {Roo.Element} el
6148 * @param {Number} rowIndex
6149 * @param {Roo.EventObject} e
6151 "rowdblclick" : true,
6154 * Fires when a mouseover occur
6155 * @param {Roo.bootstrap.Table} this
6156 * @param {Roo.Element} el
6157 * @param {Number} rowIndex
6158 * @param {Number} columnIndex
6159 * @param {Roo.EventObject} e
6164 * Fires when a mouseout occur
6165 * @param {Roo.bootstrap.Table} this
6166 * @param {Roo.Element} el
6167 * @param {Number} rowIndex
6168 * @param {Number} columnIndex
6169 * @param {Roo.EventObject} e
6174 * Fires when a row is rendered, so you can change add a style to it.
6175 * @param {Roo.bootstrap.Table} this
6176 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6180 * @event rowsrendered
6181 * Fires when all the rows have been rendered
6182 * @param {Roo.bootstrap.Table} this
6184 'rowsrendered' : true,
6186 * @event contextmenu
6187 * The raw contextmenu event for the entire grid.
6188 * @param {Roo.EventObject} e
6190 "contextmenu" : true,
6192 * @event rowcontextmenu
6193 * Fires when a row is right clicked
6194 * @param {Roo.bootstrap.Table} this
6195 * @param {Number} rowIndex
6196 * @param {Roo.EventObject} e
6198 "rowcontextmenu" : true,
6200 * @event cellcontextmenu
6201 * Fires when a cell is right clicked
6202 * @param {Roo.bootstrap.Table} this
6203 * @param {Number} rowIndex
6204 * @param {Number} cellIndex
6205 * @param {Roo.EventObject} e
6207 "cellcontextmenu" : true,
6209 * @event headercontextmenu
6210 * Fires when a header is right clicked
6211 * @param {Roo.bootstrap.Table} this
6212 * @param {Number} columnIndex
6213 * @param {Roo.EventObject} e
6215 "headercontextmenu" : true
6219 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6245 rowSelection : false,
6246 cellSelection : false,
6249 // Roo.Element - the tbody
6251 // Roo.Element - thead element
6254 container: false, // used by gridpanel...
6260 auto_hide_footer : false,
6262 getAutoCreate : function()
6264 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6271 if (this.scrollBody) {
6272 cfg.cls += ' table-body-fixed';
6275 cfg.cls += ' table-striped';
6279 cfg.cls += ' table-hover';
6281 if (this.bordered) {
6282 cfg.cls += ' table-bordered';
6284 if (this.condensed) {
6285 cfg.cls += ' table-condensed';
6287 if (this.responsive) {
6288 cfg.cls += ' table-responsive';
6292 cfg.cls+= ' ' +this.cls;
6295 // this lot should be simplifed...
6308 ].forEach(function(k) {
6316 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6319 if(this.store || this.cm){
6320 if(this.headerShow){
6321 cfg.cn.push(this.renderHeader());
6324 cfg.cn.push(this.renderBody());
6326 if(this.footerShow){
6327 cfg.cn.push(this.renderFooter());
6329 // where does this come from?
6330 //cfg.cls+= ' TableGrid';
6333 return { cn : [ cfg ] };
6336 initEvents : function()
6338 if(!this.store || !this.cm){
6341 if (this.selModel) {
6342 this.selModel.initEvents();
6346 //Roo.log('initEvents with ds!!!!');
6348 this.mainBody = this.el.select('tbody', true).first();
6349 this.mainHead = this.el.select('thead', true).first();
6350 this.mainFoot = this.el.select('tfoot', true).first();
6356 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6357 e.on('click', _this.sort, _this);
6360 this.mainBody.on("click", this.onClick, this);
6361 this.mainBody.on("dblclick", this.onDblClick, this);
6363 // why is this done????? = it breaks dialogs??
6364 //this.parent().el.setStyle('position', 'relative');
6368 this.footer.parentId = this.id;
6369 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6372 this.el.select('tfoot tr td').first().addClass('hide');
6377 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6380 this.store.on('load', this.onLoad, this);
6381 this.store.on('beforeload', this.onBeforeLoad, this);
6382 this.store.on('update', this.onUpdate, this);
6383 this.store.on('add', this.onAdd, this);
6384 this.store.on("clear", this.clear, this);
6386 this.el.on("contextmenu", this.onContextMenu, this);
6388 this.mainBody.on('scroll', this.onBodyScroll, this);
6390 this.cm.on("headerchange", this.onHeaderChange, this);
6392 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6396 onContextMenu : function(e, t)
6398 this.processEvent("contextmenu", e);
6401 processEvent : function(name, e)
6403 if (name != 'touchstart' ) {
6404 this.fireEvent(name, e);
6407 var t = e.getTarget();
6409 var cell = Roo.get(t);
6415 if(cell.findParent('tfoot', false, true)){
6419 if(cell.findParent('thead', false, true)){
6421 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6422 cell = Roo.get(t).findParent('th', false, true);
6424 Roo.log("failed to find th in thead?");
6425 Roo.log(e.getTarget());
6430 var cellIndex = cell.dom.cellIndex;
6432 var ename = name == 'touchstart' ? 'click' : name;
6433 this.fireEvent("header" + ename, this, cellIndex, e);
6438 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6439 cell = Roo.get(t).findParent('td', false, true);
6441 Roo.log("failed to find th in tbody?");
6442 Roo.log(e.getTarget());
6447 var row = cell.findParent('tr', false, true);
6448 var cellIndex = cell.dom.cellIndex;
6449 var rowIndex = row.dom.rowIndex - 1;
6453 this.fireEvent("row" + name, this, rowIndex, e);
6457 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6463 onMouseover : function(e, el)
6465 var cell = Roo.get(el);
6471 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6472 cell = cell.findParent('td', false, true);
6475 var row = cell.findParent('tr', false, true);
6476 var cellIndex = cell.dom.cellIndex;
6477 var rowIndex = row.dom.rowIndex - 1; // start from 0
6479 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6483 onMouseout : function(e, el)
6485 var cell = Roo.get(el);
6491 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6492 cell = cell.findParent('td', false, true);
6495 var row = cell.findParent('tr', false, true);
6496 var cellIndex = cell.dom.cellIndex;
6497 var rowIndex = row.dom.rowIndex - 1; // start from 0
6499 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6503 onClick : function(e, el)
6505 var cell = Roo.get(el);
6507 if(!cell || (!this.cellSelection && !this.rowSelection)){
6511 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6512 cell = cell.findParent('td', false, true);
6515 if(!cell || typeof(cell) == 'undefined'){
6519 var row = cell.findParent('tr', false, true);
6521 if(!row || typeof(row) == 'undefined'){
6525 var cellIndex = cell.dom.cellIndex;
6526 var rowIndex = this.getRowIndex(row);
6528 // why??? - should these not be based on SelectionModel?
6529 if(this.cellSelection){
6530 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6533 if(this.rowSelection){
6534 this.fireEvent('rowclick', this, row, rowIndex, e);
6540 onDblClick : function(e,el)
6542 var cell = Roo.get(el);
6544 if(!cell || (!this.cellSelection && !this.rowSelection)){
6548 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6549 cell = cell.findParent('td', false, true);
6552 if(!cell || typeof(cell) == 'undefined'){
6556 var row = cell.findParent('tr', false, true);
6558 if(!row || typeof(row) == 'undefined'){
6562 var cellIndex = cell.dom.cellIndex;
6563 var rowIndex = this.getRowIndex(row);
6565 if(this.cellSelection){
6566 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6569 if(this.rowSelection){
6570 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6574 sort : function(e,el)
6576 var col = Roo.get(el);
6578 if(!col.hasClass('sortable')){
6582 var sort = col.attr('sort');
6585 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6589 this.store.sortInfo = {field : sort, direction : dir};
6592 Roo.log("calling footer first");
6593 this.footer.onClick('first');
6596 this.store.load({ params : { start : 0 } });
6600 renderHeader : function()
6608 this.totalWidth = 0;
6610 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6612 var config = cm.config[i];
6616 cls : 'x-hcol-' + i,
6618 html: cm.getColumnHeader(i)
6623 if(typeof(config.sortable) != 'undefined' && config.sortable){
6625 c.html = '<i class="glyphicon"></i>' + c.html;
6628 if(typeof(config.lgHeader) != 'undefined'){
6629 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6632 if(typeof(config.mdHeader) != 'undefined'){
6633 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6636 if(typeof(config.smHeader) != 'undefined'){
6637 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6640 if(typeof(config.xsHeader) != 'undefined'){
6641 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6648 if(typeof(config.tooltip) != 'undefined'){
6649 c.tooltip = config.tooltip;
6652 if(typeof(config.colspan) != 'undefined'){
6653 c.colspan = config.colspan;
6656 if(typeof(config.hidden) != 'undefined' && config.hidden){
6657 c.style += ' display:none;';
6660 if(typeof(config.dataIndex) != 'undefined'){
6661 c.sort = config.dataIndex;
6666 if(typeof(config.align) != 'undefined' && config.align.length){
6667 c.style += ' text-align:' + config.align + ';';
6670 if(typeof(config.width) != 'undefined'){
6671 c.style += ' width:' + config.width + 'px;';
6672 this.totalWidth += config.width;
6674 this.totalWidth += 100; // assume minimum of 100 per column?
6677 if(typeof(config.cls) != 'undefined'){
6678 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6681 ['xs','sm','md','lg'].map(function(size){
6683 if(typeof(config[size]) == 'undefined'){
6687 if (!config[size]) { // 0 = hidden
6688 c.cls += ' hidden-' + size;
6692 c.cls += ' col-' + size + '-' + config[size];
6702 renderBody : function()
6712 colspan : this.cm.getColumnCount()
6722 renderFooter : function()
6732 colspan : this.cm.getColumnCount()
6746 // Roo.log('ds onload');
6751 var ds = this.store;
6753 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6754 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6755 if (_this.store.sortInfo) {
6757 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6758 e.select('i', true).addClass(['glyphicon-arrow-up']);
6761 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6762 e.select('i', true).addClass(['glyphicon-arrow-down']);
6767 var tbody = this.mainBody;
6769 if(ds.getCount() > 0){
6770 ds.data.each(function(d,rowIndex){
6771 var row = this.renderRow(cm, ds, rowIndex);
6773 tbody.createChild(row);
6777 if(row.cellObjects.length){
6778 Roo.each(row.cellObjects, function(r){
6779 _this.renderCellObject(r);
6786 var tfoot = this.el.select('tfoot', true).first();
6788 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6790 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6792 var total = this.ds.getTotalCount();
6794 if(this.footer.pageSize < total){
6795 this.mainFoot.show();
6799 Roo.each(this.el.select('tbody td', true).elements, function(e){
6800 e.on('mouseover', _this.onMouseover, _this);
6803 Roo.each(this.el.select('tbody td', true).elements, function(e){
6804 e.on('mouseout', _this.onMouseout, _this);
6806 this.fireEvent('rowsrendered', this);
6812 onUpdate : function(ds,record)
6814 this.refreshRow(record);
6818 onRemove : function(ds, record, index, isUpdate){
6819 if(isUpdate !== true){
6820 this.fireEvent("beforerowremoved", this, index, record);
6822 var bt = this.mainBody.dom;
6824 var rows = this.el.select('tbody > tr', true).elements;
6826 if(typeof(rows[index]) != 'undefined'){
6827 bt.removeChild(rows[index].dom);
6830 // if(bt.rows[index]){
6831 // bt.removeChild(bt.rows[index]);
6834 if(isUpdate !== true){
6835 //this.stripeRows(index);
6836 //this.syncRowHeights(index, index);
6838 this.fireEvent("rowremoved", this, index, record);
6842 onAdd : function(ds, records, rowIndex)
6844 //Roo.log('on Add called');
6845 // - note this does not handle multiple adding very well..
6846 var bt = this.mainBody.dom;
6847 for (var i =0 ; i < records.length;i++) {
6848 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6849 //Roo.log(records[i]);
6850 //Roo.log(this.store.getAt(rowIndex+i));
6851 this.insertRow(this.store, rowIndex + i, false);
6858 refreshRow : function(record){
6859 var ds = this.store, index;
6860 if(typeof record == 'number'){
6862 record = ds.getAt(index);
6864 index = ds.indexOf(record);
6866 this.insertRow(ds, index, true);
6868 this.onRemove(ds, record, index+1, true);
6870 //this.syncRowHeights(index, index);
6872 this.fireEvent("rowupdated", this, index, record);
6875 insertRow : function(dm, rowIndex, isUpdate){
6878 this.fireEvent("beforerowsinserted", this, rowIndex);
6880 //var s = this.getScrollState();
6881 var row = this.renderRow(this.cm, this.store, rowIndex);
6882 // insert before rowIndex..
6883 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6887 if(row.cellObjects.length){
6888 Roo.each(row.cellObjects, function(r){
6889 _this.renderCellObject(r);
6894 this.fireEvent("rowsinserted", this, rowIndex);
6895 //this.syncRowHeights(firstRow, lastRow);
6896 //this.stripeRows(firstRow);
6903 getRowDom : function(rowIndex)
6905 var rows = this.el.select('tbody > tr', true).elements;
6907 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6910 // returns the object tree for a tr..
6913 renderRow : function(cm, ds, rowIndex)
6915 var d = ds.getAt(rowIndex);
6919 cls : 'x-row-' + rowIndex,
6923 var cellObjects = [];
6925 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6926 var config = cm.config[i];
6928 var renderer = cm.getRenderer(i);
6932 if(typeof(renderer) !== 'undefined'){
6933 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6935 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6936 // and are rendered into the cells after the row is rendered - using the id for the element.
6938 if(typeof(value) === 'object'){
6948 rowIndex : rowIndex,
6953 this.fireEvent('rowclass', this, rowcfg);
6957 cls : rowcfg.rowClass + ' x-col-' + i,
6959 html: (typeof(value) === 'object') ? '' : value
6966 if(typeof(config.colspan) != 'undefined'){
6967 td.colspan = config.colspan;
6970 if(typeof(config.hidden) != 'undefined' && config.hidden){
6971 td.style += ' display:none;';
6974 if(typeof(config.align) != 'undefined' && config.align.length){
6975 td.style += ' text-align:' + config.align + ';';
6977 if(typeof(config.valign) != 'undefined' && config.valign.length){
6978 td.style += ' vertical-align:' + config.valign + ';';
6981 if(typeof(config.width) != 'undefined'){
6982 td.style += ' width:' + config.width + 'px;';
6985 if(typeof(config.cursor) != 'undefined'){
6986 td.style += ' cursor:' + config.cursor + ';';
6989 if(typeof(config.cls) != 'undefined'){
6990 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6993 ['xs','sm','md','lg'].map(function(size){
6995 if(typeof(config[size]) == 'undefined'){
6999 if (!config[size]) { // 0 = hidden
7000 td.cls += ' hidden-' + size;
7004 td.cls += ' col-' + size + '-' + config[size];
7012 row.cellObjects = cellObjects;
7020 onBeforeLoad : function()
7029 this.el.select('tbody', true).first().dom.innerHTML = '';
7032 * Show or hide a row.
7033 * @param {Number} rowIndex to show or hide
7034 * @param {Boolean} state hide
7036 setRowVisibility : function(rowIndex, state)
7038 var bt = this.mainBody.dom;
7040 var rows = this.el.select('tbody > tr', true).elements;
7042 if(typeof(rows[rowIndex]) == 'undefined'){
7045 rows[rowIndex].dom.style.display = state ? '' : 'none';
7049 getSelectionModel : function(){
7051 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7053 return this.selModel;
7056 * Render the Roo.bootstrap object from renderder
7058 renderCellObject : function(r)
7062 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7064 var t = r.cfg.render(r.container);
7067 Roo.each(r.cfg.cn, function(c){
7069 container: t.getChildContainer(),
7072 _this.renderCellObject(child);
7077 getRowIndex : function(row)
7081 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7092 * Returns the grid's underlying element = used by panel.Grid
7093 * @return {Element} The element
7095 getGridEl : function(){
7099 * Forces a resize - used by panel.Grid
7100 * @return {Element} The element
7102 autoSize : function()
7104 //var ctr = Roo.get(this.container.dom.parentElement);
7105 var ctr = Roo.get(this.el.dom);
7107 var thd = this.getGridEl().select('thead',true).first();
7108 var tbd = this.getGridEl().select('tbody', true).first();
7109 var tfd = this.getGridEl().select('tfoot', true).first();
7111 var cw = ctr.getWidth();
7115 tbd.setSize(ctr.getWidth(),
7116 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7118 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7121 cw = Math.max(cw, this.totalWidth);
7122 this.getGridEl().select('tr',true).setWidth(cw);
7123 // resize 'expandable coloumn?
7125 return; // we doe not have a view in this design..
7128 onBodyScroll: function()
7130 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7132 this.mainHead.setStyle({
7133 'position' : 'relative',
7134 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7140 var scrollHeight = this.mainBody.dom.scrollHeight;
7142 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7144 var height = this.mainBody.getHeight();
7146 if(scrollHeight - height == scrollTop) {
7148 var total = this.ds.getTotalCount();
7150 if(this.footer.cursor + this.footer.pageSize < total){
7152 this.footer.ds.load({
7154 start : this.footer.cursor + this.footer.pageSize,
7155 limit : this.footer.pageSize
7165 onHeaderChange : function()
7167 var header = this.renderHeader();
7168 var table = this.el.select('table', true).first();
7170 this.mainHead.remove();
7171 this.mainHead = table.createChild(header, this.mainBody, false);
7174 onHiddenChange : function(colModel, colIndex, hidden)
7176 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7177 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7179 this.CSS.updateRule(thSelector, "display", "");
7180 this.CSS.updateRule(tdSelector, "display", "");
7183 this.CSS.updateRule(thSelector, "display", "none");
7184 this.CSS.updateRule(tdSelector, "display", "none");
7187 this.onHeaderChange();
7191 setColumnWidth: function(col_index, width)
7193 // width = "md-2 xs-2..."
7194 if(!this.colModel.config[col_index]) {
7198 var w = width.split(" ");
7200 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7202 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7205 for(var j = 0; j < w.length; j++) {
7211 var size_cls = w[j].split("-");
7213 if(!Number.isInteger(size_cls[1] * 1)) {
7217 if(!this.colModel.config[col_index][size_cls[0]]) {
7221 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7225 h_row[0].classList.replace(
7226 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7227 "col-"+size_cls[0]+"-"+size_cls[1]
7230 for(var i = 0; i < rows.length; i++) {
7232 var size_cls = w[j].split("-");
7234 if(!Number.isInteger(size_cls[1] * 1)) {
7238 if(!this.colModel.config[col_index][size_cls[0]]) {
7242 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7246 rows[i].classList.replace(
7247 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7248 "col-"+size_cls[0]+"-"+size_cls[1]
7252 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7267 * @class Roo.bootstrap.TableCell
7268 * @extends Roo.bootstrap.Component
7269 * Bootstrap TableCell class
7270 * @cfg {String} html cell contain text
7271 * @cfg {String} cls cell class
7272 * @cfg {String} tag cell tag (td|th) default td
7273 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7274 * @cfg {String} align Aligns the content in a cell
7275 * @cfg {String} axis Categorizes cells
7276 * @cfg {String} bgcolor Specifies the background color of a cell
7277 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7278 * @cfg {Number} colspan Specifies the number of columns a cell should span
7279 * @cfg {String} headers Specifies one or more header cells a cell is related to
7280 * @cfg {Number} height Sets the height of a cell
7281 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7282 * @cfg {Number} rowspan Sets the number of rows a cell should span
7283 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7284 * @cfg {String} valign Vertical aligns the content in a cell
7285 * @cfg {Number} width Specifies the width of a cell
7288 * Create a new TableCell
7289 * @param {Object} config The config object
7292 Roo.bootstrap.TableCell = function(config){
7293 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7296 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7316 getAutoCreate : function(){
7317 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7337 cfg.align=this.align
7343 cfg.bgcolor=this.bgcolor
7346 cfg.charoff=this.charoff
7349 cfg.colspan=this.colspan
7352 cfg.headers=this.headers
7355 cfg.height=this.height
7358 cfg.nowrap=this.nowrap
7361 cfg.rowspan=this.rowspan
7364 cfg.scope=this.scope
7367 cfg.valign=this.valign
7370 cfg.width=this.width
7389 * @class Roo.bootstrap.TableRow
7390 * @extends Roo.bootstrap.Component
7391 * Bootstrap TableRow class
7392 * @cfg {String} cls row class
7393 * @cfg {String} align Aligns the content in a table row
7394 * @cfg {String} bgcolor Specifies a background color for a table row
7395 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7396 * @cfg {String} valign Vertical aligns the content in a table row
7399 * Create a new TableRow
7400 * @param {Object} config The config object
7403 Roo.bootstrap.TableRow = function(config){
7404 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7407 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7415 getAutoCreate : function(){
7416 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7426 cfg.align = this.align;
7429 cfg.bgcolor = this.bgcolor;
7432 cfg.charoff = this.charoff;
7435 cfg.valign = this.valign;
7453 * @class Roo.bootstrap.TableBody
7454 * @extends Roo.bootstrap.Component
7455 * Bootstrap TableBody class
7456 * @cfg {String} cls element class
7457 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7458 * @cfg {String} align Aligns the content inside the element
7459 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7460 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7463 * Create a new TableBody
7464 * @param {Object} config The config object
7467 Roo.bootstrap.TableBody = function(config){
7468 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7471 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7479 getAutoCreate : function(){
7480 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7494 cfg.align = this.align;
7497 cfg.charoff = this.charoff;
7500 cfg.valign = this.valign;
7507 // initEvents : function()
7514 // this.store = Roo.factory(this.store, Roo.data);
7515 // this.store.on('load', this.onLoad, this);
7517 // this.store.load();
7521 // onLoad: function ()
7523 // this.fireEvent('load', this);
7533 * Ext JS Library 1.1.1
7534 * Copyright(c) 2006-2007, Ext JS, LLC.
7536 * Originally Released Under LGPL - original licence link has changed is not relivant.
7539 * <script type="text/javascript">
7542 // as we use this in bootstrap.
7543 Roo.namespace('Roo.form');
7545 * @class Roo.form.Action
7546 * Internal Class used to handle form actions
7548 * @param {Roo.form.BasicForm} el The form element or its id
7549 * @param {Object} config Configuration options
7554 // define the action interface
7555 Roo.form.Action = function(form, options){
7557 this.options = options || {};
7560 * Client Validation Failed
7563 Roo.form.Action.CLIENT_INVALID = 'client';
7565 * Server Validation Failed
7568 Roo.form.Action.SERVER_INVALID = 'server';
7570 * Connect to Server Failed
7573 Roo.form.Action.CONNECT_FAILURE = 'connect';
7575 * Reading Data from Server Failed
7578 Roo.form.Action.LOAD_FAILURE = 'load';
7580 Roo.form.Action.prototype = {
7582 failureType : undefined,
7583 response : undefined,
7587 run : function(options){
7592 success : function(response){
7597 handleResponse : function(response){
7601 // default connection failure
7602 failure : function(response){
7604 this.response = response;
7605 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7606 this.form.afterAction(this, false);
7609 processResponse : function(response){
7610 this.response = response;
7611 if(!response.responseText){
7614 this.result = this.handleResponse(response);
7618 // utility functions used internally
7619 getUrl : function(appendParams){
7620 var url = this.options.url || this.form.url || this.form.el.dom.action;
7622 var p = this.getParams();
7624 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7630 getMethod : function(){
7631 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7634 getParams : function(){
7635 var bp = this.form.baseParams;
7636 var p = this.options.params;
7638 if(typeof p == "object"){
7639 p = Roo.urlEncode(Roo.applyIf(p, bp));
7640 }else if(typeof p == 'string' && bp){
7641 p += '&' + Roo.urlEncode(bp);
7644 p = Roo.urlEncode(bp);
7649 createCallback : function(){
7651 success: this.success,
7652 failure: this.failure,
7654 timeout: (this.form.timeout*1000),
7655 upload: this.form.fileUpload ? this.success : undefined
7660 Roo.form.Action.Submit = function(form, options){
7661 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7664 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7667 haveProgress : false,
7668 uploadComplete : false,
7670 // uploadProgress indicator.
7671 uploadProgress : function()
7673 if (!this.form.progressUrl) {
7677 if (!this.haveProgress) {
7678 Roo.MessageBox.progress("Uploading", "Uploading");
7680 if (this.uploadComplete) {
7681 Roo.MessageBox.hide();
7685 this.haveProgress = true;
7687 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7689 var c = new Roo.data.Connection();
7691 url : this.form.progressUrl,
7696 success : function(req){
7697 //console.log(data);
7701 rdata = Roo.decode(req.responseText)
7703 Roo.log("Invalid data from server..");
7707 if (!rdata || !rdata.success) {
7709 Roo.MessageBox.alert(Roo.encode(rdata));
7712 var data = rdata.data;
7714 if (this.uploadComplete) {
7715 Roo.MessageBox.hide();
7720 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7721 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7724 this.uploadProgress.defer(2000,this);
7727 failure: function(data) {
7728 Roo.log('progress url failed ');
7739 // run get Values on the form, so it syncs any secondary forms.
7740 this.form.getValues();
7742 var o = this.options;
7743 var method = this.getMethod();
7744 var isPost = method == 'POST';
7745 if(o.clientValidation === false || this.form.isValid()){
7747 if (this.form.progressUrl) {
7748 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7749 (new Date() * 1) + '' + Math.random());
7754 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7755 form:this.form.el.dom,
7756 url:this.getUrl(!isPost),
7758 params:isPost ? this.getParams() : null,
7759 isUpload: this.form.fileUpload
7762 this.uploadProgress();
7764 }else if (o.clientValidation !== false){ // client validation failed
7765 this.failureType = Roo.form.Action.CLIENT_INVALID;
7766 this.form.afterAction(this, false);
7770 success : function(response)
7772 this.uploadComplete= true;
7773 if (this.haveProgress) {
7774 Roo.MessageBox.hide();
7778 var result = this.processResponse(response);
7779 if(result === true || result.success){
7780 this.form.afterAction(this, true);
7784 this.form.markInvalid(result.errors);
7785 this.failureType = Roo.form.Action.SERVER_INVALID;
7787 this.form.afterAction(this, false);
7789 failure : function(response)
7791 this.uploadComplete= true;
7792 if (this.haveProgress) {
7793 Roo.MessageBox.hide();
7796 this.response = response;
7797 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7798 this.form.afterAction(this, false);
7801 handleResponse : function(response){
7802 if(this.form.errorReader){
7803 var rs = this.form.errorReader.read(response);
7806 for(var i = 0, len = rs.records.length; i < len; i++) {
7807 var r = rs.records[i];
7811 if(errors.length < 1){
7815 success : rs.success,
7821 ret = Roo.decode(response.responseText);
7825 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7835 Roo.form.Action.Load = function(form, options){
7836 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7837 this.reader = this.form.reader;
7840 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7845 Roo.Ajax.request(Roo.apply(
7846 this.createCallback(), {
7847 method:this.getMethod(),
7848 url:this.getUrl(false),
7849 params:this.getParams()
7853 success : function(response){
7855 var result = this.processResponse(response);
7856 if(result === true || !result.success || !result.data){
7857 this.failureType = Roo.form.Action.LOAD_FAILURE;
7858 this.form.afterAction(this, false);
7861 this.form.clearInvalid();
7862 this.form.setValues(result.data);
7863 this.form.afterAction(this, true);
7866 handleResponse : function(response){
7867 if(this.form.reader){
7868 var rs = this.form.reader.read(response);
7869 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7871 success : rs.success,
7875 return Roo.decode(response.responseText);
7879 Roo.form.Action.ACTION_TYPES = {
7880 'load' : Roo.form.Action.Load,
7881 'submit' : Roo.form.Action.Submit
7890 * @class Roo.bootstrap.Form
7891 * @extends Roo.bootstrap.Component
7892 * Bootstrap Form class
7893 * @cfg {String} method GET | POST (default POST)
7894 * @cfg {String} labelAlign top | left (default top)
7895 * @cfg {String} align left | right - for navbars
7896 * @cfg {Boolean} loadMask load mask when submit (default true)
7901 * @param {Object} config The config object
7905 Roo.bootstrap.Form = function(config){
7907 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7909 Roo.bootstrap.Form.popover.apply();
7913 * @event clientvalidation
7914 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7915 * @param {Form} this
7916 * @param {Boolean} valid true if the form has passed client-side validation
7918 clientvalidation: true,
7920 * @event beforeaction
7921 * Fires before any action is performed. Return false to cancel the action.
7922 * @param {Form} this
7923 * @param {Action} action The action to be performed
7927 * @event actionfailed
7928 * Fires when an action fails.
7929 * @param {Form} this
7930 * @param {Action} action The action that failed
7932 actionfailed : true,
7934 * @event actioncomplete
7935 * Fires when an action is completed.
7936 * @param {Form} this
7937 * @param {Action} action The action that completed
7939 actioncomplete : true
7943 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7946 * @cfg {String} method
7947 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7952 * The URL to use for form actions if one isn't supplied in the action options.
7955 * @cfg {Boolean} fileUpload
7956 * Set to true if this form is a file upload.
7960 * @cfg {Object} baseParams
7961 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7965 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7969 * @cfg {Sting} align (left|right) for navbar forms
7974 activeAction : null,
7977 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7978 * element by passing it or its id or mask the form itself by passing in true.
7981 waitMsgTarget : false,
7986 * @cfg {Boolean} errorMask (true|false) default false
7991 * @cfg {Number} maskOffset Default 100
7996 * @cfg {Boolean} maskBody
8000 getAutoCreate : function(){
8004 method : this.method || 'POST',
8005 id : this.id || Roo.id(),
8008 if (this.parent().xtype.match(/^Nav/)) {
8009 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8013 if (this.labelAlign == 'left' ) {
8014 cfg.cls += ' form-horizontal';
8020 initEvents : function()
8022 this.el.on('submit', this.onSubmit, this);
8023 // this was added as random key presses on the form where triggering form submit.
8024 this.el.on('keypress', function(e) {
8025 if (e.getCharCode() != 13) {
8028 // we might need to allow it for textareas.. and some other items.
8029 // check e.getTarget().
8031 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8035 Roo.log("keypress blocked");
8043 onSubmit : function(e){
8048 * Returns true if client-side validation on the form is successful.
8051 isValid : function(){
8052 var items = this.getItems();
8056 items.each(function(f){
8062 Roo.log('invalid field: ' + f.name);
8066 if(!target && f.el.isVisible(true)){
8072 if(this.errorMask && !valid){
8073 Roo.bootstrap.Form.popover.mask(this, target);
8080 * Returns true if any fields in this form have changed since their original load.
8083 isDirty : function(){
8085 var items = this.getItems();
8086 items.each(function(f){
8096 * Performs a predefined action (submit or load) or custom actions you define on this form.
8097 * @param {String} actionName The name of the action type
8098 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8099 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8100 * accept other config options):
8102 Property Type Description
8103 ---------------- --------------- ----------------------------------------------------------------------------------
8104 url String The url for the action (defaults to the form's url)
8105 method String The form method to use (defaults to the form's method, or POST if not defined)
8106 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8107 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8108 validate the form on the client (defaults to false)
8110 * @return {BasicForm} this
8112 doAction : function(action, options){
8113 if(typeof action == 'string'){
8114 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8116 if(this.fireEvent('beforeaction', this, action) !== false){
8117 this.beforeAction(action);
8118 action.run.defer(100, action);
8124 beforeAction : function(action){
8125 var o = action.options;
8130 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8132 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8135 // not really supported yet.. ??
8137 //if(this.waitMsgTarget === true){
8138 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8139 //}else if(this.waitMsgTarget){
8140 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8141 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8143 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8149 afterAction : function(action, success){
8150 this.activeAction = null;
8151 var o = action.options;
8156 Roo.get(document.body).unmask();
8162 //if(this.waitMsgTarget === true){
8163 // this.el.unmask();
8164 //}else if(this.waitMsgTarget){
8165 // this.waitMsgTarget.unmask();
8167 // Roo.MessageBox.updateProgress(1);
8168 // Roo.MessageBox.hide();
8175 Roo.callback(o.success, o.scope, [this, action]);
8176 this.fireEvent('actioncomplete', this, action);
8180 // failure condition..
8181 // we have a scenario where updates need confirming.
8182 // eg. if a locking scenario exists..
8183 // we look for { errors : { needs_confirm : true }} in the response.
8185 (typeof(action.result) != 'undefined') &&
8186 (typeof(action.result.errors) != 'undefined') &&
8187 (typeof(action.result.errors.needs_confirm) != 'undefined')
8190 Roo.log("not supported yet");
8193 Roo.MessageBox.confirm(
8194 "Change requires confirmation",
8195 action.result.errorMsg,
8200 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8210 Roo.callback(o.failure, o.scope, [this, action]);
8211 // show an error message if no failed handler is set..
8212 if (!this.hasListener('actionfailed')) {
8213 Roo.log("need to add dialog support");
8215 Roo.MessageBox.alert("Error",
8216 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8217 action.result.errorMsg :
8218 "Saving Failed, please check your entries or try again"
8223 this.fireEvent('actionfailed', this, action);
8228 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8229 * @param {String} id The value to search for
8232 findField : function(id){
8233 var items = this.getItems();
8234 var field = items.get(id);
8236 items.each(function(f){
8237 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8244 return field || null;
8247 * Mark fields in this form invalid in bulk.
8248 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8249 * @return {BasicForm} this
8251 markInvalid : function(errors){
8252 if(errors instanceof Array){
8253 for(var i = 0, len = errors.length; i < len; i++){
8254 var fieldError = errors[i];
8255 var f = this.findField(fieldError.id);
8257 f.markInvalid(fieldError.msg);
8263 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8264 field.markInvalid(errors[id]);
8268 //Roo.each(this.childForms || [], function (f) {
8269 // f.markInvalid(errors);
8276 * Set values for fields in this form in bulk.
8277 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8278 * @return {BasicForm} this
8280 setValues : function(values){
8281 if(values instanceof Array){ // array of objects
8282 for(var i = 0, len = values.length; i < len; i++){
8284 var f = this.findField(v.id);
8286 f.setValue(v.value);
8287 if(this.trackResetOnLoad){
8288 f.originalValue = f.getValue();
8292 }else{ // object hash
8295 if(typeof values[id] != 'function' && (field = this.findField(id))){
8297 if (field.setFromData &&
8299 field.displayField &&
8300 // combos' with local stores can
8301 // be queried via setValue()
8302 // to set their value..
8303 (field.store && !field.store.isLocal)
8307 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8308 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8309 field.setFromData(sd);
8311 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8313 field.setFromData(values);
8316 field.setValue(values[id]);
8320 if(this.trackResetOnLoad){
8321 field.originalValue = field.getValue();
8327 //Roo.each(this.childForms || [], function (f) {
8328 // f.setValues(values);
8335 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8336 * they are returned as an array.
8337 * @param {Boolean} asString
8340 getValues : function(asString){
8341 //if (this.childForms) {
8342 // copy values from the child forms
8343 // Roo.each(this.childForms, function (f) {
8344 // this.setValues(f.getValues());
8350 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8351 if(asString === true){
8354 return Roo.urlDecode(fs);
8358 * Returns the fields in this form as an object with key/value pairs.
8359 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8362 getFieldValues : function(with_hidden)
8364 var items = this.getItems();
8366 items.each(function(f){
8372 var v = f.getValue();
8374 if (f.inputType =='radio') {
8375 if (typeof(ret[f.getName()]) == 'undefined') {
8376 ret[f.getName()] = ''; // empty..
8379 if (!f.el.dom.checked) {
8387 if(f.xtype == 'MoneyField'){
8388 ret[f.currencyName] = f.getCurrency();
8391 // not sure if this supported any more..
8392 if ((typeof(v) == 'object') && f.getRawValue) {
8393 v = f.getRawValue() ; // dates..
8395 // combo boxes where name != hiddenName...
8396 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8397 ret[f.name] = f.getRawValue();
8399 ret[f.getName()] = v;
8406 * Clears all invalid messages in this form.
8407 * @return {BasicForm} this
8409 clearInvalid : function(){
8410 var items = this.getItems();
8412 items.each(function(f){
8421 * @return {BasicForm} this
8424 var items = this.getItems();
8425 items.each(function(f){
8429 Roo.each(this.childForms || [], function (f) {
8437 getItems : function()
8439 var r=new Roo.util.MixedCollection(false, function(o){
8440 return o.id || (o.id = Roo.id());
8442 var iter = function(el) {
8449 Roo.each(el.items,function(e) {
8458 hideFields : function(items)
8460 Roo.each(items, function(i){
8462 var f = this.findField(i);
8473 showFields : function(items)
8475 Roo.each(items, function(i){
8477 var f = this.findField(i);
8490 Roo.apply(Roo.bootstrap.Form, {
8517 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8518 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8519 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8520 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8523 this.maskEl.top.enableDisplayMode("block");
8524 this.maskEl.left.enableDisplayMode("block");
8525 this.maskEl.bottom.enableDisplayMode("block");
8526 this.maskEl.right.enableDisplayMode("block");
8528 this.toolTip = new Roo.bootstrap.Tooltip({
8529 cls : 'roo-form-error-popover',
8531 'left' : ['r-l', [-2,0], 'right'],
8532 'right' : ['l-r', [2,0], 'left'],
8533 'bottom' : ['tl-bl', [0,2], 'top'],
8534 'top' : [ 'bl-tl', [0,-2], 'bottom']
8538 this.toolTip.render(Roo.get(document.body));
8540 this.toolTip.el.enableDisplayMode("block");
8542 Roo.get(document.body).on('click', function(){
8546 Roo.get(document.body).on('touchstart', function(){
8550 this.isApplied = true
8553 mask : function(form, target)
8557 this.target = target;
8559 if(!this.form.errorMask || !target.el){
8563 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8565 Roo.log(scrollable);
8567 var ot = this.target.el.calcOffsetsTo(scrollable);
8569 var scrollTo = ot[1] - this.form.maskOffset;
8571 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8573 scrollable.scrollTo('top', scrollTo);
8575 var box = this.target.el.getBox();
8577 var zIndex = Roo.bootstrap.Modal.zIndex++;
8580 this.maskEl.top.setStyle('position', 'absolute');
8581 this.maskEl.top.setStyle('z-index', zIndex);
8582 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8583 this.maskEl.top.setLeft(0);
8584 this.maskEl.top.setTop(0);
8585 this.maskEl.top.show();
8587 this.maskEl.left.setStyle('position', 'absolute');
8588 this.maskEl.left.setStyle('z-index', zIndex);
8589 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8590 this.maskEl.left.setLeft(0);
8591 this.maskEl.left.setTop(box.y - this.padding);
8592 this.maskEl.left.show();
8594 this.maskEl.bottom.setStyle('position', 'absolute');
8595 this.maskEl.bottom.setStyle('z-index', zIndex);
8596 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8597 this.maskEl.bottom.setLeft(0);
8598 this.maskEl.bottom.setTop(box.bottom + this.padding);
8599 this.maskEl.bottom.show();
8601 this.maskEl.right.setStyle('position', 'absolute');
8602 this.maskEl.right.setStyle('z-index', zIndex);
8603 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8604 this.maskEl.right.setLeft(box.right + this.padding);
8605 this.maskEl.right.setTop(box.y - this.padding);
8606 this.maskEl.right.show();
8608 this.toolTip.bindEl = this.target.el;
8610 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8612 var tip = this.target.blankText;
8614 if(this.target.getValue() !== '' ) {
8616 if (this.target.invalidText.length) {
8617 tip = this.target.invalidText;
8618 } else if (this.target.regexText.length){
8619 tip = this.target.regexText;
8623 this.toolTip.show(tip);
8625 this.intervalID = window.setInterval(function() {
8626 Roo.bootstrap.Form.popover.unmask();
8629 window.onwheel = function(){ return false;};
8631 (function(){ this.isMasked = true; }).defer(500, this);
8637 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8641 this.maskEl.top.setStyle('position', 'absolute');
8642 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8643 this.maskEl.top.hide();
8645 this.maskEl.left.setStyle('position', 'absolute');
8646 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8647 this.maskEl.left.hide();
8649 this.maskEl.bottom.setStyle('position', 'absolute');
8650 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8651 this.maskEl.bottom.hide();
8653 this.maskEl.right.setStyle('position', 'absolute');
8654 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8655 this.maskEl.right.hide();
8657 this.toolTip.hide();
8659 this.toolTip.el.hide();
8661 window.onwheel = function(){ return true;};
8663 if(this.intervalID){
8664 window.clearInterval(this.intervalID);
8665 this.intervalID = false;
8668 this.isMasked = false;
8678 * Ext JS Library 1.1.1
8679 * Copyright(c) 2006-2007, Ext JS, LLC.
8681 * Originally Released Under LGPL - original licence link has changed is not relivant.
8684 * <script type="text/javascript">
8687 * @class Roo.form.VTypes
8688 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8691 Roo.form.VTypes = function(){
8692 // closure these in so they are only created once.
8693 var alpha = /^[a-zA-Z_]+$/;
8694 var alphanum = /^[a-zA-Z0-9_]+$/;
8695 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8696 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8698 // All these messages and functions are configurable
8701 * The function used to validate email addresses
8702 * @param {String} value The email address
8704 'email' : function(v){
8705 return email.test(v);
8708 * The error text to display when the email validation function returns false
8711 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8713 * The keystroke filter mask to be applied on email input
8716 'emailMask' : /[a-z0-9_\.\-@]/i,
8719 * The function used to validate URLs
8720 * @param {String} value The URL
8722 'url' : function(v){
8726 * The error text to display when the url validation function returns false
8729 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8732 * The function used to validate alpha values
8733 * @param {String} value The value
8735 'alpha' : function(v){
8736 return alpha.test(v);
8739 * The error text to display when the alpha validation function returns false
8742 'alphaText' : 'This field should only contain letters and _',
8744 * The keystroke filter mask to be applied on alpha input
8747 'alphaMask' : /[a-z_]/i,
8750 * The function used to validate alphanumeric values
8751 * @param {String} value The value
8753 'alphanum' : function(v){
8754 return alphanum.test(v);
8757 * The error text to display when the alphanumeric validation function returns false
8760 'alphanumText' : 'This field should only contain letters, numbers and _',
8762 * The keystroke filter mask to be applied on alphanumeric input
8765 'alphanumMask' : /[a-z0-9_]/i
8775 * @class Roo.bootstrap.Input
8776 * @extends Roo.bootstrap.Component
8777 * Bootstrap Input class
8778 * @cfg {Boolean} disabled is it disabled
8779 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8780 * @cfg {String} name name of the input
8781 * @cfg {string} fieldLabel - the label associated
8782 * @cfg {string} placeholder - placeholder to put in text.
8783 * @cfg {string} before - input group add on before
8784 * @cfg {string} after - input group add on after
8785 * @cfg {string} size - (lg|sm) or leave empty..
8786 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8787 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8788 * @cfg {Number} md colspan out of 12 for computer-sized screens
8789 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8790 * @cfg {string} value default value of the input
8791 * @cfg {Number} labelWidth set the width of label
8792 * @cfg {Number} labellg set the width of label (1-12)
8793 * @cfg {Number} labelmd set the width of label (1-12)
8794 * @cfg {Number} labelsm set the width of label (1-12)
8795 * @cfg {Number} labelxs set the width of label (1-12)
8796 * @cfg {String} labelAlign (top|left)
8797 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8798 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8799 * @cfg {String} indicatorpos (left|right) default left
8800 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8801 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8803 * @cfg {String} align (left|center|right) Default left
8804 * @cfg {Boolean} forceFeedback (true|false) Default false
8807 * Create a new Input
8808 * @param {Object} config The config object
8811 Roo.bootstrap.Input = function(config){
8813 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8818 * Fires when this field receives input focus.
8819 * @param {Roo.form.Field} this
8824 * Fires when this field loses input focus.
8825 * @param {Roo.form.Field} this
8830 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8831 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8832 * @param {Roo.form.Field} this
8833 * @param {Roo.EventObject} e The event object
8838 * Fires just before the field blurs if the field value has changed.
8839 * @param {Roo.form.Field} this
8840 * @param {Mixed} newValue The new value
8841 * @param {Mixed} oldValue The original value
8846 * Fires after the field has been marked as invalid.
8847 * @param {Roo.form.Field} this
8848 * @param {String} msg The validation message
8853 * Fires after the field has been validated with no errors.
8854 * @param {Roo.form.Field} this
8859 * Fires after the key up
8860 * @param {Roo.form.Field} this
8861 * @param {Roo.EventObject} e The event Object
8867 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8869 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8870 automatic validation (defaults to "keyup").
8872 validationEvent : "keyup",
8874 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8876 validateOnBlur : true,
8878 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8880 validationDelay : 250,
8882 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8884 focusClass : "x-form-focus", // not needed???
8888 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8890 invalidClass : "has-warning",
8893 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8895 validClass : "has-success",
8898 * @cfg {Boolean} hasFeedback (true|false) default true
8903 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8905 invalidFeedbackClass : "glyphicon-warning-sign",
8908 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8910 validFeedbackClass : "glyphicon-ok",
8913 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8915 selectOnFocus : false,
8918 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8922 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8927 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8929 disableKeyFilter : false,
8932 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8936 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8940 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8942 blankText : "Please complete this mandatory field",
8945 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8949 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8951 maxLength : Number.MAX_VALUE,
8953 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8955 minLengthText : "The minimum length for this field is {0}",
8957 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8959 maxLengthText : "The maximum length for this field is {0}",
8963 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8964 * If available, this function will be called only after the basic validators all return true, and will be passed the
8965 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8969 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8970 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8971 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8975 * @cfg {String} regexText -- Depricated - use Invalid Text
8980 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8986 autocomplete: false,
9005 formatedValue : false,
9006 forceFeedback : false,
9008 indicatorpos : 'left',
9018 parentLabelAlign : function()
9021 while (parent.parent()) {
9022 parent = parent.parent();
9023 if (typeof(parent.labelAlign) !='undefined') {
9024 return parent.labelAlign;
9031 getAutoCreate : function()
9033 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9039 if(this.inputType != 'hidden'){
9040 cfg.cls = 'form-group' //input-group
9046 type : this.inputType,
9048 cls : 'form-control',
9049 placeholder : this.placeholder || '',
9050 autocomplete : this.autocomplete || 'new-password'
9053 if(this.capture.length){
9054 input.capture = this.capture;
9057 if(this.accept.length){
9058 input.accept = this.accept + "/*";
9062 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9065 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9066 input.maxLength = this.maxLength;
9069 if (this.disabled) {
9070 input.disabled=true;
9073 if (this.readOnly) {
9074 input.readonly=true;
9078 input.name = this.name;
9082 input.cls += ' input-' + this.size;
9086 ['xs','sm','md','lg'].map(function(size){
9087 if (settings[size]) {
9088 cfg.cls += ' col-' + size + '-' + settings[size];
9092 var inputblock = input;
9096 cls: 'glyphicon form-control-feedback'
9099 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9102 cls : 'has-feedback',
9110 if (this.before || this.after) {
9113 cls : 'input-group',
9117 if (this.before && typeof(this.before) == 'string') {
9119 inputblock.cn.push({
9121 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9125 if (this.before && typeof(this.before) == 'object') {
9126 this.before = Roo.factory(this.before);
9128 inputblock.cn.push({
9130 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9131 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9135 inputblock.cn.push(input);
9137 if (this.after && typeof(this.after) == 'string') {
9138 inputblock.cn.push({
9140 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9144 if (this.after && typeof(this.after) == 'object') {
9145 this.after = Roo.factory(this.after);
9147 inputblock.cn.push({
9149 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9150 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9154 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9155 inputblock.cls += ' has-feedback';
9156 inputblock.cn.push(feedback);
9161 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9162 tooltip : 'This field is required'
9164 if (Roo.bootstrap.version == 4) {
9167 style : 'display-none'
9170 if (align ==='left' && this.fieldLabel.length) {
9172 cfg.cls += ' roo-form-group-label-left row';
9179 cls : 'control-label col-form-label',
9180 html : this.fieldLabel
9191 var labelCfg = cfg.cn[1];
9192 var contentCfg = cfg.cn[2];
9194 if(this.indicatorpos == 'right'){
9199 cls : 'control-label col-form-label',
9203 html : this.fieldLabel
9217 labelCfg = cfg.cn[0];
9218 contentCfg = cfg.cn[1];
9222 if(this.labelWidth > 12){
9223 labelCfg.style = "width: " + this.labelWidth + 'px';
9226 if(this.labelWidth < 13 && this.labelmd == 0){
9227 this.labelmd = this.labelWidth;
9230 if(this.labellg > 0){
9231 labelCfg.cls += ' col-lg-' + this.labellg;
9232 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9235 if(this.labelmd > 0){
9236 labelCfg.cls += ' col-md-' + this.labelmd;
9237 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9240 if(this.labelsm > 0){
9241 labelCfg.cls += ' col-sm-' + this.labelsm;
9242 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9245 if(this.labelxs > 0){
9246 labelCfg.cls += ' col-xs-' + this.labelxs;
9247 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9251 } else if ( this.fieldLabel.length) {
9256 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9257 tooltip : 'This field is required'
9261 //cls : 'input-group-addon',
9262 html : this.fieldLabel
9270 if(this.indicatorpos == 'right'){
9275 //cls : 'input-group-addon',
9276 html : this.fieldLabel
9281 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9282 tooltip : 'This field is required'
9302 if (this.parentType === 'Navbar' && this.parent().bar) {
9303 cfg.cls += ' navbar-form';
9306 if (this.parentType === 'NavGroup') {
9307 cfg.cls += ' navbar-form';
9315 * return the real input element.
9317 inputEl: function ()
9319 return this.el.select('input.form-control',true).first();
9322 tooltipEl : function()
9324 return this.inputEl();
9327 indicatorEl : function()
9329 if (Roo.bootstrap.version == 4) {
9330 return false; // not enabled in v4 yet.
9333 var indicator = this.el.select('i.roo-required-indicator',true).first();
9343 setDisabled : function(v)
9345 var i = this.inputEl().dom;
9347 i.removeAttribute('disabled');
9351 i.setAttribute('disabled','true');
9353 initEvents : function()
9356 this.inputEl().on("keydown" , this.fireKey, this);
9357 this.inputEl().on("focus", this.onFocus, this);
9358 this.inputEl().on("blur", this.onBlur, this);
9360 this.inputEl().relayEvent('keyup', this);
9362 this.indicator = this.indicatorEl();
9365 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9368 // reference to original value for reset
9369 this.originalValue = this.getValue();
9370 //Roo.form.TextField.superclass.initEvents.call(this);
9371 if(this.validationEvent == 'keyup'){
9372 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9373 this.inputEl().on('keyup', this.filterValidation, this);
9375 else if(this.validationEvent !== false){
9376 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9379 if(this.selectOnFocus){
9380 this.on("focus", this.preFocus, this);
9383 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9384 this.inputEl().on("keypress", this.filterKeys, this);
9386 this.inputEl().relayEvent('keypress', this);
9389 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9390 this.el.on("click", this.autoSize, this);
9393 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9394 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9397 if (typeof(this.before) == 'object') {
9398 this.before.render(this.el.select('.roo-input-before',true).first());
9400 if (typeof(this.after) == 'object') {
9401 this.after.render(this.el.select('.roo-input-after',true).first());
9404 this.inputEl().on('change', this.onChange, this);
9407 filterValidation : function(e){
9408 if(!e.isNavKeyPress()){
9409 this.validationTask.delay(this.validationDelay);
9413 * Validates the field value
9414 * @return {Boolean} True if the value is valid, else false
9416 validate : function(){
9417 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9418 if(this.disabled || this.validateValue(this.getRawValue())){
9429 * Validates a value according to the field's validation rules and marks the field as invalid
9430 * if the validation fails
9431 * @param {Mixed} value The value to validate
9432 * @return {Boolean} True if the value is valid, else false
9434 validateValue : function(value)
9436 if(this.getVisibilityEl().hasClass('hidden')){
9440 if(value.length < 1) { // if it's blank
9441 if(this.allowBlank){
9447 if(value.length < this.minLength){
9450 if(value.length > this.maxLength){
9454 var vt = Roo.form.VTypes;
9455 if(!vt[this.vtype](value, this)){
9459 if(typeof this.validator == "function"){
9460 var msg = this.validator(value);
9464 if (typeof(msg) == 'string') {
9465 this.invalidText = msg;
9469 if(this.regex && !this.regex.test(value)){
9477 fireKey : function(e){
9478 //Roo.log('field ' + e.getKey());
9479 if(e.isNavKeyPress()){
9480 this.fireEvent("specialkey", this, e);
9483 focus : function (selectText){
9485 this.inputEl().focus();
9486 if(selectText === true){
9487 this.inputEl().dom.select();
9493 onFocus : function(){
9494 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9495 // this.el.addClass(this.focusClass);
9498 this.hasFocus = true;
9499 this.startValue = this.getValue();
9500 this.fireEvent("focus", this);
9504 beforeBlur : Roo.emptyFn,
9508 onBlur : function(){
9510 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9511 //this.el.removeClass(this.focusClass);
9513 this.hasFocus = false;
9514 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9517 var v = this.getValue();
9518 if(String(v) !== String(this.startValue)){
9519 this.fireEvent('change', this, v, this.startValue);
9521 this.fireEvent("blur", this);
9524 onChange : function(e)
9526 var v = this.getValue();
9527 if(String(v) !== String(this.startValue)){
9528 this.fireEvent('change', this, v, this.startValue);
9534 * Resets the current field value to the originally loaded value and clears any validation messages
9537 this.setValue(this.originalValue);
9541 * Returns the name of the field
9542 * @return {Mixed} name The name field
9544 getName: function(){
9548 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9549 * @return {Mixed} value The field value
9551 getValue : function(){
9553 var v = this.inputEl().getValue();
9558 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9559 * @return {Mixed} value The field value
9561 getRawValue : function(){
9562 var v = this.inputEl().getValue();
9568 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9569 * @param {Mixed} value The value to set
9571 setRawValue : function(v){
9572 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9575 selectText : function(start, end){
9576 var v = this.getRawValue();
9578 start = start === undefined ? 0 : start;
9579 end = end === undefined ? v.length : end;
9580 var d = this.inputEl().dom;
9581 if(d.setSelectionRange){
9582 d.setSelectionRange(start, end);
9583 }else if(d.createTextRange){
9584 var range = d.createTextRange();
9585 range.moveStart("character", start);
9586 range.moveEnd("character", v.length-end);
9593 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9594 * @param {Mixed} value The value to set
9596 setValue : function(v){
9599 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9605 processValue : function(value){
9606 if(this.stripCharsRe){
9607 var newValue = value.replace(this.stripCharsRe, '');
9608 if(newValue !== value){
9609 this.setRawValue(newValue);
9616 preFocus : function(){
9618 if(this.selectOnFocus){
9619 this.inputEl().dom.select();
9622 filterKeys : function(e){
9624 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9627 var c = e.getCharCode(), cc = String.fromCharCode(c);
9628 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9631 if(!this.maskRe.test(cc)){
9636 * Clear any invalid styles/messages for this field
9638 clearInvalid : function(){
9640 if(!this.el || this.preventMark){ // not rendered
9645 this.el.removeClass(this.invalidClass);
9647 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9649 var feedback = this.el.select('.form-control-feedback', true).first();
9652 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9658 this.indicator.removeClass('visible');
9659 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9662 this.fireEvent('valid', this);
9666 * Mark this field as valid
9668 markValid : function()
9670 if(!this.el || this.preventMark){ // not rendered...
9674 this.el.removeClass([this.invalidClass, this.validClass]);
9676 var feedback = this.el.select('.form-control-feedback', true).first();
9679 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9683 this.indicator.removeClass('visible');
9684 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9691 if(this.allowBlank && !this.getRawValue().length){
9695 this.el.addClass(this.validClass);
9697 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9699 var feedback = this.el.select('.form-control-feedback', true).first();
9702 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9703 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9708 this.fireEvent('valid', this);
9712 * Mark this field as invalid
9713 * @param {String} msg The validation message
9715 markInvalid : function(msg)
9717 if(!this.el || this.preventMark){ // not rendered
9721 this.el.removeClass([this.invalidClass, this.validClass]);
9723 var feedback = this.el.select('.form-control-feedback', true).first();
9726 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9733 if(this.allowBlank && !this.getRawValue().length){
9738 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9739 this.indicator.addClass('visible');
9742 this.el.addClass(this.invalidClass);
9744 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9746 var feedback = this.el.select('.form-control-feedback', true).first();
9749 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9751 if(this.getValue().length || this.forceFeedback){
9752 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9759 this.fireEvent('invalid', this, msg);
9762 SafariOnKeyDown : function(event)
9764 // this is a workaround for a password hang bug on chrome/ webkit.
9765 if (this.inputEl().dom.type != 'password') {
9769 var isSelectAll = false;
9771 if(this.inputEl().dom.selectionEnd > 0){
9772 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9774 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9775 event.preventDefault();
9780 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9782 event.preventDefault();
9783 // this is very hacky as keydown always get's upper case.
9785 var cc = String.fromCharCode(event.getCharCode());
9786 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9790 adjustWidth : function(tag, w){
9791 tag = tag.toLowerCase();
9792 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9793 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9797 if(tag == 'textarea'){
9800 }else if(Roo.isOpera){
9804 if(tag == 'textarea'){
9812 setFieldLabel : function(v)
9818 if(this.indicatorEl()){
9819 var ar = this.el.select('label > span',true);
9821 if (ar.elements.length) {
9822 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9823 this.fieldLabel = v;
9827 var br = this.el.select('label',true);
9829 if(br.elements.length) {
9830 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9831 this.fieldLabel = v;
9835 Roo.log('Cannot Found any of label > span || label in input');
9839 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9840 this.fieldLabel = v;
9855 * @class Roo.bootstrap.TextArea
9856 * @extends Roo.bootstrap.Input
9857 * Bootstrap TextArea class
9858 * @cfg {Number} cols Specifies the visible width of a text area
9859 * @cfg {Number} rows Specifies the visible number of lines in a text area
9860 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9861 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9862 * @cfg {string} html text
9865 * Create a new TextArea
9866 * @param {Object} config The config object
9869 Roo.bootstrap.TextArea = function(config){
9870 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9874 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9884 getAutoCreate : function(){
9886 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9892 if(this.inputType != 'hidden'){
9893 cfg.cls = 'form-group' //input-group
9901 value : this.value || '',
9902 html: this.html || '',
9903 cls : 'form-control',
9904 placeholder : this.placeholder || ''
9908 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9909 input.maxLength = this.maxLength;
9913 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9917 input.cols = this.cols;
9920 if (this.readOnly) {
9921 input.readonly = true;
9925 input.name = this.name;
9929 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9933 ['xs','sm','md','lg'].map(function(size){
9934 if (settings[size]) {
9935 cfg.cls += ' col-' + size + '-' + settings[size];
9939 var inputblock = input;
9941 if(this.hasFeedback && !this.allowBlank){
9945 cls: 'glyphicon form-control-feedback'
9949 cls : 'has-feedback',
9958 if (this.before || this.after) {
9961 cls : 'input-group',
9965 inputblock.cn.push({
9967 cls : 'input-group-addon',
9972 inputblock.cn.push(input);
9974 if(this.hasFeedback && !this.allowBlank){
9975 inputblock.cls += ' has-feedback';
9976 inputblock.cn.push(feedback);
9980 inputblock.cn.push({
9982 cls : 'input-group-addon',
9989 if (align ==='left' && this.fieldLabel.length) {
9994 cls : 'control-label',
9995 html : this.fieldLabel
10006 if(this.labelWidth > 12){
10007 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10010 if(this.labelWidth < 13 && this.labelmd == 0){
10011 this.labelmd = this.labelWidth;
10014 if(this.labellg > 0){
10015 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10016 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10019 if(this.labelmd > 0){
10020 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10021 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10024 if(this.labelsm > 0){
10025 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10026 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10029 if(this.labelxs > 0){
10030 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10031 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10034 } else if ( this.fieldLabel.length) {
10039 //cls : 'input-group-addon',
10040 html : this.fieldLabel
10058 if (this.disabled) {
10059 input.disabled=true;
10066 * return the real textarea element.
10068 inputEl: function ()
10070 return this.el.select('textarea.form-control',true).first();
10074 * Clear any invalid styles/messages for this field
10076 clearInvalid : function()
10079 if(!this.el || this.preventMark){ // not rendered
10083 var label = this.el.select('label', true).first();
10084 var icon = this.el.select('i.fa-star', true).first();
10090 this.el.removeClass(this.invalidClass);
10092 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10094 var feedback = this.el.select('.form-control-feedback', true).first();
10097 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10102 this.fireEvent('valid', this);
10106 * Mark this field as valid
10108 markValid : function()
10110 if(!this.el || this.preventMark){ // not rendered
10114 this.el.removeClass([this.invalidClass, this.validClass]);
10116 var feedback = this.el.select('.form-control-feedback', true).first();
10119 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10122 if(this.disabled || this.allowBlank){
10126 var label = this.el.select('label', true).first();
10127 var icon = this.el.select('i.fa-star', true).first();
10133 this.el.addClass(this.validClass);
10135 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10137 var feedback = this.el.select('.form-control-feedback', true).first();
10140 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10141 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10146 this.fireEvent('valid', this);
10150 * Mark this field as invalid
10151 * @param {String} msg The validation message
10153 markInvalid : function(msg)
10155 if(!this.el || this.preventMark){ // not rendered
10159 this.el.removeClass([this.invalidClass, this.validClass]);
10161 var feedback = this.el.select('.form-control-feedback', true).first();
10164 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10167 if(this.disabled || this.allowBlank){
10171 var label = this.el.select('label', true).first();
10172 var icon = this.el.select('i.fa-star', true).first();
10174 if(!this.getValue().length && label && !icon){
10175 this.el.createChild({
10177 cls : 'text-danger fa fa-lg fa-star',
10178 tooltip : 'This field is required',
10179 style : 'margin-right:5px;'
10183 this.el.addClass(this.invalidClass);
10185 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10187 var feedback = this.el.select('.form-control-feedback', true).first();
10190 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10192 if(this.getValue().length || this.forceFeedback){
10193 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10200 this.fireEvent('invalid', this, msg);
10208 * trigger field - base class for combo..
10213 * @class Roo.bootstrap.TriggerField
10214 * @extends Roo.bootstrap.Input
10215 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10216 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10217 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10218 * for which you can provide a custom implementation. For example:
10220 var trigger = new Roo.bootstrap.TriggerField();
10221 trigger.onTriggerClick = myTriggerFn;
10222 trigger.applyTo('my-field');
10225 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10226 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10227 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10228 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10229 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10232 * Create a new TriggerField.
10233 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10234 * to the base TextField)
10236 Roo.bootstrap.TriggerField = function(config){
10237 this.mimicing = false;
10238 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10241 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10243 * @cfg {String} triggerClass A CSS class to apply to the trigger
10246 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10251 * @cfg {Boolean} removable (true|false) special filter default false
10255 /** @cfg {Boolean} grow @hide */
10256 /** @cfg {Number} growMin @hide */
10257 /** @cfg {Number} growMax @hide */
10263 autoSize: Roo.emptyFn,
10267 deferHeight : true,
10270 actionMode : 'wrap',
10275 getAutoCreate : function(){
10277 var align = this.labelAlign || this.parentLabelAlign();
10282 cls: 'form-group' //input-group
10289 type : this.inputType,
10290 cls : 'form-control',
10291 autocomplete: 'new-password',
10292 placeholder : this.placeholder || ''
10296 input.name = this.name;
10299 input.cls += ' input-' + this.size;
10302 if (this.disabled) {
10303 input.disabled=true;
10306 var inputblock = input;
10308 if(this.hasFeedback && !this.allowBlank){
10312 cls: 'glyphicon form-control-feedback'
10315 if(this.removable && !this.editable && !this.tickable){
10317 cls : 'has-feedback',
10323 cls : 'roo-combo-removable-btn close'
10330 cls : 'has-feedback',
10339 if(this.removable && !this.editable && !this.tickable){
10341 cls : 'roo-removable',
10347 cls : 'roo-combo-removable-btn close'
10354 if (this.before || this.after) {
10357 cls : 'input-group',
10361 inputblock.cn.push({
10363 cls : 'input-group-addon input-group-prepend input-group-text',
10368 inputblock.cn.push(input);
10370 if(this.hasFeedback && !this.allowBlank){
10371 inputblock.cls += ' has-feedback';
10372 inputblock.cn.push(feedback);
10376 inputblock.cn.push({
10378 cls : 'input-group-addon input-group-append input-group-text',
10387 var ibwrap = inputblock;
10392 cls: 'roo-select2-choices',
10396 cls: 'roo-select2-search-field',
10408 cls: 'roo-select2-container input-group',
10413 cls: 'form-hidden-field'
10419 if(!this.multiple && this.showToggleBtn){
10425 if (this.caret != false) {
10428 cls: 'fa fa-' + this.caret
10435 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10440 cls: 'combobox-clear',
10454 combobox.cls += ' roo-select2-container-multi';
10458 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10459 tooltip : 'This field is required'
10461 if (Roo.bootstrap.version == 4) {
10464 style : 'display:none'
10469 if (align ==='left' && this.fieldLabel.length) {
10471 cfg.cls += ' roo-form-group-label-left row';
10478 cls : 'control-label',
10479 html : this.fieldLabel
10491 var labelCfg = cfg.cn[1];
10492 var contentCfg = cfg.cn[2];
10494 if(this.indicatorpos == 'right'){
10499 cls : 'control-label',
10503 html : this.fieldLabel
10517 labelCfg = cfg.cn[0];
10518 contentCfg = cfg.cn[1];
10521 if(this.labelWidth > 12){
10522 labelCfg.style = "width: " + this.labelWidth + 'px';
10525 if(this.labelWidth < 13 && this.labelmd == 0){
10526 this.labelmd = this.labelWidth;
10529 if(this.labellg > 0){
10530 labelCfg.cls += ' col-lg-' + this.labellg;
10531 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10534 if(this.labelmd > 0){
10535 labelCfg.cls += ' col-md-' + this.labelmd;
10536 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10539 if(this.labelsm > 0){
10540 labelCfg.cls += ' col-sm-' + this.labelsm;
10541 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10544 if(this.labelxs > 0){
10545 labelCfg.cls += ' col-xs-' + this.labelxs;
10546 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10549 } else if ( this.fieldLabel.length) {
10550 // Roo.log(" label");
10555 //cls : 'input-group-addon',
10556 html : this.fieldLabel
10564 if(this.indicatorpos == 'right'){
10572 html : this.fieldLabel
10586 // Roo.log(" no label && no align");
10593 ['xs','sm','md','lg'].map(function(size){
10594 if (settings[size]) {
10595 cfg.cls += ' col-' + size + '-' + settings[size];
10606 onResize : function(w, h){
10607 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10608 // if(typeof w == 'number'){
10609 // var x = w - this.trigger.getWidth();
10610 // this.inputEl().setWidth(this.adjustWidth('input', x));
10611 // this.trigger.setStyle('left', x+'px');
10616 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10619 getResizeEl : function(){
10620 return this.inputEl();
10624 getPositionEl : function(){
10625 return this.inputEl();
10629 alignErrorIcon : function(){
10630 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10634 initEvents : function(){
10638 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10639 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10640 if(!this.multiple && this.showToggleBtn){
10641 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10642 if(this.hideTrigger){
10643 this.trigger.setDisplayed(false);
10645 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10649 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10652 if(this.removable && !this.editable && !this.tickable){
10653 var close = this.closeTriggerEl();
10656 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10657 close.on('click', this.removeBtnClick, this, close);
10661 //this.trigger.addClassOnOver('x-form-trigger-over');
10662 //this.trigger.addClassOnClick('x-form-trigger-click');
10665 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10669 closeTriggerEl : function()
10671 var close = this.el.select('.roo-combo-removable-btn', true).first();
10672 return close ? close : false;
10675 removeBtnClick : function(e, h, el)
10677 e.preventDefault();
10679 if(this.fireEvent("remove", this) !== false){
10681 this.fireEvent("afterremove", this)
10685 createList : function()
10687 this.list = Roo.get(document.body).createChild({
10688 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10689 cls: 'typeahead typeahead-long dropdown-menu',
10690 style: 'display:none'
10693 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10698 initTrigger : function(){
10703 onDestroy : function(){
10705 this.trigger.removeAllListeners();
10706 // this.trigger.remove();
10709 // this.wrap.remove();
10711 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10715 onFocus : function(){
10716 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10718 if(!this.mimicing){
10719 this.wrap.addClass('x-trigger-wrap-focus');
10720 this.mimicing = true;
10721 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10722 if(this.monitorTab){
10723 this.el.on("keydown", this.checkTab, this);
10730 checkTab : function(e){
10731 if(e.getKey() == e.TAB){
10732 this.triggerBlur();
10737 onBlur : function(){
10742 mimicBlur : function(e, t){
10744 if(!this.wrap.contains(t) && this.validateBlur()){
10745 this.triggerBlur();
10751 triggerBlur : function(){
10752 this.mimicing = false;
10753 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10754 if(this.monitorTab){
10755 this.el.un("keydown", this.checkTab, this);
10757 //this.wrap.removeClass('x-trigger-wrap-focus');
10758 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10762 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10763 validateBlur : function(e, t){
10768 onDisable : function(){
10769 this.inputEl().dom.disabled = true;
10770 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10772 // this.wrap.addClass('x-item-disabled');
10777 onEnable : function(){
10778 this.inputEl().dom.disabled = false;
10779 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10781 // this.el.removeClass('x-item-disabled');
10786 onShow : function(){
10787 var ae = this.getActionEl();
10790 ae.dom.style.display = '';
10791 ae.dom.style.visibility = 'visible';
10797 onHide : function(){
10798 var ae = this.getActionEl();
10799 ae.dom.style.display = 'none';
10803 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10804 * by an implementing function.
10806 * @param {EventObject} e
10808 onTriggerClick : Roo.emptyFn
10812 * Ext JS Library 1.1.1
10813 * Copyright(c) 2006-2007, Ext JS, LLC.
10815 * Originally Released Under LGPL - original licence link has changed is not relivant.
10818 * <script type="text/javascript">
10823 * @class Roo.data.SortTypes
10825 * Defines the default sorting (casting?) comparison functions used when sorting data.
10827 Roo.data.SortTypes = {
10829 * Default sort that does nothing
10830 * @param {Mixed} s The value being converted
10831 * @return {Mixed} The comparison value
10833 none : function(s){
10838 * The regular expression used to strip tags
10842 stripTagsRE : /<\/?[^>]+>/gi,
10845 * Strips all HTML tags to sort on text only
10846 * @param {Mixed} s The value being converted
10847 * @return {String} The comparison value
10849 asText : function(s){
10850 return String(s).replace(this.stripTagsRE, "");
10854 * Strips all HTML tags to sort on text only - Case insensitive
10855 * @param {Mixed} s The value being converted
10856 * @return {String} The comparison value
10858 asUCText : function(s){
10859 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10863 * Case insensitive string
10864 * @param {Mixed} s The value being converted
10865 * @return {String} The comparison value
10867 asUCString : function(s) {
10868 return String(s).toUpperCase();
10873 * @param {Mixed} s The value being converted
10874 * @return {Number} The comparison value
10876 asDate : function(s) {
10880 if(s instanceof Date){
10881 return s.getTime();
10883 return Date.parse(String(s));
10888 * @param {Mixed} s The value being converted
10889 * @return {Float} The comparison value
10891 asFloat : function(s) {
10892 var val = parseFloat(String(s).replace(/,/g, ""));
10901 * @param {Mixed} s The value being converted
10902 * @return {Number} The comparison value
10904 asInt : function(s) {
10905 var val = parseInt(String(s).replace(/,/g, ""));
10913 * Ext JS Library 1.1.1
10914 * Copyright(c) 2006-2007, Ext JS, LLC.
10916 * Originally Released Under LGPL - original licence link has changed is not relivant.
10919 * <script type="text/javascript">
10923 * @class Roo.data.Record
10924 * Instances of this class encapsulate both record <em>definition</em> information, and record
10925 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10926 * to access Records cached in an {@link Roo.data.Store} object.<br>
10928 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10929 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10932 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10934 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10935 * {@link #create}. The parameters are the same.
10936 * @param {Array} data An associative Array of data values keyed by the field name.
10937 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10938 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10939 * not specified an integer id is generated.
10941 Roo.data.Record = function(data, id){
10942 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10947 * Generate a constructor for a specific record layout.
10948 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10949 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10950 * Each field definition object may contain the following properties: <ul>
10951 * <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,
10952 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10953 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10954 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10955 * is being used, then this is a string containing the javascript expression to reference the data relative to
10956 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10957 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10958 * this may be omitted.</p></li>
10959 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10960 * <ul><li>auto (Default, implies no conversion)</li>
10965 * <li>date</li></ul></p></li>
10966 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10967 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10968 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10969 * by the Reader into an object that will be stored in the Record. It is passed the
10970 * following parameters:<ul>
10971 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10973 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10975 * <br>usage:<br><pre><code>
10976 var TopicRecord = Roo.data.Record.create(
10977 {name: 'title', mapping: 'topic_title'},
10978 {name: 'author', mapping: 'username'},
10979 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10980 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10981 {name: 'lastPoster', mapping: 'user2'},
10982 {name: 'excerpt', mapping: 'post_text'}
10985 var myNewRecord = new TopicRecord({
10986 title: 'Do my job please',
10989 lastPost: new Date(),
10990 lastPoster: 'Animal',
10991 excerpt: 'No way dude!'
10993 myStore.add(myNewRecord);
10998 Roo.data.Record.create = function(o){
10999 var f = function(){
11000 f.superclass.constructor.apply(this, arguments);
11002 Roo.extend(f, Roo.data.Record);
11003 var p = f.prototype;
11004 p.fields = new Roo.util.MixedCollection(false, function(field){
11007 for(var i = 0, len = o.length; i < len; i++){
11008 p.fields.add(new Roo.data.Field(o[i]));
11010 f.getField = function(name){
11011 return p.fields.get(name);
11016 Roo.data.Record.AUTO_ID = 1000;
11017 Roo.data.Record.EDIT = 'edit';
11018 Roo.data.Record.REJECT = 'reject';
11019 Roo.data.Record.COMMIT = 'commit';
11021 Roo.data.Record.prototype = {
11023 * Readonly flag - true if this record has been modified.
11032 join : function(store){
11033 this.store = store;
11037 * Set the named field to the specified value.
11038 * @param {String} name The name of the field to set.
11039 * @param {Object} value The value to set the field to.
11041 set : function(name, value){
11042 if(this.data[name] == value){
11046 if(!this.modified){
11047 this.modified = {};
11049 if(typeof this.modified[name] == 'undefined'){
11050 this.modified[name] = this.data[name];
11052 this.data[name] = value;
11053 if(!this.editing && this.store){
11054 this.store.afterEdit(this);
11059 * Get the value of the named field.
11060 * @param {String} name The name of the field to get the value of.
11061 * @return {Object} The value of the field.
11063 get : function(name){
11064 return this.data[name];
11068 beginEdit : function(){
11069 this.editing = true;
11070 this.modified = {};
11074 cancelEdit : function(){
11075 this.editing = false;
11076 delete this.modified;
11080 endEdit : function(){
11081 this.editing = false;
11082 if(this.dirty && this.store){
11083 this.store.afterEdit(this);
11088 * Usually called by the {@link Roo.data.Store} which owns the Record.
11089 * Rejects all changes made to the Record since either creation, or the last commit operation.
11090 * Modified fields are reverted to their original values.
11092 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11093 * of reject operations.
11095 reject : function(){
11096 var m = this.modified;
11098 if(typeof m[n] != "function"){
11099 this.data[n] = m[n];
11102 this.dirty = false;
11103 delete this.modified;
11104 this.editing = false;
11106 this.store.afterReject(this);
11111 * Usually called by the {@link Roo.data.Store} which owns the Record.
11112 * Commits all changes made to the Record since either creation, or the last commit operation.
11114 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11115 * of commit operations.
11117 commit : function(){
11118 this.dirty = false;
11119 delete this.modified;
11120 this.editing = false;
11122 this.store.afterCommit(this);
11127 hasError : function(){
11128 return this.error != null;
11132 clearError : function(){
11137 * Creates a copy of this record.
11138 * @param {String} id (optional) A new record id if you don't want to use this record's id
11141 copy : function(newId) {
11142 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11146 * Ext JS Library 1.1.1
11147 * Copyright(c) 2006-2007, Ext JS, LLC.
11149 * Originally Released Under LGPL - original licence link has changed is not relivant.
11152 * <script type="text/javascript">
11158 * @class Roo.data.Store
11159 * @extends Roo.util.Observable
11160 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11161 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11163 * 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
11164 * has no knowledge of the format of the data returned by the Proxy.<br>
11166 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11167 * instances from the data object. These records are cached and made available through accessor functions.
11169 * Creates a new Store.
11170 * @param {Object} config A config object containing the objects needed for the Store to access data,
11171 * and read the data into Records.
11173 Roo.data.Store = function(config){
11174 this.data = new Roo.util.MixedCollection(false);
11175 this.data.getKey = function(o){
11178 this.baseParams = {};
11180 this.paramNames = {
11185 "multisort" : "_multisort"
11188 if(config && config.data){
11189 this.inlineData = config.data;
11190 delete config.data;
11193 Roo.apply(this, config);
11195 if(this.reader){ // reader passed
11196 this.reader = Roo.factory(this.reader, Roo.data);
11197 this.reader.xmodule = this.xmodule || false;
11198 if(!this.recordType){
11199 this.recordType = this.reader.recordType;
11201 if(this.reader.onMetaChange){
11202 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11206 if(this.recordType){
11207 this.fields = this.recordType.prototype.fields;
11209 this.modified = [];
11213 * @event datachanged
11214 * Fires when the data cache has changed, and a widget which is using this Store
11215 * as a Record cache should refresh its view.
11216 * @param {Store} this
11218 datachanged : true,
11220 * @event metachange
11221 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11222 * @param {Store} this
11223 * @param {Object} meta The JSON metadata
11228 * Fires when Records have been added to the Store
11229 * @param {Store} this
11230 * @param {Roo.data.Record[]} records The array of Records added
11231 * @param {Number} index The index at which the record(s) were added
11236 * Fires when a Record has been removed from the Store
11237 * @param {Store} this
11238 * @param {Roo.data.Record} record The Record that was removed
11239 * @param {Number} index The index at which the record was removed
11244 * Fires when a Record has been updated
11245 * @param {Store} this
11246 * @param {Roo.data.Record} record The Record that was updated
11247 * @param {String} operation The update operation being performed. Value may be one of:
11249 Roo.data.Record.EDIT
11250 Roo.data.Record.REJECT
11251 Roo.data.Record.COMMIT
11257 * Fires when the data cache has been cleared.
11258 * @param {Store} this
11262 * @event beforeload
11263 * Fires before a request is made for a new data object. If the beforeload handler returns false
11264 * the load action will be canceled.
11265 * @param {Store} this
11266 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11270 * @event beforeloadadd
11271 * Fires after a new set of Records has been loaded.
11272 * @param {Store} this
11273 * @param {Roo.data.Record[]} records The Records that were loaded
11274 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11276 beforeloadadd : true,
11279 * Fires after a new set of Records has been loaded, before they are added to the store.
11280 * @param {Store} this
11281 * @param {Roo.data.Record[]} records The Records that were loaded
11282 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11283 * @params {Object} return from reader
11287 * @event loadexception
11288 * Fires if an exception occurs in the Proxy during loading.
11289 * Called with the signature of the Proxy's "loadexception" event.
11290 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11293 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11294 * @param {Object} load options
11295 * @param {Object} jsonData from your request (normally this contains the Exception)
11297 loadexception : true
11301 this.proxy = Roo.factory(this.proxy, Roo.data);
11302 this.proxy.xmodule = this.xmodule || false;
11303 this.relayEvents(this.proxy, ["loadexception"]);
11305 this.sortToggle = {};
11306 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11308 Roo.data.Store.superclass.constructor.call(this);
11310 if(this.inlineData){
11311 this.loadData(this.inlineData);
11312 delete this.inlineData;
11316 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11318 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11319 * without a remote query - used by combo/forms at present.
11323 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11326 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11329 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11330 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11333 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11334 * on any HTTP request
11337 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11340 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11344 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11345 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11347 remoteSort : false,
11350 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11351 * loaded or when a record is removed. (defaults to false).
11353 pruneModifiedRecords : false,
11356 lastOptions : null,
11359 * Add Records to the Store and fires the add event.
11360 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11362 add : function(records){
11363 records = [].concat(records);
11364 for(var i = 0, len = records.length; i < len; i++){
11365 records[i].join(this);
11367 var index = this.data.length;
11368 this.data.addAll(records);
11369 this.fireEvent("add", this, records, index);
11373 * Remove a Record from the Store and fires the remove event.
11374 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11376 remove : function(record){
11377 var index = this.data.indexOf(record);
11378 this.data.removeAt(index);
11380 if(this.pruneModifiedRecords){
11381 this.modified.remove(record);
11383 this.fireEvent("remove", this, record, index);
11387 * Remove all Records from the Store and fires the clear event.
11389 removeAll : function(){
11391 if(this.pruneModifiedRecords){
11392 this.modified = [];
11394 this.fireEvent("clear", this);
11398 * Inserts Records to the Store at the given index and fires the add event.
11399 * @param {Number} index The start index at which to insert the passed Records.
11400 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11402 insert : function(index, records){
11403 records = [].concat(records);
11404 for(var i = 0, len = records.length; i < len; i++){
11405 this.data.insert(index, records[i]);
11406 records[i].join(this);
11408 this.fireEvent("add", this, records, index);
11412 * Get the index within the cache of the passed Record.
11413 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11414 * @return {Number} The index of the passed Record. Returns -1 if not found.
11416 indexOf : function(record){
11417 return this.data.indexOf(record);
11421 * Get the index within the cache of the Record with the passed id.
11422 * @param {String} id The id of the Record to find.
11423 * @return {Number} The index of the Record. Returns -1 if not found.
11425 indexOfId : function(id){
11426 return this.data.indexOfKey(id);
11430 * Get the Record with the specified id.
11431 * @param {String} id The id of the Record to find.
11432 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11434 getById : function(id){
11435 return this.data.key(id);
11439 * Get the Record at the specified index.
11440 * @param {Number} index The index of the Record to find.
11441 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11443 getAt : function(index){
11444 return this.data.itemAt(index);
11448 * Returns a range of Records between specified indices.
11449 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11450 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11451 * @return {Roo.data.Record[]} An array of Records
11453 getRange : function(start, end){
11454 return this.data.getRange(start, end);
11458 storeOptions : function(o){
11459 o = Roo.apply({}, o);
11462 this.lastOptions = o;
11466 * Loads the Record cache from the configured Proxy using the configured Reader.
11468 * If using remote paging, then the first load call must specify the <em>start</em>
11469 * and <em>limit</em> properties in the options.params property to establish the initial
11470 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11472 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11473 * and this call will return before the new data has been loaded. Perform any post-processing
11474 * in a callback function, or in a "load" event handler.</strong>
11476 * @param {Object} options An object containing properties which control loading options:<ul>
11477 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11478 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11479 * passed the following arguments:<ul>
11480 * <li>r : Roo.data.Record[]</li>
11481 * <li>options: Options object from the load call</li>
11482 * <li>success: Boolean success indicator</li></ul></li>
11483 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11484 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11487 load : function(options){
11488 options = options || {};
11489 if(this.fireEvent("beforeload", this, options) !== false){
11490 this.storeOptions(options);
11491 var p = Roo.apply(options.params || {}, this.baseParams);
11492 // if meta was not loaded from remote source.. try requesting it.
11493 if (!this.reader.metaFromRemote) {
11494 p._requestMeta = 1;
11496 if(this.sortInfo && this.remoteSort){
11497 var pn = this.paramNames;
11498 p[pn["sort"]] = this.sortInfo.field;
11499 p[pn["dir"]] = this.sortInfo.direction;
11501 if (this.multiSort) {
11502 var pn = this.paramNames;
11503 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11506 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11511 * Reloads the Record cache from the configured Proxy using the configured Reader and
11512 * the options from the last load operation performed.
11513 * @param {Object} options (optional) An object containing properties which may override the options
11514 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11515 * the most recently used options are reused).
11517 reload : function(options){
11518 this.load(Roo.applyIf(options||{}, this.lastOptions));
11522 // Called as a callback by the Reader during a load operation.
11523 loadRecords : function(o, options, success){
11524 if(!o || success === false){
11525 if(success !== false){
11526 this.fireEvent("load", this, [], options, o);
11528 if(options.callback){
11529 options.callback.call(options.scope || this, [], options, false);
11533 // if data returned failure - throw an exception.
11534 if (o.success === false) {
11535 // show a message if no listener is registered.
11536 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11537 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11539 // loadmask wil be hooked into this..
11540 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11543 var r = o.records, t = o.totalRecords || r.length;
11545 this.fireEvent("beforeloadadd", this, r, options, o);
11547 if(!options || options.add !== true){
11548 if(this.pruneModifiedRecords){
11549 this.modified = [];
11551 for(var i = 0, len = r.length; i < len; i++){
11555 this.data = this.snapshot;
11556 delete this.snapshot;
11559 this.data.addAll(r);
11560 this.totalLength = t;
11562 this.fireEvent("datachanged", this);
11564 this.totalLength = Math.max(t, this.data.length+r.length);
11568 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11570 var e = new Roo.data.Record({});
11572 e.set(this.parent.displayField, this.parent.emptyTitle);
11573 e.set(this.parent.valueField, '');
11578 this.fireEvent("load", this, r, options, o);
11579 if(options.callback){
11580 options.callback.call(options.scope || this, r, options, true);
11586 * Loads data from a passed data block. A Reader which understands the format of the data
11587 * must have been configured in the constructor.
11588 * @param {Object} data The data block from which to read the Records. The format of the data expected
11589 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11590 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11592 loadData : function(o, append){
11593 var r = this.reader.readRecords(o);
11594 this.loadRecords(r, {add: append}, true);
11598 * Gets the number of cached records.
11600 * <em>If using paging, this may not be the total size of the dataset. If the data object
11601 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11602 * the data set size</em>
11604 getCount : function(){
11605 return this.data.length || 0;
11609 * Gets the total number of records in the dataset as returned by the server.
11611 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11612 * the dataset size</em>
11614 getTotalCount : function(){
11615 return this.totalLength || 0;
11619 * Returns the sort state of the Store as an object with two properties:
11621 field {String} The name of the field by which the Records are sorted
11622 direction {String} The sort order, "ASC" or "DESC"
11625 getSortState : function(){
11626 return this.sortInfo;
11630 applySort : function(){
11631 if(this.sortInfo && !this.remoteSort){
11632 var s = this.sortInfo, f = s.field;
11633 var st = this.fields.get(f).sortType;
11634 var fn = function(r1, r2){
11635 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11636 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11638 this.data.sort(s.direction, fn);
11639 if(this.snapshot && this.snapshot != this.data){
11640 this.snapshot.sort(s.direction, fn);
11646 * Sets the default sort column and order to be used by the next load operation.
11647 * @param {String} fieldName The name of the field to sort by.
11648 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11650 setDefaultSort : function(field, dir){
11651 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11655 * Sort the Records.
11656 * If remote sorting is used, the sort is performed on the server, and the cache is
11657 * reloaded. If local sorting is used, the cache is sorted internally.
11658 * @param {String} fieldName The name of the field to sort by.
11659 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11661 sort : function(fieldName, dir){
11662 var f = this.fields.get(fieldName);
11664 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11666 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11667 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11672 this.sortToggle[f.name] = dir;
11673 this.sortInfo = {field: f.name, direction: dir};
11674 if(!this.remoteSort){
11676 this.fireEvent("datachanged", this);
11678 this.load(this.lastOptions);
11683 * Calls the specified function for each of the Records in the cache.
11684 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11685 * Returning <em>false</em> aborts and exits the iteration.
11686 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11688 each : function(fn, scope){
11689 this.data.each(fn, scope);
11693 * Gets all records modified since the last commit. Modified records are persisted across load operations
11694 * (e.g., during paging).
11695 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11697 getModifiedRecords : function(){
11698 return this.modified;
11702 createFilterFn : function(property, value, anyMatch){
11703 if(!value.exec){ // not a regex
11704 value = String(value);
11705 if(value.length == 0){
11708 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11710 return function(r){
11711 return value.test(r.data[property]);
11716 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11717 * @param {String} property A field on your records
11718 * @param {Number} start The record index to start at (defaults to 0)
11719 * @param {Number} end The last record index to include (defaults to length - 1)
11720 * @return {Number} The sum
11722 sum : function(property, start, end){
11723 var rs = this.data.items, v = 0;
11724 start = start || 0;
11725 end = (end || end === 0) ? end : rs.length-1;
11727 for(var i = start; i <= end; i++){
11728 v += (rs[i].data[property] || 0);
11734 * Filter the records by a specified property.
11735 * @param {String} field A field on your records
11736 * @param {String/RegExp} value Either a string that the field
11737 * should start with or a RegExp to test against the field
11738 * @param {Boolean} anyMatch True to match any part not just the beginning
11740 filter : function(property, value, anyMatch){
11741 var fn = this.createFilterFn(property, value, anyMatch);
11742 return fn ? this.filterBy(fn) : this.clearFilter();
11746 * Filter by a function. The specified function will be called with each
11747 * record in this data source. If the function returns true the record is included,
11748 * otherwise it is filtered.
11749 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11750 * @param {Object} scope (optional) The scope of the function (defaults to this)
11752 filterBy : function(fn, scope){
11753 this.snapshot = this.snapshot || this.data;
11754 this.data = this.queryBy(fn, scope||this);
11755 this.fireEvent("datachanged", this);
11759 * Query the records by a specified property.
11760 * @param {String} field A field on your records
11761 * @param {String/RegExp} value Either a string that the field
11762 * should start with or a RegExp to test against the field
11763 * @param {Boolean} anyMatch True to match any part not just the beginning
11764 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11766 query : function(property, value, anyMatch){
11767 var fn = this.createFilterFn(property, value, anyMatch);
11768 return fn ? this.queryBy(fn) : this.data.clone();
11772 * Query by a function. The specified function will be called with each
11773 * record in this data source. If the function returns true the record is included
11775 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11776 * @param {Object} scope (optional) The scope of the function (defaults to this)
11777 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11779 queryBy : function(fn, scope){
11780 var data = this.snapshot || this.data;
11781 return data.filterBy(fn, scope||this);
11785 * Collects unique values for a particular dataIndex from this store.
11786 * @param {String} dataIndex The property to collect
11787 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11788 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11789 * @return {Array} An array of the unique values
11791 collect : function(dataIndex, allowNull, bypassFilter){
11792 var d = (bypassFilter === true && this.snapshot) ?
11793 this.snapshot.items : this.data.items;
11794 var v, sv, r = [], l = {};
11795 for(var i = 0, len = d.length; i < len; i++){
11796 v = d[i].data[dataIndex];
11798 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11807 * Revert to a view of the Record cache with no filtering applied.
11808 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11810 clearFilter : function(suppressEvent){
11811 if(this.snapshot && this.snapshot != this.data){
11812 this.data = this.snapshot;
11813 delete this.snapshot;
11814 if(suppressEvent !== true){
11815 this.fireEvent("datachanged", this);
11821 afterEdit : function(record){
11822 if(this.modified.indexOf(record) == -1){
11823 this.modified.push(record);
11825 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11829 afterReject : function(record){
11830 this.modified.remove(record);
11831 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11835 afterCommit : function(record){
11836 this.modified.remove(record);
11837 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11841 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11842 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11844 commitChanges : function(){
11845 var m = this.modified.slice(0);
11846 this.modified = [];
11847 for(var i = 0, len = m.length; i < len; i++){
11853 * Cancel outstanding changes on all changed records.
11855 rejectChanges : function(){
11856 var m = this.modified.slice(0);
11857 this.modified = [];
11858 for(var i = 0, len = m.length; i < len; i++){
11863 onMetaChange : function(meta, rtype, o){
11864 this.recordType = rtype;
11865 this.fields = rtype.prototype.fields;
11866 delete this.snapshot;
11867 this.sortInfo = meta.sortInfo || this.sortInfo;
11868 this.modified = [];
11869 this.fireEvent('metachange', this, this.reader.meta);
11872 moveIndex : function(data, type)
11874 var index = this.indexOf(data);
11876 var newIndex = index + type;
11880 this.insert(newIndex, data);
11885 * Ext JS Library 1.1.1
11886 * Copyright(c) 2006-2007, Ext JS, LLC.
11888 * Originally Released Under LGPL - original licence link has changed is not relivant.
11891 * <script type="text/javascript">
11895 * @class Roo.data.SimpleStore
11896 * @extends Roo.data.Store
11897 * Small helper class to make creating Stores from Array data easier.
11898 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11899 * @cfg {Array} fields An array of field definition objects, or field name strings.
11900 * @cfg {Array} data The multi-dimensional array of data
11902 * @param {Object} config
11904 Roo.data.SimpleStore = function(config){
11905 Roo.data.SimpleStore.superclass.constructor.call(this, {
11907 reader: new Roo.data.ArrayReader({
11910 Roo.data.Record.create(config.fields)
11912 proxy : new Roo.data.MemoryProxy(config.data)
11916 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11918 * Ext JS Library 1.1.1
11919 * Copyright(c) 2006-2007, Ext JS, LLC.
11921 * Originally Released Under LGPL - original licence link has changed is not relivant.
11924 * <script type="text/javascript">
11929 * @extends Roo.data.Store
11930 * @class Roo.data.JsonStore
11931 * Small helper class to make creating Stores for JSON data easier. <br/>
11933 var store = new Roo.data.JsonStore({
11934 url: 'get-images.php',
11936 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11939 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11940 * JsonReader and HttpProxy (unless inline data is provided).</b>
11941 * @cfg {Array} fields An array of field definition objects, or field name strings.
11943 * @param {Object} config
11945 Roo.data.JsonStore = function(c){
11946 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11947 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11948 reader: new Roo.data.JsonReader(c, c.fields)
11951 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11953 * Ext JS Library 1.1.1
11954 * Copyright(c) 2006-2007, Ext JS, LLC.
11956 * Originally Released Under LGPL - original licence link has changed is not relivant.
11959 * <script type="text/javascript">
11963 Roo.data.Field = function(config){
11964 if(typeof config == "string"){
11965 config = {name: config};
11967 Roo.apply(this, config);
11970 this.type = "auto";
11973 var st = Roo.data.SortTypes;
11974 // named sortTypes are supported, here we look them up
11975 if(typeof this.sortType == "string"){
11976 this.sortType = st[this.sortType];
11979 // set default sortType for strings and dates
11980 if(!this.sortType){
11983 this.sortType = st.asUCString;
11986 this.sortType = st.asDate;
11989 this.sortType = st.none;
11994 var stripRe = /[\$,%]/g;
11996 // prebuilt conversion function for this field, instead of
11997 // switching every time we're reading a value
11999 var cv, dateFormat = this.dateFormat;
12004 cv = function(v){ return v; };
12007 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12011 return v !== undefined && v !== null && v !== '' ?
12012 parseInt(String(v).replace(stripRe, ""), 10) : '';
12017 return v !== undefined && v !== null && v !== '' ?
12018 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12023 cv = function(v){ return v === true || v === "true" || v == 1; };
12030 if(v instanceof Date){
12034 if(dateFormat == "timestamp"){
12035 return new Date(v*1000);
12037 return Date.parseDate(v, dateFormat);
12039 var parsed = Date.parse(v);
12040 return parsed ? new Date(parsed) : null;
12049 Roo.data.Field.prototype = {
12057 * Ext JS Library 1.1.1
12058 * Copyright(c) 2006-2007, Ext JS, LLC.
12060 * Originally Released Under LGPL - original licence link has changed is not relivant.
12063 * <script type="text/javascript">
12066 // Base class for reading structured data from a data source. This class is intended to be
12067 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12070 * @class Roo.data.DataReader
12071 * Base class for reading structured data from a data source. This class is intended to be
12072 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12075 Roo.data.DataReader = function(meta, recordType){
12079 this.recordType = recordType instanceof Array ?
12080 Roo.data.Record.create(recordType) : recordType;
12083 Roo.data.DataReader.prototype = {
12085 * Create an empty record
12086 * @param {Object} data (optional) - overlay some values
12087 * @return {Roo.data.Record} record created.
12089 newRow : function(d) {
12091 this.recordType.prototype.fields.each(function(c) {
12093 case 'int' : da[c.name] = 0; break;
12094 case 'date' : da[c.name] = new Date(); break;
12095 case 'float' : da[c.name] = 0.0; break;
12096 case 'boolean' : da[c.name] = false; break;
12097 default : da[c.name] = ""; break;
12101 return new this.recordType(Roo.apply(da, d));
12106 * Ext JS Library 1.1.1
12107 * Copyright(c) 2006-2007, Ext JS, LLC.
12109 * Originally Released Under LGPL - original licence link has changed is not relivant.
12112 * <script type="text/javascript">
12116 * @class Roo.data.DataProxy
12117 * @extends Roo.data.Observable
12118 * This class is an abstract base class for implementations which provide retrieval of
12119 * unformatted data objects.<br>
12121 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12122 * (of the appropriate type which knows how to parse the data object) to provide a block of
12123 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12125 * Custom implementations must implement the load method as described in
12126 * {@link Roo.data.HttpProxy#load}.
12128 Roo.data.DataProxy = function(){
12131 * @event beforeload
12132 * Fires before a network request is made to retrieve a data object.
12133 * @param {Object} This DataProxy object.
12134 * @param {Object} params The params parameter to the load function.
12139 * Fires before the load method's callback is called.
12140 * @param {Object} This DataProxy object.
12141 * @param {Object} o The data object.
12142 * @param {Object} arg The callback argument object passed to the load function.
12146 * @event loadexception
12147 * Fires if an Exception occurs during data retrieval.
12148 * @param {Object} This DataProxy object.
12149 * @param {Object} o The data object.
12150 * @param {Object} arg The callback argument object passed to the load function.
12151 * @param {Object} e The Exception.
12153 loadexception : true
12155 Roo.data.DataProxy.superclass.constructor.call(this);
12158 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12161 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12165 * Ext JS Library 1.1.1
12166 * Copyright(c) 2006-2007, Ext JS, LLC.
12168 * Originally Released Under LGPL - original licence link has changed is not relivant.
12171 * <script type="text/javascript">
12174 * @class Roo.data.MemoryProxy
12175 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12176 * to the Reader when its load method is called.
12178 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12180 Roo.data.MemoryProxy = function(data){
12184 Roo.data.MemoryProxy.superclass.constructor.call(this);
12188 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12191 * Load data from the requested source (in this case an in-memory
12192 * data object passed to the constructor), read the data object into
12193 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12194 * process that block using the passed callback.
12195 * @param {Object} params This parameter is not used by the MemoryProxy class.
12196 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12197 * object into a block of Roo.data.Records.
12198 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12199 * The function must be passed <ul>
12200 * <li>The Record block object</li>
12201 * <li>The "arg" argument from the load function</li>
12202 * <li>A boolean success indicator</li>
12204 * @param {Object} scope The scope in which to call the callback
12205 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12207 load : function(params, reader, callback, scope, arg){
12208 params = params || {};
12211 result = reader.readRecords(this.data);
12213 this.fireEvent("loadexception", this, arg, null, e);
12214 callback.call(scope, null, arg, false);
12217 callback.call(scope, result, arg, true);
12221 update : function(params, records){
12226 * Ext JS Library 1.1.1
12227 * Copyright(c) 2006-2007, Ext JS, LLC.
12229 * Originally Released Under LGPL - original licence link has changed is not relivant.
12232 * <script type="text/javascript">
12235 * @class Roo.data.HttpProxy
12236 * @extends Roo.data.DataProxy
12237 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12238 * configured to reference a certain URL.<br><br>
12240 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12241 * from which the running page was served.<br><br>
12243 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12245 * Be aware that to enable the browser to parse an XML document, the server must set
12246 * the Content-Type header in the HTTP response to "text/xml".
12248 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12249 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12250 * will be used to make the request.
12252 Roo.data.HttpProxy = function(conn){
12253 Roo.data.HttpProxy.superclass.constructor.call(this);
12254 // is conn a conn config or a real conn?
12256 this.useAjax = !conn || !conn.events;
12260 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12261 // thse are take from connection...
12264 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12267 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12268 * extra parameters to each request made by this object. (defaults to undefined)
12271 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12272 * to each request made by this object. (defaults to undefined)
12275 * @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)
12278 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12281 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12287 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12291 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12292 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12293 * a finer-grained basis than the DataProxy events.
12295 getConnection : function(){
12296 return this.useAjax ? Roo.Ajax : this.conn;
12300 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12301 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12302 * process that block using the passed callback.
12303 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12304 * for the request to the remote server.
12305 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12306 * object into a block of Roo.data.Records.
12307 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12308 * The function must be passed <ul>
12309 * <li>The Record block object</li>
12310 * <li>The "arg" argument from the load function</li>
12311 * <li>A boolean success indicator</li>
12313 * @param {Object} scope The scope in which to call the callback
12314 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12316 load : function(params, reader, callback, scope, arg){
12317 if(this.fireEvent("beforeload", this, params) !== false){
12319 params : params || {},
12321 callback : callback,
12326 callback : this.loadResponse,
12330 Roo.applyIf(o, this.conn);
12331 if(this.activeRequest){
12332 Roo.Ajax.abort(this.activeRequest);
12334 this.activeRequest = Roo.Ajax.request(o);
12336 this.conn.request(o);
12339 callback.call(scope||this, null, arg, false);
12344 loadResponse : function(o, success, response){
12345 delete this.activeRequest;
12347 this.fireEvent("loadexception", this, o, response);
12348 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12353 result = o.reader.read(response);
12355 this.fireEvent("loadexception", this, o, response, e);
12356 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12360 this.fireEvent("load", this, o, o.request.arg);
12361 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12365 update : function(dataSet){
12370 updateResponse : function(dataSet){
12375 * Ext JS Library 1.1.1
12376 * Copyright(c) 2006-2007, Ext JS, LLC.
12378 * Originally Released Under LGPL - original licence link has changed is not relivant.
12381 * <script type="text/javascript">
12385 * @class Roo.data.ScriptTagProxy
12386 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12387 * other than the originating domain of the running page.<br><br>
12389 * <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
12390 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12392 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12393 * source code that is used as the source inside a <script> tag.<br><br>
12395 * In order for the browser to process the returned data, the server must wrap the data object
12396 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12397 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12398 * depending on whether the callback name was passed:
12401 boolean scriptTag = false;
12402 String cb = request.getParameter("callback");
12405 response.setContentType("text/javascript");
12407 response.setContentType("application/x-json");
12409 Writer out = response.getWriter();
12411 out.write(cb + "(");
12413 out.print(dataBlock.toJsonString());
12420 * @param {Object} config A configuration object.
12422 Roo.data.ScriptTagProxy = function(config){
12423 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12424 Roo.apply(this, config);
12425 this.head = document.getElementsByTagName("head")[0];
12428 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12430 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12432 * @cfg {String} url The URL from which to request the data object.
12435 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12439 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12440 * the server the name of the callback function set up by the load call to process the returned data object.
12441 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12442 * javascript output which calls this named function passing the data object as its only parameter.
12444 callbackParam : "callback",
12446 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12447 * name to the request.
12452 * Load data from the configured URL, read the data object into
12453 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12454 * process that block using the passed callback.
12455 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12456 * for the request to the remote server.
12457 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12458 * object into a block of Roo.data.Records.
12459 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12460 * The function must be passed <ul>
12461 * <li>The Record block object</li>
12462 * <li>The "arg" argument from the load function</li>
12463 * <li>A boolean success indicator</li>
12465 * @param {Object} scope The scope in which to call the callback
12466 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12468 load : function(params, reader, callback, scope, arg){
12469 if(this.fireEvent("beforeload", this, params) !== false){
12471 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12473 var url = this.url;
12474 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12476 url += "&_dc=" + (new Date().getTime());
12478 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12481 cb : "stcCallback"+transId,
12482 scriptId : "stcScript"+transId,
12486 callback : callback,
12492 window[trans.cb] = function(o){
12493 conn.handleResponse(o, trans);
12496 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12498 if(this.autoAbort !== false){
12502 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12504 var script = document.createElement("script");
12505 script.setAttribute("src", url);
12506 script.setAttribute("type", "text/javascript");
12507 script.setAttribute("id", trans.scriptId);
12508 this.head.appendChild(script);
12510 this.trans = trans;
12512 callback.call(scope||this, null, arg, false);
12517 isLoading : function(){
12518 return this.trans ? true : false;
12522 * Abort the current server request.
12524 abort : function(){
12525 if(this.isLoading()){
12526 this.destroyTrans(this.trans);
12531 destroyTrans : function(trans, isLoaded){
12532 this.head.removeChild(document.getElementById(trans.scriptId));
12533 clearTimeout(trans.timeoutId);
12535 window[trans.cb] = undefined;
12537 delete window[trans.cb];
12540 // if hasn't been loaded, wait for load to remove it to prevent script error
12541 window[trans.cb] = function(){
12542 window[trans.cb] = undefined;
12544 delete window[trans.cb];
12551 handleResponse : function(o, trans){
12552 this.trans = false;
12553 this.destroyTrans(trans, true);
12556 result = trans.reader.readRecords(o);
12558 this.fireEvent("loadexception", this, o, trans.arg, e);
12559 trans.callback.call(trans.scope||window, null, trans.arg, false);
12562 this.fireEvent("load", this, o, trans.arg);
12563 trans.callback.call(trans.scope||window, result, trans.arg, true);
12567 handleFailure : function(trans){
12568 this.trans = false;
12569 this.destroyTrans(trans, false);
12570 this.fireEvent("loadexception", this, null, trans.arg);
12571 trans.callback.call(trans.scope||window, null, trans.arg, false);
12575 * Ext JS Library 1.1.1
12576 * Copyright(c) 2006-2007, Ext JS, LLC.
12578 * Originally Released Under LGPL - original licence link has changed is not relivant.
12581 * <script type="text/javascript">
12585 * @class Roo.data.JsonReader
12586 * @extends Roo.data.DataReader
12587 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12588 * based on mappings in a provided Roo.data.Record constructor.
12590 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12591 * in the reply previously.
12596 var RecordDef = Roo.data.Record.create([
12597 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12598 {name: 'occupation'} // This field will use "occupation" as the mapping.
12600 var myReader = new Roo.data.JsonReader({
12601 totalProperty: "results", // The property which contains the total dataset size (optional)
12602 root: "rows", // The property which contains an Array of row objects
12603 id: "id" // The property within each row object that provides an ID for the record (optional)
12607 * This would consume a JSON file like this:
12609 { 'results': 2, 'rows': [
12610 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12611 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12614 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12615 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12616 * paged from the remote server.
12617 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12618 * @cfg {String} root name of the property which contains the Array of row objects.
12619 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12620 * @cfg {Array} fields Array of field definition objects
12622 * Create a new JsonReader
12623 * @param {Object} meta Metadata configuration options
12624 * @param {Object} recordType Either an Array of field definition objects,
12625 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12627 Roo.data.JsonReader = function(meta, recordType){
12630 // set some defaults:
12631 Roo.applyIf(meta, {
12632 totalProperty: 'total',
12633 successProperty : 'success',
12638 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12640 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12643 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12644 * Used by Store query builder to append _requestMeta to params.
12647 metaFromRemote : false,
12649 * This method is only used by a DataProxy which has retrieved data from a remote server.
12650 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12651 * @return {Object} data A data block which is used by an Roo.data.Store object as
12652 * a cache of Roo.data.Records.
12654 read : function(response){
12655 var json = response.responseText;
12657 var o = /* eval:var:o */ eval("("+json+")");
12659 throw {message: "JsonReader.read: Json object not found"};
12665 this.metaFromRemote = true;
12666 this.meta = o.metaData;
12667 this.recordType = Roo.data.Record.create(o.metaData.fields);
12668 this.onMetaChange(this.meta, this.recordType, o);
12670 return this.readRecords(o);
12673 // private function a store will implement
12674 onMetaChange : function(meta, recordType, o){
12681 simpleAccess: function(obj, subsc) {
12688 getJsonAccessor: function(){
12690 return function(expr) {
12692 return(re.test(expr))
12693 ? new Function("obj", "return obj." + expr)
12698 return Roo.emptyFn;
12703 * Create a data block containing Roo.data.Records from an XML document.
12704 * @param {Object} o An object which contains an Array of row objects in the property specified
12705 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12706 * which contains the total size of the dataset.
12707 * @return {Object} data A data block which is used by an Roo.data.Store object as
12708 * a cache of Roo.data.Records.
12710 readRecords : function(o){
12712 * After any data loads, the raw JSON data is available for further custom processing.
12716 var s = this.meta, Record = this.recordType,
12717 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12719 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12721 if(s.totalProperty) {
12722 this.getTotal = this.getJsonAccessor(s.totalProperty);
12724 if(s.successProperty) {
12725 this.getSuccess = this.getJsonAccessor(s.successProperty);
12727 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12729 var g = this.getJsonAccessor(s.id);
12730 this.getId = function(rec) {
12732 return (r === undefined || r === "") ? null : r;
12735 this.getId = function(){return null;};
12738 for(var jj = 0; jj < fl; jj++){
12740 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12741 this.ef[jj] = this.getJsonAccessor(map);
12745 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12746 if(s.totalProperty){
12747 var vt = parseInt(this.getTotal(o), 10);
12752 if(s.successProperty){
12753 var vs = this.getSuccess(o);
12754 if(vs === false || vs === 'false'){
12759 for(var i = 0; i < c; i++){
12762 var id = this.getId(n);
12763 for(var j = 0; j < fl; j++){
12765 var v = this.ef[j](n);
12767 Roo.log('missing convert for ' + f.name);
12771 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12773 var record = new Record(values, id);
12775 records[i] = record;
12781 totalRecords : totalRecords
12786 * Ext JS Library 1.1.1
12787 * Copyright(c) 2006-2007, Ext JS, LLC.
12789 * Originally Released Under LGPL - original licence link has changed is not relivant.
12792 * <script type="text/javascript">
12796 * @class Roo.data.ArrayReader
12797 * @extends Roo.data.DataReader
12798 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12799 * Each element of that Array represents a row of data fields. The
12800 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12801 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12805 var RecordDef = Roo.data.Record.create([
12806 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12807 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12809 var myReader = new Roo.data.ArrayReader({
12810 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12814 * This would consume an Array like this:
12816 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12818 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12820 * Create a new JsonReader
12821 * @param {Object} meta Metadata configuration options.
12822 * @param {Object} recordType Either an Array of field definition objects
12823 * as specified to {@link Roo.data.Record#create},
12824 * or an {@link Roo.data.Record} object
12825 * created using {@link Roo.data.Record#create}.
12827 Roo.data.ArrayReader = function(meta, recordType){
12828 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12831 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12833 * Create a data block containing Roo.data.Records from an XML document.
12834 * @param {Object} o An Array of row objects which represents the dataset.
12835 * @return {Object} data A data block which is used by an Roo.data.Store object as
12836 * a cache of Roo.data.Records.
12838 readRecords : function(o){
12839 var sid = this.meta ? this.meta.id : null;
12840 var recordType = this.recordType, fields = recordType.prototype.fields;
12843 for(var i = 0; i < root.length; i++){
12846 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12847 for(var j = 0, jlen = fields.length; j < jlen; j++){
12848 var f = fields.items[j];
12849 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12850 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12852 values[f.name] = v;
12854 var record = new recordType(values, id);
12856 records[records.length] = record;
12860 totalRecords : records.length
12869 * @class Roo.bootstrap.ComboBox
12870 * @extends Roo.bootstrap.TriggerField
12871 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12872 * @cfg {Boolean} append (true|false) default false
12873 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12874 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12875 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12876 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12877 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12878 * @cfg {Boolean} animate default true
12879 * @cfg {Boolean} emptyResultText only for touch device
12880 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12881 * @cfg {String} emptyTitle default ''
12883 * Create a new ComboBox.
12884 * @param {Object} config Configuration options
12886 Roo.bootstrap.ComboBox = function(config){
12887 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12891 * Fires when the dropdown list is expanded
12892 * @param {Roo.bootstrap.ComboBox} combo This combo box
12897 * Fires when the dropdown list is collapsed
12898 * @param {Roo.bootstrap.ComboBox} combo This combo box
12902 * @event beforeselect
12903 * Fires before a list item is selected. Return false to cancel the selection.
12904 * @param {Roo.bootstrap.ComboBox} combo This combo box
12905 * @param {Roo.data.Record} record The data record returned from the underlying store
12906 * @param {Number} index The index of the selected item in the dropdown list
12908 'beforeselect' : true,
12911 * Fires when a list item is selected
12912 * @param {Roo.bootstrap.ComboBox} combo This combo box
12913 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12914 * @param {Number} index The index of the selected item in the dropdown list
12918 * @event beforequery
12919 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12920 * The event object passed has these properties:
12921 * @param {Roo.bootstrap.ComboBox} combo This combo box
12922 * @param {String} query The query
12923 * @param {Boolean} forceAll true to force "all" query
12924 * @param {Boolean} cancel true to cancel the query
12925 * @param {Object} e The query event object
12927 'beforequery': true,
12930 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12931 * @param {Roo.bootstrap.ComboBox} combo This combo box
12936 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12937 * @param {Roo.bootstrap.ComboBox} combo This combo box
12938 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12943 * Fires when the remove value from the combobox array
12944 * @param {Roo.bootstrap.ComboBox} combo This combo box
12948 * @event afterremove
12949 * Fires when the remove value from the combobox array
12950 * @param {Roo.bootstrap.ComboBox} combo This combo box
12952 'afterremove' : true,
12954 * @event specialfilter
12955 * Fires when specialfilter
12956 * @param {Roo.bootstrap.ComboBox} combo This combo box
12958 'specialfilter' : true,
12961 * Fires when tick the element
12962 * @param {Roo.bootstrap.ComboBox} combo This combo box
12966 * @event touchviewdisplay
12967 * Fires when touch view require special display (default is using displayField)
12968 * @param {Roo.bootstrap.ComboBox} combo This combo box
12969 * @param {Object} cfg set html .
12971 'touchviewdisplay' : true
12976 this.tickItems = [];
12978 this.selectedIndex = -1;
12979 if(this.mode == 'local'){
12980 if(config.queryDelay === undefined){
12981 this.queryDelay = 10;
12983 if(config.minChars === undefined){
12989 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12992 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12993 * rendering into an Roo.Editor, defaults to false)
12996 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12997 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13000 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13003 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13004 * the dropdown list (defaults to undefined, with no header element)
13008 * @cfg {String/Roo.Template} tpl The template to use to render the output
13012 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13014 listWidth: undefined,
13016 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13017 * mode = 'remote' or 'text' if mode = 'local')
13019 displayField: undefined,
13022 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13023 * mode = 'remote' or 'value' if mode = 'local').
13024 * Note: use of a valueField requires the user make a selection
13025 * in order for a value to be mapped.
13027 valueField: undefined,
13029 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13034 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13035 * field's data value (defaults to the underlying DOM element's name)
13037 hiddenName: undefined,
13039 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13043 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13045 selectedClass: 'active',
13048 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13052 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13053 * anchor positions (defaults to 'tl-bl')
13055 listAlign: 'tl-bl?',
13057 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13061 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13062 * query specified by the allQuery config option (defaults to 'query')
13064 triggerAction: 'query',
13066 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13067 * (defaults to 4, does not apply if editable = false)
13071 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13072 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13076 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13077 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13081 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13082 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13086 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13087 * when editable = true (defaults to false)
13089 selectOnFocus:false,
13091 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13093 queryParam: 'query',
13095 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13096 * when mode = 'remote' (defaults to 'Loading...')
13098 loadingText: 'Loading...',
13100 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13104 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13108 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13109 * traditional select (defaults to true)
13113 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13117 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13121 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13122 * listWidth has a higher value)
13126 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13127 * allow the user to set arbitrary text into the field (defaults to false)
13129 forceSelection:false,
13131 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13132 * if typeAhead = true (defaults to 250)
13134 typeAheadDelay : 250,
13136 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13137 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13139 valueNotFoundText : undefined,
13141 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13143 blockFocus : false,
13146 * @cfg {Boolean} disableClear Disable showing of clear button.
13148 disableClear : false,
13150 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13152 alwaysQuery : false,
13155 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13160 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13162 invalidClass : "has-warning",
13165 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13167 validClass : "has-success",
13170 * @cfg {Boolean} specialFilter (true|false) special filter default false
13172 specialFilter : false,
13175 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13177 mobileTouchView : true,
13180 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13182 useNativeIOS : false,
13185 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13187 mobile_restrict_height : false,
13189 ios_options : false,
13201 btnPosition : 'right',
13202 triggerList : true,
13203 showToggleBtn : true,
13205 emptyResultText: 'Empty',
13206 triggerText : 'Select',
13209 // element that contains real text value.. (when hidden is used..)
13211 getAutoCreate : function()
13216 * Render classic select for iso
13219 if(Roo.isIOS && this.useNativeIOS){
13220 cfg = this.getAutoCreateNativeIOS();
13228 if(Roo.isTouch && this.mobileTouchView){
13229 cfg = this.getAutoCreateTouchView();
13236 if(!this.tickable){
13237 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13242 * ComboBox with tickable selections
13245 var align = this.labelAlign || this.parentLabelAlign();
13248 cls : 'form-group roo-combobox-tickable' //input-group
13251 var btn_text_select = '';
13252 var btn_text_done = '';
13253 var btn_text_cancel = '';
13255 if (this.btn_text_show) {
13256 btn_text_select = 'Select';
13257 btn_text_done = 'Done';
13258 btn_text_cancel = 'Cancel';
13263 cls : 'tickable-buttons',
13268 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13269 //html : this.triggerText
13270 html: btn_text_select
13276 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13278 html: btn_text_done
13284 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13286 html: btn_text_cancel
13292 buttons.cn.unshift({
13294 cls: 'roo-select2-search-field-input'
13300 Roo.each(buttons.cn, function(c){
13302 c.cls += ' btn-' + _this.size;
13305 if (_this.disabled) {
13316 cls: 'form-hidden-field'
13320 cls: 'roo-select2-choices',
13324 cls: 'roo-select2-search-field',
13335 cls: 'roo-select2-container input-group roo-select2-container-multi',
13341 // cls: 'typeahead typeahead-long dropdown-menu',
13342 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13347 if(this.hasFeedback && !this.allowBlank){
13351 cls: 'glyphicon form-control-feedback'
13354 combobox.cn.push(feedback);
13359 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13360 tooltip : 'This field is required'
13362 if (Roo.bootstrap.version == 4) {
13365 style : 'display:none'
13368 if (align ==='left' && this.fieldLabel.length) {
13370 cfg.cls += ' roo-form-group-label-left row';
13377 cls : 'control-label col-form-label',
13378 html : this.fieldLabel
13390 var labelCfg = cfg.cn[1];
13391 var contentCfg = cfg.cn[2];
13394 if(this.indicatorpos == 'right'){
13400 cls : 'control-label col-form-label',
13404 html : this.fieldLabel
13420 labelCfg = cfg.cn[0];
13421 contentCfg = cfg.cn[1];
13425 if(this.labelWidth > 12){
13426 labelCfg.style = "width: " + this.labelWidth + 'px';
13429 if(this.labelWidth < 13 && this.labelmd == 0){
13430 this.labelmd = this.labelWidth;
13433 if(this.labellg > 0){
13434 labelCfg.cls += ' col-lg-' + this.labellg;
13435 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13438 if(this.labelmd > 0){
13439 labelCfg.cls += ' col-md-' + this.labelmd;
13440 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13443 if(this.labelsm > 0){
13444 labelCfg.cls += ' col-sm-' + this.labelsm;
13445 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13448 if(this.labelxs > 0){
13449 labelCfg.cls += ' col-xs-' + this.labelxs;
13450 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13454 } else if ( this.fieldLabel.length) {
13455 // Roo.log(" label");
13460 //cls : 'input-group-addon',
13461 html : this.fieldLabel
13466 if(this.indicatorpos == 'right'){
13470 //cls : 'input-group-addon',
13471 html : this.fieldLabel
13481 // Roo.log(" no label && no align");
13488 ['xs','sm','md','lg'].map(function(size){
13489 if (settings[size]) {
13490 cfg.cls += ' col-' + size + '-' + settings[size];
13498 _initEventsCalled : false,
13501 initEvents: function()
13503 if (this._initEventsCalled) { // as we call render... prevent looping...
13506 this._initEventsCalled = true;
13509 throw "can not find store for combo";
13512 this.indicator = this.indicatorEl();
13514 this.store = Roo.factory(this.store, Roo.data);
13515 this.store.parent = this;
13517 // if we are building from html. then this element is so complex, that we can not really
13518 // use the rendered HTML.
13519 // so we have to trash and replace the previous code.
13520 if (Roo.XComponent.build_from_html) {
13521 // remove this element....
13522 var e = this.el.dom, k=0;
13523 while (e ) { e = e.previousSibling; ++k;}
13528 this.rendered = false;
13530 this.render(this.parent().getChildContainer(true), k);
13533 if(Roo.isIOS && this.useNativeIOS){
13534 this.initIOSView();
13542 if(Roo.isTouch && this.mobileTouchView){
13543 this.initTouchView();
13548 this.initTickableEvents();
13552 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13554 if(this.hiddenName){
13556 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13558 this.hiddenField.dom.value =
13559 this.hiddenValue !== undefined ? this.hiddenValue :
13560 this.value !== undefined ? this.value : '';
13562 // prevent input submission
13563 this.el.dom.removeAttribute('name');
13564 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13569 // this.el.dom.setAttribute('autocomplete', 'off');
13572 var cls = 'x-combo-list';
13574 //this.list = new Roo.Layer({
13575 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13581 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13582 _this.list.setWidth(lw);
13585 this.list.on('mouseover', this.onViewOver, this);
13586 this.list.on('mousemove', this.onViewMove, this);
13587 this.list.on('scroll', this.onViewScroll, this);
13590 this.list.swallowEvent('mousewheel');
13591 this.assetHeight = 0;
13594 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13595 this.assetHeight += this.header.getHeight();
13598 this.innerList = this.list.createChild({cls:cls+'-inner'});
13599 this.innerList.on('mouseover', this.onViewOver, this);
13600 this.innerList.on('mousemove', this.onViewMove, this);
13601 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13603 if(this.allowBlank && !this.pageSize && !this.disableClear){
13604 this.footer = this.list.createChild({cls:cls+'-ft'});
13605 this.pageTb = new Roo.Toolbar(this.footer);
13609 this.footer = this.list.createChild({cls:cls+'-ft'});
13610 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13611 {pageSize: this.pageSize});
13615 if (this.pageTb && this.allowBlank && !this.disableClear) {
13617 this.pageTb.add(new Roo.Toolbar.Fill(), {
13618 cls: 'x-btn-icon x-btn-clear',
13620 handler: function()
13623 _this.clearValue();
13624 _this.onSelect(false, -1);
13629 this.assetHeight += this.footer.getHeight();
13634 this.tpl = Roo.bootstrap.version == 4 ?
13635 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
13636 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13639 this.view = new Roo.View(this.list, this.tpl, {
13640 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13642 //this.view.wrapEl.setDisplayed(false);
13643 this.view.on('click', this.onViewClick, this);
13646 this.store.on('beforeload', this.onBeforeLoad, this);
13647 this.store.on('load', this.onLoad, this);
13648 this.store.on('loadexception', this.onLoadException, this);
13650 if(this.resizable){
13651 this.resizer = new Roo.Resizable(this.list, {
13652 pinned:true, handles:'se'
13654 this.resizer.on('resize', function(r, w, h){
13655 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13656 this.listWidth = w;
13657 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13658 this.restrictHeight();
13660 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13663 if(!this.editable){
13664 this.editable = true;
13665 this.setEditable(false);
13670 if (typeof(this.events.add.listeners) != 'undefined') {
13672 this.addicon = this.wrap.createChild(
13673 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13675 this.addicon.on('click', function(e) {
13676 this.fireEvent('add', this);
13679 if (typeof(this.events.edit.listeners) != 'undefined') {
13681 this.editicon = this.wrap.createChild(
13682 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13683 if (this.addicon) {
13684 this.editicon.setStyle('margin-left', '40px');
13686 this.editicon.on('click', function(e) {
13688 // we fire even if inothing is selected..
13689 this.fireEvent('edit', this, this.lastData );
13695 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13696 "up" : function(e){
13697 this.inKeyMode = true;
13701 "down" : function(e){
13702 if(!this.isExpanded()){
13703 this.onTriggerClick();
13705 this.inKeyMode = true;
13710 "enter" : function(e){
13711 // this.onViewClick();
13715 if(this.fireEvent("specialkey", this, e)){
13716 this.onViewClick(false);
13722 "esc" : function(e){
13726 "tab" : function(e){
13729 if(this.fireEvent("specialkey", this, e)){
13730 this.onViewClick(false);
13738 doRelay : function(foo, bar, hname){
13739 if(hname == 'down' || this.scope.isExpanded()){
13740 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13749 this.queryDelay = Math.max(this.queryDelay || 10,
13750 this.mode == 'local' ? 10 : 250);
13753 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13755 if(this.typeAhead){
13756 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13758 if(this.editable !== false){
13759 this.inputEl().on("keyup", this.onKeyUp, this);
13761 if(this.forceSelection){
13762 this.inputEl().on('blur', this.doForce, this);
13766 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13767 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13771 initTickableEvents: function()
13775 if(this.hiddenName){
13777 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13779 this.hiddenField.dom.value =
13780 this.hiddenValue !== undefined ? this.hiddenValue :
13781 this.value !== undefined ? this.value : '';
13783 // prevent input submission
13784 this.el.dom.removeAttribute('name');
13785 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13790 // this.list = this.el.select('ul.dropdown-menu',true).first();
13792 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13793 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13794 if(this.triggerList){
13795 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13798 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13799 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13801 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13802 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13804 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13805 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13807 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13808 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13809 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13812 this.cancelBtn.hide();
13817 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13818 _this.list.setWidth(lw);
13821 this.list.on('mouseover', this.onViewOver, this);
13822 this.list.on('mousemove', this.onViewMove, this);
13824 this.list.on('scroll', this.onViewScroll, this);
13827 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13828 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13831 this.view = new Roo.View(this.list, this.tpl, {
13836 selectedClass: this.selectedClass
13839 //this.view.wrapEl.setDisplayed(false);
13840 this.view.on('click', this.onViewClick, this);
13844 this.store.on('beforeload', this.onBeforeLoad, this);
13845 this.store.on('load', this.onLoad, this);
13846 this.store.on('loadexception', this.onLoadException, this);
13849 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13850 "up" : function(e){
13851 this.inKeyMode = true;
13855 "down" : function(e){
13856 this.inKeyMode = true;
13860 "enter" : function(e){
13861 if(this.fireEvent("specialkey", this, e)){
13862 this.onViewClick(false);
13868 "esc" : function(e){
13869 this.onTickableFooterButtonClick(e, false, false);
13872 "tab" : function(e){
13873 this.fireEvent("specialkey", this, e);
13875 this.onTickableFooterButtonClick(e, false, false);
13882 doRelay : function(e, fn, key){
13883 if(this.scope.isExpanded()){
13884 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13893 this.queryDelay = Math.max(this.queryDelay || 10,
13894 this.mode == 'local' ? 10 : 250);
13897 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13899 if(this.typeAhead){
13900 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13903 if(this.editable !== false){
13904 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13907 this.indicator = this.indicatorEl();
13909 if(this.indicator){
13910 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13911 this.indicator.hide();
13916 onDestroy : function(){
13918 this.view.setStore(null);
13919 this.view.el.removeAllListeners();
13920 this.view.el.remove();
13921 this.view.purgeListeners();
13924 this.list.dom.innerHTML = '';
13928 this.store.un('beforeload', this.onBeforeLoad, this);
13929 this.store.un('load', this.onLoad, this);
13930 this.store.un('loadexception', this.onLoadException, this);
13932 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13936 fireKey : function(e){
13937 if(e.isNavKeyPress() && !this.list.isVisible()){
13938 this.fireEvent("specialkey", this, e);
13943 onResize: function(w, h){
13944 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13946 // if(typeof w != 'number'){
13947 // // we do not handle it!?!?
13950 // var tw = this.trigger.getWidth();
13951 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13952 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13954 // this.inputEl().setWidth( this.adjustWidth('input', x));
13956 // //this.trigger.setStyle('left', x+'px');
13958 // if(this.list && this.listWidth === undefined){
13959 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13960 // this.list.setWidth(lw);
13961 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13969 * Allow or prevent the user from directly editing the field text. If false is passed,
13970 * the user will only be able to select from the items defined in the dropdown list. This method
13971 * is the runtime equivalent of setting the 'editable' config option at config time.
13972 * @param {Boolean} value True to allow the user to directly edit the field text
13974 setEditable : function(value){
13975 if(value == this.editable){
13978 this.editable = value;
13980 this.inputEl().dom.setAttribute('readOnly', true);
13981 this.inputEl().on('mousedown', this.onTriggerClick, this);
13982 this.inputEl().addClass('x-combo-noedit');
13984 this.inputEl().dom.setAttribute('readOnly', false);
13985 this.inputEl().un('mousedown', this.onTriggerClick, this);
13986 this.inputEl().removeClass('x-combo-noedit');
13992 onBeforeLoad : function(combo,opts){
13993 if(!this.hasFocus){
13997 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13999 this.restrictHeight();
14000 this.selectedIndex = -1;
14004 onLoad : function(){
14006 this.hasQuery = false;
14008 if(!this.hasFocus){
14012 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14013 this.loading.hide();
14016 if(this.store.getCount() > 0){
14019 this.restrictHeight();
14020 if(this.lastQuery == this.allQuery){
14021 if(this.editable && !this.tickable){
14022 this.inputEl().dom.select();
14026 !this.selectByValue(this.value, true) &&
14029 !this.store.lastOptions ||
14030 typeof(this.store.lastOptions.add) == 'undefined' ||
14031 this.store.lastOptions.add != true
14034 this.select(0, true);
14037 if(this.autoFocus){
14040 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14041 this.taTask.delay(this.typeAheadDelay);
14045 this.onEmptyResults();
14051 onLoadException : function()
14053 this.hasQuery = false;
14055 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14056 this.loading.hide();
14059 if(this.tickable && this.editable){
14064 // only causes errors at present
14065 //Roo.log(this.store.reader.jsonData);
14066 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14068 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14074 onTypeAhead : function(){
14075 if(this.store.getCount() > 0){
14076 var r = this.store.getAt(0);
14077 var newValue = r.data[this.displayField];
14078 var len = newValue.length;
14079 var selStart = this.getRawValue().length;
14081 if(selStart != len){
14082 this.setRawValue(newValue);
14083 this.selectText(selStart, newValue.length);
14089 onSelect : function(record, index){
14091 if(this.fireEvent('beforeselect', this, record, index) !== false){
14093 this.setFromData(index > -1 ? record.data : false);
14096 this.fireEvent('select', this, record, index);
14101 * Returns the currently selected field value or empty string if no value is set.
14102 * @return {String} value The selected value
14104 getValue : function()
14106 if(Roo.isIOS && this.useNativeIOS){
14107 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14111 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14114 if(this.valueField){
14115 return typeof this.value != 'undefined' ? this.value : '';
14117 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14121 getRawValue : function()
14123 if(Roo.isIOS && this.useNativeIOS){
14124 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14127 var v = this.inputEl().getValue();
14133 * Clears any text/value currently set in the field
14135 clearValue : function(){
14137 if(this.hiddenField){
14138 this.hiddenField.dom.value = '';
14141 this.setRawValue('');
14142 this.lastSelectionText = '';
14143 this.lastData = false;
14145 var close = this.closeTriggerEl();
14156 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14157 * will be displayed in the field. If the value does not match the data value of an existing item,
14158 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14159 * Otherwise the field will be blank (although the value will still be set).
14160 * @param {String} value The value to match
14162 setValue : function(v)
14164 if(Roo.isIOS && this.useNativeIOS){
14165 this.setIOSValue(v);
14175 if(this.valueField){
14176 var r = this.findRecord(this.valueField, v);
14178 text = r.data[this.displayField];
14179 }else if(this.valueNotFoundText !== undefined){
14180 text = this.valueNotFoundText;
14183 this.lastSelectionText = text;
14184 if(this.hiddenField){
14185 this.hiddenField.dom.value = v;
14187 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14190 var close = this.closeTriggerEl();
14193 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14199 * @property {Object} the last set data for the element
14204 * Sets the value of the field based on a object which is related to the record format for the store.
14205 * @param {Object} value the value to set as. or false on reset?
14207 setFromData : function(o){
14214 var dv = ''; // display value
14215 var vv = ''; // value value..
14217 if (this.displayField) {
14218 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14220 // this is an error condition!!!
14221 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14224 if(this.valueField){
14225 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14228 var close = this.closeTriggerEl();
14231 if(dv.length || vv * 1 > 0){
14233 this.blockFocus=true;
14239 if(this.hiddenField){
14240 this.hiddenField.dom.value = vv;
14242 this.lastSelectionText = dv;
14243 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14247 // no hidden field.. - we store the value in 'value', but still display
14248 // display field!!!!
14249 this.lastSelectionText = dv;
14250 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14257 reset : function(){
14258 // overridden so that last data is reset..
14265 this.setValue(this.originalValue);
14266 //this.clearInvalid();
14267 this.lastData = false;
14269 this.view.clearSelections();
14275 findRecord : function(prop, value){
14277 if(this.store.getCount() > 0){
14278 this.store.each(function(r){
14279 if(r.data[prop] == value){
14289 getName: function()
14291 // returns hidden if it's set..
14292 if (!this.rendered) {return ''};
14293 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14297 onViewMove : function(e, t){
14298 this.inKeyMode = false;
14302 onViewOver : function(e, t){
14303 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14306 var item = this.view.findItemFromChild(t);
14309 var index = this.view.indexOf(item);
14310 this.select(index, false);
14315 onViewClick : function(view, doFocus, el, e)
14317 var index = this.view.getSelectedIndexes()[0];
14319 var r = this.store.getAt(index);
14323 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14330 Roo.each(this.tickItems, function(v,k){
14332 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14334 _this.tickItems.splice(k, 1);
14336 if(typeof(e) == 'undefined' && view == false){
14337 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14349 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14350 this.tickItems.push(r.data);
14353 if(typeof(e) == 'undefined' && view == false){
14354 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14361 this.onSelect(r, index);
14363 if(doFocus !== false && !this.blockFocus){
14364 this.inputEl().focus();
14369 restrictHeight : function(){
14370 //this.innerList.dom.style.height = '';
14371 //var inner = this.innerList.dom;
14372 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14373 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14374 //this.list.beginUpdate();
14375 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14376 this.list.alignTo(this.inputEl(), this.listAlign);
14377 this.list.alignTo(this.inputEl(), this.listAlign);
14378 //this.list.endUpdate();
14382 onEmptyResults : function(){
14384 if(this.tickable && this.editable){
14385 this.hasFocus = false;
14386 this.restrictHeight();
14394 * Returns true if the dropdown list is expanded, else false.
14396 isExpanded : function(){
14397 return this.list.isVisible();
14401 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14402 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14403 * @param {String} value The data value of the item to select
14404 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14405 * selected item if it is not currently in view (defaults to true)
14406 * @return {Boolean} True if the value matched an item in the list, else false
14408 selectByValue : function(v, scrollIntoView){
14409 if(v !== undefined && v !== null){
14410 var r = this.findRecord(this.valueField || this.displayField, v);
14412 this.select(this.store.indexOf(r), scrollIntoView);
14420 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14421 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14422 * @param {Number} index The zero-based index of the list item to select
14423 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14424 * selected item if it is not currently in view (defaults to true)
14426 select : function(index, scrollIntoView){
14427 this.selectedIndex = index;
14428 this.view.select(index);
14429 if(scrollIntoView !== false){
14430 var el = this.view.getNode(index);
14432 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14435 this.list.scrollChildIntoView(el, false);
14441 selectNext : function(){
14442 var ct = this.store.getCount();
14444 if(this.selectedIndex == -1){
14446 }else if(this.selectedIndex < ct-1){
14447 this.select(this.selectedIndex+1);
14453 selectPrev : function(){
14454 var ct = this.store.getCount();
14456 if(this.selectedIndex == -1){
14458 }else if(this.selectedIndex != 0){
14459 this.select(this.selectedIndex-1);
14465 onKeyUp : function(e){
14466 if(this.editable !== false && !e.isSpecialKey()){
14467 this.lastKey = e.getKey();
14468 this.dqTask.delay(this.queryDelay);
14473 validateBlur : function(){
14474 return !this.list || !this.list.isVisible();
14478 initQuery : function(){
14480 var v = this.getRawValue();
14482 if(this.tickable && this.editable){
14483 v = this.tickableInputEl().getValue();
14490 doForce : function(){
14491 if(this.inputEl().dom.value.length > 0){
14492 this.inputEl().dom.value =
14493 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14499 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14500 * query allowing the query action to be canceled if needed.
14501 * @param {String} query The SQL query to execute
14502 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14503 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14504 * saved in the current store (defaults to false)
14506 doQuery : function(q, forceAll){
14508 if(q === undefined || q === null){
14513 forceAll: forceAll,
14517 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14522 forceAll = qe.forceAll;
14523 if(forceAll === true || (q.length >= this.minChars)){
14525 this.hasQuery = true;
14527 if(this.lastQuery != q || this.alwaysQuery){
14528 this.lastQuery = q;
14529 if(this.mode == 'local'){
14530 this.selectedIndex = -1;
14532 this.store.clearFilter();
14535 if(this.specialFilter){
14536 this.fireEvent('specialfilter', this);
14541 this.store.filter(this.displayField, q);
14544 this.store.fireEvent("datachanged", this.store);
14551 this.store.baseParams[this.queryParam] = q;
14553 var options = {params : this.getParams(q)};
14556 options.add = true;
14557 options.params.start = this.page * this.pageSize;
14560 this.store.load(options);
14563 * this code will make the page width larger, at the beginning, the list not align correctly,
14564 * we should expand the list on onLoad
14565 * so command out it
14570 this.selectedIndex = -1;
14575 this.loadNext = false;
14579 getParams : function(q){
14581 //p[this.queryParam] = q;
14585 p.limit = this.pageSize;
14591 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14593 collapse : function(){
14594 if(!this.isExpanded()){
14600 this.hasFocus = false;
14604 this.cancelBtn.hide();
14605 this.trigger.show();
14608 this.tickableInputEl().dom.value = '';
14609 this.tickableInputEl().blur();
14614 Roo.get(document).un('mousedown', this.collapseIf, this);
14615 Roo.get(document).un('mousewheel', this.collapseIf, this);
14616 if (!this.editable) {
14617 Roo.get(document).un('keydown', this.listKeyPress, this);
14619 this.fireEvent('collapse', this);
14625 collapseIf : function(e){
14626 var in_combo = e.within(this.el);
14627 var in_list = e.within(this.list);
14628 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14630 if (in_combo || in_list || is_list) {
14631 //e.stopPropagation();
14636 this.onTickableFooterButtonClick(e, false, false);
14644 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14646 expand : function(){
14648 if(this.isExpanded() || !this.hasFocus){
14652 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14653 this.list.setWidth(lw);
14659 this.restrictHeight();
14663 this.tickItems = Roo.apply([], this.item);
14666 this.cancelBtn.show();
14667 this.trigger.hide();
14670 this.tickableInputEl().focus();
14675 Roo.get(document).on('mousedown', this.collapseIf, this);
14676 Roo.get(document).on('mousewheel', this.collapseIf, this);
14677 if (!this.editable) {
14678 Roo.get(document).on('keydown', this.listKeyPress, this);
14681 this.fireEvent('expand', this);
14685 // Implements the default empty TriggerField.onTriggerClick function
14686 onTriggerClick : function(e)
14688 Roo.log('trigger click');
14690 if(this.disabled || !this.triggerList){
14695 this.loadNext = false;
14697 if(this.isExpanded()){
14699 if (!this.blockFocus) {
14700 this.inputEl().focus();
14704 this.hasFocus = true;
14705 if(this.triggerAction == 'all') {
14706 this.doQuery(this.allQuery, true);
14708 this.doQuery(this.getRawValue());
14710 if (!this.blockFocus) {
14711 this.inputEl().focus();
14716 onTickableTriggerClick : function(e)
14723 this.loadNext = false;
14724 this.hasFocus = true;
14726 if(this.triggerAction == 'all') {
14727 this.doQuery(this.allQuery, true);
14729 this.doQuery(this.getRawValue());
14733 onSearchFieldClick : function(e)
14735 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14736 this.onTickableFooterButtonClick(e, false, false);
14740 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14745 this.loadNext = false;
14746 this.hasFocus = true;
14748 if(this.triggerAction == 'all') {
14749 this.doQuery(this.allQuery, true);
14751 this.doQuery(this.getRawValue());
14755 listKeyPress : function(e)
14757 //Roo.log('listkeypress');
14758 // scroll to first matching element based on key pres..
14759 if (e.isSpecialKey()) {
14762 var k = String.fromCharCode(e.getKey()).toUpperCase();
14765 var csel = this.view.getSelectedNodes();
14766 var cselitem = false;
14768 var ix = this.view.indexOf(csel[0]);
14769 cselitem = this.store.getAt(ix);
14770 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14776 this.store.each(function(v) {
14778 // start at existing selection.
14779 if (cselitem.id == v.id) {
14785 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14786 match = this.store.indexOf(v);
14792 if (match === false) {
14793 return true; // no more action?
14796 this.view.select(match);
14797 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14798 sn.scrollIntoView(sn.dom.parentNode, false);
14801 onViewScroll : function(e, t){
14803 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){
14807 this.hasQuery = true;
14809 this.loading = this.list.select('.loading', true).first();
14811 if(this.loading === null){
14812 this.list.createChild({
14814 cls: 'loading roo-select2-more-results roo-select2-active',
14815 html: 'Loading more results...'
14818 this.loading = this.list.select('.loading', true).first();
14820 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14822 this.loading.hide();
14825 this.loading.show();
14830 this.loadNext = true;
14832 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14837 addItem : function(o)
14839 var dv = ''; // display value
14841 if (this.displayField) {
14842 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14844 // this is an error condition!!!
14845 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14852 var choice = this.choices.createChild({
14854 cls: 'roo-select2-search-choice',
14863 cls: 'roo-select2-search-choice-close fa fa-times',
14868 }, this.searchField);
14870 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14872 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14880 this.inputEl().dom.value = '';
14885 onRemoveItem : function(e, _self, o)
14887 e.preventDefault();
14889 this.lastItem = Roo.apply([], this.item);
14891 var index = this.item.indexOf(o.data) * 1;
14894 Roo.log('not this item?!');
14898 this.item.splice(index, 1);
14903 this.fireEvent('remove', this, e);
14909 syncValue : function()
14911 if(!this.item.length){
14918 Roo.each(this.item, function(i){
14919 if(_this.valueField){
14920 value.push(i[_this.valueField]);
14927 this.value = value.join(',');
14929 if(this.hiddenField){
14930 this.hiddenField.dom.value = this.value;
14933 this.store.fireEvent("datachanged", this.store);
14938 clearItem : function()
14940 if(!this.multiple){
14946 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14954 if(this.tickable && !Roo.isTouch){
14955 this.view.refresh();
14959 inputEl: function ()
14961 if(Roo.isIOS && this.useNativeIOS){
14962 return this.el.select('select.roo-ios-select', true).first();
14965 if(Roo.isTouch && this.mobileTouchView){
14966 return this.el.select('input.form-control',true).first();
14970 return this.searchField;
14973 return this.el.select('input.form-control',true).first();
14976 onTickableFooterButtonClick : function(e, btn, el)
14978 e.preventDefault();
14980 this.lastItem = Roo.apply([], this.item);
14982 if(btn && btn.name == 'cancel'){
14983 this.tickItems = Roo.apply([], this.item);
14992 Roo.each(this.tickItems, function(o){
15000 validate : function()
15002 if(this.getVisibilityEl().hasClass('hidden')){
15006 var v = this.getRawValue();
15009 v = this.getValue();
15012 if(this.disabled || this.allowBlank || v.length){
15017 this.markInvalid();
15021 tickableInputEl : function()
15023 if(!this.tickable || !this.editable){
15024 return this.inputEl();
15027 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15031 getAutoCreateTouchView : function()
15036 cls: 'form-group' //input-group
15042 type : this.inputType,
15043 cls : 'form-control x-combo-noedit',
15044 autocomplete: 'new-password',
15045 placeholder : this.placeholder || '',
15050 input.name = this.name;
15054 input.cls += ' input-' + this.size;
15057 if (this.disabled) {
15058 input.disabled = true;
15069 inputblock.cls += ' input-group';
15071 inputblock.cn.unshift({
15073 cls : 'input-group-addon input-group-prepend input-group-text',
15078 if(this.removable && !this.multiple){
15079 inputblock.cls += ' roo-removable';
15081 inputblock.cn.push({
15084 cls : 'roo-combo-removable-btn close'
15088 if(this.hasFeedback && !this.allowBlank){
15090 inputblock.cls += ' has-feedback';
15092 inputblock.cn.push({
15094 cls: 'glyphicon form-control-feedback'
15101 inputblock.cls += (this.before) ? '' : ' input-group';
15103 inputblock.cn.push({
15105 cls : 'input-group-addon input-group-append input-group-text',
15111 var ibwrap = inputblock;
15116 cls: 'roo-select2-choices',
15120 cls: 'roo-select2-search-field',
15133 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15138 cls: 'form-hidden-field'
15144 if(!this.multiple && this.showToggleBtn){
15151 if (this.caret != false) {
15154 cls: 'fa fa-' + this.caret
15161 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15166 cls: 'combobox-clear',
15180 combobox.cls += ' roo-select2-container-multi';
15183 var align = this.labelAlign || this.parentLabelAlign();
15185 if (align ==='left' && this.fieldLabel.length) {
15190 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15191 tooltip : 'This field is required'
15195 cls : 'control-label col-form-label',
15196 html : this.fieldLabel
15207 var labelCfg = cfg.cn[1];
15208 var contentCfg = cfg.cn[2];
15211 if(this.indicatorpos == 'right'){
15216 cls : 'control-label col-form-label',
15220 html : this.fieldLabel
15224 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15225 tooltip : 'This field is required'
15238 labelCfg = cfg.cn[0];
15239 contentCfg = cfg.cn[1];
15244 if(this.labelWidth > 12){
15245 labelCfg.style = "width: " + this.labelWidth + 'px';
15248 if(this.labelWidth < 13 && this.labelmd == 0){
15249 this.labelmd = this.labelWidth;
15252 if(this.labellg > 0){
15253 labelCfg.cls += ' col-lg-' + this.labellg;
15254 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15257 if(this.labelmd > 0){
15258 labelCfg.cls += ' col-md-' + this.labelmd;
15259 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15262 if(this.labelsm > 0){
15263 labelCfg.cls += ' col-sm-' + this.labelsm;
15264 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15267 if(this.labelxs > 0){
15268 labelCfg.cls += ' col-xs-' + this.labelxs;
15269 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15273 } else if ( this.fieldLabel.length) {
15277 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15278 tooltip : 'This field is required'
15282 cls : 'control-label',
15283 html : this.fieldLabel
15294 if(this.indicatorpos == 'right'){
15298 cls : 'control-label',
15299 html : this.fieldLabel,
15303 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15304 tooltip : 'This field is required'
15321 var settings = this;
15323 ['xs','sm','md','lg'].map(function(size){
15324 if (settings[size]) {
15325 cfg.cls += ' col-' + size + '-' + settings[size];
15332 initTouchView : function()
15334 this.renderTouchView();
15336 this.touchViewEl.on('scroll', function(){
15337 this.el.dom.scrollTop = 0;
15340 this.originalValue = this.getValue();
15342 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15344 this.inputEl().on("click", this.showTouchView, this);
15345 if (this.triggerEl) {
15346 this.triggerEl.on("click", this.showTouchView, this);
15350 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15351 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15353 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15355 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15356 this.store.on('load', this.onTouchViewLoad, this);
15357 this.store.on('loadexception', this.onTouchViewLoadException, this);
15359 if(this.hiddenName){
15361 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15363 this.hiddenField.dom.value =
15364 this.hiddenValue !== undefined ? this.hiddenValue :
15365 this.value !== undefined ? this.value : '';
15367 this.el.dom.removeAttribute('name');
15368 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15372 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15373 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15376 if(this.removable && !this.multiple){
15377 var close = this.closeTriggerEl();
15379 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15380 close.on('click', this.removeBtnClick, this, close);
15384 * fix the bug in Safari iOS8
15386 this.inputEl().on("focus", function(e){
15387 document.activeElement.blur();
15390 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15397 renderTouchView : function()
15399 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15400 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15402 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15403 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15405 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15406 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15407 this.touchViewBodyEl.setStyle('overflow', 'auto');
15409 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15410 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15412 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15413 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15417 showTouchView : function()
15423 this.touchViewHeaderEl.hide();
15425 if(this.modalTitle.length){
15426 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15427 this.touchViewHeaderEl.show();
15430 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15431 this.touchViewEl.show();
15433 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15435 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15436 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15438 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15440 if(this.modalTitle.length){
15441 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15444 this.touchViewBodyEl.setHeight(bodyHeight);
15448 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15450 this.touchViewEl.addClass('in');
15453 if(this._touchViewMask){
15454 Roo.get(document.body).addClass("x-body-masked");
15455 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15456 this._touchViewMask.setStyle('z-index', 10000);
15457 this._touchViewMask.addClass('show');
15460 this.doTouchViewQuery();
15464 hideTouchView : function()
15466 this.touchViewEl.removeClass('in');
15470 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15472 this.touchViewEl.setStyle('display', 'none');
15475 if(this._touchViewMask){
15476 this._touchViewMask.removeClass('show');
15477 Roo.get(document.body).removeClass("x-body-masked");
15481 setTouchViewValue : function()
15488 Roo.each(this.tickItems, function(o){
15493 this.hideTouchView();
15496 doTouchViewQuery : function()
15505 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15509 if(!this.alwaysQuery || this.mode == 'local'){
15510 this.onTouchViewLoad();
15517 onTouchViewBeforeLoad : function(combo,opts)
15523 onTouchViewLoad : function()
15525 if(this.store.getCount() < 1){
15526 this.onTouchViewEmptyResults();
15530 this.clearTouchView();
15532 var rawValue = this.getRawValue();
15534 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15536 this.tickItems = [];
15538 this.store.data.each(function(d, rowIndex){
15539 var row = this.touchViewListGroup.createChild(template);
15541 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15542 row.addClass(d.data.cls);
15545 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15548 html : d.data[this.displayField]
15551 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15552 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15555 row.removeClass('selected');
15556 if(!this.multiple && this.valueField &&
15557 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15560 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15561 row.addClass('selected');
15564 if(this.multiple && this.valueField &&
15565 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15569 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15570 this.tickItems.push(d.data);
15573 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15577 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15579 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15581 if(this.modalTitle.length){
15582 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15585 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15587 if(this.mobile_restrict_height && listHeight < bodyHeight){
15588 this.touchViewBodyEl.setHeight(listHeight);
15593 if(firstChecked && listHeight > bodyHeight){
15594 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15599 onTouchViewLoadException : function()
15601 this.hideTouchView();
15604 onTouchViewEmptyResults : function()
15606 this.clearTouchView();
15608 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15610 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15614 clearTouchView : function()
15616 this.touchViewListGroup.dom.innerHTML = '';
15619 onTouchViewClick : function(e, el, o)
15621 e.preventDefault();
15624 var rowIndex = o.rowIndex;
15626 var r = this.store.getAt(rowIndex);
15628 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15630 if(!this.multiple){
15631 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15632 c.dom.removeAttribute('checked');
15635 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15637 this.setFromData(r.data);
15639 var close = this.closeTriggerEl();
15645 this.hideTouchView();
15647 this.fireEvent('select', this, r, rowIndex);
15652 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15653 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15654 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15658 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15659 this.addItem(r.data);
15660 this.tickItems.push(r.data);
15664 getAutoCreateNativeIOS : function()
15667 cls: 'form-group' //input-group,
15672 cls : 'roo-ios-select'
15676 combobox.name = this.name;
15679 if (this.disabled) {
15680 combobox.disabled = true;
15683 var settings = this;
15685 ['xs','sm','md','lg'].map(function(size){
15686 if (settings[size]) {
15687 cfg.cls += ' col-' + size + '-' + settings[size];
15697 initIOSView : function()
15699 this.store.on('load', this.onIOSViewLoad, this);
15704 onIOSViewLoad : function()
15706 if(this.store.getCount() < 1){
15710 this.clearIOSView();
15712 if(this.allowBlank) {
15714 var default_text = '-- SELECT --';
15716 if(this.placeholder.length){
15717 default_text = this.placeholder;
15720 if(this.emptyTitle.length){
15721 default_text += ' - ' + this.emptyTitle + ' -';
15724 var opt = this.inputEl().createChild({
15727 html : default_text
15731 o[this.valueField] = 0;
15732 o[this.displayField] = default_text;
15734 this.ios_options.push({
15741 this.store.data.each(function(d, rowIndex){
15745 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15746 html = d.data[this.displayField];
15751 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15752 value = d.data[this.valueField];
15761 if(this.value == d.data[this.valueField]){
15762 option['selected'] = true;
15765 var opt = this.inputEl().createChild(option);
15767 this.ios_options.push({
15774 this.inputEl().on('change', function(){
15775 this.fireEvent('select', this);
15780 clearIOSView: function()
15782 this.inputEl().dom.innerHTML = '';
15784 this.ios_options = [];
15787 setIOSValue: function(v)
15791 if(!this.ios_options){
15795 Roo.each(this.ios_options, function(opts){
15797 opts.el.dom.removeAttribute('selected');
15799 if(opts.data[this.valueField] != v){
15803 opts.el.dom.setAttribute('selected', true);
15809 * @cfg {Boolean} grow
15813 * @cfg {Number} growMin
15817 * @cfg {Number} growMax
15826 Roo.apply(Roo.bootstrap.ComboBox, {
15830 cls: 'modal-header',
15852 cls: 'list-group-item',
15856 cls: 'roo-combobox-list-group-item-value'
15860 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15874 listItemCheckbox : {
15876 cls: 'list-group-item',
15880 cls: 'roo-combobox-list-group-item-value'
15884 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15900 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15905 cls: 'modal-footer',
15913 cls: 'col-xs-6 text-left',
15916 cls: 'btn btn-danger roo-touch-view-cancel',
15922 cls: 'col-xs-6 text-right',
15925 cls: 'btn btn-success roo-touch-view-ok',
15936 Roo.apply(Roo.bootstrap.ComboBox, {
15938 touchViewTemplate : {
15940 cls: 'modal fade roo-combobox-touch-view',
15944 cls: 'modal-dialog',
15945 style : 'position:fixed', // we have to fix position....
15949 cls: 'modal-content',
15951 Roo.bootstrap.ComboBox.header,
15952 Roo.bootstrap.ComboBox.body,
15953 Roo.bootstrap.ComboBox.footer
15962 * Ext JS Library 1.1.1
15963 * Copyright(c) 2006-2007, Ext JS, LLC.
15965 * Originally Released Under LGPL - original licence link has changed is not relivant.
15968 * <script type="text/javascript">
15973 * @extends Roo.util.Observable
15974 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15975 * This class also supports single and multi selection modes. <br>
15976 * Create a data model bound view:
15978 var store = new Roo.data.Store(...);
15980 var view = new Roo.View({
15982 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15984 singleSelect: true,
15985 selectedClass: "ydataview-selected",
15989 // listen for node click?
15990 view.on("click", function(vw, index, node, e){
15991 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15995 dataModel.load("foobar.xml");
15997 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15999 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16000 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16002 * Note: old style constructor is still suported (container, template, config)
16005 * Create a new View
16006 * @param {Object} config The config object
16009 Roo.View = function(config, depreciated_tpl, depreciated_config){
16011 this.parent = false;
16013 if (typeof(depreciated_tpl) == 'undefined') {
16014 // new way.. - universal constructor.
16015 Roo.apply(this, config);
16016 this.el = Roo.get(this.el);
16019 this.el = Roo.get(config);
16020 this.tpl = depreciated_tpl;
16021 Roo.apply(this, depreciated_config);
16023 this.wrapEl = this.el.wrap().wrap();
16024 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16027 if(typeof(this.tpl) == "string"){
16028 this.tpl = new Roo.Template(this.tpl);
16030 // support xtype ctors..
16031 this.tpl = new Roo.factory(this.tpl, Roo);
16035 this.tpl.compile();
16040 * @event beforeclick
16041 * Fires before a click is processed. Returns false to cancel the default action.
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
16047 "beforeclick" : true,
16050 * Fires when a template node is 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
16059 * Fires when a template node is double clicked.
16060 * @param {Roo.View} this
16061 * @param {Number} index The index of the target node
16062 * @param {HTMLElement} node The target node
16063 * @param {Roo.EventObject} e The raw event object
16067 * @event contextmenu
16068 * Fires when a template node is right clicked.
16069 * @param {Roo.View} this
16070 * @param {Number} index The index of the target node
16071 * @param {HTMLElement} node The target node
16072 * @param {Roo.EventObject} e The raw event object
16074 "contextmenu" : true,
16076 * @event selectionchange
16077 * Fires when the selected nodes change.
16078 * @param {Roo.View} this
16079 * @param {Array} selections Array of the selected nodes
16081 "selectionchange" : true,
16084 * @event beforeselect
16085 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16086 * @param {Roo.View} this
16087 * @param {HTMLElement} node The node to be selected
16088 * @param {Array} selections Array of currently selected nodes
16090 "beforeselect" : true,
16092 * @event preparedata
16093 * Fires on every row to render, to allow you to change the data.
16094 * @param {Roo.View} this
16095 * @param {Object} data to be rendered (change this)
16097 "preparedata" : true
16105 "click": this.onClick,
16106 "dblclick": this.onDblClick,
16107 "contextmenu": this.onContextMenu,
16111 this.selections = [];
16113 this.cmp = new Roo.CompositeElementLite([]);
16115 this.store = Roo.factory(this.store, Roo.data);
16116 this.setStore(this.store, true);
16119 if ( this.footer && this.footer.xtype) {
16121 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16123 this.footer.dataSource = this.store;
16124 this.footer.container = fctr;
16125 this.footer = Roo.factory(this.footer, Roo);
16126 fctr.insertFirst(this.el);
16128 // this is a bit insane - as the paging toolbar seems to detach the el..
16129 // dom.parentNode.parentNode.parentNode
16130 // they get detached?
16134 Roo.View.superclass.constructor.call(this);
16139 Roo.extend(Roo.View, Roo.util.Observable, {
16142 * @cfg {Roo.data.Store} store Data store to load data from.
16147 * @cfg {String|Roo.Element} el The container element.
16152 * @cfg {String|Roo.Template} tpl The template used by this View
16156 * @cfg {String} dataName the named area of the template to use as the data area
16157 * Works with domtemplates roo-name="name"
16161 * @cfg {String} selectedClass The css class to add to selected nodes
16163 selectedClass : "x-view-selected",
16165 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16170 * @cfg {String} text to display on mask (default Loading)
16174 * @cfg {Boolean} multiSelect Allow multiple selection
16176 multiSelect : false,
16178 * @cfg {Boolean} singleSelect Allow single selection
16180 singleSelect: false,
16183 * @cfg {Boolean} toggleSelect - selecting
16185 toggleSelect : false,
16188 * @cfg {Boolean} tickable - selecting
16193 * Returns the element this view is bound to.
16194 * @return {Roo.Element}
16196 getEl : function(){
16197 return this.wrapEl;
16203 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16205 refresh : function(){
16206 //Roo.log('refresh');
16209 // if we are using something like 'domtemplate', then
16210 // the what gets used is:
16211 // t.applySubtemplate(NAME, data, wrapping data..)
16212 // the outer template then get' applied with
16213 // the store 'extra data'
16214 // and the body get's added to the
16215 // roo-name="data" node?
16216 // <span class='roo-tpl-{name}'></span> ?????
16220 this.clearSelections();
16221 this.el.update("");
16223 var records = this.store.getRange();
16224 if(records.length < 1) {
16226 // is this valid?? = should it render a template??
16228 this.el.update(this.emptyText);
16232 if (this.dataName) {
16233 this.el.update(t.apply(this.store.meta)); //????
16234 el = this.el.child('.roo-tpl-' + this.dataName);
16237 for(var i = 0, len = records.length; i < len; i++){
16238 var data = this.prepareData(records[i].data, i, records[i]);
16239 this.fireEvent("preparedata", this, data, i, records[i]);
16241 var d = Roo.apply({}, data);
16244 Roo.apply(d, {'roo-id' : Roo.id()});
16248 Roo.each(this.parent.item, function(item){
16249 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16252 Roo.apply(d, {'roo-data-checked' : 'checked'});
16256 html[html.length] = Roo.util.Format.trim(
16258 t.applySubtemplate(this.dataName, d, this.store.meta) :
16265 el.update(html.join(""));
16266 this.nodes = el.dom.childNodes;
16267 this.updateIndexes(0);
16272 * Function to override to reformat the data that is sent to
16273 * the template for each node.
16274 * DEPRICATED - use the preparedata event handler.
16275 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16276 * a JSON object for an UpdateManager bound view).
16278 prepareData : function(data, index, record)
16280 this.fireEvent("preparedata", this, data, index, record);
16284 onUpdate : function(ds, record){
16285 // Roo.log('on update');
16286 this.clearSelections();
16287 var index = this.store.indexOf(record);
16288 var n = this.nodes[index];
16289 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16290 n.parentNode.removeChild(n);
16291 this.updateIndexes(index, index);
16297 onAdd : function(ds, records, index)
16299 //Roo.log(['on Add', ds, records, index] );
16300 this.clearSelections();
16301 if(this.nodes.length == 0){
16305 var n = this.nodes[index];
16306 for(var i = 0, len = records.length; i < len; i++){
16307 var d = this.prepareData(records[i].data, i, records[i]);
16309 this.tpl.insertBefore(n, d);
16312 this.tpl.append(this.el, d);
16315 this.updateIndexes(index);
16318 onRemove : function(ds, record, index){
16319 // Roo.log('onRemove');
16320 this.clearSelections();
16321 var el = this.dataName ?
16322 this.el.child('.roo-tpl-' + this.dataName) :
16325 el.dom.removeChild(this.nodes[index]);
16326 this.updateIndexes(index);
16330 * Refresh an individual node.
16331 * @param {Number} index
16333 refreshNode : function(index){
16334 this.onUpdate(this.store, this.store.getAt(index));
16337 updateIndexes : function(startIndex, endIndex){
16338 var ns = this.nodes;
16339 startIndex = startIndex || 0;
16340 endIndex = endIndex || ns.length - 1;
16341 for(var i = startIndex; i <= endIndex; i++){
16342 ns[i].nodeIndex = i;
16347 * Changes the data store this view uses and refresh the view.
16348 * @param {Store} store
16350 setStore : function(store, initial){
16351 if(!initial && this.store){
16352 this.store.un("datachanged", this.refresh);
16353 this.store.un("add", this.onAdd);
16354 this.store.un("remove", this.onRemove);
16355 this.store.un("update", this.onUpdate);
16356 this.store.un("clear", this.refresh);
16357 this.store.un("beforeload", this.onBeforeLoad);
16358 this.store.un("load", this.onLoad);
16359 this.store.un("loadexception", this.onLoad);
16363 store.on("datachanged", this.refresh, this);
16364 store.on("add", this.onAdd, this);
16365 store.on("remove", this.onRemove, this);
16366 store.on("update", this.onUpdate, this);
16367 store.on("clear", this.refresh, this);
16368 store.on("beforeload", this.onBeforeLoad, this);
16369 store.on("load", this.onLoad, this);
16370 store.on("loadexception", this.onLoad, this);
16378 * onbeforeLoad - masks the loading area.
16381 onBeforeLoad : function(store,opts)
16383 //Roo.log('onBeforeLoad');
16385 this.el.update("");
16387 this.el.mask(this.mask ? this.mask : "Loading" );
16389 onLoad : function ()
16396 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16397 * @param {HTMLElement} node
16398 * @return {HTMLElement} The template node
16400 findItemFromChild : function(node){
16401 var el = this.dataName ?
16402 this.el.child('.roo-tpl-' + this.dataName,true) :
16405 if(!node || node.parentNode == el){
16408 var p = node.parentNode;
16409 while(p && p != el){
16410 if(p.parentNode == el){
16419 onClick : function(e){
16420 var item = this.findItemFromChild(e.getTarget());
16422 var index = this.indexOf(item);
16423 if(this.onItemClick(item, index, e) !== false){
16424 this.fireEvent("click", this, index, item, e);
16427 this.clearSelections();
16432 onContextMenu : function(e){
16433 var item = this.findItemFromChild(e.getTarget());
16435 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16440 onDblClick : function(e){
16441 var item = this.findItemFromChild(e.getTarget());
16443 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16447 onItemClick : function(item, index, e)
16449 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16452 if (this.toggleSelect) {
16453 var m = this.isSelected(item) ? 'unselect' : 'select';
16456 _t[m](item, true, false);
16459 if(this.multiSelect || this.singleSelect){
16460 if(this.multiSelect && e.shiftKey && this.lastSelection){
16461 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16463 this.select(item, this.multiSelect && e.ctrlKey);
16464 this.lastSelection = item;
16467 if(!this.tickable){
16468 e.preventDefault();
16476 * Get the number of selected nodes.
16479 getSelectionCount : function(){
16480 return this.selections.length;
16484 * Get the currently selected nodes.
16485 * @return {Array} An array of HTMLElements
16487 getSelectedNodes : function(){
16488 return this.selections;
16492 * Get the indexes of the selected nodes.
16495 getSelectedIndexes : function(){
16496 var indexes = [], s = this.selections;
16497 for(var i = 0, len = s.length; i < len; i++){
16498 indexes.push(s[i].nodeIndex);
16504 * Clear all selections
16505 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16507 clearSelections : function(suppressEvent){
16508 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16509 this.cmp.elements = this.selections;
16510 this.cmp.removeClass(this.selectedClass);
16511 this.selections = [];
16512 if(!suppressEvent){
16513 this.fireEvent("selectionchange", this, this.selections);
16519 * Returns true if the passed node is selected
16520 * @param {HTMLElement/Number} node The node or node index
16521 * @return {Boolean}
16523 isSelected : function(node){
16524 var s = this.selections;
16528 node = this.getNode(node);
16529 return s.indexOf(node) !== -1;
16534 * @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
16535 * @param {Boolean} keepExisting (optional) true to keep existing selections
16536 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16538 select : function(nodeInfo, keepExisting, suppressEvent){
16539 if(nodeInfo instanceof Array){
16541 this.clearSelections(true);
16543 for(var i = 0, len = nodeInfo.length; i < len; i++){
16544 this.select(nodeInfo[i], true, true);
16548 var node = this.getNode(nodeInfo);
16549 if(!node || this.isSelected(node)){
16550 return; // already selected.
16553 this.clearSelections(true);
16556 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16557 Roo.fly(node).addClass(this.selectedClass);
16558 this.selections.push(node);
16559 if(!suppressEvent){
16560 this.fireEvent("selectionchange", this, this.selections);
16568 * @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
16569 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16570 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16572 unselect : function(nodeInfo, keepExisting, suppressEvent)
16574 if(nodeInfo instanceof Array){
16575 Roo.each(this.selections, function(s) {
16576 this.unselect(s, nodeInfo);
16580 var node = this.getNode(nodeInfo);
16581 if(!node || !this.isSelected(node)){
16582 //Roo.log("not selected");
16583 return; // not selected.
16587 Roo.each(this.selections, function(s) {
16589 Roo.fly(node).removeClass(this.selectedClass);
16596 this.selections= ns;
16597 this.fireEvent("selectionchange", this, this.selections);
16601 * Gets a template node.
16602 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16603 * @return {HTMLElement} The node or null if it wasn't found
16605 getNode : function(nodeInfo){
16606 if(typeof nodeInfo == "string"){
16607 return document.getElementById(nodeInfo);
16608 }else if(typeof nodeInfo == "number"){
16609 return this.nodes[nodeInfo];
16615 * Gets a range template nodes.
16616 * @param {Number} startIndex
16617 * @param {Number} endIndex
16618 * @return {Array} An array of nodes
16620 getNodes : function(start, end){
16621 var ns = this.nodes;
16622 start = start || 0;
16623 end = typeof end == "undefined" ? ns.length - 1 : end;
16626 for(var i = start; i <= end; i++){
16630 for(var i = start; i >= end; i--){
16638 * Finds the index of the passed node
16639 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16640 * @return {Number} The index of the node or -1
16642 indexOf : function(node){
16643 node = this.getNode(node);
16644 if(typeof node.nodeIndex == "number"){
16645 return node.nodeIndex;
16647 var ns = this.nodes;
16648 for(var i = 0, len = ns.length; i < len; i++){
16659 * based on jquery fullcalendar
16663 Roo.bootstrap = Roo.bootstrap || {};
16665 * @class Roo.bootstrap.Calendar
16666 * @extends Roo.bootstrap.Component
16667 * Bootstrap Calendar class
16668 * @cfg {Boolean} loadMask (true|false) default false
16669 * @cfg {Object} header generate the user specific header of the calendar, default false
16672 * Create a new Container
16673 * @param {Object} config The config object
16678 Roo.bootstrap.Calendar = function(config){
16679 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16683 * Fires when a date is selected
16684 * @param {DatePicker} this
16685 * @param {Date} date The selected date
16689 * @event monthchange
16690 * Fires when the displayed month changes
16691 * @param {DatePicker} this
16692 * @param {Date} date The selected month
16694 'monthchange': true,
16696 * @event evententer
16697 * Fires when mouse over an event
16698 * @param {Calendar} this
16699 * @param {event} Event
16701 'evententer': true,
16703 * @event eventleave
16704 * Fires when the mouse leaves an
16705 * @param {Calendar} this
16708 'eventleave': true,
16710 * @event eventclick
16711 * Fires when the mouse click an
16712 * @param {Calendar} this
16721 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16724 * @cfg {Number} startDay
16725 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16733 getAutoCreate : function(){
16736 var fc_button = function(name, corner, style, content ) {
16737 return Roo.apply({},{
16739 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16741 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16744 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16755 style : 'width:100%',
16762 cls : 'fc-header-left',
16764 fc_button('prev', 'left', 'arrow', '‹' ),
16765 fc_button('next', 'right', 'arrow', '›' ),
16766 { tag: 'span', cls: 'fc-header-space' },
16767 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16775 cls : 'fc-header-center',
16779 cls: 'fc-header-title',
16782 html : 'month / year'
16790 cls : 'fc-header-right',
16792 /* fc_button('month', 'left', '', 'month' ),
16793 fc_button('week', '', '', 'week' ),
16794 fc_button('day', 'right', '', 'day' )
16806 header = this.header;
16809 var cal_heads = function() {
16811 // fixme - handle this.
16813 for (var i =0; i < Date.dayNames.length; i++) {
16814 var d = Date.dayNames[i];
16817 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16818 html : d.substring(0,3)
16822 ret[0].cls += ' fc-first';
16823 ret[6].cls += ' fc-last';
16826 var cal_cell = function(n) {
16829 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16834 cls: 'fc-day-number',
16838 cls: 'fc-day-content',
16842 style: 'position: relative;' // height: 17px;
16854 var cal_rows = function() {
16857 for (var r = 0; r < 6; r++) {
16864 for (var i =0; i < Date.dayNames.length; i++) {
16865 var d = Date.dayNames[i];
16866 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16869 row.cn[0].cls+=' fc-first';
16870 row.cn[0].cn[0].style = 'min-height:90px';
16871 row.cn[6].cls+=' fc-last';
16875 ret[0].cls += ' fc-first';
16876 ret[4].cls += ' fc-prev-last';
16877 ret[5].cls += ' fc-last';
16884 cls: 'fc-border-separate',
16885 style : 'width:100%',
16893 cls : 'fc-first fc-last',
16911 cls : 'fc-content',
16912 style : "position: relative;",
16915 cls : 'fc-view fc-view-month fc-grid',
16916 style : 'position: relative',
16917 unselectable : 'on',
16920 cls : 'fc-event-container',
16921 style : 'position:absolute;z-index:8;top:0;left:0;'
16939 initEvents : function()
16942 throw "can not find store for calendar";
16948 style: "text-align:center",
16952 style: "background-color:white;width:50%;margin:250 auto",
16956 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16967 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16969 var size = this.el.select('.fc-content', true).first().getSize();
16970 this.maskEl.setSize(size.width, size.height);
16971 this.maskEl.enableDisplayMode("block");
16972 if(!this.loadMask){
16973 this.maskEl.hide();
16976 this.store = Roo.factory(this.store, Roo.data);
16977 this.store.on('load', this.onLoad, this);
16978 this.store.on('beforeload', this.onBeforeLoad, this);
16982 this.cells = this.el.select('.fc-day',true);
16983 //Roo.log(this.cells);
16984 this.textNodes = this.el.query('.fc-day-number');
16985 this.cells.addClassOnOver('fc-state-hover');
16987 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16988 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16989 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16990 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16992 this.on('monthchange', this.onMonthChange, this);
16994 this.update(new Date().clearTime());
16997 resize : function() {
16998 var sz = this.el.getSize();
17000 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17001 this.el.select('.fc-day-content div',true).setHeight(34);
17006 showPrevMonth : function(e){
17007 this.update(this.activeDate.add("mo", -1));
17009 showToday : function(e){
17010 this.update(new Date().clearTime());
17013 showNextMonth : function(e){
17014 this.update(this.activeDate.add("mo", 1));
17018 showPrevYear : function(){
17019 this.update(this.activeDate.add("y", -1));
17023 showNextYear : function(){
17024 this.update(this.activeDate.add("y", 1));
17029 update : function(date)
17031 var vd = this.activeDate;
17032 this.activeDate = date;
17033 // if(vd && this.el){
17034 // var t = date.getTime();
17035 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17036 // Roo.log('using add remove');
17038 // this.fireEvent('monthchange', this, date);
17040 // this.cells.removeClass("fc-state-highlight");
17041 // this.cells.each(function(c){
17042 // if(c.dateValue == t){
17043 // c.addClass("fc-state-highlight");
17044 // setTimeout(function(){
17045 // try{c.dom.firstChild.focus();}catch(e){}
17055 var days = date.getDaysInMonth();
17057 var firstOfMonth = date.getFirstDateOfMonth();
17058 var startingPos = firstOfMonth.getDay()-this.startDay;
17060 if(startingPos < this.startDay){
17064 var pm = date.add(Date.MONTH, -1);
17065 var prevStart = pm.getDaysInMonth()-startingPos;
17067 this.cells = this.el.select('.fc-day',true);
17068 this.textNodes = this.el.query('.fc-day-number');
17069 this.cells.addClassOnOver('fc-state-hover');
17071 var cells = this.cells.elements;
17072 var textEls = this.textNodes;
17074 Roo.each(cells, function(cell){
17075 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17078 days += startingPos;
17080 // convert everything to numbers so it's fast
17081 var day = 86400000;
17082 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17085 //Roo.log(prevStart);
17087 var today = new Date().clearTime().getTime();
17088 var sel = date.clearTime().getTime();
17089 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17090 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17091 var ddMatch = this.disabledDatesRE;
17092 var ddText = this.disabledDatesText;
17093 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17094 var ddaysText = this.disabledDaysText;
17095 var format = this.format;
17097 var setCellClass = function(cal, cell){
17101 //Roo.log('set Cell Class');
17103 var t = d.getTime();
17107 cell.dateValue = t;
17109 cell.className += " fc-today";
17110 cell.className += " fc-state-highlight";
17111 cell.title = cal.todayText;
17114 // disable highlight in other month..
17115 //cell.className += " fc-state-highlight";
17120 cell.className = " fc-state-disabled";
17121 cell.title = cal.minText;
17125 cell.className = " fc-state-disabled";
17126 cell.title = cal.maxText;
17130 if(ddays.indexOf(d.getDay()) != -1){
17131 cell.title = ddaysText;
17132 cell.className = " fc-state-disabled";
17135 if(ddMatch && format){
17136 var fvalue = d.dateFormat(format);
17137 if(ddMatch.test(fvalue)){
17138 cell.title = ddText.replace("%0", fvalue);
17139 cell.className = " fc-state-disabled";
17143 if (!cell.initialClassName) {
17144 cell.initialClassName = cell.dom.className;
17147 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17152 for(; i < startingPos; i++) {
17153 textEls[i].innerHTML = (++prevStart);
17154 d.setDate(d.getDate()+1);
17156 cells[i].className = "fc-past fc-other-month";
17157 setCellClass(this, cells[i]);
17162 for(; i < days; i++){
17163 intDay = i - startingPos + 1;
17164 textEls[i].innerHTML = (intDay);
17165 d.setDate(d.getDate()+1);
17167 cells[i].className = ''; // "x-date-active";
17168 setCellClass(this, cells[i]);
17172 for(; i < 42; i++) {
17173 textEls[i].innerHTML = (++extraDays);
17174 d.setDate(d.getDate()+1);
17176 cells[i].className = "fc-future fc-other-month";
17177 setCellClass(this, cells[i]);
17180 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17182 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17184 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17185 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17187 if(totalRows != 6){
17188 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17189 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17192 this.fireEvent('monthchange', this, date);
17196 if(!this.internalRender){
17197 var main = this.el.dom.firstChild;
17198 var w = main.offsetWidth;
17199 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17200 Roo.fly(main).setWidth(w);
17201 this.internalRender = true;
17202 // opera does not respect the auto grow header center column
17203 // then, after it gets a width opera refuses to recalculate
17204 // without a second pass
17205 if(Roo.isOpera && !this.secondPass){
17206 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17207 this.secondPass = true;
17208 this.update.defer(10, this, [date]);
17215 findCell : function(dt) {
17216 dt = dt.clearTime().getTime();
17218 this.cells.each(function(c){
17219 //Roo.log("check " +c.dateValue + '?=' + dt);
17220 if(c.dateValue == dt){
17230 findCells : function(ev) {
17231 var s = ev.start.clone().clearTime().getTime();
17233 var e= ev.end.clone().clearTime().getTime();
17236 this.cells.each(function(c){
17237 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17239 if(c.dateValue > e){
17242 if(c.dateValue < s){
17251 // findBestRow: function(cells)
17255 // for (var i =0 ; i < cells.length;i++) {
17256 // ret = Math.max(cells[i].rows || 0,ret);
17263 addItem : function(ev)
17265 // look for vertical location slot in
17266 var cells = this.findCells(ev);
17268 // ev.row = this.findBestRow(cells);
17270 // work out the location.
17274 for(var i =0; i < cells.length; i++) {
17276 cells[i].row = cells[0].row;
17279 cells[i].row = cells[i].row + 1;
17289 if (crow.start.getY() == cells[i].getY()) {
17291 crow.end = cells[i];
17308 cells[0].events.push(ev);
17310 this.calevents.push(ev);
17313 clearEvents: function() {
17315 if(!this.calevents){
17319 Roo.each(this.cells.elements, function(c){
17325 Roo.each(this.calevents, function(e) {
17326 Roo.each(e.els, function(el) {
17327 el.un('mouseenter' ,this.onEventEnter, this);
17328 el.un('mouseleave' ,this.onEventLeave, this);
17333 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17339 renderEvents: function()
17343 this.cells.each(function(c) {
17352 if(c.row != c.events.length){
17353 r = 4 - (4 - (c.row - c.events.length));
17356 c.events = ev.slice(0, r);
17357 c.more = ev.slice(r);
17359 if(c.more.length && c.more.length == 1){
17360 c.events.push(c.more.pop());
17363 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17367 this.cells.each(function(c) {
17369 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17372 for (var e = 0; e < c.events.length; e++){
17373 var ev = c.events[e];
17374 var rows = ev.rows;
17376 for(var i = 0; i < rows.length; i++) {
17378 // how many rows should it span..
17381 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17382 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17384 unselectable : "on",
17387 cls: 'fc-event-inner',
17391 // cls: 'fc-event-time',
17392 // html : cells.length > 1 ? '' : ev.time
17396 cls: 'fc-event-title',
17397 html : String.format('{0}', ev.title)
17404 cls: 'ui-resizable-handle ui-resizable-e',
17405 html : '  '
17412 cfg.cls += ' fc-event-start';
17414 if ((i+1) == rows.length) {
17415 cfg.cls += ' fc-event-end';
17418 var ctr = _this.el.select('.fc-event-container',true).first();
17419 var cg = ctr.createChild(cfg);
17421 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17422 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17424 var r = (c.more.length) ? 1 : 0;
17425 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17426 cg.setWidth(ebox.right - sbox.x -2);
17428 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17429 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17430 cg.on('click', _this.onEventClick, _this, ev);
17441 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17442 style : 'position: absolute',
17443 unselectable : "on",
17446 cls: 'fc-event-inner',
17450 cls: 'fc-event-title',
17458 cls: 'ui-resizable-handle ui-resizable-e',
17459 html : '  '
17465 var ctr = _this.el.select('.fc-event-container',true).first();
17466 var cg = ctr.createChild(cfg);
17468 var sbox = c.select('.fc-day-content',true).first().getBox();
17469 var ebox = c.select('.fc-day-content',true).first().getBox();
17471 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17472 cg.setWidth(ebox.right - sbox.x -2);
17474 cg.on('click', _this.onMoreEventClick, _this, c.more);
17484 onEventEnter: function (e, el,event,d) {
17485 this.fireEvent('evententer', this, el, event);
17488 onEventLeave: function (e, el,event,d) {
17489 this.fireEvent('eventleave', this, el, event);
17492 onEventClick: function (e, el,event,d) {
17493 this.fireEvent('eventclick', this, el, event);
17496 onMonthChange: function () {
17500 onMoreEventClick: function(e, el, more)
17504 this.calpopover.placement = 'right';
17505 this.calpopover.setTitle('More');
17507 this.calpopover.setContent('');
17509 var ctr = this.calpopover.el.select('.popover-content', true).first();
17511 Roo.each(more, function(m){
17513 cls : 'fc-event-hori fc-event-draggable',
17516 var cg = ctr.createChild(cfg);
17518 cg.on('click', _this.onEventClick, _this, m);
17521 this.calpopover.show(el);
17526 onLoad: function ()
17528 this.calevents = [];
17531 if(this.store.getCount() > 0){
17532 this.store.data.each(function(d){
17535 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17536 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17537 time : d.data.start_time,
17538 title : d.data.title,
17539 description : d.data.description,
17540 venue : d.data.venue
17545 this.renderEvents();
17547 if(this.calevents.length && this.loadMask){
17548 this.maskEl.hide();
17552 onBeforeLoad: function()
17554 this.clearEvents();
17556 this.maskEl.show();
17570 * @class Roo.bootstrap.Popover
17571 * @extends Roo.bootstrap.Component
17572 * Bootstrap Popover class
17573 * @cfg {String} html contents of the popover (or false to use children..)
17574 * @cfg {String} title of popover (or false to hide)
17575 * @cfg {String} placement how it is placed
17576 * @cfg {String} trigger click || hover (or false to trigger manually)
17577 * @cfg {String} over what (parent or false to trigger manually.)
17578 * @cfg {Number} delay - delay before showing
17581 * Create a new Popover
17582 * @param {Object} config The config object
17585 Roo.bootstrap.Popover = function(config){
17586 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17592 * After the popover show
17594 * @param {Roo.bootstrap.Popover} this
17599 * After the popover hide
17601 * @param {Roo.bootstrap.Popover} this
17607 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17609 title: 'Fill in a title',
17612 placement : 'right',
17613 trigger : 'hover', // hover
17619 can_build_overlaid : false,
17621 getChildContainer : function()
17623 return this.el.select('.popover-content',true).first();
17626 getAutoCreate : function(){
17629 cls : 'popover roo-dynamic',
17630 style: 'display:block',
17636 cls : 'popover-inner',
17640 cls: 'popover-title popover-header',
17644 cls : 'popover-content popover-body',
17655 setTitle: function(str)
17658 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17660 setContent: function(str)
17663 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17665 // as it get's added to the bottom of the page.
17666 onRender : function(ct, position)
17668 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17670 var cfg = Roo.apply({}, this.getAutoCreate());
17674 cfg.cls += ' ' + this.cls;
17677 cfg.style = this.style;
17679 //Roo.log("adding to ");
17680 this.el = Roo.get(document.body).createChild(cfg, position);
17681 // Roo.log(this.el);
17686 initEvents : function()
17688 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17689 this.el.enableDisplayMode('block');
17691 if (this.over === false) {
17694 if (this.triggers === false) {
17697 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17698 var triggers = this.trigger ? this.trigger.split(' ') : [];
17699 Roo.each(triggers, function(trigger) {
17701 if (trigger == 'click') {
17702 on_el.on('click', this.toggle, this);
17703 } else if (trigger != 'manual') {
17704 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17705 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17707 on_el.on(eventIn ,this.enter, this);
17708 on_el.on(eventOut, this.leave, this);
17719 toggle : function () {
17720 this.hoverState == 'in' ? this.leave() : this.enter();
17723 enter : function () {
17725 clearTimeout(this.timeout);
17727 this.hoverState = 'in';
17729 if (!this.delay || !this.delay.show) {
17734 this.timeout = setTimeout(function () {
17735 if (_t.hoverState == 'in') {
17738 }, this.delay.show)
17741 leave : function() {
17742 clearTimeout(this.timeout);
17744 this.hoverState = 'out';
17746 if (!this.delay || !this.delay.hide) {
17751 this.timeout = setTimeout(function () {
17752 if (_t.hoverState == 'out') {
17755 }, this.delay.hide)
17758 show : function (on_el)
17761 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17765 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17766 if (this.html !== false) {
17767 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17769 this.el.removeClass([
17770 'fade','top','bottom', 'left', 'right','in',
17771 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17773 if (!this.title.length) {
17774 this.el.select('.popover-title',true).hide();
17777 var placement = typeof this.placement == 'function' ?
17778 this.placement.call(this, this.el, on_el) :
17781 var autoToken = /\s?auto?\s?/i;
17782 var autoPlace = autoToken.test(placement);
17784 placement = placement.replace(autoToken, '') || 'top';
17788 //this.el.setXY([0,0]);
17790 this.el.dom.style.display='block';
17791 this.el.addClass(placement);
17793 //this.el.appendTo(on_el);
17795 var p = this.getPosition();
17796 var box = this.el.getBox();
17801 var align = Roo.bootstrap.Popover.alignment[placement];
17804 this.el.alignTo(on_el, align[0],align[1]);
17805 //var arrow = this.el.select('.arrow',true).first();
17806 //arrow.set(align[2],
17808 this.el.addClass('in');
17811 if (this.el.hasClass('fade')) {
17815 this.hoverState = 'in';
17817 this.fireEvent('show', this);
17822 this.el.setXY([0,0]);
17823 this.el.removeClass('in');
17825 this.hoverState = null;
17827 this.fireEvent('hide', this);
17832 Roo.bootstrap.Popover.alignment = {
17833 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17834 'right' : ['l-r', [10,0], 'left bs-popover-left'],
17835 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17836 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17847 * @class Roo.bootstrap.Progress
17848 * @extends Roo.bootstrap.Component
17849 * Bootstrap Progress class
17850 * @cfg {Boolean} striped striped of the progress bar
17851 * @cfg {Boolean} active animated of the progress bar
17855 * Create a new Progress
17856 * @param {Object} config The config object
17859 Roo.bootstrap.Progress = function(config){
17860 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17863 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17868 getAutoCreate : function(){
17876 cfg.cls += ' progress-striped';
17880 cfg.cls += ' active';
17899 * @class Roo.bootstrap.ProgressBar
17900 * @extends Roo.bootstrap.Component
17901 * Bootstrap ProgressBar class
17902 * @cfg {Number} aria_valuenow aria-value now
17903 * @cfg {Number} aria_valuemin aria-value min
17904 * @cfg {Number} aria_valuemax aria-value max
17905 * @cfg {String} label label for the progress bar
17906 * @cfg {String} panel (success | info | warning | danger )
17907 * @cfg {String} role role of the progress bar
17908 * @cfg {String} sr_only text
17912 * Create a new ProgressBar
17913 * @param {Object} config The config object
17916 Roo.bootstrap.ProgressBar = function(config){
17917 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17920 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17924 aria_valuemax : 100,
17930 getAutoCreate : function()
17935 cls: 'progress-bar',
17936 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17948 cfg.role = this.role;
17951 if(this.aria_valuenow){
17952 cfg['aria-valuenow'] = this.aria_valuenow;
17955 if(this.aria_valuemin){
17956 cfg['aria-valuemin'] = this.aria_valuemin;
17959 if(this.aria_valuemax){
17960 cfg['aria-valuemax'] = this.aria_valuemax;
17963 if(this.label && !this.sr_only){
17964 cfg.html = this.label;
17968 cfg.cls += ' progress-bar-' + this.panel;
17974 update : function(aria_valuenow)
17976 this.aria_valuenow = aria_valuenow;
17978 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17993 * @class Roo.bootstrap.TabGroup
17994 * @extends Roo.bootstrap.Column
17995 * Bootstrap Column class
17996 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17997 * @cfg {Boolean} carousel true to make the group behave like a carousel
17998 * @cfg {Boolean} bullets show bullets for the panels
17999 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18000 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18001 * @cfg {Boolean} showarrow (true|false) show arrow default true
18004 * Create a new TabGroup
18005 * @param {Object} config The config object
18008 Roo.bootstrap.TabGroup = function(config){
18009 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18011 this.navId = Roo.id();
18014 Roo.bootstrap.TabGroup.register(this);
18018 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18021 transition : false,
18026 slideOnTouch : false,
18029 getAutoCreate : function()
18031 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18033 cfg.cls += ' tab-content';
18035 if (this.carousel) {
18036 cfg.cls += ' carousel slide';
18039 cls : 'carousel-inner',
18043 if(this.bullets && !Roo.isTouch){
18046 cls : 'carousel-bullets',
18050 if(this.bullets_cls){
18051 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18058 cfg.cn[0].cn.push(bullets);
18061 if(this.showarrow){
18062 cfg.cn[0].cn.push({
18064 class : 'carousel-arrow',
18068 class : 'carousel-prev',
18072 class : 'fa fa-chevron-left'
18078 class : 'carousel-next',
18082 class : 'fa fa-chevron-right'
18095 initEvents: function()
18097 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18098 // this.el.on("touchstart", this.onTouchStart, this);
18101 if(this.autoslide){
18104 this.slideFn = window.setInterval(function() {
18105 _this.showPanelNext();
18109 if(this.showarrow){
18110 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18111 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18117 // onTouchStart : function(e, el, o)
18119 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18123 // this.showPanelNext();
18127 getChildContainer : function()
18129 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18133 * register a Navigation item
18134 * @param {Roo.bootstrap.NavItem} the navitem to add
18136 register : function(item)
18138 this.tabs.push( item);
18139 item.navId = this.navId; // not really needed..
18144 getActivePanel : function()
18147 Roo.each(this.tabs, function(t) {
18157 getPanelByName : function(n)
18160 Roo.each(this.tabs, function(t) {
18161 if (t.tabId == n) {
18169 indexOfPanel : function(p)
18172 Roo.each(this.tabs, function(t,i) {
18173 if (t.tabId == p.tabId) {
18182 * show a specific panel
18183 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18184 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18186 showPanel : function (pan)
18188 if(this.transition || typeof(pan) == 'undefined'){
18189 Roo.log("waiting for the transitionend");
18193 if (typeof(pan) == 'number') {
18194 pan = this.tabs[pan];
18197 if (typeof(pan) == 'string') {
18198 pan = this.getPanelByName(pan);
18201 var cur = this.getActivePanel();
18204 Roo.log('pan or acitve pan is undefined');
18208 if (pan.tabId == this.getActivePanel().tabId) {
18212 if (false === cur.fireEvent('beforedeactivate')) {
18216 if(this.bullets > 0 && !Roo.isTouch){
18217 this.setActiveBullet(this.indexOfPanel(pan));
18220 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18222 this.transition = true;
18223 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18224 var lr = dir == 'next' ? 'left' : 'right';
18225 pan.el.addClass(dir); // or prev
18226 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18227 cur.el.addClass(lr); // or right
18228 pan.el.addClass(lr);
18231 cur.el.on('transitionend', function() {
18232 Roo.log("trans end?");
18234 pan.el.removeClass([lr,dir]);
18235 pan.setActive(true);
18237 cur.el.removeClass([lr]);
18238 cur.setActive(false);
18240 _this.transition = false;
18242 }, this, { single: true } );
18247 cur.setActive(false);
18248 pan.setActive(true);
18253 showPanelNext : function()
18255 var i = this.indexOfPanel(this.getActivePanel());
18257 if (i >= this.tabs.length - 1 && !this.autoslide) {
18261 if (i >= this.tabs.length - 1 && this.autoslide) {
18265 this.showPanel(this.tabs[i+1]);
18268 showPanelPrev : function()
18270 var i = this.indexOfPanel(this.getActivePanel());
18272 if (i < 1 && !this.autoslide) {
18276 if (i < 1 && this.autoslide) {
18277 i = this.tabs.length;
18280 this.showPanel(this.tabs[i-1]);
18284 addBullet: function()
18286 if(!this.bullets || Roo.isTouch){
18289 var ctr = this.el.select('.carousel-bullets',true).first();
18290 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18291 var bullet = ctr.createChild({
18292 cls : 'bullet bullet-' + i
18293 },ctr.dom.lastChild);
18298 bullet.on('click', (function(e, el, o, ii, t){
18300 e.preventDefault();
18302 this.showPanel(ii);
18304 if(this.autoslide && this.slideFn){
18305 clearInterval(this.slideFn);
18306 this.slideFn = window.setInterval(function() {
18307 _this.showPanelNext();
18311 }).createDelegate(this, [i, bullet], true));
18316 setActiveBullet : function(i)
18322 Roo.each(this.el.select('.bullet', true).elements, function(el){
18323 el.removeClass('selected');
18326 var bullet = this.el.select('.bullet-' + i, true).first();
18332 bullet.addClass('selected');
18343 Roo.apply(Roo.bootstrap.TabGroup, {
18347 * register a Navigation Group
18348 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18350 register : function(navgrp)
18352 this.groups[navgrp.navId] = navgrp;
18356 * fetch a Navigation Group based on the navigation ID
18357 * if one does not exist , it will get created.
18358 * @param {string} the navgroup to add
18359 * @returns {Roo.bootstrap.NavGroup} the navgroup
18361 get: function(navId) {
18362 if (typeof(this.groups[navId]) == 'undefined') {
18363 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18365 return this.groups[navId] ;
18380 * @class Roo.bootstrap.TabPanel
18381 * @extends Roo.bootstrap.Component
18382 * Bootstrap TabPanel class
18383 * @cfg {Boolean} active panel active
18384 * @cfg {String} html panel content
18385 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18386 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18387 * @cfg {String} href click to link..
18391 * Create a new TabPanel
18392 * @param {Object} config The config object
18395 Roo.bootstrap.TabPanel = function(config){
18396 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18400 * Fires when the active status changes
18401 * @param {Roo.bootstrap.TabPanel} this
18402 * @param {Boolean} state the new state
18407 * @event beforedeactivate
18408 * Fires before a tab is de-activated - can be used to do validation on a form.
18409 * @param {Roo.bootstrap.TabPanel} this
18410 * @return {Boolean} false if there is an error
18413 'beforedeactivate': true
18416 this.tabId = this.tabId || Roo.id();
18420 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18428 getAutoCreate : function(){
18431 // item is needed for carousel - not sure if it has any effect otherwise
18432 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18433 html: this.html || ''
18437 cfg.cls += ' active';
18441 cfg.tabId = this.tabId;
18448 initEvents: function()
18450 var p = this.parent();
18452 this.navId = this.navId || p.navId;
18454 if (typeof(this.navId) != 'undefined') {
18455 // not really needed.. but just in case.. parent should be a NavGroup.
18456 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18460 var i = tg.tabs.length - 1;
18462 if(this.active && tg.bullets > 0 && i < tg.bullets){
18463 tg.setActiveBullet(i);
18467 this.el.on('click', this.onClick, this);
18470 this.el.on("touchstart", this.onTouchStart, this);
18471 this.el.on("touchmove", this.onTouchMove, this);
18472 this.el.on("touchend", this.onTouchEnd, this);
18477 onRender : function(ct, position)
18479 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18482 setActive : function(state)
18484 Roo.log("panel - set active " + this.tabId + "=" + state);
18486 this.active = state;
18488 this.el.removeClass('active');
18490 } else if (!this.el.hasClass('active')) {
18491 this.el.addClass('active');
18494 this.fireEvent('changed', this, state);
18497 onClick : function(e)
18499 e.preventDefault();
18501 if(!this.href.length){
18505 window.location.href = this.href;
18514 onTouchStart : function(e)
18516 this.swiping = false;
18518 this.startX = e.browserEvent.touches[0].clientX;
18519 this.startY = e.browserEvent.touches[0].clientY;
18522 onTouchMove : function(e)
18524 this.swiping = true;
18526 this.endX = e.browserEvent.touches[0].clientX;
18527 this.endY = e.browserEvent.touches[0].clientY;
18530 onTouchEnd : function(e)
18537 var tabGroup = this.parent();
18539 if(this.endX > this.startX){ // swiping right
18540 tabGroup.showPanelPrev();
18544 if(this.startX > this.endX){ // swiping left
18545 tabGroup.showPanelNext();
18564 * @class Roo.bootstrap.DateField
18565 * @extends Roo.bootstrap.Input
18566 * Bootstrap DateField class
18567 * @cfg {Number} weekStart default 0
18568 * @cfg {String} viewMode default empty, (months|years)
18569 * @cfg {String} minViewMode default empty, (months|years)
18570 * @cfg {Number} startDate default -Infinity
18571 * @cfg {Number} endDate default Infinity
18572 * @cfg {Boolean} todayHighlight default false
18573 * @cfg {Boolean} todayBtn default false
18574 * @cfg {Boolean} calendarWeeks default false
18575 * @cfg {Object} daysOfWeekDisabled default empty
18576 * @cfg {Boolean} singleMode default false (true | false)
18578 * @cfg {Boolean} keyboardNavigation default true
18579 * @cfg {String} language default en
18582 * Create a new DateField
18583 * @param {Object} config The config object
18586 Roo.bootstrap.DateField = function(config){
18587 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18591 * Fires when this field show.
18592 * @param {Roo.bootstrap.DateField} this
18593 * @param {Mixed} date The date value
18598 * Fires when this field hide.
18599 * @param {Roo.bootstrap.DateField} this
18600 * @param {Mixed} date The date value
18605 * Fires when select a date.
18606 * @param {Roo.bootstrap.DateField} this
18607 * @param {Mixed} date The date value
18611 * @event beforeselect
18612 * Fires when before select a date.
18613 * @param {Roo.bootstrap.DateField} this
18614 * @param {Mixed} date The date value
18616 beforeselect : true
18620 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18623 * @cfg {String} format
18624 * The default date format string which can be overriden for localization support. The format must be
18625 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18629 * @cfg {String} altFormats
18630 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18631 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18633 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18641 todayHighlight : false,
18647 keyboardNavigation: true,
18649 calendarWeeks: false,
18651 startDate: -Infinity,
18655 daysOfWeekDisabled: [],
18659 singleMode : false,
18661 UTCDate: function()
18663 return new Date(Date.UTC.apply(Date, arguments));
18666 UTCToday: function()
18668 var today = new Date();
18669 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18672 getDate: function() {
18673 var d = this.getUTCDate();
18674 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18677 getUTCDate: function() {
18681 setDate: function(d) {
18682 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18685 setUTCDate: function(d) {
18687 this.setValue(this.formatDate(this.date));
18690 onRender: function(ct, position)
18693 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18695 this.language = this.language || 'en';
18696 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18697 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18699 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18700 this.format = this.format || 'm/d/y';
18701 this.isInline = false;
18702 this.isInput = true;
18703 this.component = this.el.select('.add-on', true).first() || false;
18704 this.component = (this.component && this.component.length === 0) ? false : this.component;
18705 this.hasInput = this.component && this.inputEl().length;
18707 if (typeof(this.minViewMode === 'string')) {
18708 switch (this.minViewMode) {
18710 this.minViewMode = 1;
18713 this.minViewMode = 2;
18716 this.minViewMode = 0;
18721 if (typeof(this.viewMode === 'string')) {
18722 switch (this.viewMode) {
18735 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18737 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18739 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18741 this.picker().on('mousedown', this.onMousedown, this);
18742 this.picker().on('click', this.onClick, this);
18744 this.picker().addClass('datepicker-dropdown');
18746 this.startViewMode = this.viewMode;
18748 if(this.singleMode){
18749 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18750 v.setVisibilityMode(Roo.Element.DISPLAY);
18754 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18755 v.setStyle('width', '189px');
18759 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18760 if(!this.calendarWeeks){
18765 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18766 v.attr('colspan', function(i, val){
18767 return parseInt(val) + 1;
18772 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18774 this.setStartDate(this.startDate);
18775 this.setEndDate(this.endDate);
18777 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18784 if(this.isInline) {
18789 picker : function()
18791 return this.pickerEl;
18792 // return this.el.select('.datepicker', true).first();
18795 fillDow: function()
18797 var dowCnt = this.weekStart;
18806 if(this.calendarWeeks){
18814 while (dowCnt < this.weekStart + 7) {
18818 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18822 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18825 fillMonths: function()
18828 var months = this.picker().select('>.datepicker-months td', true).first();
18830 months.dom.innerHTML = '';
18836 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18839 months.createChild(month);
18846 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;
18848 if (this.date < this.startDate) {
18849 this.viewDate = new Date(this.startDate);
18850 } else if (this.date > this.endDate) {
18851 this.viewDate = new Date(this.endDate);
18853 this.viewDate = new Date(this.date);
18861 var d = new Date(this.viewDate),
18862 year = d.getUTCFullYear(),
18863 month = d.getUTCMonth(),
18864 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18865 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18866 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18867 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18868 currentDate = this.date && this.date.valueOf(),
18869 today = this.UTCToday();
18871 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18873 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18875 // this.picker.select('>tfoot th.today').
18876 // .text(dates[this.language].today)
18877 // .toggle(this.todayBtn !== false);
18879 this.updateNavArrows();
18882 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18884 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18886 prevMonth.setUTCDate(day);
18888 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18890 var nextMonth = new Date(prevMonth);
18892 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18894 nextMonth = nextMonth.valueOf();
18896 var fillMonths = false;
18898 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18900 while(prevMonth.valueOf() <= nextMonth) {
18903 if (prevMonth.getUTCDay() === this.weekStart) {
18905 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18913 if(this.calendarWeeks){
18914 // ISO 8601: First week contains first thursday.
18915 // ISO also states week starts on Monday, but we can be more abstract here.
18917 // Start of current week: based on weekstart/current date
18918 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18919 // Thursday of this week
18920 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18921 // First Thursday of year, year from thursday
18922 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18923 // Calendar week: ms between thursdays, div ms per day, div 7 days
18924 calWeek = (th - yth) / 864e5 / 7 + 1;
18926 fillMonths.cn.push({
18934 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18936 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18939 if (this.todayHighlight &&
18940 prevMonth.getUTCFullYear() == today.getFullYear() &&
18941 prevMonth.getUTCMonth() == today.getMonth() &&
18942 prevMonth.getUTCDate() == today.getDate()) {
18943 clsName += ' today';
18946 if (currentDate && prevMonth.valueOf() === currentDate) {
18947 clsName += ' active';
18950 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18951 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18952 clsName += ' disabled';
18955 fillMonths.cn.push({
18957 cls: 'day ' + clsName,
18958 html: prevMonth.getDate()
18961 prevMonth.setDate(prevMonth.getDate()+1);
18964 var currentYear = this.date && this.date.getUTCFullYear();
18965 var currentMonth = this.date && this.date.getUTCMonth();
18967 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18969 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18970 v.removeClass('active');
18972 if(currentYear === year && k === currentMonth){
18973 v.addClass('active');
18976 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18977 v.addClass('disabled');
18983 year = parseInt(year/10, 10) * 10;
18985 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18987 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18990 for (var i = -1; i < 11; i++) {
18991 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18993 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19001 showMode: function(dir)
19004 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19007 Roo.each(this.picker().select('>div',true).elements, function(v){
19008 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19011 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19016 if(this.isInline) {
19020 this.picker().removeClass(['bottom', 'top']);
19022 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19024 * place to the top of element!
19028 this.picker().addClass('top');
19029 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19034 this.picker().addClass('bottom');
19036 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19039 parseDate : function(value)
19041 if(!value || value instanceof Date){
19044 var v = Date.parseDate(value, this.format);
19045 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19046 v = Date.parseDate(value, 'Y-m-d');
19048 if(!v && this.altFormats){
19049 if(!this.altFormatsArray){
19050 this.altFormatsArray = this.altFormats.split("|");
19052 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19053 v = Date.parseDate(value, this.altFormatsArray[i]);
19059 formatDate : function(date, fmt)
19061 return (!date || !(date instanceof Date)) ?
19062 date : date.dateFormat(fmt || this.format);
19065 onFocus : function()
19067 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19071 onBlur : function()
19073 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19075 var d = this.inputEl().getValue();
19082 showPopup : function()
19084 this.picker().show();
19088 this.fireEvent('showpopup', this, this.date);
19091 hidePopup : function()
19093 if(this.isInline) {
19096 this.picker().hide();
19097 this.viewMode = this.startViewMode;
19100 this.fireEvent('hidepopup', this, this.date);
19104 onMousedown: function(e)
19106 e.stopPropagation();
19107 e.preventDefault();
19112 Roo.bootstrap.DateField.superclass.keyup.call(this);
19116 setValue: function(v)
19118 if(this.fireEvent('beforeselect', this, v) !== false){
19119 var d = new Date(this.parseDate(v) ).clearTime();
19121 if(isNaN(d.getTime())){
19122 this.date = this.viewDate = '';
19123 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19127 v = this.formatDate(d);
19129 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19131 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19135 this.fireEvent('select', this, this.date);
19139 getValue: function()
19141 return this.formatDate(this.date);
19144 fireKey: function(e)
19146 if (!this.picker().isVisible()){
19147 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19153 var dateChanged = false,
19155 newDate, newViewDate;
19160 e.preventDefault();
19164 if (!this.keyboardNavigation) {
19167 dir = e.keyCode == 37 ? -1 : 1;
19170 newDate = this.moveYear(this.date, dir);
19171 newViewDate = this.moveYear(this.viewDate, dir);
19172 } else if (e.shiftKey){
19173 newDate = this.moveMonth(this.date, dir);
19174 newViewDate = this.moveMonth(this.viewDate, dir);
19176 newDate = new Date(this.date);
19177 newDate.setUTCDate(this.date.getUTCDate() + dir);
19178 newViewDate = new Date(this.viewDate);
19179 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19181 if (this.dateWithinRange(newDate)){
19182 this.date = newDate;
19183 this.viewDate = newViewDate;
19184 this.setValue(this.formatDate(this.date));
19186 e.preventDefault();
19187 dateChanged = true;
19192 if (!this.keyboardNavigation) {
19195 dir = e.keyCode == 38 ? -1 : 1;
19197 newDate = this.moveYear(this.date, dir);
19198 newViewDate = this.moveYear(this.viewDate, dir);
19199 } else if (e.shiftKey){
19200 newDate = this.moveMonth(this.date, dir);
19201 newViewDate = this.moveMonth(this.viewDate, dir);
19203 newDate = new Date(this.date);
19204 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19205 newViewDate = new Date(this.viewDate);
19206 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19208 if (this.dateWithinRange(newDate)){
19209 this.date = newDate;
19210 this.viewDate = newViewDate;
19211 this.setValue(this.formatDate(this.date));
19213 e.preventDefault();
19214 dateChanged = true;
19218 this.setValue(this.formatDate(this.date));
19220 e.preventDefault();
19223 this.setValue(this.formatDate(this.date));
19237 onClick: function(e)
19239 e.stopPropagation();
19240 e.preventDefault();
19242 var target = e.getTarget();
19244 if(target.nodeName.toLowerCase() === 'i'){
19245 target = Roo.get(target).dom.parentNode;
19248 var nodeName = target.nodeName;
19249 var className = target.className;
19250 var html = target.innerHTML;
19251 //Roo.log(nodeName);
19253 switch(nodeName.toLowerCase()) {
19255 switch(className) {
19261 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19262 switch(this.viewMode){
19264 this.viewDate = this.moveMonth(this.viewDate, dir);
19268 this.viewDate = this.moveYear(this.viewDate, dir);
19274 var date = new Date();
19275 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19277 this.setValue(this.formatDate(this.date));
19284 if (className.indexOf('disabled') < 0) {
19285 this.viewDate.setUTCDate(1);
19286 if (className.indexOf('month') > -1) {
19287 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19289 var year = parseInt(html, 10) || 0;
19290 this.viewDate.setUTCFullYear(year);
19294 if(this.singleMode){
19295 this.setValue(this.formatDate(this.viewDate));
19306 //Roo.log(className);
19307 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19308 var day = parseInt(html, 10) || 1;
19309 var year = this.viewDate.getUTCFullYear(),
19310 month = this.viewDate.getUTCMonth();
19312 if (className.indexOf('old') > -1) {
19319 } else if (className.indexOf('new') > -1) {
19327 //Roo.log([year,month,day]);
19328 this.date = this.UTCDate(year, month, day,0,0,0,0);
19329 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19331 //Roo.log(this.formatDate(this.date));
19332 this.setValue(this.formatDate(this.date));
19339 setStartDate: function(startDate)
19341 this.startDate = startDate || -Infinity;
19342 if (this.startDate !== -Infinity) {
19343 this.startDate = this.parseDate(this.startDate);
19346 this.updateNavArrows();
19349 setEndDate: function(endDate)
19351 this.endDate = endDate || Infinity;
19352 if (this.endDate !== Infinity) {
19353 this.endDate = this.parseDate(this.endDate);
19356 this.updateNavArrows();
19359 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19361 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19362 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19363 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19365 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19366 return parseInt(d, 10);
19369 this.updateNavArrows();
19372 updateNavArrows: function()
19374 if(this.singleMode){
19378 var d = new Date(this.viewDate),
19379 year = d.getUTCFullYear(),
19380 month = d.getUTCMonth();
19382 Roo.each(this.picker().select('.prev', true).elements, function(v){
19384 switch (this.viewMode) {
19387 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19393 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19400 Roo.each(this.picker().select('.next', true).elements, function(v){
19402 switch (this.viewMode) {
19405 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19411 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19419 moveMonth: function(date, dir)
19424 var new_date = new Date(date.valueOf()),
19425 day = new_date.getUTCDate(),
19426 month = new_date.getUTCMonth(),
19427 mag = Math.abs(dir),
19429 dir = dir > 0 ? 1 : -1;
19432 // If going back one month, make sure month is not current month
19433 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19435 return new_date.getUTCMonth() == month;
19437 // If going forward one month, make sure month is as expected
19438 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19440 return new_date.getUTCMonth() != new_month;
19442 new_month = month + dir;
19443 new_date.setUTCMonth(new_month);
19444 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19445 if (new_month < 0 || new_month > 11) {
19446 new_month = (new_month + 12) % 12;
19449 // For magnitudes >1, move one month at a time...
19450 for (var i=0; i<mag; i++) {
19451 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19452 new_date = this.moveMonth(new_date, dir);
19454 // ...then reset the day, keeping it in the new month
19455 new_month = new_date.getUTCMonth();
19456 new_date.setUTCDate(day);
19458 return new_month != new_date.getUTCMonth();
19461 // Common date-resetting loop -- if date is beyond end of month, make it
19464 new_date.setUTCDate(--day);
19465 new_date.setUTCMonth(new_month);
19470 moveYear: function(date, dir)
19472 return this.moveMonth(date, dir*12);
19475 dateWithinRange: function(date)
19477 return date >= this.startDate && date <= this.endDate;
19483 this.picker().remove();
19486 validateValue : function(value)
19488 if(this.getVisibilityEl().hasClass('hidden')){
19492 if(value.length < 1) {
19493 if(this.allowBlank){
19499 if(value.length < this.minLength){
19502 if(value.length > this.maxLength){
19506 var vt = Roo.form.VTypes;
19507 if(!vt[this.vtype](value, this)){
19511 if(typeof this.validator == "function"){
19512 var msg = this.validator(value);
19518 if(this.regex && !this.regex.test(value)){
19522 if(typeof(this.parseDate(value)) == 'undefined'){
19526 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19530 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19540 this.date = this.viewDate = '';
19542 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19547 Roo.apply(Roo.bootstrap.DateField, {
19558 html: '<i class="fa fa-arrow-left"/>'
19568 html: '<i class="fa fa-arrow-right"/>'
19610 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19611 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19612 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19613 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19614 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19627 navFnc: 'FullYear',
19632 navFnc: 'FullYear',
19637 Roo.apply(Roo.bootstrap.DateField, {
19641 cls: 'datepicker dropdown-menu roo-dynamic',
19645 cls: 'datepicker-days',
19649 cls: 'table-condensed',
19651 Roo.bootstrap.DateField.head,
19655 Roo.bootstrap.DateField.footer
19662 cls: 'datepicker-months',
19666 cls: 'table-condensed',
19668 Roo.bootstrap.DateField.head,
19669 Roo.bootstrap.DateField.content,
19670 Roo.bootstrap.DateField.footer
19677 cls: 'datepicker-years',
19681 cls: 'table-condensed',
19683 Roo.bootstrap.DateField.head,
19684 Roo.bootstrap.DateField.content,
19685 Roo.bootstrap.DateField.footer
19704 * @class Roo.bootstrap.TimeField
19705 * @extends Roo.bootstrap.Input
19706 * Bootstrap DateField class
19710 * Create a new TimeField
19711 * @param {Object} config The config object
19714 Roo.bootstrap.TimeField = function(config){
19715 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19719 * Fires when this field show.
19720 * @param {Roo.bootstrap.DateField} thisthis
19721 * @param {Mixed} date The date value
19726 * Fires when this field hide.
19727 * @param {Roo.bootstrap.DateField} this
19728 * @param {Mixed} date The date value
19733 * Fires when select a date.
19734 * @param {Roo.bootstrap.DateField} this
19735 * @param {Mixed} date The date value
19741 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19744 * @cfg {String} format
19745 * The default time format string which can be overriden for localization support. The format must be
19746 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19750 onRender: function(ct, position)
19753 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19755 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19757 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19759 this.pop = this.picker().select('>.datepicker-time',true).first();
19760 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19762 this.picker().on('mousedown', this.onMousedown, this);
19763 this.picker().on('click', this.onClick, this);
19765 this.picker().addClass('datepicker-dropdown');
19770 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19771 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19772 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19773 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19774 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19775 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19779 fireKey: function(e){
19780 if (!this.picker().isVisible()){
19781 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19787 e.preventDefault();
19795 this.onTogglePeriod();
19798 this.onIncrementMinutes();
19801 this.onDecrementMinutes();
19810 onClick: function(e) {
19811 e.stopPropagation();
19812 e.preventDefault();
19815 picker : function()
19817 return this.el.select('.datepicker', true).first();
19820 fillTime: function()
19822 var time = this.pop.select('tbody', true).first();
19824 time.dom.innerHTML = '';
19839 cls: 'hours-up glyphicon glyphicon-chevron-up'
19859 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19880 cls: 'timepicker-hour',
19895 cls: 'timepicker-minute',
19910 cls: 'btn btn-primary period',
19932 cls: 'hours-down glyphicon glyphicon-chevron-down'
19952 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19970 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19977 var hours = this.time.getHours();
19978 var minutes = this.time.getMinutes();
19991 hours = hours - 12;
19995 hours = '0' + hours;
19999 minutes = '0' + minutes;
20002 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20003 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20004 this.pop.select('button', true).first().dom.innerHTML = period;
20010 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20012 var cls = ['bottom'];
20014 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20021 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20026 this.picker().addClass(cls.join('-'));
20030 Roo.each(cls, function(c){
20032 _this.picker().setTop(_this.inputEl().getHeight());
20036 _this.picker().setTop(0 - _this.picker().getHeight());
20041 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20045 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20052 onFocus : function()
20054 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20058 onBlur : function()
20060 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20066 this.picker().show();
20071 this.fireEvent('show', this, this.date);
20076 this.picker().hide();
20079 this.fireEvent('hide', this, this.date);
20082 setTime : function()
20085 this.setValue(this.time.format(this.format));
20087 this.fireEvent('select', this, this.date);
20092 onMousedown: function(e){
20093 e.stopPropagation();
20094 e.preventDefault();
20097 onIncrementHours: function()
20099 Roo.log('onIncrementHours');
20100 this.time = this.time.add(Date.HOUR, 1);
20105 onDecrementHours: function()
20107 Roo.log('onDecrementHours');
20108 this.time = this.time.add(Date.HOUR, -1);
20112 onIncrementMinutes: function()
20114 Roo.log('onIncrementMinutes');
20115 this.time = this.time.add(Date.MINUTE, 1);
20119 onDecrementMinutes: function()
20121 Roo.log('onDecrementMinutes');
20122 this.time = this.time.add(Date.MINUTE, -1);
20126 onTogglePeriod: function()
20128 Roo.log('onTogglePeriod');
20129 this.time = this.time.add(Date.HOUR, 12);
20136 Roo.apply(Roo.bootstrap.TimeField, {
20166 cls: 'btn btn-info ok',
20178 Roo.apply(Roo.bootstrap.TimeField, {
20182 cls: 'datepicker dropdown-menu',
20186 cls: 'datepicker-time',
20190 cls: 'table-condensed',
20192 Roo.bootstrap.TimeField.content,
20193 Roo.bootstrap.TimeField.footer
20212 * @class Roo.bootstrap.MonthField
20213 * @extends Roo.bootstrap.Input
20214 * Bootstrap MonthField class
20216 * @cfg {String} language default en
20219 * Create a new MonthField
20220 * @param {Object} config The config object
20223 Roo.bootstrap.MonthField = function(config){
20224 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20229 * Fires when this field show.
20230 * @param {Roo.bootstrap.MonthField} this
20231 * @param {Mixed} date The date value
20236 * Fires when this field hide.
20237 * @param {Roo.bootstrap.MonthField} this
20238 * @param {Mixed} date The date value
20243 * Fires when select a date.
20244 * @param {Roo.bootstrap.MonthField} this
20245 * @param {String} oldvalue The old value
20246 * @param {String} newvalue The new value
20252 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20254 onRender: function(ct, position)
20257 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20259 this.language = this.language || 'en';
20260 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20261 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20263 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20264 this.isInline = false;
20265 this.isInput = true;
20266 this.component = this.el.select('.add-on', true).first() || false;
20267 this.component = (this.component && this.component.length === 0) ? false : this.component;
20268 this.hasInput = this.component && this.inputEL().length;
20270 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20272 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20274 this.picker().on('mousedown', this.onMousedown, this);
20275 this.picker().on('click', this.onClick, this);
20277 this.picker().addClass('datepicker-dropdown');
20279 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20280 v.setStyle('width', '189px');
20287 if(this.isInline) {
20293 setValue: function(v, suppressEvent)
20295 var o = this.getValue();
20297 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20301 if(suppressEvent !== true){
20302 this.fireEvent('select', this, o, v);
20307 getValue: function()
20312 onClick: function(e)
20314 e.stopPropagation();
20315 e.preventDefault();
20317 var target = e.getTarget();
20319 if(target.nodeName.toLowerCase() === 'i'){
20320 target = Roo.get(target).dom.parentNode;
20323 var nodeName = target.nodeName;
20324 var className = target.className;
20325 var html = target.innerHTML;
20327 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20331 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20333 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20339 picker : function()
20341 return this.pickerEl;
20344 fillMonths: function()
20347 var months = this.picker().select('>.datepicker-months td', true).first();
20349 months.dom.innerHTML = '';
20355 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20358 months.createChild(month);
20367 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20368 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20371 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20372 e.removeClass('active');
20374 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20375 e.addClass('active');
20382 if(this.isInline) {
20386 this.picker().removeClass(['bottom', 'top']);
20388 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20390 * place to the top of element!
20394 this.picker().addClass('top');
20395 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20400 this.picker().addClass('bottom');
20402 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20405 onFocus : function()
20407 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20411 onBlur : function()
20413 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20415 var d = this.inputEl().getValue();
20424 this.picker().show();
20425 this.picker().select('>.datepicker-months', true).first().show();
20429 this.fireEvent('show', this, this.date);
20434 if(this.isInline) {
20437 this.picker().hide();
20438 this.fireEvent('hide', this, this.date);
20442 onMousedown: function(e)
20444 e.stopPropagation();
20445 e.preventDefault();
20450 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20454 fireKey: function(e)
20456 if (!this.picker().isVisible()){
20457 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20468 e.preventDefault();
20472 dir = e.keyCode == 37 ? -1 : 1;
20474 this.vIndex = this.vIndex + dir;
20476 if(this.vIndex < 0){
20480 if(this.vIndex > 11){
20484 if(isNaN(this.vIndex)){
20488 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20494 dir = e.keyCode == 38 ? -1 : 1;
20496 this.vIndex = this.vIndex + dir * 4;
20498 if(this.vIndex < 0){
20502 if(this.vIndex > 11){
20506 if(isNaN(this.vIndex)){
20510 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20515 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20516 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20520 e.preventDefault();
20523 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20524 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20540 this.picker().remove();
20545 Roo.apply(Roo.bootstrap.MonthField, {
20564 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20565 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20570 Roo.apply(Roo.bootstrap.MonthField, {
20574 cls: 'datepicker dropdown-menu roo-dynamic',
20578 cls: 'datepicker-months',
20582 cls: 'table-condensed',
20584 Roo.bootstrap.DateField.content
20604 * @class Roo.bootstrap.CheckBox
20605 * @extends Roo.bootstrap.Input
20606 * Bootstrap CheckBox class
20608 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20609 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20610 * @cfg {String} boxLabel The text that appears beside the checkbox
20611 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20612 * @cfg {Boolean} checked initnal the element
20613 * @cfg {Boolean} inline inline the element (default false)
20614 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20615 * @cfg {String} tooltip label tooltip
20618 * Create a new CheckBox
20619 * @param {Object} config The config object
20622 Roo.bootstrap.CheckBox = function(config){
20623 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20628 * Fires when the element is checked or unchecked.
20629 * @param {Roo.bootstrap.CheckBox} this This input
20630 * @param {Boolean} checked The new checked value
20635 * Fires when the element is click.
20636 * @param {Roo.bootstrap.CheckBox} this This input
20643 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20645 inputType: 'checkbox',
20654 getAutoCreate : function()
20656 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20662 cfg.cls = 'form-group ' + this.inputType; //input-group
20665 cfg.cls += ' ' + this.inputType + '-inline';
20671 type : this.inputType,
20672 value : this.inputValue,
20673 cls : 'roo-' + this.inputType, //'form-box',
20674 placeholder : this.placeholder || ''
20678 if(this.inputType != 'radio'){
20682 cls : 'roo-hidden-value',
20683 value : this.checked ? this.inputValue : this.valueOff
20688 if (this.weight) { // Validity check?
20689 cfg.cls += " " + this.inputType + "-" + this.weight;
20692 if (this.disabled) {
20693 input.disabled=true;
20697 input.checked = this.checked;
20702 input.name = this.name;
20704 if(this.inputType != 'radio'){
20705 hidden.name = this.name;
20706 input.name = '_hidden_' + this.name;
20711 input.cls += ' input-' + this.size;
20716 ['xs','sm','md','lg'].map(function(size){
20717 if (settings[size]) {
20718 cfg.cls += ' col-' + size + '-' + settings[size];
20722 var inputblock = input;
20724 if (this.before || this.after) {
20727 cls : 'input-group',
20732 inputblock.cn.push({
20734 cls : 'input-group-addon',
20739 inputblock.cn.push(input);
20741 if(this.inputType != 'radio'){
20742 inputblock.cn.push(hidden);
20746 inputblock.cn.push({
20748 cls : 'input-group-addon',
20755 if (align ==='left' && this.fieldLabel.length) {
20756 // Roo.log("left and has label");
20761 cls : 'control-label',
20762 html : this.fieldLabel
20772 if(this.labelWidth > 12){
20773 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20776 if(this.labelWidth < 13 && this.labelmd == 0){
20777 this.labelmd = this.labelWidth;
20780 if(this.labellg > 0){
20781 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20782 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20785 if(this.labelmd > 0){
20786 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20787 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20790 if(this.labelsm > 0){
20791 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20792 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20795 if(this.labelxs > 0){
20796 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20797 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20800 } else if ( this.fieldLabel.length) {
20801 // Roo.log(" label");
20805 tag: this.boxLabel ? 'span' : 'label',
20807 cls: 'control-label box-input-label',
20808 //cls : 'input-group-addon',
20809 html : this.fieldLabel
20818 // Roo.log(" no label && no align");
20819 cfg.cn = [ inputblock ] ;
20825 var boxLabelCfg = {
20827 //'for': id, // box label is handled by onclick - so no for...
20829 html: this.boxLabel
20833 boxLabelCfg.tooltip = this.tooltip;
20836 cfg.cn.push(boxLabelCfg);
20839 if(this.inputType != 'radio'){
20840 cfg.cn.push(hidden);
20848 * return the real input element.
20850 inputEl: function ()
20852 return this.el.select('input.roo-' + this.inputType,true).first();
20854 hiddenEl: function ()
20856 return this.el.select('input.roo-hidden-value',true).first();
20859 labelEl: function()
20861 return this.el.select('label.control-label',true).first();
20863 /* depricated... */
20867 return this.labelEl();
20870 boxLabelEl: function()
20872 return this.el.select('label.box-label',true).first();
20875 initEvents : function()
20877 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20879 this.inputEl().on('click', this.onClick, this);
20881 if (this.boxLabel) {
20882 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20885 this.startValue = this.getValue();
20888 Roo.bootstrap.CheckBox.register(this);
20892 onClick : function(e)
20894 if(this.fireEvent('click', this, e) !== false){
20895 this.setChecked(!this.checked);
20900 setChecked : function(state,suppressEvent)
20902 this.startValue = this.getValue();
20904 if(this.inputType == 'radio'){
20906 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20907 e.dom.checked = false;
20910 this.inputEl().dom.checked = true;
20912 this.inputEl().dom.value = this.inputValue;
20914 if(suppressEvent !== true){
20915 this.fireEvent('check', this, true);
20923 this.checked = state;
20925 this.inputEl().dom.checked = state;
20928 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20930 if(suppressEvent !== true){
20931 this.fireEvent('check', this, state);
20937 getValue : function()
20939 if(this.inputType == 'radio'){
20940 return this.getGroupValue();
20943 return this.hiddenEl().dom.value;
20947 getGroupValue : function()
20949 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20953 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20956 setValue : function(v,suppressEvent)
20958 if(this.inputType == 'radio'){
20959 this.setGroupValue(v, suppressEvent);
20963 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20968 setGroupValue : function(v, suppressEvent)
20970 this.startValue = this.getValue();
20972 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20973 e.dom.checked = false;
20975 if(e.dom.value == v){
20976 e.dom.checked = true;
20980 if(suppressEvent !== true){
20981 this.fireEvent('check', this, true);
20989 validate : function()
20991 if(this.getVisibilityEl().hasClass('hidden')){
20997 (this.inputType == 'radio' && this.validateRadio()) ||
20998 (this.inputType == 'checkbox' && this.validateCheckbox())
21004 this.markInvalid();
21008 validateRadio : function()
21010 if(this.getVisibilityEl().hasClass('hidden')){
21014 if(this.allowBlank){
21020 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21021 if(!e.dom.checked){
21033 validateCheckbox : function()
21036 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21037 //return (this.getValue() == this.inputValue) ? true : false;
21040 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21048 for(var i in group){
21049 if(group[i].el.isVisible(true)){
21057 for(var i in group){
21062 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21069 * Mark this field as valid
21071 markValid : function()
21075 this.fireEvent('valid', this);
21077 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21080 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21087 if(this.inputType == 'radio'){
21088 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21089 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21090 e.findParent('.form-group', false, true).addClass(_this.validClass);
21097 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21098 this.el.findParent('.form-group', false, true).addClass(this.validClass);
21102 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21108 for(var i in group){
21109 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21110 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21115 * Mark this field as invalid
21116 * @param {String} msg The validation message
21118 markInvalid : function(msg)
21120 if(this.allowBlank){
21126 this.fireEvent('invalid', this, msg);
21128 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21131 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21135 label.markInvalid();
21138 if(this.inputType == 'radio'){
21139 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21140 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21141 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21148 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21149 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21153 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21159 for(var i in group){
21160 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21161 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21166 clearInvalid : function()
21168 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21170 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21172 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21174 if (label && label.iconEl) {
21175 label.iconEl.removeClass(label.validClass);
21176 label.iconEl.removeClass(label.invalidClass);
21180 disable : function()
21182 if(this.inputType != 'radio'){
21183 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21190 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21191 _this.getActionEl().addClass(this.disabledClass);
21192 e.dom.disabled = true;
21196 this.disabled = true;
21197 this.fireEvent("disable", this);
21201 enable : function()
21203 if(this.inputType != 'radio'){
21204 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21211 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21212 _this.getActionEl().removeClass(this.disabledClass);
21213 e.dom.disabled = false;
21217 this.disabled = false;
21218 this.fireEvent("enable", this);
21222 setBoxLabel : function(v)
21227 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21233 Roo.apply(Roo.bootstrap.CheckBox, {
21238 * register a CheckBox Group
21239 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21241 register : function(checkbox)
21243 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21244 this.groups[checkbox.groupId] = {};
21247 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21251 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21255 * fetch a CheckBox Group based on the group ID
21256 * @param {string} the group ID
21257 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21259 get: function(groupId) {
21260 if (typeof(this.groups[groupId]) == 'undefined') {
21264 return this.groups[groupId] ;
21277 * @class Roo.bootstrap.Radio
21278 * @extends Roo.bootstrap.Component
21279 * Bootstrap Radio class
21280 * @cfg {String} boxLabel - the label associated
21281 * @cfg {String} value - the value of radio
21284 * Create a new Radio
21285 * @param {Object} config The config object
21287 Roo.bootstrap.Radio = function(config){
21288 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21292 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21298 getAutoCreate : function()
21302 cls : 'form-group radio',
21307 html : this.boxLabel
21315 initEvents : function()
21317 this.parent().register(this);
21319 this.el.on('click', this.onClick, this);
21323 onClick : function(e)
21325 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21326 this.setChecked(true);
21330 setChecked : function(state, suppressEvent)
21332 this.parent().setValue(this.value, suppressEvent);
21336 setBoxLabel : function(v)
21341 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21356 * @class Roo.bootstrap.SecurePass
21357 * @extends Roo.bootstrap.Input
21358 * Bootstrap SecurePass class
21362 * Create a new SecurePass
21363 * @param {Object} config The config object
21366 Roo.bootstrap.SecurePass = function (config) {
21367 // these go here, so the translation tool can replace them..
21369 PwdEmpty: "Please type a password, and then retype it to confirm.",
21370 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21371 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21372 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21373 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21374 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21375 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21376 TooWeak: "Your password is Too Weak."
21378 this.meterLabel = "Password strength:";
21379 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21380 this.meterClass = [
21381 "roo-password-meter-tooweak",
21382 "roo-password-meter-weak",
21383 "roo-password-meter-medium",
21384 "roo-password-meter-strong",
21385 "roo-password-meter-grey"
21390 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21393 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21395 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21397 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21398 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21399 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21400 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21401 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21402 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21403 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21413 * @cfg {String/Object} Label for the strength meter (defaults to
21414 * 'Password strength:')
21419 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21420 * ['Weak', 'Medium', 'Strong'])
21423 pwdStrengths: false,
21436 initEvents: function ()
21438 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21440 if (this.el.is('input[type=password]') && Roo.isSafari) {
21441 this.el.on('keydown', this.SafariOnKeyDown, this);
21444 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21447 onRender: function (ct, position)
21449 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21450 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21451 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21453 this.trigger.createChild({
21458 cls: 'roo-password-meter-grey col-xs-12',
21461 //width: this.meterWidth + 'px'
21465 cls: 'roo-password-meter-text'
21471 if (this.hideTrigger) {
21472 this.trigger.setDisplayed(false);
21474 this.setSize(this.width || '', this.height || '');
21477 onDestroy: function ()
21479 if (this.trigger) {
21480 this.trigger.removeAllListeners();
21481 this.trigger.remove();
21484 this.wrap.remove();
21486 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21489 checkStrength: function ()
21491 var pwd = this.inputEl().getValue();
21492 if (pwd == this._lastPwd) {
21497 if (this.ClientSideStrongPassword(pwd)) {
21499 } else if (this.ClientSideMediumPassword(pwd)) {
21501 } else if (this.ClientSideWeakPassword(pwd)) {
21507 Roo.log('strength1: ' + strength);
21509 //var pm = this.trigger.child('div/div/div').dom;
21510 var pm = this.trigger.child('div/div');
21511 pm.removeClass(this.meterClass);
21512 pm.addClass(this.meterClass[strength]);
21515 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21517 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21519 this._lastPwd = pwd;
21523 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21525 this._lastPwd = '';
21527 var pm = this.trigger.child('div/div');
21528 pm.removeClass(this.meterClass);
21529 pm.addClass('roo-password-meter-grey');
21532 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21535 this.inputEl().dom.type='password';
21538 validateValue: function (value)
21541 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21544 if (value.length == 0) {
21545 if (this.allowBlank) {
21546 this.clearInvalid();
21550 this.markInvalid(this.errors.PwdEmpty);
21551 this.errorMsg = this.errors.PwdEmpty;
21559 if ('[\x21-\x7e]*'.match(value)) {
21560 this.markInvalid(this.errors.PwdBadChar);
21561 this.errorMsg = this.errors.PwdBadChar;
21564 if (value.length < 6) {
21565 this.markInvalid(this.errors.PwdShort);
21566 this.errorMsg = this.errors.PwdShort;
21569 if (value.length > 16) {
21570 this.markInvalid(this.errors.PwdLong);
21571 this.errorMsg = this.errors.PwdLong;
21575 if (this.ClientSideStrongPassword(value)) {
21577 } else if (this.ClientSideMediumPassword(value)) {
21579 } else if (this.ClientSideWeakPassword(value)) {
21586 if (strength < 2) {
21587 //this.markInvalid(this.errors.TooWeak);
21588 this.errorMsg = this.errors.TooWeak;
21593 console.log('strength2: ' + strength);
21595 //var pm = this.trigger.child('div/div/div').dom;
21597 var pm = this.trigger.child('div/div');
21598 pm.removeClass(this.meterClass);
21599 pm.addClass(this.meterClass[strength]);
21601 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21603 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21605 this.errorMsg = '';
21609 CharacterSetChecks: function (type)
21612 this.fResult = false;
21615 isctype: function (character, type)
21618 case this.kCapitalLetter:
21619 if (character >= 'A' && character <= 'Z') {
21624 case this.kSmallLetter:
21625 if (character >= 'a' && character <= 'z') {
21631 if (character >= '0' && character <= '9') {
21636 case this.kPunctuation:
21637 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21648 IsLongEnough: function (pwd, size)
21650 return !(pwd == null || isNaN(size) || pwd.length < size);
21653 SpansEnoughCharacterSets: function (word, nb)
21655 if (!this.IsLongEnough(word, nb))
21660 var characterSetChecks = new Array(
21661 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21662 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21665 for (var index = 0; index < word.length; ++index) {
21666 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21667 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21668 characterSetChecks[nCharSet].fResult = true;
21675 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21676 if (characterSetChecks[nCharSet].fResult) {
21681 if (nCharSets < nb) {
21687 ClientSideStrongPassword: function (pwd)
21689 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21692 ClientSideMediumPassword: function (pwd)
21694 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21697 ClientSideWeakPassword: function (pwd)
21699 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21702 })//<script type="text/javascript">
21705 * Based Ext JS Library 1.1.1
21706 * Copyright(c) 2006-2007, Ext JS, LLC.
21712 * @class Roo.HtmlEditorCore
21713 * @extends Roo.Component
21714 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21716 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21719 Roo.HtmlEditorCore = function(config){
21722 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21727 * @event initialize
21728 * Fires when the editor is fully initialized (including the iframe)
21729 * @param {Roo.HtmlEditorCore} this
21734 * Fires when the editor is first receives the focus. Any insertion must wait
21735 * until after this event.
21736 * @param {Roo.HtmlEditorCore} this
21740 * @event beforesync
21741 * Fires before the textarea is updated with content from the editor iframe. Return false
21742 * to cancel the sync.
21743 * @param {Roo.HtmlEditorCore} this
21744 * @param {String} html
21748 * @event beforepush
21749 * Fires before the iframe editor is updated with content from the textarea. Return false
21750 * to cancel the push.
21751 * @param {Roo.HtmlEditorCore} this
21752 * @param {String} html
21757 * Fires when the textarea is updated with content from the editor iframe.
21758 * @param {Roo.HtmlEditorCore} this
21759 * @param {String} html
21764 * Fires when the iframe editor is updated with content from the textarea.
21765 * @param {Roo.HtmlEditorCore} this
21766 * @param {String} html
21771 * @event editorevent
21772 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21773 * @param {Roo.HtmlEditorCore} this
21779 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21781 // defaults : white / black...
21782 this.applyBlacklists();
21789 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21793 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21799 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21804 * @cfg {Number} height (in pixels)
21808 * @cfg {Number} width (in pixels)
21813 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21816 stylesheets: false,
21821 // private properties
21822 validationEvent : false,
21824 initialized : false,
21826 sourceEditMode : false,
21827 onFocus : Roo.emptyFn,
21829 hideMode:'offsets',
21833 // blacklist + whitelisted elements..
21840 * Protected method that will not generally be called directly. It
21841 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21842 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21844 getDocMarkup : function(){
21848 // inherit styels from page...??
21849 if (this.stylesheets === false) {
21851 Roo.get(document.head).select('style').each(function(node) {
21852 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21855 Roo.get(document.head).select('link').each(function(node) {
21856 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21859 } else if (!this.stylesheets.length) {
21861 st = '<style type="text/css">' +
21862 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21865 st = '<style type="text/css">' +
21870 st += '<style type="text/css">' +
21871 'IMG { cursor: pointer } ' +
21874 var cls = 'roo-htmleditor-body';
21876 if(this.bodyCls.length){
21877 cls += ' ' + this.bodyCls;
21880 return '<html><head>' + st +
21881 //<style type="text/css">' +
21882 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21884 ' </head><body class="' + cls + '"></body></html>';
21888 onRender : function(ct, position)
21891 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21892 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21895 this.el.dom.style.border = '0 none';
21896 this.el.dom.setAttribute('tabIndex', -1);
21897 this.el.addClass('x-hidden hide');
21901 if(Roo.isIE){ // fix IE 1px bogus margin
21902 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21906 this.frameId = Roo.id();
21910 var iframe = this.owner.wrap.createChild({
21912 cls: 'form-control', // bootstrap..
21914 name: this.frameId,
21915 frameBorder : 'no',
21916 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21921 this.iframe = iframe.dom;
21923 this.assignDocWin();
21925 this.doc.designMode = 'on';
21928 this.doc.write(this.getDocMarkup());
21932 var task = { // must defer to wait for browser to be ready
21934 //console.log("run task?" + this.doc.readyState);
21935 this.assignDocWin();
21936 if(this.doc.body || this.doc.readyState == 'complete'){
21938 this.doc.designMode="on";
21942 Roo.TaskMgr.stop(task);
21943 this.initEditor.defer(10, this);
21950 Roo.TaskMgr.start(task);
21955 onResize : function(w, h)
21957 Roo.log('resize: ' +w + ',' + h );
21958 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21962 if(typeof w == 'number'){
21964 this.iframe.style.width = w + 'px';
21966 if(typeof h == 'number'){
21968 this.iframe.style.height = h + 'px';
21970 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21977 * Toggles the editor between standard and source edit mode.
21978 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21980 toggleSourceEdit : function(sourceEditMode){
21982 this.sourceEditMode = sourceEditMode === true;
21984 if(this.sourceEditMode){
21986 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21989 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21990 //this.iframe.className = '';
21993 //this.setSize(this.owner.wrap.getSize());
21994 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22001 * Protected method that will not generally be called directly. If you need/want
22002 * custom HTML cleanup, this is the method you should override.
22003 * @param {String} html The HTML to be cleaned
22004 * return {String} The cleaned HTML
22006 cleanHtml : function(html){
22007 html = String(html);
22008 if(html.length > 5){
22009 if(Roo.isSafari){ // strip safari nonsense
22010 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22013 if(html == ' '){
22020 * HTML Editor -> Textarea
22021 * Protected method that will not generally be called directly. Syncs the contents
22022 * of the editor iframe with the textarea.
22024 syncValue : function(){
22025 if(this.initialized){
22026 var bd = (this.doc.body || this.doc.documentElement);
22027 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22028 var html = bd.innerHTML;
22030 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22031 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22033 html = '<div style="'+m[0]+'">' + html + '</div>';
22036 html = this.cleanHtml(html);
22037 // fix up the special chars.. normaly like back quotes in word...
22038 // however we do not want to do this with chinese..
22039 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22040 var cc = b.charCodeAt();
22042 (cc >= 0x4E00 && cc < 0xA000 ) ||
22043 (cc >= 0x3400 && cc < 0x4E00 ) ||
22044 (cc >= 0xf900 && cc < 0xfb00 )
22050 if(this.owner.fireEvent('beforesync', this, html) !== false){
22051 this.el.dom.value = html;
22052 this.owner.fireEvent('sync', this, html);
22058 * Protected method that will not generally be called directly. Pushes the value of the textarea
22059 * into the iframe editor.
22061 pushValue : function(){
22062 if(this.initialized){
22063 var v = this.el.dom.value.trim();
22065 // if(v.length < 1){
22069 if(this.owner.fireEvent('beforepush', this, v) !== false){
22070 var d = (this.doc.body || this.doc.documentElement);
22072 this.cleanUpPaste();
22073 this.el.dom.value = d.innerHTML;
22074 this.owner.fireEvent('push', this, v);
22080 deferFocus : function(){
22081 this.focus.defer(10, this);
22085 focus : function(){
22086 if(this.win && !this.sourceEditMode){
22093 assignDocWin: function()
22095 var iframe = this.iframe;
22098 this.doc = iframe.contentWindow.document;
22099 this.win = iframe.contentWindow;
22101 // if (!Roo.get(this.frameId)) {
22104 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22105 // this.win = Roo.get(this.frameId).dom.contentWindow;
22107 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22111 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22112 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22117 initEditor : function(){
22118 //console.log("INIT EDITOR");
22119 this.assignDocWin();
22123 this.doc.designMode="on";
22125 this.doc.write(this.getDocMarkup());
22128 var dbody = (this.doc.body || this.doc.documentElement);
22129 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22130 // this copies styles from the containing element into thsi one..
22131 // not sure why we need all of this..
22132 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22134 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22135 //ss['background-attachment'] = 'fixed'; // w3c
22136 dbody.bgProperties = 'fixed'; // ie
22137 //Roo.DomHelper.applyStyles(dbody, ss);
22138 Roo.EventManager.on(this.doc, {
22139 //'mousedown': this.onEditorEvent,
22140 'mouseup': this.onEditorEvent,
22141 'dblclick': this.onEditorEvent,
22142 'click': this.onEditorEvent,
22143 'keyup': this.onEditorEvent,
22148 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22150 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22151 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22153 this.initialized = true;
22155 this.owner.fireEvent('initialize', this);
22160 onDestroy : function(){
22166 //for (var i =0; i < this.toolbars.length;i++) {
22167 // // fixme - ask toolbars for heights?
22168 // this.toolbars[i].onDestroy();
22171 //this.wrap.dom.innerHTML = '';
22172 //this.wrap.remove();
22177 onFirstFocus : function(){
22179 this.assignDocWin();
22182 this.activated = true;
22185 if(Roo.isGecko){ // prevent silly gecko errors
22187 var s = this.win.getSelection();
22188 if(!s.focusNode || s.focusNode.nodeType != 3){
22189 var r = s.getRangeAt(0);
22190 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22195 this.execCmd('useCSS', true);
22196 this.execCmd('styleWithCSS', false);
22199 this.owner.fireEvent('activate', this);
22203 adjustFont: function(btn){
22204 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22205 //if(Roo.isSafari){ // safari
22208 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22209 if(Roo.isSafari){ // safari
22210 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22211 v = (v < 10) ? 10 : v;
22212 v = (v > 48) ? 48 : v;
22213 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22218 v = Math.max(1, v+adjust);
22220 this.execCmd('FontSize', v );
22223 onEditorEvent : function(e)
22225 this.owner.fireEvent('editorevent', this, e);
22226 // this.updateToolbar();
22227 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22230 insertTag : function(tg)
22232 // could be a bit smarter... -> wrap the current selected tRoo..
22233 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22235 range = this.createRange(this.getSelection());
22236 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22237 wrappingNode.appendChild(range.extractContents());
22238 range.insertNode(wrappingNode);
22245 this.execCmd("formatblock", tg);
22249 insertText : function(txt)
22253 var range = this.createRange();
22254 range.deleteContents();
22255 //alert(Sender.getAttribute('label'));
22257 range.insertNode(this.doc.createTextNode(txt));
22263 * Executes a Midas editor command on the editor document and performs necessary focus and
22264 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22265 * @param {String} cmd The Midas command
22266 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22268 relayCmd : function(cmd, value){
22270 this.execCmd(cmd, value);
22271 this.owner.fireEvent('editorevent', this);
22272 //this.updateToolbar();
22273 this.owner.deferFocus();
22277 * Executes a Midas editor command directly on the editor document.
22278 * For visual commands, you should use {@link #relayCmd} instead.
22279 * <b>This should only be called after the editor is initialized.</b>
22280 * @param {String} cmd The Midas command
22281 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22283 execCmd : function(cmd, value){
22284 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22291 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22293 * @param {String} text | dom node..
22295 insertAtCursor : function(text)
22298 if(!this.activated){
22304 var r = this.doc.selection.createRange();
22315 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22319 // from jquery ui (MIT licenced)
22321 var win = this.win;
22323 if (win.getSelection && win.getSelection().getRangeAt) {
22324 range = win.getSelection().getRangeAt(0);
22325 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22326 range.insertNode(node);
22327 } else if (win.document.selection && win.document.selection.createRange) {
22328 // no firefox support
22329 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22330 win.document.selection.createRange().pasteHTML(txt);
22332 // no firefox support
22333 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22334 this.execCmd('InsertHTML', txt);
22343 mozKeyPress : function(e){
22345 var c = e.getCharCode(), cmd;
22348 c = String.fromCharCode(c).toLowerCase();
22362 this.cleanUpPaste.defer(100, this);
22370 e.preventDefault();
22378 fixKeys : function(){ // load time branching for fastest keydown performance
22380 return function(e){
22381 var k = e.getKey(), r;
22384 r = this.doc.selection.createRange();
22387 r.pasteHTML('    ');
22394 r = this.doc.selection.createRange();
22396 var target = r.parentElement();
22397 if(!target || target.tagName.toLowerCase() != 'li'){
22399 r.pasteHTML('<br />');
22405 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22406 this.cleanUpPaste.defer(100, this);
22412 }else if(Roo.isOpera){
22413 return function(e){
22414 var k = e.getKey();
22418 this.execCmd('InsertHTML','    ');
22421 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22422 this.cleanUpPaste.defer(100, this);
22427 }else if(Roo.isSafari){
22428 return function(e){
22429 var k = e.getKey();
22433 this.execCmd('InsertText','\t');
22437 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22438 this.cleanUpPaste.defer(100, this);
22446 getAllAncestors: function()
22448 var p = this.getSelectedNode();
22451 a.push(p); // push blank onto stack..
22452 p = this.getParentElement();
22456 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22460 a.push(this.doc.body);
22464 lastSelNode : false,
22467 getSelection : function()
22469 this.assignDocWin();
22470 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22473 getSelectedNode: function()
22475 // this may only work on Gecko!!!
22477 // should we cache this!!!!
22482 var range = this.createRange(this.getSelection()).cloneRange();
22485 var parent = range.parentElement();
22487 var testRange = range.duplicate();
22488 testRange.moveToElementText(parent);
22489 if (testRange.inRange(range)) {
22492 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22495 parent = parent.parentElement;
22500 // is ancestor a text element.
22501 var ac = range.commonAncestorContainer;
22502 if (ac.nodeType == 3) {
22503 ac = ac.parentNode;
22506 var ar = ac.childNodes;
22509 var other_nodes = [];
22510 var has_other_nodes = false;
22511 for (var i=0;i<ar.length;i++) {
22512 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22515 // fullly contained node.
22517 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22522 // probably selected..
22523 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22524 other_nodes.push(ar[i]);
22528 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22533 has_other_nodes = true;
22535 if (!nodes.length && other_nodes.length) {
22536 nodes= other_nodes;
22538 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22544 createRange: function(sel)
22546 // this has strange effects when using with
22547 // top toolbar - not sure if it's a great idea.
22548 //this.editor.contentWindow.focus();
22549 if (typeof sel != "undefined") {
22551 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22553 return this.doc.createRange();
22556 return this.doc.createRange();
22559 getParentElement: function()
22562 this.assignDocWin();
22563 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22565 var range = this.createRange(sel);
22568 var p = range.commonAncestorContainer;
22569 while (p.nodeType == 3) { // text node
22580 * Range intersection.. the hard stuff...
22584 * [ -- selected range --- ]
22588 * if end is before start or hits it. fail.
22589 * if start is after end or hits it fail.
22591 * if either hits (but other is outside. - then it's not
22597 // @see http://www.thismuchiknow.co.uk/?p=64.
22598 rangeIntersectsNode : function(range, node)
22600 var nodeRange = node.ownerDocument.createRange();
22602 nodeRange.selectNode(node);
22604 nodeRange.selectNodeContents(node);
22607 var rangeStartRange = range.cloneRange();
22608 rangeStartRange.collapse(true);
22610 var rangeEndRange = range.cloneRange();
22611 rangeEndRange.collapse(false);
22613 var nodeStartRange = nodeRange.cloneRange();
22614 nodeStartRange.collapse(true);
22616 var nodeEndRange = nodeRange.cloneRange();
22617 nodeEndRange.collapse(false);
22619 return rangeStartRange.compareBoundaryPoints(
22620 Range.START_TO_START, nodeEndRange) == -1 &&
22621 rangeEndRange.compareBoundaryPoints(
22622 Range.START_TO_START, nodeStartRange) == 1;
22626 rangeCompareNode : function(range, node)
22628 var nodeRange = node.ownerDocument.createRange();
22630 nodeRange.selectNode(node);
22632 nodeRange.selectNodeContents(node);
22636 range.collapse(true);
22638 nodeRange.collapse(true);
22640 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22641 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22643 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22645 var nodeIsBefore = ss == 1;
22646 var nodeIsAfter = ee == -1;
22648 if (nodeIsBefore && nodeIsAfter) {
22651 if (!nodeIsBefore && nodeIsAfter) {
22652 return 1; //right trailed.
22655 if (nodeIsBefore && !nodeIsAfter) {
22656 return 2; // left trailed.
22662 // private? - in a new class?
22663 cleanUpPaste : function()
22665 // cleans up the whole document..
22666 Roo.log('cleanuppaste');
22668 this.cleanUpChildren(this.doc.body);
22669 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22670 if (clean != this.doc.body.innerHTML) {
22671 this.doc.body.innerHTML = clean;
22676 cleanWordChars : function(input) {// change the chars to hex code
22677 var he = Roo.HtmlEditorCore;
22679 var output = input;
22680 Roo.each(he.swapCodes, function(sw) {
22681 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22683 output = output.replace(swapper, sw[1]);
22690 cleanUpChildren : function (n)
22692 if (!n.childNodes.length) {
22695 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22696 this.cleanUpChild(n.childNodes[i]);
22703 cleanUpChild : function (node)
22706 //console.log(node);
22707 if (node.nodeName == "#text") {
22708 // clean up silly Windows -- stuff?
22711 if (node.nodeName == "#comment") {
22712 node.parentNode.removeChild(node);
22713 // clean up silly Windows -- stuff?
22716 var lcname = node.tagName.toLowerCase();
22717 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22718 // whitelist of tags..
22720 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22722 node.parentNode.removeChild(node);
22727 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22729 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22730 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22732 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22733 // remove_keep_children = true;
22736 if (remove_keep_children) {
22737 this.cleanUpChildren(node);
22738 // inserts everything just before this node...
22739 while (node.childNodes.length) {
22740 var cn = node.childNodes[0];
22741 node.removeChild(cn);
22742 node.parentNode.insertBefore(cn, node);
22744 node.parentNode.removeChild(node);
22748 if (!node.attributes || !node.attributes.length) {
22749 this.cleanUpChildren(node);
22753 function cleanAttr(n,v)
22756 if (v.match(/^\./) || v.match(/^\//)) {
22759 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22762 if (v.match(/^#/)) {
22765 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22766 node.removeAttribute(n);
22770 var cwhite = this.cwhite;
22771 var cblack = this.cblack;
22773 function cleanStyle(n,v)
22775 if (v.match(/expression/)) { //XSS?? should we even bother..
22776 node.removeAttribute(n);
22780 var parts = v.split(/;/);
22783 Roo.each(parts, function(p) {
22784 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22788 var l = p.split(':').shift().replace(/\s+/g,'');
22789 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22791 if ( cwhite.length && cblack.indexOf(l) > -1) {
22792 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22793 //node.removeAttribute(n);
22797 // only allow 'c whitelisted system attributes'
22798 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22799 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22800 //node.removeAttribute(n);
22810 if (clean.length) {
22811 node.setAttribute(n, clean.join(';'));
22813 node.removeAttribute(n);
22819 for (var i = node.attributes.length-1; i > -1 ; i--) {
22820 var a = node.attributes[i];
22823 if (a.name.toLowerCase().substr(0,2)=='on') {
22824 node.removeAttribute(a.name);
22827 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22828 node.removeAttribute(a.name);
22831 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22832 cleanAttr(a.name,a.value); // fixme..
22835 if (a.name == 'style') {
22836 cleanStyle(a.name,a.value);
22839 /// clean up MS crap..
22840 // tecnically this should be a list of valid class'es..
22843 if (a.name == 'class') {
22844 if (a.value.match(/^Mso/)) {
22845 node.className = '';
22848 if (a.value.match(/^body$/)) {
22849 node.className = '';
22860 this.cleanUpChildren(node);
22866 * Clean up MS wordisms...
22868 cleanWord : function(node)
22873 this.cleanWord(this.doc.body);
22876 if (node.nodeName == "#text") {
22877 // clean up silly Windows -- stuff?
22880 if (node.nodeName == "#comment") {
22881 node.parentNode.removeChild(node);
22882 // clean up silly Windows -- stuff?
22886 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22887 node.parentNode.removeChild(node);
22891 // remove - but keep children..
22892 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22893 while (node.childNodes.length) {
22894 var cn = node.childNodes[0];
22895 node.removeChild(cn);
22896 node.parentNode.insertBefore(cn, node);
22898 node.parentNode.removeChild(node);
22899 this.iterateChildren(node, this.cleanWord);
22903 if (node.className.length) {
22905 var cn = node.className.split(/\W+/);
22907 Roo.each(cn, function(cls) {
22908 if (cls.match(/Mso[a-zA-Z]+/)) {
22913 node.className = cna.length ? cna.join(' ') : '';
22915 node.removeAttribute("class");
22919 if (node.hasAttribute("lang")) {
22920 node.removeAttribute("lang");
22923 if (node.hasAttribute("style")) {
22925 var styles = node.getAttribute("style").split(";");
22927 Roo.each(styles, function(s) {
22928 if (!s.match(/:/)) {
22931 var kv = s.split(":");
22932 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22935 // what ever is left... we allow.
22938 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22939 if (!nstyle.length) {
22940 node.removeAttribute('style');
22943 this.iterateChildren(node, this.cleanWord);
22949 * iterateChildren of a Node, calling fn each time, using this as the scole..
22950 * @param {DomNode} node node to iterate children of.
22951 * @param {Function} fn method of this class to call on each item.
22953 iterateChildren : function(node, fn)
22955 if (!node.childNodes.length) {
22958 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22959 fn.call(this, node.childNodes[i])
22965 * cleanTableWidths.
22967 * Quite often pasting from word etc.. results in tables with column and widths.
22968 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22971 cleanTableWidths : function(node)
22976 this.cleanTableWidths(this.doc.body);
22981 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22984 Roo.log(node.tagName);
22985 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22986 this.iterateChildren(node, this.cleanTableWidths);
22989 if (node.hasAttribute('width')) {
22990 node.removeAttribute('width');
22994 if (node.hasAttribute("style")) {
22997 var styles = node.getAttribute("style").split(";");
22999 Roo.each(styles, function(s) {
23000 if (!s.match(/:/)) {
23003 var kv = s.split(":");
23004 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23007 // what ever is left... we allow.
23010 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23011 if (!nstyle.length) {
23012 node.removeAttribute('style');
23016 this.iterateChildren(node, this.cleanTableWidths);
23024 domToHTML : function(currentElement, depth, nopadtext) {
23026 depth = depth || 0;
23027 nopadtext = nopadtext || false;
23029 if (!currentElement) {
23030 return this.domToHTML(this.doc.body);
23033 //Roo.log(currentElement);
23035 var allText = false;
23036 var nodeName = currentElement.nodeName;
23037 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23039 if (nodeName == '#text') {
23041 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23046 if (nodeName != 'BODY') {
23049 // Prints the node tagName, such as <A>, <IMG>, etc
23052 for(i = 0; i < currentElement.attributes.length;i++) {
23054 var aname = currentElement.attributes.item(i).name;
23055 if (!currentElement.attributes.item(i).value.length) {
23058 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23061 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23070 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23073 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23078 // Traverse the tree
23080 var currentElementChild = currentElement.childNodes.item(i);
23081 var allText = true;
23082 var innerHTML = '';
23084 while (currentElementChild) {
23085 // Formatting code (indent the tree so it looks nice on the screen)
23086 var nopad = nopadtext;
23087 if (lastnode == 'SPAN') {
23091 if (currentElementChild.nodeName == '#text') {
23092 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23093 toadd = nopadtext ? toadd : toadd.trim();
23094 if (!nopad && toadd.length > 80) {
23095 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23097 innerHTML += toadd;
23100 currentElementChild = currentElement.childNodes.item(i);
23106 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23108 // Recursively traverse the tree structure of the child node
23109 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23110 lastnode = currentElementChild.nodeName;
23112 currentElementChild=currentElement.childNodes.item(i);
23118 // The remaining code is mostly for formatting the tree
23119 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23124 ret+= "</"+tagName+">";
23130 applyBlacklists : function()
23132 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23133 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23137 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23138 if (b.indexOf(tag) > -1) {
23141 this.white.push(tag);
23145 Roo.each(w, function(tag) {
23146 if (b.indexOf(tag) > -1) {
23149 if (this.white.indexOf(tag) > -1) {
23152 this.white.push(tag);
23157 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23158 if (w.indexOf(tag) > -1) {
23161 this.black.push(tag);
23165 Roo.each(b, function(tag) {
23166 if (w.indexOf(tag) > -1) {
23169 if (this.black.indexOf(tag) > -1) {
23172 this.black.push(tag);
23177 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23178 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23182 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23183 if (b.indexOf(tag) > -1) {
23186 this.cwhite.push(tag);
23190 Roo.each(w, function(tag) {
23191 if (b.indexOf(tag) > -1) {
23194 if (this.cwhite.indexOf(tag) > -1) {
23197 this.cwhite.push(tag);
23202 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23203 if (w.indexOf(tag) > -1) {
23206 this.cblack.push(tag);
23210 Roo.each(b, function(tag) {
23211 if (w.indexOf(tag) > -1) {
23214 if (this.cblack.indexOf(tag) > -1) {
23217 this.cblack.push(tag);
23222 setStylesheets : function(stylesheets)
23224 if(typeof(stylesheets) == 'string'){
23225 Roo.get(this.iframe.contentDocument.head).createChild({
23227 rel : 'stylesheet',
23236 Roo.each(stylesheets, function(s) {
23241 Roo.get(_this.iframe.contentDocument.head).createChild({
23243 rel : 'stylesheet',
23252 removeStylesheets : function()
23256 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23261 setStyle : function(style)
23263 Roo.get(this.iframe.contentDocument.head).createChild({
23272 // hide stuff that is not compatible
23286 * @event specialkey
23290 * @cfg {String} fieldClass @hide
23293 * @cfg {String} focusClass @hide
23296 * @cfg {String} autoCreate @hide
23299 * @cfg {String} inputType @hide
23302 * @cfg {String} invalidClass @hide
23305 * @cfg {String} invalidText @hide
23308 * @cfg {String} msgFx @hide
23311 * @cfg {String} validateOnBlur @hide
23315 Roo.HtmlEditorCore.white = [
23316 'area', 'br', 'img', 'input', 'hr', 'wbr',
23318 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23319 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23320 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23321 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23322 'table', 'ul', 'xmp',
23324 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23327 'dir', 'menu', 'ol', 'ul', 'dl',
23333 Roo.HtmlEditorCore.black = [
23334 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23336 'base', 'basefont', 'bgsound', 'blink', 'body',
23337 'frame', 'frameset', 'head', 'html', 'ilayer',
23338 'iframe', 'layer', 'link', 'meta', 'object',
23339 'script', 'style' ,'title', 'xml' // clean later..
23341 Roo.HtmlEditorCore.clean = [
23342 'script', 'style', 'title', 'xml'
23344 Roo.HtmlEditorCore.remove = [
23349 Roo.HtmlEditorCore.ablack = [
23353 Roo.HtmlEditorCore.aclean = [
23354 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23358 Roo.HtmlEditorCore.pwhite= [
23359 'http', 'https', 'mailto'
23362 // white listed style attributes.
23363 Roo.HtmlEditorCore.cwhite= [
23364 // 'text-align', /// default is to allow most things..
23370 // black listed style attributes.
23371 Roo.HtmlEditorCore.cblack= [
23372 // 'font-size' -- this can be set by the project
23376 Roo.HtmlEditorCore.swapCodes =[
23395 * @class Roo.bootstrap.HtmlEditor
23396 * @extends Roo.bootstrap.TextArea
23397 * Bootstrap HtmlEditor class
23400 * Create a new HtmlEditor
23401 * @param {Object} config The config object
23404 Roo.bootstrap.HtmlEditor = function(config){
23405 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23406 if (!this.toolbars) {
23407 this.toolbars = [];
23410 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23413 * @event initialize
23414 * Fires when the editor is fully initialized (including the iframe)
23415 * @param {HtmlEditor} this
23420 * Fires when the editor is first receives the focus. Any insertion must wait
23421 * until after this event.
23422 * @param {HtmlEditor} this
23426 * @event beforesync
23427 * Fires before the textarea is updated with content from the editor iframe. Return false
23428 * to cancel the sync.
23429 * @param {HtmlEditor} this
23430 * @param {String} html
23434 * @event beforepush
23435 * Fires before the iframe editor is updated with content from the textarea. Return false
23436 * to cancel the push.
23437 * @param {HtmlEditor} this
23438 * @param {String} html
23443 * Fires when the textarea is updated with content from the editor iframe.
23444 * @param {HtmlEditor} this
23445 * @param {String} html
23450 * Fires when the iframe editor is updated with content from the textarea.
23451 * @param {HtmlEditor} this
23452 * @param {String} html
23456 * @event editmodechange
23457 * Fires when the editor switches edit modes
23458 * @param {HtmlEditor} this
23459 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23461 editmodechange: true,
23463 * @event editorevent
23464 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23465 * @param {HtmlEditor} this
23469 * @event firstfocus
23470 * Fires when on first focus - needed by toolbars..
23471 * @param {HtmlEditor} this
23476 * Auto save the htmlEditor value as a file into Events
23477 * @param {HtmlEditor} this
23481 * @event savedpreview
23482 * preview the saved version of htmlEditor
23483 * @param {HtmlEditor} this
23490 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23494 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23499 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23504 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23509 * @cfg {Number} height (in pixels)
23513 * @cfg {Number} width (in pixels)
23518 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23521 stylesheets: false,
23526 // private properties
23527 validationEvent : false,
23529 initialized : false,
23532 onFocus : Roo.emptyFn,
23534 hideMode:'offsets',
23536 tbContainer : false,
23540 toolbarContainer :function() {
23541 return this.wrap.select('.x-html-editor-tb',true).first();
23545 * Protected method that will not generally be called directly. It
23546 * is called when the editor creates its toolbar. Override this method if you need to
23547 * add custom toolbar buttons.
23548 * @param {HtmlEditor} editor
23550 createToolbar : function(){
23551 Roo.log('renewing');
23552 Roo.log("create toolbars");
23554 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23555 this.toolbars[0].render(this.toolbarContainer());
23559 // if (!editor.toolbars || !editor.toolbars.length) {
23560 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23563 // for (var i =0 ; i < editor.toolbars.length;i++) {
23564 // editor.toolbars[i] = Roo.factory(
23565 // typeof(editor.toolbars[i]) == 'string' ?
23566 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23567 // Roo.bootstrap.HtmlEditor);
23568 // editor.toolbars[i].init(editor);
23574 onRender : function(ct, position)
23576 // Roo.log("Call onRender: " + this.xtype);
23578 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23580 this.wrap = this.inputEl().wrap({
23581 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23584 this.editorcore.onRender(ct, position);
23586 if (this.resizable) {
23587 this.resizeEl = new Roo.Resizable(this.wrap, {
23591 minHeight : this.height,
23592 height: this.height,
23593 handles : this.resizable,
23596 resize : function(r, w, h) {
23597 _t.onResize(w,h); // -something
23603 this.createToolbar(this);
23606 if(!this.width && this.resizable){
23607 this.setSize(this.wrap.getSize());
23609 if (this.resizeEl) {
23610 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23611 // should trigger onReize..
23617 onResize : function(w, h)
23619 Roo.log('resize: ' +w + ',' + h );
23620 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23624 if(this.inputEl() ){
23625 if(typeof w == 'number'){
23626 var aw = w - this.wrap.getFrameWidth('lr');
23627 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23630 if(typeof h == 'number'){
23631 var tbh = -11; // fixme it needs to tool bar size!
23632 for (var i =0; i < this.toolbars.length;i++) {
23633 // fixme - ask toolbars for heights?
23634 tbh += this.toolbars[i].el.getHeight();
23635 //if (this.toolbars[i].footer) {
23636 // tbh += this.toolbars[i].footer.el.getHeight();
23644 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23645 ah -= 5; // knock a few pixes off for look..
23646 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23650 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23651 this.editorcore.onResize(ew,eh);
23656 * Toggles the editor between standard and source edit mode.
23657 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23659 toggleSourceEdit : function(sourceEditMode)
23661 this.editorcore.toggleSourceEdit(sourceEditMode);
23663 if(this.editorcore.sourceEditMode){
23664 Roo.log('editor - showing textarea');
23667 // Roo.log(this.syncValue());
23669 this.inputEl().removeClass(['hide', 'x-hidden']);
23670 this.inputEl().dom.removeAttribute('tabIndex');
23671 this.inputEl().focus();
23673 Roo.log('editor - hiding textarea');
23675 // Roo.log(this.pushValue());
23678 this.inputEl().addClass(['hide', 'x-hidden']);
23679 this.inputEl().dom.setAttribute('tabIndex', -1);
23680 //this.deferFocus();
23683 if(this.resizable){
23684 this.setSize(this.wrap.getSize());
23687 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23690 // private (for BoxComponent)
23691 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23693 // private (for BoxComponent)
23694 getResizeEl : function(){
23698 // private (for BoxComponent)
23699 getPositionEl : function(){
23704 initEvents : function(){
23705 this.originalValue = this.getValue();
23709 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23712 // markInvalid : Roo.emptyFn,
23714 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23717 // clearInvalid : Roo.emptyFn,
23719 setValue : function(v){
23720 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23721 this.editorcore.pushValue();
23726 deferFocus : function(){
23727 this.focus.defer(10, this);
23731 focus : function(){
23732 this.editorcore.focus();
23738 onDestroy : function(){
23744 for (var i =0; i < this.toolbars.length;i++) {
23745 // fixme - ask toolbars for heights?
23746 this.toolbars[i].onDestroy();
23749 this.wrap.dom.innerHTML = '';
23750 this.wrap.remove();
23755 onFirstFocus : function(){
23756 //Roo.log("onFirstFocus");
23757 this.editorcore.onFirstFocus();
23758 for (var i =0; i < this.toolbars.length;i++) {
23759 this.toolbars[i].onFirstFocus();
23765 syncValue : function()
23767 this.editorcore.syncValue();
23770 pushValue : function()
23772 this.editorcore.pushValue();
23776 // hide stuff that is not compatible
23790 * @event specialkey
23794 * @cfg {String} fieldClass @hide
23797 * @cfg {String} focusClass @hide
23800 * @cfg {String} autoCreate @hide
23803 * @cfg {String} inputType @hide
23806 * @cfg {String} invalidClass @hide
23809 * @cfg {String} invalidText @hide
23812 * @cfg {String} msgFx @hide
23815 * @cfg {String} validateOnBlur @hide
23824 Roo.namespace('Roo.bootstrap.htmleditor');
23826 * @class Roo.bootstrap.HtmlEditorToolbar1
23831 new Roo.bootstrap.HtmlEditor({
23834 new Roo.bootstrap.HtmlEditorToolbar1({
23835 disable : { fonts: 1 , format: 1, ..., ... , ...],
23841 * @cfg {Object} disable List of elements to disable..
23842 * @cfg {Array} btns List of additional buttons.
23846 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23849 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23852 Roo.apply(this, config);
23854 // default disabled, based on 'good practice'..
23855 this.disable = this.disable || {};
23856 Roo.applyIf(this.disable, {
23859 specialElements : true
23861 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23863 this.editor = config.editor;
23864 this.editorcore = config.editor.editorcore;
23866 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23868 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23869 // dont call parent... till later.
23871 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23876 editorcore : false,
23881 "h1","h2","h3","h4","h5","h6",
23883 "abbr", "acronym", "address", "cite", "samp", "var",
23887 onRender : function(ct, position)
23889 // Roo.log("Call onRender: " + this.xtype);
23891 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23893 this.el.dom.style.marginBottom = '0';
23895 var editorcore = this.editorcore;
23896 var editor= this.editor;
23899 var btn = function(id,cmd , toggle, handler, html){
23901 var event = toggle ? 'toggle' : 'click';
23906 xns: Roo.bootstrap,
23909 enableToggle:toggle !== false,
23911 pressed : toggle ? false : null,
23914 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23915 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23921 // var cb_box = function...
23926 xns: Roo.bootstrap,
23927 glyphicon : 'font',
23931 xns: Roo.bootstrap,
23935 Roo.each(this.formats, function(f) {
23936 style.menu.items.push({
23938 xns: Roo.bootstrap,
23939 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23944 editorcore.insertTag(this.tagname);
23951 children.push(style);
23953 btn('bold',false,true);
23954 btn('italic',false,true);
23955 btn('align-left', 'justifyleft',true);
23956 btn('align-center', 'justifycenter',true);
23957 btn('align-right' , 'justifyright',true);
23958 btn('link', false, false, function(btn) {
23959 //Roo.log("create link?");
23960 var url = prompt(this.createLinkText, this.defaultLinkValue);
23961 if(url && url != 'http:/'+'/'){
23962 this.editorcore.relayCmd('createlink', url);
23965 btn('list','insertunorderedlist',true);
23966 btn('pencil', false,true, function(btn){
23968 this.toggleSourceEdit(btn.pressed);
23971 if (this.editor.btns.length > 0) {
23972 for (var i = 0; i<this.editor.btns.length; i++) {
23973 children.push(this.editor.btns[i]);
23981 xns: Roo.bootstrap,
23986 xns: Roo.bootstrap,
23991 cog.menu.items.push({
23993 xns: Roo.bootstrap,
23994 html : Clean styles,
23999 editorcore.insertTag(this.tagname);
24008 this.xtype = 'NavSimplebar';
24010 for(var i=0;i< children.length;i++) {
24012 this.buttons.add(this.addxtypeChild(children[i]));
24016 editor.on('editorevent', this.updateToolbar, this);
24018 onBtnClick : function(id)
24020 this.editorcore.relayCmd(id);
24021 this.editorcore.focus();
24025 * Protected method that will not generally be called directly. It triggers
24026 * a toolbar update by reading the markup state of the current selection in the editor.
24028 updateToolbar: function(){
24030 if(!this.editorcore.activated){
24031 this.editor.onFirstFocus(); // is this neeed?
24035 var btns = this.buttons;
24036 var doc = this.editorcore.doc;
24037 btns.get('bold').setActive(doc.queryCommandState('bold'));
24038 btns.get('italic').setActive(doc.queryCommandState('italic'));
24039 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24041 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24042 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24043 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24045 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24046 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24049 var ans = this.editorcore.getAllAncestors();
24050 if (this.formatCombo) {
24053 var store = this.formatCombo.store;
24054 this.formatCombo.setValue("");
24055 for (var i =0; i < ans.length;i++) {
24056 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24058 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24066 // hides menus... - so this cant be on a menu...
24067 Roo.bootstrap.MenuMgr.hideAll();
24069 Roo.bootstrap.MenuMgr.hideAll();
24070 //this.editorsyncValue();
24072 onFirstFocus: function() {
24073 this.buttons.each(function(item){
24077 toggleSourceEdit : function(sourceEditMode){
24080 if(sourceEditMode){
24081 Roo.log("disabling buttons");
24082 this.buttons.each( function(item){
24083 if(item.cmd != 'pencil'){
24089 Roo.log("enabling buttons");
24090 if(this.editorcore.initialized){
24091 this.buttons.each( function(item){
24097 Roo.log("calling toggole on editor");
24098 // tell the editor that it's been pressed..
24099 this.editor.toggleSourceEdit(sourceEditMode);
24109 * @class Roo.bootstrap.Table.AbstractSelectionModel
24110 * @extends Roo.util.Observable
24111 * Abstract base class for grid SelectionModels. It provides the interface that should be
24112 * implemented by descendant classes. This class should not be directly instantiated.
24115 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24116 this.locked = false;
24117 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24121 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24122 /** @ignore Called by the grid automatically. Do not call directly. */
24123 init : function(grid){
24129 * Locks the selections.
24132 this.locked = true;
24136 * Unlocks the selections.
24138 unlock : function(){
24139 this.locked = false;
24143 * Returns true if the selections are locked.
24144 * @return {Boolean}
24146 isLocked : function(){
24147 return this.locked;
24151 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24152 * @class Roo.bootstrap.Table.RowSelectionModel
24153 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24154 * It supports multiple selections and keyboard selection/navigation.
24156 * @param {Object} config
24159 Roo.bootstrap.Table.RowSelectionModel = function(config){
24160 Roo.apply(this, config);
24161 this.selections = new Roo.util.MixedCollection(false, function(o){
24166 this.lastActive = false;
24170 * @event selectionchange
24171 * Fires when the selection changes
24172 * @param {SelectionModel} this
24174 "selectionchange" : true,
24176 * @event afterselectionchange
24177 * Fires after the selection changes (eg. by key press or clicking)
24178 * @param {SelectionModel} this
24180 "afterselectionchange" : true,
24182 * @event beforerowselect
24183 * Fires when a row is selected being selected, return false to cancel.
24184 * @param {SelectionModel} this
24185 * @param {Number} rowIndex The selected index
24186 * @param {Boolean} keepExisting False if other selections will be cleared
24188 "beforerowselect" : true,
24191 * Fires when a row is selected.
24192 * @param {SelectionModel} this
24193 * @param {Number} rowIndex The selected index
24194 * @param {Roo.data.Record} r The record
24196 "rowselect" : true,
24198 * @event rowdeselect
24199 * Fires when a row is deselected.
24200 * @param {SelectionModel} this
24201 * @param {Number} rowIndex The selected index
24203 "rowdeselect" : true
24205 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24206 this.locked = false;
24209 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24211 * @cfg {Boolean} singleSelect
24212 * True to allow selection of only one row at a time (defaults to false)
24214 singleSelect : false,
24217 initEvents : function()
24220 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24221 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24222 //}else{ // allow click to work like normal
24223 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24225 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24226 this.grid.on("rowclick", this.handleMouseDown, this);
24228 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24229 "up" : function(e){
24231 this.selectPrevious(e.shiftKey);
24232 }else if(this.last !== false && this.lastActive !== false){
24233 var last = this.last;
24234 this.selectRange(this.last, this.lastActive-1);
24235 this.grid.getView().focusRow(this.lastActive);
24236 if(last !== false){
24240 this.selectFirstRow();
24242 this.fireEvent("afterselectionchange", this);
24244 "down" : function(e){
24246 this.selectNext(e.shiftKey);
24247 }else if(this.last !== false && this.lastActive !== false){
24248 var last = this.last;
24249 this.selectRange(this.last, this.lastActive+1);
24250 this.grid.getView().focusRow(this.lastActive);
24251 if(last !== false){
24255 this.selectFirstRow();
24257 this.fireEvent("afterselectionchange", this);
24261 this.grid.store.on('load', function(){
24262 this.selections.clear();
24265 var view = this.grid.view;
24266 view.on("refresh", this.onRefresh, this);
24267 view.on("rowupdated", this.onRowUpdated, this);
24268 view.on("rowremoved", this.onRemove, this);
24273 onRefresh : function()
24275 var ds = this.grid.store, i, v = this.grid.view;
24276 var s = this.selections;
24277 s.each(function(r){
24278 if((i = ds.indexOfId(r.id)) != -1){
24287 onRemove : function(v, index, r){
24288 this.selections.remove(r);
24292 onRowUpdated : function(v, index, r){
24293 if(this.isSelected(r)){
24294 v.onRowSelect(index);
24300 * @param {Array} records The records to select
24301 * @param {Boolean} keepExisting (optional) True to keep existing selections
24303 selectRecords : function(records, keepExisting)
24306 this.clearSelections();
24308 var ds = this.grid.store;
24309 for(var i = 0, len = records.length; i < len; i++){
24310 this.selectRow(ds.indexOf(records[i]), true);
24315 * Gets the number of selected rows.
24318 getCount : function(){
24319 return this.selections.length;
24323 * Selects the first row in the grid.
24325 selectFirstRow : function(){
24330 * Select the last row.
24331 * @param {Boolean} keepExisting (optional) True to keep existing selections
24333 selectLastRow : function(keepExisting){
24334 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24335 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24339 * Selects the row immediately following the last selected row.
24340 * @param {Boolean} keepExisting (optional) True to keep existing selections
24342 selectNext : function(keepExisting)
24344 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24345 this.selectRow(this.last+1, keepExisting);
24346 this.grid.getView().focusRow(this.last);
24351 * Selects the row that precedes the last selected row.
24352 * @param {Boolean} keepExisting (optional) True to keep existing selections
24354 selectPrevious : function(keepExisting){
24356 this.selectRow(this.last-1, keepExisting);
24357 this.grid.getView().focusRow(this.last);
24362 * Returns the selected records
24363 * @return {Array} Array of selected records
24365 getSelections : function(){
24366 return [].concat(this.selections.items);
24370 * Returns the first selected record.
24373 getSelected : function(){
24374 return this.selections.itemAt(0);
24379 * Clears all selections.
24381 clearSelections : function(fast)
24387 var ds = this.grid.store;
24388 var s = this.selections;
24389 s.each(function(r){
24390 this.deselectRow(ds.indexOfId(r.id));
24394 this.selections.clear();
24401 * Selects all rows.
24403 selectAll : function(){
24407 this.selections.clear();
24408 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24409 this.selectRow(i, true);
24414 * Returns True if there is a selection.
24415 * @return {Boolean}
24417 hasSelection : function(){
24418 return this.selections.length > 0;
24422 * Returns True if the specified row is selected.
24423 * @param {Number/Record} record The record or index of the record to check
24424 * @return {Boolean}
24426 isSelected : function(index){
24427 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24428 return (r && this.selections.key(r.id) ? true : false);
24432 * Returns True if the specified record id is selected.
24433 * @param {String} id The id of record to check
24434 * @return {Boolean}
24436 isIdSelected : function(id){
24437 return (this.selections.key(id) ? true : false);
24442 handleMouseDBClick : function(e, t){
24446 handleMouseDown : function(e, t)
24448 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24449 if(this.isLocked() || rowIndex < 0 ){
24452 if(e.shiftKey && this.last !== false){
24453 var last = this.last;
24454 this.selectRange(last, rowIndex, e.ctrlKey);
24455 this.last = last; // reset the last
24459 var isSelected = this.isSelected(rowIndex);
24460 //Roo.log("select row:" + rowIndex);
24462 this.deselectRow(rowIndex);
24464 this.selectRow(rowIndex, true);
24468 if(e.button !== 0 && isSelected){
24469 alert('rowIndex 2: ' + rowIndex);
24470 view.focusRow(rowIndex);
24471 }else if(e.ctrlKey && isSelected){
24472 this.deselectRow(rowIndex);
24473 }else if(!isSelected){
24474 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24475 view.focusRow(rowIndex);
24479 this.fireEvent("afterselectionchange", this);
24482 handleDragableRowClick : function(grid, rowIndex, e)
24484 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24485 this.selectRow(rowIndex, false);
24486 grid.view.focusRow(rowIndex);
24487 this.fireEvent("afterselectionchange", this);
24492 * Selects multiple rows.
24493 * @param {Array} rows Array of the indexes of the row to select
24494 * @param {Boolean} keepExisting (optional) True to keep existing selections
24496 selectRows : function(rows, keepExisting){
24498 this.clearSelections();
24500 for(var i = 0, len = rows.length; i < len; i++){
24501 this.selectRow(rows[i], true);
24506 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24507 * @param {Number} startRow The index of the first row in the range
24508 * @param {Number} endRow The index of the last row in the range
24509 * @param {Boolean} keepExisting (optional) True to retain existing selections
24511 selectRange : function(startRow, endRow, keepExisting){
24516 this.clearSelections();
24518 if(startRow <= endRow){
24519 for(var i = startRow; i <= endRow; i++){
24520 this.selectRow(i, true);
24523 for(var i = startRow; i >= endRow; i--){
24524 this.selectRow(i, true);
24530 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24531 * @param {Number} startRow The index of the first row in the range
24532 * @param {Number} endRow The index of the last row in the range
24534 deselectRange : function(startRow, endRow, preventViewNotify){
24538 for(var i = startRow; i <= endRow; i++){
24539 this.deselectRow(i, preventViewNotify);
24545 * @param {Number} row The index of the row to select
24546 * @param {Boolean} keepExisting (optional) True to keep existing selections
24548 selectRow : function(index, keepExisting, preventViewNotify)
24550 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24553 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24554 if(!keepExisting || this.singleSelect){
24555 this.clearSelections();
24558 var r = this.grid.store.getAt(index);
24559 //console.log('selectRow - record id :' + r.id);
24561 this.selections.add(r);
24562 this.last = this.lastActive = index;
24563 if(!preventViewNotify){
24564 var proxy = new Roo.Element(
24565 this.grid.getRowDom(index)
24567 proxy.addClass('bg-info info');
24569 this.fireEvent("rowselect", this, index, r);
24570 this.fireEvent("selectionchange", this);
24576 * @param {Number} row The index of the row to deselect
24578 deselectRow : function(index, preventViewNotify)
24583 if(this.last == index){
24586 if(this.lastActive == index){
24587 this.lastActive = false;
24590 var r = this.grid.store.getAt(index);
24595 this.selections.remove(r);
24596 //.console.log('deselectRow - record id :' + r.id);
24597 if(!preventViewNotify){
24599 var proxy = new Roo.Element(
24600 this.grid.getRowDom(index)
24602 proxy.removeClass('bg-info info');
24604 this.fireEvent("rowdeselect", this, index);
24605 this.fireEvent("selectionchange", this);
24609 restoreLast : function(){
24611 this.last = this._last;
24616 acceptsNav : function(row, col, cm){
24617 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24621 onEditorKey : function(field, e){
24622 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24627 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24629 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24631 }else if(k == e.ENTER && !e.ctrlKey){
24635 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24637 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24639 }else if(k == e.ESC){
24643 g.startEditing(newCell[0], newCell[1]);
24649 * Ext JS Library 1.1.1
24650 * Copyright(c) 2006-2007, Ext JS, LLC.
24652 * Originally Released Under LGPL - original licence link has changed is not relivant.
24655 * <script type="text/javascript">
24659 * @class Roo.bootstrap.PagingToolbar
24660 * @extends Roo.bootstrap.NavSimplebar
24661 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24663 * Create a new PagingToolbar
24664 * @param {Object} config The config object
24665 * @param {Roo.data.Store} store
24667 Roo.bootstrap.PagingToolbar = function(config)
24669 // old args format still supported... - xtype is prefered..
24670 // created from xtype...
24672 this.ds = config.dataSource;
24674 if (config.store && !this.ds) {
24675 this.store= Roo.factory(config.store, Roo.data);
24676 this.ds = this.store;
24677 this.ds.xmodule = this.xmodule || false;
24680 this.toolbarItems = [];
24681 if (config.items) {
24682 this.toolbarItems = config.items;
24685 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24690 this.bind(this.ds);
24693 if (Roo.bootstrap.version == 4) {
24694 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24696 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24701 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24703 * @cfg {Roo.data.Store} dataSource
24704 * The underlying data store providing the paged data
24707 * @cfg {String/HTMLElement/Element} container
24708 * container The id or element that will contain the toolbar
24711 * @cfg {Boolean} displayInfo
24712 * True to display the displayMsg (defaults to false)
24715 * @cfg {Number} pageSize
24716 * The number of records to display per page (defaults to 20)
24720 * @cfg {String} displayMsg
24721 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24723 displayMsg : 'Displaying {0} - {1} of {2}',
24725 * @cfg {String} emptyMsg
24726 * The message to display when no records are found (defaults to "No data to display")
24728 emptyMsg : 'No data to display',
24730 * Customizable piece of the default paging text (defaults to "Page")
24733 beforePageText : "Page",
24735 * Customizable piece of the default paging text (defaults to "of %0")
24738 afterPageText : "of {0}",
24740 * Customizable piece of the default paging text (defaults to "First Page")
24743 firstText : "First Page",
24745 * Customizable piece of the default paging text (defaults to "Previous Page")
24748 prevText : "Previous Page",
24750 * Customizable piece of the default paging text (defaults to "Next Page")
24753 nextText : "Next Page",
24755 * Customizable piece of the default paging text (defaults to "Last Page")
24758 lastText : "Last Page",
24760 * Customizable piece of the default paging text (defaults to "Refresh")
24763 refreshText : "Refresh",
24767 onRender : function(ct, position)
24769 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24770 this.navgroup.parentId = this.id;
24771 this.navgroup.onRender(this.el, null);
24772 // add the buttons to the navgroup
24774 if(this.displayInfo){
24775 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24776 this.displayEl = this.el.select('.x-paging-info', true).first();
24777 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24778 // this.displayEl = navel.el.select('span',true).first();
24784 Roo.each(_this.buttons, function(e){ // this might need to use render????
24785 Roo.factory(e).render(_this.el);
24789 Roo.each(_this.toolbarItems, function(e) {
24790 _this.navgroup.addItem(e);
24794 this.first = this.navgroup.addItem({
24795 tooltip: this.firstText,
24796 cls: "prev btn-outline-secondary",
24797 html : ' <i class="fa fa-step-backward"></i>',
24799 preventDefault: true,
24800 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24803 this.prev = this.navgroup.addItem({
24804 tooltip: this.prevText,
24805 cls: "prev btn-outline-secondary",
24806 html : ' <i class="fa fa-backward"></i>',
24808 preventDefault: true,
24809 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24811 //this.addSeparator();
24814 var field = this.navgroup.addItem( {
24816 cls : 'x-paging-position btn-outline-secondary',
24818 html : this.beforePageText +
24819 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24820 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24823 this.field = field.el.select('input', true).first();
24824 this.field.on("keydown", this.onPagingKeydown, this);
24825 this.field.on("focus", function(){this.dom.select();});
24828 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24829 //this.field.setHeight(18);
24830 //this.addSeparator();
24831 this.next = this.navgroup.addItem({
24832 tooltip: this.nextText,
24833 cls: "next btn-outline-secondary",
24834 html : ' <i class="fa fa-forward"></i>',
24836 preventDefault: true,
24837 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24839 this.last = this.navgroup.addItem({
24840 tooltip: this.lastText,
24841 html : ' <i class="fa fa-step-forward"></i>',
24842 cls: "next btn-outline-secondary",
24844 preventDefault: true,
24845 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24847 //this.addSeparator();
24848 this.loading = this.navgroup.addItem({
24849 tooltip: this.refreshText,
24850 cls: "btn-outline-secondary",
24851 html : ' <i class="fa fa-refresh"></i>',
24852 preventDefault: true,
24853 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24859 updateInfo : function(){
24860 if(this.displayEl){
24861 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24862 var msg = count == 0 ?
24866 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24868 this.displayEl.update(msg);
24873 onLoad : function(ds, r, o)
24875 this.cursor = o.params.start ? o.params.start : 0;
24877 var d = this.getPageData(),
24882 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24883 this.field.dom.value = ap;
24884 this.first.setDisabled(ap == 1);
24885 this.prev.setDisabled(ap == 1);
24886 this.next.setDisabled(ap == ps);
24887 this.last.setDisabled(ap == ps);
24888 this.loading.enable();
24893 getPageData : function(){
24894 var total = this.ds.getTotalCount();
24897 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24898 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24903 onLoadError : function(){
24904 this.loading.enable();
24908 onPagingKeydown : function(e){
24909 var k = e.getKey();
24910 var d = this.getPageData();
24912 var v = this.field.dom.value, pageNum;
24913 if(!v || isNaN(pageNum = parseInt(v, 10))){
24914 this.field.dom.value = d.activePage;
24917 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24918 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24921 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))
24923 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24924 this.field.dom.value = pageNum;
24925 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24928 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24930 var v = this.field.dom.value, pageNum;
24931 var increment = (e.shiftKey) ? 10 : 1;
24932 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24935 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24936 this.field.dom.value = d.activePage;
24939 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24941 this.field.dom.value = parseInt(v, 10) + increment;
24942 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24943 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24950 beforeLoad : function(){
24952 this.loading.disable();
24957 onClick : function(which){
24966 ds.load({params:{start: 0, limit: this.pageSize}});
24969 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24972 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24975 var total = ds.getTotalCount();
24976 var extra = total % this.pageSize;
24977 var lastStart = extra ? (total - extra) : total-this.pageSize;
24978 ds.load({params:{start: lastStart, limit: this.pageSize}});
24981 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24987 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24988 * @param {Roo.data.Store} store The data store to unbind
24990 unbind : function(ds){
24991 ds.un("beforeload", this.beforeLoad, this);
24992 ds.un("load", this.onLoad, this);
24993 ds.un("loadexception", this.onLoadError, this);
24994 ds.un("remove", this.updateInfo, this);
24995 ds.un("add", this.updateInfo, this);
24996 this.ds = undefined;
25000 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25001 * @param {Roo.data.Store} store The data store to bind
25003 bind : function(ds){
25004 ds.on("beforeload", this.beforeLoad, this);
25005 ds.on("load", this.onLoad, this);
25006 ds.on("loadexception", this.onLoadError, this);
25007 ds.on("remove", this.updateInfo, this);
25008 ds.on("add", this.updateInfo, this);
25019 * @class Roo.bootstrap.MessageBar
25020 * @extends Roo.bootstrap.Component
25021 * Bootstrap MessageBar class
25022 * @cfg {String} html contents of the MessageBar
25023 * @cfg {String} weight (info | success | warning | danger) default info
25024 * @cfg {String} beforeClass insert the bar before the given class
25025 * @cfg {Boolean} closable (true | false) default false
25026 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25029 * Create a new Element
25030 * @param {Object} config The config object
25033 Roo.bootstrap.MessageBar = function(config){
25034 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25037 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25043 beforeClass: 'bootstrap-sticky-wrap',
25045 getAutoCreate : function(){
25049 cls: 'alert alert-dismissable alert-' + this.weight,
25054 html: this.html || ''
25060 cfg.cls += ' alert-messages-fixed';
25074 onRender : function(ct, position)
25076 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25079 var cfg = Roo.apply({}, this.getAutoCreate());
25083 cfg.cls += ' ' + this.cls;
25086 cfg.style = this.style;
25088 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25090 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25093 this.el.select('>button.close').on('click', this.hide, this);
25099 if (!this.rendered) {
25105 this.fireEvent('show', this);
25111 if (!this.rendered) {
25117 this.fireEvent('hide', this);
25120 update : function()
25122 // var e = this.el.dom.firstChild;
25124 // if(this.closable){
25125 // e = e.nextSibling;
25128 // e.data = this.html || '';
25130 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25146 * @class Roo.bootstrap.Graph
25147 * @extends Roo.bootstrap.Component
25148 * Bootstrap Graph class
25152 @cfg {String} graphtype bar | vbar | pie
25153 @cfg {number} g_x coodinator | centre x (pie)
25154 @cfg {number} g_y coodinator | centre y (pie)
25155 @cfg {number} g_r radius (pie)
25156 @cfg {number} g_height height of the chart (respected by all elements in the set)
25157 @cfg {number} g_width width of the chart (respected by all elements in the set)
25158 @cfg {Object} title The title of the chart
25161 -opts (object) options for the chart
25163 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25164 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25166 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.
25167 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25169 o stretch (boolean)
25171 -opts (object) options for the pie
25174 o startAngle (number)
25175 o endAngle (number)
25179 * Create a new Input
25180 * @param {Object} config The config object
25183 Roo.bootstrap.Graph = function(config){
25184 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25190 * The img click event for the img.
25191 * @param {Roo.EventObject} e
25197 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25208 //g_colors: this.colors,
25215 getAutoCreate : function(){
25226 onRender : function(ct,position){
25229 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25231 if (typeof(Raphael) == 'undefined') {
25232 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25236 this.raphael = Raphael(this.el.dom);
25238 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25239 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25240 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25241 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25243 r.text(160, 10, "Single Series Chart").attr(txtattr);
25244 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25245 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25246 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25248 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25249 r.barchart(330, 10, 300, 220, data1);
25250 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25251 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25254 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25255 // r.barchart(30, 30, 560, 250, xdata, {
25256 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25257 // axis : "0 0 1 1",
25258 // axisxlabels : xdata
25259 // //yvalues : cols,
25262 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25264 // this.load(null,xdata,{
25265 // axis : "0 0 1 1",
25266 // axisxlabels : xdata
25271 load : function(graphtype,xdata,opts)
25273 this.raphael.clear();
25275 graphtype = this.graphtype;
25280 var r = this.raphael,
25281 fin = function () {
25282 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25284 fout = function () {
25285 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25287 pfin = function() {
25288 this.sector.stop();
25289 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25292 this.label[0].stop();
25293 this.label[0].attr({ r: 7.5 });
25294 this.label[1].attr({ "font-weight": 800 });
25297 pfout = function() {
25298 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25301 this.label[0].animate({ r: 5 }, 500, "bounce");
25302 this.label[1].attr({ "font-weight": 400 });
25308 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25311 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25314 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25315 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25317 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25324 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25329 setTitle: function(o)
25334 initEvents: function() {
25337 this.el.on('click', this.onClick, this);
25341 onClick : function(e)
25343 Roo.log('img onclick');
25344 this.fireEvent('click', this, e);
25356 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25359 * @class Roo.bootstrap.dash.NumberBox
25360 * @extends Roo.bootstrap.Component
25361 * Bootstrap NumberBox class
25362 * @cfg {String} headline Box headline
25363 * @cfg {String} content Box content
25364 * @cfg {String} icon Box icon
25365 * @cfg {String} footer Footer text
25366 * @cfg {String} fhref Footer href
25369 * Create a new NumberBox
25370 * @param {Object} config The config object
25374 Roo.bootstrap.dash.NumberBox = function(config){
25375 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25379 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25388 getAutoCreate : function(){
25392 cls : 'small-box ',
25400 cls : 'roo-headline',
25401 html : this.headline
25405 cls : 'roo-content',
25406 html : this.content
25420 cls : 'ion ' + this.icon
25429 cls : 'small-box-footer',
25430 href : this.fhref || '#',
25434 cfg.cn.push(footer);
25441 onRender : function(ct,position){
25442 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25449 setHeadline: function (value)
25451 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25454 setFooter: function (value, href)
25456 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25459 this.el.select('a.small-box-footer',true).first().attr('href', href);
25464 setContent: function (value)
25466 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25469 initEvents: function()
25483 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25486 * @class Roo.bootstrap.dash.TabBox
25487 * @extends Roo.bootstrap.Component
25488 * Bootstrap TabBox class
25489 * @cfg {String} title Title of the TabBox
25490 * @cfg {String} icon Icon of the TabBox
25491 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25492 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25495 * Create a new TabBox
25496 * @param {Object} config The config object
25500 Roo.bootstrap.dash.TabBox = function(config){
25501 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25506 * When a pane is added
25507 * @param {Roo.bootstrap.dash.TabPane} pane
25511 * @event activatepane
25512 * When a pane is activated
25513 * @param {Roo.bootstrap.dash.TabPane} pane
25515 "activatepane" : true
25523 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25528 tabScrollable : false,
25530 getChildContainer : function()
25532 return this.el.select('.tab-content', true).first();
25535 getAutoCreate : function(){
25539 cls: 'pull-left header',
25547 cls: 'fa ' + this.icon
25553 cls: 'nav nav-tabs pull-right',
25559 if(this.tabScrollable){
25566 cls: 'nav nav-tabs pull-right',
25577 cls: 'nav-tabs-custom',
25582 cls: 'tab-content no-padding',
25590 initEvents : function()
25592 //Roo.log('add add pane handler');
25593 this.on('addpane', this.onAddPane, this);
25596 * Updates the box title
25597 * @param {String} html to set the title to.
25599 setTitle : function(value)
25601 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25603 onAddPane : function(pane)
25605 this.panes.push(pane);
25606 //Roo.log('addpane');
25608 // tabs are rendere left to right..
25609 if(!this.showtabs){
25613 var ctr = this.el.select('.nav-tabs', true).first();
25616 var existing = ctr.select('.nav-tab',true);
25617 var qty = existing.getCount();;
25620 var tab = ctr.createChild({
25622 cls : 'nav-tab' + (qty ? '' : ' active'),
25630 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25633 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25635 pane.el.addClass('active');
25640 onTabClick : function(ev,un,ob,pane)
25642 //Roo.log('tab - prev default');
25643 ev.preventDefault();
25646 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25647 pane.tab.addClass('active');
25648 //Roo.log(pane.title);
25649 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25650 // technically we should have a deactivate event.. but maybe add later.
25651 // and it should not de-activate the selected tab...
25652 this.fireEvent('activatepane', pane);
25653 pane.el.addClass('active');
25654 pane.fireEvent('activate');
25659 getActivePane : function()
25662 Roo.each(this.panes, function(p) {
25663 if(p.el.hasClass('active')){
25684 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25686 * @class Roo.bootstrap.TabPane
25687 * @extends Roo.bootstrap.Component
25688 * Bootstrap TabPane class
25689 * @cfg {Boolean} active (false | true) Default false
25690 * @cfg {String} title title of panel
25694 * Create a new TabPane
25695 * @param {Object} config The config object
25698 Roo.bootstrap.dash.TabPane = function(config){
25699 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25705 * When a pane is activated
25706 * @param {Roo.bootstrap.dash.TabPane} pane
25713 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25718 // the tabBox that this is attached to.
25721 getAutoCreate : function()
25729 cfg.cls += ' active';
25734 initEvents : function()
25736 //Roo.log('trigger add pane handler');
25737 this.parent().fireEvent('addpane', this)
25741 * Updates the tab title
25742 * @param {String} html to set the title to.
25744 setTitle: function(str)
25750 this.tab.select('a', true).first().dom.innerHTML = str;
25767 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25770 * @class Roo.bootstrap.menu.Menu
25771 * @extends Roo.bootstrap.Component
25772 * Bootstrap Menu class - container for Menu
25773 * @cfg {String} html Text of the menu
25774 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25775 * @cfg {String} icon Font awesome icon
25776 * @cfg {String} pos Menu align to (top | bottom) default bottom
25780 * Create a new Menu
25781 * @param {Object} config The config object
25785 Roo.bootstrap.menu.Menu = function(config){
25786 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25790 * @event beforeshow
25791 * Fires before this menu is displayed
25792 * @param {Roo.bootstrap.menu.Menu} this
25796 * @event beforehide
25797 * Fires before this menu is hidden
25798 * @param {Roo.bootstrap.menu.Menu} this
25803 * Fires after this menu is displayed
25804 * @param {Roo.bootstrap.menu.Menu} this
25809 * Fires after this menu is hidden
25810 * @param {Roo.bootstrap.menu.Menu} this
25815 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25816 * @param {Roo.bootstrap.menu.Menu} this
25817 * @param {Roo.EventObject} e
25824 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25828 weight : 'default',
25833 getChildContainer : function() {
25834 if(this.isSubMenu){
25838 return this.el.select('ul.dropdown-menu', true).first();
25841 getAutoCreate : function()
25846 cls : 'roo-menu-text',
25854 cls : 'fa ' + this.icon
25865 cls : 'dropdown-button btn btn-' + this.weight,
25870 cls : 'dropdown-toggle btn btn-' + this.weight,
25880 cls : 'dropdown-menu'
25886 if(this.pos == 'top'){
25887 cfg.cls += ' dropup';
25890 if(this.isSubMenu){
25893 cls : 'dropdown-menu'
25900 onRender : function(ct, position)
25902 this.isSubMenu = ct.hasClass('dropdown-submenu');
25904 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25907 initEvents : function()
25909 if(this.isSubMenu){
25913 this.hidden = true;
25915 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25916 this.triggerEl.on('click', this.onTriggerPress, this);
25918 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25919 this.buttonEl.on('click', this.onClick, this);
25925 if(this.isSubMenu){
25929 return this.el.select('ul.dropdown-menu', true).first();
25932 onClick : function(e)
25934 this.fireEvent("click", this, e);
25937 onTriggerPress : function(e)
25939 if (this.isVisible()) {
25946 isVisible : function(){
25947 return !this.hidden;
25952 this.fireEvent("beforeshow", this);
25954 this.hidden = false;
25955 this.el.addClass('open');
25957 Roo.get(document).on("mouseup", this.onMouseUp, this);
25959 this.fireEvent("show", this);
25966 this.fireEvent("beforehide", this);
25968 this.hidden = true;
25969 this.el.removeClass('open');
25971 Roo.get(document).un("mouseup", this.onMouseUp);
25973 this.fireEvent("hide", this);
25976 onMouseUp : function()
25990 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25993 * @class Roo.bootstrap.menu.Item
25994 * @extends Roo.bootstrap.Component
25995 * Bootstrap MenuItem class
25996 * @cfg {Boolean} submenu (true | false) default false
25997 * @cfg {String} html text of the item
25998 * @cfg {String} href the link
25999 * @cfg {Boolean} disable (true | false) default false
26000 * @cfg {Boolean} preventDefault (true | false) default true
26001 * @cfg {String} icon Font awesome icon
26002 * @cfg {String} pos Submenu align to (left | right) default right
26006 * Create a new Item
26007 * @param {Object} config The config object
26011 Roo.bootstrap.menu.Item = function(config){
26012 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26016 * Fires when the mouse is hovering over this menu
26017 * @param {Roo.bootstrap.menu.Item} this
26018 * @param {Roo.EventObject} e
26023 * Fires when the mouse exits this menu
26024 * @param {Roo.bootstrap.menu.Item} this
26025 * @param {Roo.EventObject} e
26031 * The raw click event for the entire grid.
26032 * @param {Roo.EventObject} e
26038 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26043 preventDefault: true,
26048 getAutoCreate : function()
26053 cls : 'roo-menu-item-text',
26061 cls : 'fa ' + this.icon
26070 href : this.href || '#',
26077 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26081 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26083 if(this.pos == 'left'){
26084 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26091 initEvents : function()
26093 this.el.on('mouseover', this.onMouseOver, this);
26094 this.el.on('mouseout', this.onMouseOut, this);
26096 this.el.select('a', true).first().on('click', this.onClick, this);
26100 onClick : function(e)
26102 if(this.preventDefault){
26103 e.preventDefault();
26106 this.fireEvent("click", this, e);
26109 onMouseOver : function(e)
26111 if(this.submenu && this.pos == 'left'){
26112 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26115 this.fireEvent("mouseover", this, e);
26118 onMouseOut : function(e)
26120 this.fireEvent("mouseout", this, e);
26132 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26135 * @class Roo.bootstrap.menu.Separator
26136 * @extends Roo.bootstrap.Component
26137 * Bootstrap Separator class
26140 * Create a new Separator
26141 * @param {Object} config The config object
26145 Roo.bootstrap.menu.Separator = function(config){
26146 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26149 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26151 getAutoCreate : function(){
26172 * @class Roo.bootstrap.Tooltip
26173 * Bootstrap Tooltip class
26174 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26175 * to determine which dom element triggers the tooltip.
26177 * It needs to add support for additional attributes like tooltip-position
26180 * Create a new Toolti
26181 * @param {Object} config The config object
26184 Roo.bootstrap.Tooltip = function(config){
26185 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26187 this.alignment = Roo.bootstrap.Tooltip.alignment;
26189 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26190 this.alignment = config.alignment;
26195 Roo.apply(Roo.bootstrap.Tooltip, {
26197 * @function init initialize tooltip monitoring.
26201 currentTip : false,
26202 currentRegion : false,
26208 Roo.get(document).on('mouseover', this.enter ,this);
26209 Roo.get(document).on('mouseout', this.leave, this);
26212 this.currentTip = new Roo.bootstrap.Tooltip();
26215 enter : function(ev)
26217 var dom = ev.getTarget();
26219 //Roo.log(['enter',dom]);
26220 var el = Roo.fly(dom);
26221 if (this.currentEl) {
26223 //Roo.log(this.currentEl);
26224 //Roo.log(this.currentEl.contains(dom));
26225 if (this.currentEl == el) {
26228 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26234 if (this.currentTip.el) {
26235 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26239 if(!el || el.dom == document){
26245 // you can not look for children, as if el is the body.. then everythign is the child..
26246 if (!el.attr('tooltip')) { //
26247 if (!el.select("[tooltip]").elements.length) {
26250 // is the mouse over this child...?
26251 bindEl = el.select("[tooltip]").first();
26252 var xy = ev.getXY();
26253 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26254 //Roo.log("not in region.");
26257 //Roo.log("child element over..");
26260 this.currentEl = bindEl;
26261 this.currentTip.bind(bindEl);
26262 this.currentRegion = Roo.lib.Region.getRegion(dom);
26263 this.currentTip.enter();
26266 leave : function(ev)
26268 var dom = ev.getTarget();
26269 //Roo.log(['leave',dom]);
26270 if (!this.currentEl) {
26275 if (dom != this.currentEl.dom) {
26278 var xy = ev.getXY();
26279 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26282 // only activate leave if mouse cursor is outside... bounding box..
26287 if (this.currentTip) {
26288 this.currentTip.leave();
26290 //Roo.log('clear currentEl');
26291 this.currentEl = false;
26296 'left' : ['r-l', [-2,0], 'right'],
26297 'right' : ['l-r', [2,0], 'left'],
26298 'bottom' : ['t-b', [0,2], 'top'],
26299 'top' : [ 'b-t', [0,-2], 'bottom']
26305 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26310 delay : null, // can be { show : 300 , hide: 500}
26314 hoverState : null, //???
26316 placement : 'bottom',
26320 getAutoCreate : function(){
26327 cls : 'tooltip-arrow'
26330 cls : 'tooltip-inner'
26337 bind : function(el)
26343 enter : function () {
26345 if (this.timeout != null) {
26346 clearTimeout(this.timeout);
26349 this.hoverState = 'in';
26350 //Roo.log("enter - show");
26351 if (!this.delay || !this.delay.show) {
26356 this.timeout = setTimeout(function () {
26357 if (_t.hoverState == 'in') {
26360 }, this.delay.show);
26364 clearTimeout(this.timeout);
26366 this.hoverState = 'out';
26367 if (!this.delay || !this.delay.hide) {
26373 this.timeout = setTimeout(function () {
26374 //Roo.log("leave - timeout");
26376 if (_t.hoverState == 'out') {
26378 Roo.bootstrap.Tooltip.currentEl = false;
26383 show : function (msg)
26386 this.render(document.body);
26389 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26391 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26393 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26395 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26397 var placement = typeof this.placement == 'function' ?
26398 this.placement.call(this, this.el, on_el) :
26401 var autoToken = /\s?auto?\s?/i;
26402 var autoPlace = autoToken.test(placement);
26404 placement = placement.replace(autoToken, '') || 'top';
26408 //this.el.setXY([0,0]);
26410 //this.el.dom.style.display='block';
26412 //this.el.appendTo(on_el);
26414 var p = this.getPosition();
26415 var box = this.el.getBox();
26421 var align = this.alignment[placement];
26423 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26425 if(placement == 'top' || placement == 'bottom'){
26427 placement = 'right';
26430 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26431 placement = 'left';
26434 var scroll = Roo.select('body', true).first().getScroll();
26436 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26440 align = this.alignment[placement];
26443 this.el.alignTo(this.bindEl, align[0],align[1]);
26444 //var arrow = this.el.select('.arrow',true).first();
26445 //arrow.set(align[2],
26447 this.el.addClass(placement);
26449 this.el.addClass('in fade');
26451 this.hoverState = null;
26453 if (this.el.hasClass('fade')) {
26464 //this.el.setXY([0,0]);
26465 this.el.removeClass('in');
26481 * @class Roo.bootstrap.LocationPicker
26482 * @extends Roo.bootstrap.Component
26483 * Bootstrap LocationPicker class
26484 * @cfg {Number} latitude Position when init default 0
26485 * @cfg {Number} longitude Position when init default 0
26486 * @cfg {Number} zoom default 15
26487 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26488 * @cfg {Boolean} mapTypeControl default false
26489 * @cfg {Boolean} disableDoubleClickZoom default false
26490 * @cfg {Boolean} scrollwheel default true
26491 * @cfg {Boolean} streetViewControl default false
26492 * @cfg {Number} radius default 0
26493 * @cfg {String} locationName
26494 * @cfg {Boolean} draggable default true
26495 * @cfg {Boolean} enableAutocomplete default false
26496 * @cfg {Boolean} enableReverseGeocode default true
26497 * @cfg {String} markerTitle
26500 * Create a new LocationPicker
26501 * @param {Object} config The config object
26505 Roo.bootstrap.LocationPicker = function(config){
26507 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26512 * Fires when the picker initialized.
26513 * @param {Roo.bootstrap.LocationPicker} this
26514 * @param {Google Location} location
26518 * @event positionchanged
26519 * Fires when the picker position changed.
26520 * @param {Roo.bootstrap.LocationPicker} this
26521 * @param {Google Location} location
26523 positionchanged : true,
26526 * Fires when the map resize.
26527 * @param {Roo.bootstrap.LocationPicker} this
26532 * Fires when the map show.
26533 * @param {Roo.bootstrap.LocationPicker} this
26538 * Fires when the map hide.
26539 * @param {Roo.bootstrap.LocationPicker} this
26544 * Fires when click the map.
26545 * @param {Roo.bootstrap.LocationPicker} this
26546 * @param {Map event} e
26550 * @event mapRightClick
26551 * Fires when right click the map.
26552 * @param {Roo.bootstrap.LocationPicker} this
26553 * @param {Map event} e
26555 mapRightClick : true,
26557 * @event markerClick
26558 * Fires when click the marker.
26559 * @param {Roo.bootstrap.LocationPicker} this
26560 * @param {Map event} e
26562 markerClick : true,
26564 * @event markerRightClick
26565 * Fires when right click the marker.
26566 * @param {Roo.bootstrap.LocationPicker} this
26567 * @param {Map event} e
26569 markerRightClick : true,
26571 * @event OverlayViewDraw
26572 * Fires when OverlayView Draw
26573 * @param {Roo.bootstrap.LocationPicker} this
26575 OverlayViewDraw : true,
26577 * @event OverlayViewOnAdd
26578 * Fires when OverlayView Draw
26579 * @param {Roo.bootstrap.LocationPicker} this
26581 OverlayViewOnAdd : true,
26583 * @event OverlayViewOnRemove
26584 * Fires when OverlayView Draw
26585 * @param {Roo.bootstrap.LocationPicker} this
26587 OverlayViewOnRemove : true,
26589 * @event OverlayViewShow
26590 * Fires when OverlayView Draw
26591 * @param {Roo.bootstrap.LocationPicker} this
26592 * @param {Pixel} cpx
26594 OverlayViewShow : true,
26596 * @event OverlayViewHide
26597 * Fires when OverlayView Draw
26598 * @param {Roo.bootstrap.LocationPicker} this
26600 OverlayViewHide : true,
26602 * @event loadexception
26603 * Fires when load google lib failed.
26604 * @param {Roo.bootstrap.LocationPicker} this
26606 loadexception : true
26611 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26613 gMapContext: false,
26619 mapTypeControl: false,
26620 disableDoubleClickZoom: false,
26622 streetViewControl: false,
26626 enableAutocomplete: false,
26627 enableReverseGeocode: true,
26630 getAutoCreate: function()
26635 cls: 'roo-location-picker'
26641 initEvents: function(ct, position)
26643 if(!this.el.getWidth() || this.isApplied()){
26647 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26652 initial: function()
26654 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26655 this.fireEvent('loadexception', this);
26659 if(!this.mapTypeId){
26660 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26663 this.gMapContext = this.GMapContext();
26665 this.initOverlayView();
26667 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26671 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26672 _this.setPosition(_this.gMapContext.marker.position);
26675 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26676 _this.fireEvent('mapClick', this, event);
26680 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26681 _this.fireEvent('mapRightClick', this, event);
26685 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26686 _this.fireEvent('markerClick', this, event);
26690 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26691 _this.fireEvent('markerRightClick', this, event);
26695 this.setPosition(this.gMapContext.location);
26697 this.fireEvent('initial', this, this.gMapContext.location);
26700 initOverlayView: function()
26704 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26708 _this.fireEvent('OverlayViewDraw', _this);
26713 _this.fireEvent('OverlayViewOnAdd', _this);
26716 onRemove: function()
26718 _this.fireEvent('OverlayViewOnRemove', _this);
26721 show: function(cpx)
26723 _this.fireEvent('OverlayViewShow', _this, cpx);
26728 _this.fireEvent('OverlayViewHide', _this);
26734 fromLatLngToContainerPixel: function(event)
26736 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26739 isApplied: function()
26741 return this.getGmapContext() == false ? false : true;
26744 getGmapContext: function()
26746 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26749 GMapContext: function()
26751 var position = new google.maps.LatLng(this.latitude, this.longitude);
26753 var _map = new google.maps.Map(this.el.dom, {
26756 mapTypeId: this.mapTypeId,
26757 mapTypeControl: this.mapTypeControl,
26758 disableDoubleClickZoom: this.disableDoubleClickZoom,
26759 scrollwheel: this.scrollwheel,
26760 streetViewControl: this.streetViewControl,
26761 locationName: this.locationName,
26762 draggable: this.draggable,
26763 enableAutocomplete: this.enableAutocomplete,
26764 enableReverseGeocode: this.enableReverseGeocode
26767 var _marker = new google.maps.Marker({
26768 position: position,
26770 title: this.markerTitle,
26771 draggable: this.draggable
26778 location: position,
26779 radius: this.radius,
26780 locationName: this.locationName,
26781 addressComponents: {
26782 formatted_address: null,
26783 addressLine1: null,
26784 addressLine2: null,
26786 streetNumber: null,
26790 stateOrProvince: null
26793 domContainer: this.el.dom,
26794 geodecoder: new google.maps.Geocoder()
26798 drawCircle: function(center, radius, options)
26800 if (this.gMapContext.circle != null) {
26801 this.gMapContext.circle.setMap(null);
26805 options = Roo.apply({}, options, {
26806 strokeColor: "#0000FF",
26807 strokeOpacity: .35,
26809 fillColor: "#0000FF",
26813 options.map = this.gMapContext.map;
26814 options.radius = radius;
26815 options.center = center;
26816 this.gMapContext.circle = new google.maps.Circle(options);
26817 return this.gMapContext.circle;
26823 setPosition: function(location)
26825 this.gMapContext.location = location;
26826 this.gMapContext.marker.setPosition(location);
26827 this.gMapContext.map.panTo(location);
26828 this.drawCircle(location, this.gMapContext.radius, {});
26832 if (this.gMapContext.settings.enableReverseGeocode) {
26833 this.gMapContext.geodecoder.geocode({
26834 latLng: this.gMapContext.location
26835 }, function(results, status) {
26837 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26838 _this.gMapContext.locationName = results[0].formatted_address;
26839 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26841 _this.fireEvent('positionchanged', this, location);
26848 this.fireEvent('positionchanged', this, location);
26853 google.maps.event.trigger(this.gMapContext.map, "resize");
26855 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26857 this.fireEvent('resize', this);
26860 setPositionByLatLng: function(latitude, longitude)
26862 this.setPosition(new google.maps.LatLng(latitude, longitude));
26865 getCurrentPosition: function()
26868 latitude: this.gMapContext.location.lat(),
26869 longitude: this.gMapContext.location.lng()
26873 getAddressName: function()
26875 return this.gMapContext.locationName;
26878 getAddressComponents: function()
26880 return this.gMapContext.addressComponents;
26883 address_component_from_google_geocode: function(address_components)
26887 for (var i = 0; i < address_components.length; i++) {
26888 var component = address_components[i];
26889 if (component.types.indexOf("postal_code") >= 0) {
26890 result.postalCode = component.short_name;
26891 } else if (component.types.indexOf("street_number") >= 0) {
26892 result.streetNumber = component.short_name;
26893 } else if (component.types.indexOf("route") >= 0) {
26894 result.streetName = component.short_name;
26895 } else if (component.types.indexOf("neighborhood") >= 0) {
26896 result.city = component.short_name;
26897 } else if (component.types.indexOf("locality") >= 0) {
26898 result.city = component.short_name;
26899 } else if (component.types.indexOf("sublocality") >= 0) {
26900 result.district = component.short_name;
26901 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26902 result.stateOrProvince = component.short_name;
26903 } else if (component.types.indexOf("country") >= 0) {
26904 result.country = component.short_name;
26908 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26909 result.addressLine2 = "";
26913 setZoomLevel: function(zoom)
26915 this.gMapContext.map.setZoom(zoom);
26928 this.fireEvent('show', this);
26939 this.fireEvent('hide', this);
26944 Roo.apply(Roo.bootstrap.LocationPicker, {
26946 OverlayView : function(map, options)
26948 options = options || {};
26962 * @class Roo.bootstrap.Alert
26963 * @extends Roo.bootstrap.Component
26964 * Bootstrap Alert class
26965 * @cfg {String} title The title of alert
26966 * @cfg {String} html The content of alert
26967 * @cfg {String} weight ( success | info | warning | danger )
26968 * @cfg {String} faicon font-awesomeicon
26971 * Create a new alert
26972 * @param {Object} config The config object
26976 Roo.bootstrap.Alert = function(config){
26977 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26981 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26988 getAutoCreate : function()
26997 cls : 'roo-alert-icon'
27002 cls : 'roo-alert-title',
27007 cls : 'roo-alert-text',
27014 cfg.cn[0].cls += ' fa ' + this.faicon;
27018 cfg.cls += ' alert-' + this.weight;
27024 initEvents: function()
27026 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27029 setTitle : function(str)
27031 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27034 setText : function(str)
27036 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27039 setWeight : function(weight)
27042 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27045 this.weight = weight;
27047 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27050 setIcon : function(icon)
27053 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27056 this.faicon = icon;
27058 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27079 * @class Roo.bootstrap.UploadCropbox
27080 * @extends Roo.bootstrap.Component
27081 * Bootstrap UploadCropbox class
27082 * @cfg {String} emptyText show when image has been loaded
27083 * @cfg {String} rotateNotify show when image too small to rotate
27084 * @cfg {Number} errorTimeout default 3000
27085 * @cfg {Number} minWidth default 300
27086 * @cfg {Number} minHeight default 300
27087 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27088 * @cfg {Boolean} isDocument (true|false) default false
27089 * @cfg {String} url action url
27090 * @cfg {String} paramName default 'imageUpload'
27091 * @cfg {String} method default POST
27092 * @cfg {Boolean} loadMask (true|false) default true
27093 * @cfg {Boolean} loadingText default 'Loading...'
27096 * Create a new UploadCropbox
27097 * @param {Object} config The config object
27100 Roo.bootstrap.UploadCropbox = function(config){
27101 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27105 * @event beforeselectfile
27106 * Fire before select file
27107 * @param {Roo.bootstrap.UploadCropbox} this
27109 "beforeselectfile" : true,
27112 * Fire after initEvent
27113 * @param {Roo.bootstrap.UploadCropbox} this
27118 * Fire after initEvent
27119 * @param {Roo.bootstrap.UploadCropbox} this
27120 * @param {String} data
27125 * Fire when preparing the file data
27126 * @param {Roo.bootstrap.UploadCropbox} this
27127 * @param {Object} file
27132 * Fire when get exception
27133 * @param {Roo.bootstrap.UploadCropbox} this
27134 * @param {XMLHttpRequest} xhr
27136 "exception" : true,
27138 * @event beforeloadcanvas
27139 * Fire before load the canvas
27140 * @param {Roo.bootstrap.UploadCropbox} this
27141 * @param {String} src
27143 "beforeloadcanvas" : true,
27146 * Fire when trash image
27147 * @param {Roo.bootstrap.UploadCropbox} this
27152 * Fire when download the image
27153 * @param {Roo.bootstrap.UploadCropbox} this
27157 * @event footerbuttonclick
27158 * Fire when footerbuttonclick
27159 * @param {Roo.bootstrap.UploadCropbox} this
27160 * @param {String} type
27162 "footerbuttonclick" : true,
27166 * @param {Roo.bootstrap.UploadCropbox} this
27171 * Fire when rotate the image
27172 * @param {Roo.bootstrap.UploadCropbox} this
27173 * @param {String} pos
27178 * Fire when inspect the file
27179 * @param {Roo.bootstrap.UploadCropbox} this
27180 * @param {Object} file
27185 * Fire when xhr upload the file
27186 * @param {Roo.bootstrap.UploadCropbox} this
27187 * @param {Object} data
27192 * Fire when arrange the file data
27193 * @param {Roo.bootstrap.UploadCropbox} this
27194 * @param {Object} formData
27199 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27202 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27204 emptyText : 'Click to upload image',
27205 rotateNotify : 'Image is too small to rotate',
27206 errorTimeout : 3000,
27220 cropType : 'image/jpeg',
27222 canvasLoaded : false,
27223 isDocument : false,
27225 paramName : 'imageUpload',
27227 loadingText : 'Loading...',
27230 getAutoCreate : function()
27234 cls : 'roo-upload-cropbox',
27238 cls : 'roo-upload-cropbox-selector',
27243 cls : 'roo-upload-cropbox-body',
27244 style : 'cursor:pointer',
27248 cls : 'roo-upload-cropbox-preview'
27252 cls : 'roo-upload-cropbox-thumb'
27256 cls : 'roo-upload-cropbox-empty-notify',
27257 html : this.emptyText
27261 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27262 html : this.rotateNotify
27268 cls : 'roo-upload-cropbox-footer',
27271 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27281 onRender : function(ct, position)
27283 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27285 if (this.buttons.length) {
27287 Roo.each(this.buttons, function(bb) {
27289 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27291 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27297 this.maskEl = this.el;
27301 initEvents : function()
27303 this.urlAPI = (window.createObjectURL && window) ||
27304 (window.URL && URL.revokeObjectURL && URL) ||
27305 (window.webkitURL && webkitURL);
27307 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27308 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27310 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27311 this.selectorEl.hide();
27313 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27314 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27316 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27317 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27318 this.thumbEl.hide();
27320 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27321 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27323 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27324 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27325 this.errorEl.hide();
27327 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27328 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27329 this.footerEl.hide();
27331 this.setThumbBoxSize();
27337 this.fireEvent('initial', this);
27344 window.addEventListener("resize", function() { _this.resize(); } );
27346 this.bodyEl.on('click', this.beforeSelectFile, this);
27349 this.bodyEl.on('touchstart', this.onTouchStart, this);
27350 this.bodyEl.on('touchmove', this.onTouchMove, this);
27351 this.bodyEl.on('touchend', this.onTouchEnd, this);
27355 this.bodyEl.on('mousedown', this.onMouseDown, this);
27356 this.bodyEl.on('mousemove', this.onMouseMove, this);
27357 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27358 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27359 Roo.get(document).on('mouseup', this.onMouseUp, this);
27362 this.selectorEl.on('change', this.onFileSelected, this);
27368 this.baseScale = 1;
27370 this.baseRotate = 1;
27371 this.dragable = false;
27372 this.pinching = false;
27375 this.cropData = false;
27376 this.notifyEl.dom.innerHTML = this.emptyText;
27378 this.selectorEl.dom.value = '';
27382 resize : function()
27384 if(this.fireEvent('resize', this) != false){
27385 this.setThumbBoxPosition();
27386 this.setCanvasPosition();
27390 onFooterButtonClick : function(e, el, o, type)
27393 case 'rotate-left' :
27394 this.onRotateLeft(e);
27396 case 'rotate-right' :
27397 this.onRotateRight(e);
27400 this.beforeSelectFile(e);
27415 this.fireEvent('footerbuttonclick', this, type);
27418 beforeSelectFile : function(e)
27420 e.preventDefault();
27422 if(this.fireEvent('beforeselectfile', this) != false){
27423 this.selectorEl.dom.click();
27427 onFileSelected : function(e)
27429 e.preventDefault();
27431 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27435 var file = this.selectorEl.dom.files[0];
27437 if(this.fireEvent('inspect', this, file) != false){
27438 this.prepare(file);
27443 trash : function(e)
27445 this.fireEvent('trash', this);
27448 download : function(e)
27450 this.fireEvent('download', this);
27453 loadCanvas : function(src)
27455 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27459 this.imageEl = document.createElement('img');
27463 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27465 this.imageEl.src = src;
27469 onLoadCanvas : function()
27471 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27472 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27474 this.bodyEl.un('click', this.beforeSelectFile, this);
27476 this.notifyEl.hide();
27477 this.thumbEl.show();
27478 this.footerEl.show();
27480 this.baseRotateLevel();
27482 if(this.isDocument){
27483 this.setThumbBoxSize();
27486 this.setThumbBoxPosition();
27488 this.baseScaleLevel();
27494 this.canvasLoaded = true;
27497 this.maskEl.unmask();
27502 setCanvasPosition : function()
27504 if(!this.canvasEl){
27508 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27509 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27511 this.previewEl.setLeft(pw);
27512 this.previewEl.setTop(ph);
27516 onMouseDown : function(e)
27520 this.dragable = true;
27521 this.pinching = false;
27523 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27524 this.dragable = false;
27528 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27529 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27533 onMouseMove : function(e)
27537 if(!this.canvasLoaded){
27541 if (!this.dragable){
27545 var minX = Math.ceil(this.thumbEl.getLeft(true));
27546 var minY = Math.ceil(this.thumbEl.getTop(true));
27548 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27549 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27551 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27552 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27554 x = x - this.mouseX;
27555 y = y - this.mouseY;
27557 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27558 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27560 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27561 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27563 this.previewEl.setLeft(bgX);
27564 this.previewEl.setTop(bgY);
27566 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27567 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27570 onMouseUp : function(e)
27574 this.dragable = false;
27577 onMouseWheel : function(e)
27581 this.startScale = this.scale;
27583 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27585 if(!this.zoomable()){
27586 this.scale = this.startScale;
27595 zoomable : function()
27597 var minScale = this.thumbEl.getWidth() / this.minWidth;
27599 if(this.minWidth < this.minHeight){
27600 minScale = this.thumbEl.getHeight() / this.minHeight;
27603 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27604 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27608 (this.rotate == 0 || this.rotate == 180) &&
27610 width > this.imageEl.OriginWidth ||
27611 height > this.imageEl.OriginHeight ||
27612 (width < this.minWidth && height < this.minHeight)
27620 (this.rotate == 90 || this.rotate == 270) &&
27622 width > this.imageEl.OriginWidth ||
27623 height > this.imageEl.OriginHeight ||
27624 (width < this.minHeight && height < this.minWidth)
27631 !this.isDocument &&
27632 (this.rotate == 0 || this.rotate == 180) &&
27634 width < this.minWidth ||
27635 width > this.imageEl.OriginWidth ||
27636 height < this.minHeight ||
27637 height > this.imageEl.OriginHeight
27644 !this.isDocument &&
27645 (this.rotate == 90 || this.rotate == 270) &&
27647 width < this.minHeight ||
27648 width > this.imageEl.OriginWidth ||
27649 height < this.minWidth ||
27650 height > this.imageEl.OriginHeight
27660 onRotateLeft : function(e)
27662 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27664 var minScale = this.thumbEl.getWidth() / this.minWidth;
27666 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27667 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27669 this.startScale = this.scale;
27671 while (this.getScaleLevel() < minScale){
27673 this.scale = this.scale + 1;
27675 if(!this.zoomable()){
27680 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27681 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27686 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27693 this.scale = this.startScale;
27695 this.onRotateFail();
27700 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27702 if(this.isDocument){
27703 this.setThumbBoxSize();
27704 this.setThumbBoxPosition();
27705 this.setCanvasPosition();
27710 this.fireEvent('rotate', this, 'left');
27714 onRotateRight : function(e)
27716 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27718 var minScale = this.thumbEl.getWidth() / this.minWidth;
27720 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27721 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27723 this.startScale = this.scale;
27725 while (this.getScaleLevel() < minScale){
27727 this.scale = this.scale + 1;
27729 if(!this.zoomable()){
27734 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27735 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27740 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27747 this.scale = this.startScale;
27749 this.onRotateFail();
27754 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27756 if(this.isDocument){
27757 this.setThumbBoxSize();
27758 this.setThumbBoxPosition();
27759 this.setCanvasPosition();
27764 this.fireEvent('rotate', this, 'right');
27767 onRotateFail : function()
27769 this.errorEl.show(true);
27773 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27778 this.previewEl.dom.innerHTML = '';
27780 var canvasEl = document.createElement("canvas");
27782 var contextEl = canvasEl.getContext("2d");
27784 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27785 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27786 var center = this.imageEl.OriginWidth / 2;
27788 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27789 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27790 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27791 center = this.imageEl.OriginHeight / 2;
27794 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27796 contextEl.translate(center, center);
27797 contextEl.rotate(this.rotate * Math.PI / 180);
27799 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27801 this.canvasEl = document.createElement("canvas");
27803 this.contextEl = this.canvasEl.getContext("2d");
27805 switch (this.rotate) {
27808 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27809 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27811 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27816 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27817 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27819 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27820 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);
27824 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27829 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27830 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27832 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27833 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.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);
27842 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27843 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27845 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27846 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27850 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);
27857 this.previewEl.appendChild(this.canvasEl);
27859 this.setCanvasPosition();
27864 if(!this.canvasLoaded){
27868 var imageCanvas = document.createElement("canvas");
27870 var imageContext = imageCanvas.getContext("2d");
27872 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27873 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27875 var center = imageCanvas.width / 2;
27877 imageContext.translate(center, center);
27879 imageContext.rotate(this.rotate * Math.PI / 180);
27881 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27883 var canvas = document.createElement("canvas");
27885 var context = canvas.getContext("2d");
27887 canvas.width = this.minWidth;
27888 canvas.height = this.minHeight;
27890 switch (this.rotate) {
27893 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27894 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27896 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27897 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27899 var targetWidth = this.minWidth - 2 * x;
27900 var targetHeight = this.minHeight - 2 * y;
27904 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27905 scale = targetWidth / width;
27908 if(x > 0 && y == 0){
27909 scale = targetHeight / height;
27912 if(x > 0 && y > 0){
27913 scale = targetWidth / width;
27915 if(width < height){
27916 scale = targetHeight / height;
27920 context.scale(scale, scale);
27922 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27923 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27925 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27926 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27928 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27933 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27934 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27936 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27937 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27939 var targetWidth = this.minWidth - 2 * x;
27940 var targetHeight = this.minHeight - 2 * y;
27944 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27945 scale = targetWidth / width;
27948 if(x > 0 && y == 0){
27949 scale = targetHeight / height;
27952 if(x > 0 && y > 0){
27953 scale = targetWidth / width;
27955 if(width < height){
27956 scale = targetHeight / height;
27960 context.scale(scale, scale);
27962 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27963 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27965 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27966 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27968 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27970 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27975 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27976 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27978 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27979 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27981 var targetWidth = this.minWidth - 2 * x;
27982 var targetHeight = this.minHeight - 2 * y;
27986 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27987 scale = targetWidth / width;
27990 if(x > 0 && y == 0){
27991 scale = targetHeight / height;
27994 if(x > 0 && y > 0){
27995 scale = targetWidth / width;
27997 if(width < height){
27998 scale = targetHeight / height;
28002 context.scale(scale, scale);
28004 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28005 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28007 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28008 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28010 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28011 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28013 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28018 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28019 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28021 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28022 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28024 var targetWidth = this.minWidth - 2 * x;
28025 var targetHeight = this.minHeight - 2 * y;
28029 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28030 scale = targetWidth / width;
28033 if(x > 0 && y == 0){
28034 scale = targetHeight / height;
28037 if(x > 0 && y > 0){
28038 scale = targetWidth / width;
28040 if(width < height){
28041 scale = targetHeight / height;
28045 context.scale(scale, scale);
28047 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28048 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28050 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28051 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28053 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28055 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28062 this.cropData = canvas.toDataURL(this.cropType);
28064 if(this.fireEvent('crop', this, this.cropData) !== false){
28065 this.process(this.file, this.cropData);
28072 setThumbBoxSize : function()
28076 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28077 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28078 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28080 this.minWidth = width;
28081 this.minHeight = height;
28083 if(this.rotate == 90 || this.rotate == 270){
28084 this.minWidth = height;
28085 this.minHeight = width;
28090 width = Math.ceil(this.minWidth * height / this.minHeight);
28092 if(this.minWidth > this.minHeight){
28094 height = Math.ceil(this.minHeight * width / this.minWidth);
28097 this.thumbEl.setStyle({
28098 width : width + 'px',
28099 height : height + 'px'
28106 setThumbBoxPosition : function()
28108 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28109 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28111 this.thumbEl.setLeft(x);
28112 this.thumbEl.setTop(y);
28116 baseRotateLevel : function()
28118 this.baseRotate = 1;
28121 typeof(this.exif) != 'undefined' &&
28122 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28123 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28125 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28128 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28132 baseScaleLevel : function()
28136 if(this.isDocument){
28138 if(this.baseRotate == 6 || this.baseRotate == 8){
28140 height = this.thumbEl.getHeight();
28141 this.baseScale = height / this.imageEl.OriginWidth;
28143 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28144 width = this.thumbEl.getWidth();
28145 this.baseScale = width / this.imageEl.OriginHeight;
28151 height = this.thumbEl.getHeight();
28152 this.baseScale = height / this.imageEl.OriginHeight;
28154 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28155 width = this.thumbEl.getWidth();
28156 this.baseScale = width / this.imageEl.OriginWidth;
28162 if(this.baseRotate == 6 || this.baseRotate == 8){
28164 width = this.thumbEl.getHeight();
28165 this.baseScale = width / this.imageEl.OriginHeight;
28167 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28168 height = this.thumbEl.getWidth();
28169 this.baseScale = height / this.imageEl.OriginHeight;
28172 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28173 height = this.thumbEl.getWidth();
28174 this.baseScale = height / this.imageEl.OriginHeight;
28176 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28177 width = this.thumbEl.getHeight();
28178 this.baseScale = width / this.imageEl.OriginWidth;
28185 width = this.thumbEl.getWidth();
28186 this.baseScale = width / this.imageEl.OriginWidth;
28188 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28189 height = this.thumbEl.getHeight();
28190 this.baseScale = height / this.imageEl.OriginHeight;
28193 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28195 height = this.thumbEl.getHeight();
28196 this.baseScale = height / this.imageEl.OriginHeight;
28198 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28199 width = this.thumbEl.getWidth();
28200 this.baseScale = width / this.imageEl.OriginWidth;
28208 getScaleLevel : function()
28210 return this.baseScale * Math.pow(1.1, this.scale);
28213 onTouchStart : function(e)
28215 if(!this.canvasLoaded){
28216 this.beforeSelectFile(e);
28220 var touches = e.browserEvent.touches;
28226 if(touches.length == 1){
28227 this.onMouseDown(e);
28231 if(touches.length != 2){
28237 for(var i = 0, finger; finger = touches[i]; i++){
28238 coords.push(finger.pageX, finger.pageY);
28241 var x = Math.pow(coords[0] - coords[2], 2);
28242 var y = Math.pow(coords[1] - coords[3], 2);
28244 this.startDistance = Math.sqrt(x + y);
28246 this.startScale = this.scale;
28248 this.pinching = true;
28249 this.dragable = false;
28253 onTouchMove : function(e)
28255 if(!this.pinching && !this.dragable){
28259 var touches = e.browserEvent.touches;
28266 this.onMouseMove(e);
28272 for(var i = 0, finger; finger = touches[i]; i++){
28273 coords.push(finger.pageX, finger.pageY);
28276 var x = Math.pow(coords[0] - coords[2], 2);
28277 var y = Math.pow(coords[1] - coords[3], 2);
28279 this.endDistance = Math.sqrt(x + y);
28281 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28283 if(!this.zoomable()){
28284 this.scale = this.startScale;
28292 onTouchEnd : function(e)
28294 this.pinching = false;
28295 this.dragable = false;
28299 process : function(file, crop)
28302 this.maskEl.mask(this.loadingText);
28305 this.xhr = new XMLHttpRequest();
28307 file.xhr = this.xhr;
28309 this.xhr.open(this.method, this.url, true);
28312 "Accept": "application/json",
28313 "Cache-Control": "no-cache",
28314 "X-Requested-With": "XMLHttpRequest"
28317 for (var headerName in headers) {
28318 var headerValue = headers[headerName];
28320 this.xhr.setRequestHeader(headerName, headerValue);
28326 this.xhr.onload = function()
28328 _this.xhrOnLoad(_this.xhr);
28331 this.xhr.onerror = function()
28333 _this.xhrOnError(_this.xhr);
28336 var formData = new FormData();
28338 formData.append('returnHTML', 'NO');
28341 formData.append('crop', crop);
28344 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28345 formData.append(this.paramName, file, file.name);
28348 if(typeof(file.filename) != 'undefined'){
28349 formData.append('filename', file.filename);
28352 if(typeof(file.mimetype) != 'undefined'){
28353 formData.append('mimetype', file.mimetype);
28356 if(this.fireEvent('arrange', this, formData) != false){
28357 this.xhr.send(formData);
28361 xhrOnLoad : function(xhr)
28364 this.maskEl.unmask();
28367 if (xhr.readyState !== 4) {
28368 this.fireEvent('exception', this, xhr);
28372 var response = Roo.decode(xhr.responseText);
28374 if(!response.success){
28375 this.fireEvent('exception', this, xhr);
28379 var response = Roo.decode(xhr.responseText);
28381 this.fireEvent('upload', this, response);
28385 xhrOnError : function()
28388 this.maskEl.unmask();
28391 Roo.log('xhr on error');
28393 var response = Roo.decode(xhr.responseText);
28399 prepare : function(file)
28402 this.maskEl.mask(this.loadingText);
28408 if(typeof(file) === 'string'){
28409 this.loadCanvas(file);
28413 if(!file || !this.urlAPI){
28418 this.cropType = file.type;
28422 if(this.fireEvent('prepare', this, this.file) != false){
28424 var reader = new FileReader();
28426 reader.onload = function (e) {
28427 if (e.target.error) {
28428 Roo.log(e.target.error);
28432 var buffer = e.target.result,
28433 dataView = new DataView(buffer),
28435 maxOffset = dataView.byteLength - 4,
28439 if (dataView.getUint16(0) === 0xffd8) {
28440 while (offset < maxOffset) {
28441 markerBytes = dataView.getUint16(offset);
28443 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28444 markerLength = dataView.getUint16(offset + 2) + 2;
28445 if (offset + markerLength > dataView.byteLength) {
28446 Roo.log('Invalid meta data: Invalid segment size.');
28450 if(markerBytes == 0xffe1){
28451 _this.parseExifData(
28458 offset += markerLength;
28468 var url = _this.urlAPI.createObjectURL(_this.file);
28470 _this.loadCanvas(url);
28475 reader.readAsArrayBuffer(this.file);
28481 parseExifData : function(dataView, offset, length)
28483 var tiffOffset = offset + 10,
28487 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28488 // No Exif data, might be XMP data instead
28492 // Check for the ASCII code for "Exif" (0x45786966):
28493 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28494 // No Exif data, might be XMP data instead
28497 if (tiffOffset + 8 > dataView.byteLength) {
28498 Roo.log('Invalid Exif data: Invalid segment size.');
28501 // Check for the two null bytes:
28502 if (dataView.getUint16(offset + 8) !== 0x0000) {
28503 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28506 // Check the byte alignment:
28507 switch (dataView.getUint16(tiffOffset)) {
28509 littleEndian = true;
28512 littleEndian = false;
28515 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28518 // Check for the TIFF tag marker (0x002A):
28519 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28520 Roo.log('Invalid Exif data: Missing TIFF marker.');
28523 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28524 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28526 this.parseExifTags(
28529 tiffOffset + dirOffset,
28534 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28539 if (dirOffset + 6 > dataView.byteLength) {
28540 Roo.log('Invalid Exif data: Invalid directory offset.');
28543 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28544 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28545 if (dirEndOffset + 4 > dataView.byteLength) {
28546 Roo.log('Invalid Exif data: Invalid directory size.');
28549 for (i = 0; i < tagsNumber; i += 1) {
28553 dirOffset + 2 + 12 * i, // tag offset
28557 // Return the offset to the next directory:
28558 return dataView.getUint32(dirEndOffset, littleEndian);
28561 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28563 var tag = dataView.getUint16(offset, littleEndian);
28565 this.exif[tag] = this.getExifValue(
28569 dataView.getUint16(offset + 2, littleEndian), // tag type
28570 dataView.getUint32(offset + 4, littleEndian), // tag length
28575 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28577 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28586 Roo.log('Invalid Exif data: Invalid tag type.');
28590 tagSize = tagType.size * length;
28591 // Determine if the value is contained in the dataOffset bytes,
28592 // or if the value at the dataOffset is a pointer to the actual data:
28593 dataOffset = tagSize > 4 ?
28594 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28595 if (dataOffset + tagSize > dataView.byteLength) {
28596 Roo.log('Invalid Exif data: Invalid data offset.');
28599 if (length === 1) {
28600 return tagType.getValue(dataView, dataOffset, littleEndian);
28603 for (i = 0; i < length; i += 1) {
28604 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28607 if (tagType.ascii) {
28609 // Concatenate the chars:
28610 for (i = 0; i < values.length; i += 1) {
28612 // Ignore the terminating NULL byte(s):
28613 if (c === '\u0000') {
28625 Roo.apply(Roo.bootstrap.UploadCropbox, {
28627 'Orientation': 0x0112
28631 1: 0, //'top-left',
28633 3: 180, //'bottom-right',
28634 // 4: 'bottom-left',
28636 6: 90, //'right-top',
28637 // 7: 'right-bottom',
28638 8: 270 //'left-bottom'
28642 // byte, 8-bit unsigned int:
28644 getValue: function (dataView, dataOffset) {
28645 return dataView.getUint8(dataOffset);
28649 // ascii, 8-bit byte:
28651 getValue: function (dataView, dataOffset) {
28652 return String.fromCharCode(dataView.getUint8(dataOffset));
28657 // short, 16 bit int:
28659 getValue: function (dataView, dataOffset, littleEndian) {
28660 return dataView.getUint16(dataOffset, littleEndian);
28664 // long, 32 bit int:
28666 getValue: function (dataView, dataOffset, littleEndian) {
28667 return dataView.getUint32(dataOffset, littleEndian);
28671 // rational = two long values, first is numerator, second is denominator:
28673 getValue: function (dataView, dataOffset, littleEndian) {
28674 return dataView.getUint32(dataOffset, littleEndian) /
28675 dataView.getUint32(dataOffset + 4, littleEndian);
28679 // slong, 32 bit signed int:
28681 getValue: function (dataView, dataOffset, littleEndian) {
28682 return dataView.getInt32(dataOffset, littleEndian);
28686 // srational, two slongs, first is numerator, second is denominator:
28688 getValue: function (dataView, dataOffset, littleEndian) {
28689 return dataView.getInt32(dataOffset, littleEndian) /
28690 dataView.getInt32(dataOffset + 4, littleEndian);
28700 cls : 'btn-group roo-upload-cropbox-rotate-left',
28701 action : 'rotate-left',
28705 cls : 'btn btn-default',
28706 html : '<i class="fa fa-undo"></i>'
28712 cls : 'btn-group roo-upload-cropbox-picture',
28713 action : 'picture',
28717 cls : 'btn btn-default',
28718 html : '<i class="fa fa-picture-o"></i>'
28724 cls : 'btn-group roo-upload-cropbox-rotate-right',
28725 action : 'rotate-right',
28729 cls : 'btn btn-default',
28730 html : '<i class="fa fa-repeat"></i>'
28738 cls : 'btn-group roo-upload-cropbox-rotate-left',
28739 action : 'rotate-left',
28743 cls : 'btn btn-default',
28744 html : '<i class="fa fa-undo"></i>'
28750 cls : 'btn-group roo-upload-cropbox-download',
28751 action : 'download',
28755 cls : 'btn btn-default',
28756 html : '<i class="fa fa-download"></i>'
28762 cls : 'btn-group roo-upload-cropbox-crop',
28767 cls : 'btn btn-default',
28768 html : '<i class="fa fa-crop"></i>'
28774 cls : 'btn-group roo-upload-cropbox-trash',
28779 cls : 'btn btn-default',
28780 html : '<i class="fa fa-trash"></i>'
28786 cls : 'btn-group roo-upload-cropbox-rotate-right',
28787 action : 'rotate-right',
28791 cls : 'btn btn-default',
28792 html : '<i class="fa fa-repeat"></i>'
28800 cls : 'btn-group roo-upload-cropbox-rotate-left',
28801 action : 'rotate-left',
28805 cls : 'btn btn-default',
28806 html : '<i class="fa fa-undo"></i>'
28812 cls : 'btn-group roo-upload-cropbox-rotate-right',
28813 action : 'rotate-right',
28817 cls : 'btn btn-default',
28818 html : '<i class="fa fa-repeat"></i>'
28831 * @class Roo.bootstrap.DocumentManager
28832 * @extends Roo.bootstrap.Component
28833 * Bootstrap DocumentManager class
28834 * @cfg {String} paramName default 'imageUpload'
28835 * @cfg {String} toolTipName default 'filename'
28836 * @cfg {String} method default POST
28837 * @cfg {String} url action url
28838 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28839 * @cfg {Boolean} multiple multiple upload default true
28840 * @cfg {Number} thumbSize default 300
28841 * @cfg {String} fieldLabel
28842 * @cfg {Number} labelWidth default 4
28843 * @cfg {String} labelAlign (left|top) default left
28844 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28845 * @cfg {Number} labellg set the width of label (1-12)
28846 * @cfg {Number} labelmd set the width of label (1-12)
28847 * @cfg {Number} labelsm set the width of label (1-12)
28848 * @cfg {Number} labelxs set the width of label (1-12)
28851 * Create a new DocumentManager
28852 * @param {Object} config The config object
28855 Roo.bootstrap.DocumentManager = function(config){
28856 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28859 this.delegates = [];
28864 * Fire when initial the DocumentManager
28865 * @param {Roo.bootstrap.DocumentManager} this
28870 * inspect selected file
28871 * @param {Roo.bootstrap.DocumentManager} this
28872 * @param {File} file
28877 * Fire when xhr load exception
28878 * @param {Roo.bootstrap.DocumentManager} this
28879 * @param {XMLHttpRequest} xhr
28881 "exception" : true,
28883 * @event afterupload
28884 * Fire when xhr load exception
28885 * @param {Roo.bootstrap.DocumentManager} this
28886 * @param {XMLHttpRequest} xhr
28888 "afterupload" : true,
28891 * prepare the form data
28892 * @param {Roo.bootstrap.DocumentManager} this
28893 * @param {Object} formData
28898 * Fire when remove the file
28899 * @param {Roo.bootstrap.DocumentManager} this
28900 * @param {Object} file
28905 * Fire after refresh the file
28906 * @param {Roo.bootstrap.DocumentManager} this
28911 * Fire after click the image
28912 * @param {Roo.bootstrap.DocumentManager} this
28913 * @param {Object} file
28918 * Fire when upload a image and editable set to true
28919 * @param {Roo.bootstrap.DocumentManager} this
28920 * @param {Object} file
28924 * @event beforeselectfile
28925 * Fire before select file
28926 * @param {Roo.bootstrap.DocumentManager} this
28928 "beforeselectfile" : true,
28931 * Fire before process file
28932 * @param {Roo.bootstrap.DocumentManager} this
28933 * @param {Object} file
28937 * @event previewrendered
28938 * Fire when preview rendered
28939 * @param {Roo.bootstrap.DocumentManager} this
28940 * @param {Object} file
28942 "previewrendered" : true,
28945 "previewResize" : true
28950 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28959 paramName : 'imageUpload',
28960 toolTipName : 'filename',
28963 labelAlign : 'left',
28973 getAutoCreate : function()
28975 var managerWidget = {
28977 cls : 'roo-document-manager',
28981 cls : 'roo-document-manager-selector',
28986 cls : 'roo-document-manager-uploader',
28990 cls : 'roo-document-manager-upload-btn',
28991 html : '<i class="fa fa-plus"></i>'
29002 cls : 'column col-md-12',
29007 if(this.fieldLabel.length){
29012 cls : 'column col-md-12',
29013 html : this.fieldLabel
29017 cls : 'column col-md-12',
29022 if(this.labelAlign == 'left'){
29027 html : this.fieldLabel
29036 if(this.labelWidth > 12){
29037 content[0].style = "width: " + this.labelWidth + 'px';
29040 if(this.labelWidth < 13 && this.labelmd == 0){
29041 this.labelmd = this.labelWidth;
29044 if(this.labellg > 0){
29045 content[0].cls += ' col-lg-' + this.labellg;
29046 content[1].cls += ' col-lg-' + (12 - this.labellg);
29049 if(this.labelmd > 0){
29050 content[0].cls += ' col-md-' + this.labelmd;
29051 content[1].cls += ' col-md-' + (12 - this.labelmd);
29054 if(this.labelsm > 0){
29055 content[0].cls += ' col-sm-' + this.labelsm;
29056 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29059 if(this.labelxs > 0){
29060 content[0].cls += ' col-xs-' + this.labelxs;
29061 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29069 cls : 'row clearfix',
29077 initEvents : function()
29079 this.managerEl = this.el.select('.roo-document-manager', true).first();
29080 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29082 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29083 this.selectorEl.hide();
29086 this.selectorEl.attr('multiple', 'multiple');
29089 this.selectorEl.on('change', this.onFileSelected, this);
29091 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29092 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29094 this.uploader.on('click', this.onUploaderClick, this);
29096 this.renderProgressDialog();
29100 window.addEventListener("resize", function() { _this.refresh(); } );
29102 this.fireEvent('initial', this);
29105 renderProgressDialog : function()
29109 this.progressDialog = new Roo.bootstrap.Modal({
29110 cls : 'roo-document-manager-progress-dialog',
29111 allow_close : false,
29121 btnclick : function() {
29122 _this.uploadCancel();
29128 this.progressDialog.render(Roo.get(document.body));
29130 this.progress = new Roo.bootstrap.Progress({
29131 cls : 'roo-document-manager-progress',
29136 this.progress.render(this.progressDialog.getChildContainer());
29138 this.progressBar = new Roo.bootstrap.ProgressBar({
29139 cls : 'roo-document-manager-progress-bar',
29142 aria_valuemax : 12,
29146 this.progressBar.render(this.progress.getChildContainer());
29149 onUploaderClick : function(e)
29151 e.preventDefault();
29153 if(this.fireEvent('beforeselectfile', this) != false){
29154 this.selectorEl.dom.click();
29159 onFileSelected : function(e)
29161 e.preventDefault();
29163 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29167 Roo.each(this.selectorEl.dom.files, function(file){
29168 if(this.fireEvent('inspect', this, file) != false){
29169 this.files.push(file);
29179 this.selectorEl.dom.value = '';
29181 if(!this.files || !this.files.length){
29185 if(this.boxes > 0 && this.files.length > this.boxes){
29186 this.files = this.files.slice(0, this.boxes);
29189 this.uploader.show();
29191 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29192 this.uploader.hide();
29201 Roo.each(this.files, function(file){
29203 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29204 var f = this.renderPreview(file);
29209 if(file.type.indexOf('image') != -1){
29210 this.delegates.push(
29212 _this.process(file);
29213 }).createDelegate(this)
29221 _this.process(file);
29222 }).createDelegate(this)
29227 this.files = files;
29229 this.delegates = this.delegates.concat(docs);
29231 if(!this.delegates.length){
29236 this.progressBar.aria_valuemax = this.delegates.length;
29243 arrange : function()
29245 if(!this.delegates.length){
29246 this.progressDialog.hide();
29251 var delegate = this.delegates.shift();
29253 this.progressDialog.show();
29255 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29257 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29262 refresh : function()
29264 this.uploader.show();
29266 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29267 this.uploader.hide();
29270 Roo.isTouch ? this.closable(false) : this.closable(true);
29272 this.fireEvent('refresh', this);
29275 onRemove : function(e, el, o)
29277 e.preventDefault();
29279 this.fireEvent('remove', this, o);
29283 remove : function(o)
29287 Roo.each(this.files, function(file){
29288 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29297 this.files = files;
29304 Roo.each(this.files, function(file){
29309 file.target.remove();
29318 onClick : function(e, el, o)
29320 e.preventDefault();
29322 this.fireEvent('click', this, o);
29326 closable : function(closable)
29328 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29330 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29342 xhrOnLoad : function(xhr)
29344 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29348 if (xhr.readyState !== 4) {
29350 this.fireEvent('exception', this, xhr);
29354 var response = Roo.decode(xhr.responseText);
29356 if(!response.success){
29358 this.fireEvent('exception', this, xhr);
29362 var file = this.renderPreview(response.data);
29364 this.files.push(file);
29368 this.fireEvent('afterupload', this, xhr);
29372 xhrOnError : function(xhr)
29374 Roo.log('xhr on error');
29376 var response = Roo.decode(xhr.responseText);
29383 process : function(file)
29385 if(this.fireEvent('process', this, file) !== false){
29386 if(this.editable && file.type.indexOf('image') != -1){
29387 this.fireEvent('edit', this, file);
29391 this.uploadStart(file, false);
29398 uploadStart : function(file, crop)
29400 this.xhr = new XMLHttpRequest();
29402 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29407 file.xhr = this.xhr;
29409 this.managerEl.createChild({
29411 cls : 'roo-document-manager-loading',
29415 tooltip : file.name,
29416 cls : 'roo-document-manager-thumb',
29417 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29423 this.xhr.open(this.method, this.url, true);
29426 "Accept": "application/json",
29427 "Cache-Control": "no-cache",
29428 "X-Requested-With": "XMLHttpRequest"
29431 for (var headerName in headers) {
29432 var headerValue = headers[headerName];
29434 this.xhr.setRequestHeader(headerName, headerValue);
29440 this.xhr.onload = function()
29442 _this.xhrOnLoad(_this.xhr);
29445 this.xhr.onerror = function()
29447 _this.xhrOnError(_this.xhr);
29450 var formData = new FormData();
29452 formData.append('returnHTML', 'NO');
29455 formData.append('crop', crop);
29458 formData.append(this.paramName, file, file.name);
29465 if(this.fireEvent('prepare', this, formData, options) != false){
29467 if(options.manually){
29471 this.xhr.send(formData);
29475 this.uploadCancel();
29478 uploadCancel : function()
29484 this.delegates = [];
29486 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29493 renderPreview : function(file)
29495 if(typeof(file.target) != 'undefined' && file.target){
29499 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29501 var previewEl = this.managerEl.createChild({
29503 cls : 'roo-document-manager-preview',
29507 tooltip : file[this.toolTipName],
29508 cls : 'roo-document-manager-thumb',
29509 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29514 html : '<i class="fa fa-times-circle"></i>'
29519 var close = previewEl.select('button.close', true).first();
29521 close.on('click', this.onRemove, this, file);
29523 file.target = previewEl;
29525 var image = previewEl.select('img', true).first();
29529 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29531 image.on('click', this.onClick, this, file);
29533 this.fireEvent('previewrendered', this, file);
29539 onPreviewLoad : function(file, image)
29541 if(typeof(file.target) == 'undefined' || !file.target){
29545 var width = image.dom.naturalWidth || image.dom.width;
29546 var height = image.dom.naturalHeight || image.dom.height;
29548 if(!this.previewResize) {
29552 if(width > height){
29553 file.target.addClass('wide');
29557 file.target.addClass('tall');
29562 uploadFromSource : function(file, crop)
29564 this.xhr = new XMLHttpRequest();
29566 this.managerEl.createChild({
29568 cls : 'roo-document-manager-loading',
29572 tooltip : file.name,
29573 cls : 'roo-document-manager-thumb',
29574 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29580 this.xhr.open(this.method, this.url, true);
29583 "Accept": "application/json",
29584 "Cache-Control": "no-cache",
29585 "X-Requested-With": "XMLHttpRequest"
29588 for (var headerName in headers) {
29589 var headerValue = headers[headerName];
29591 this.xhr.setRequestHeader(headerName, headerValue);
29597 this.xhr.onload = function()
29599 _this.xhrOnLoad(_this.xhr);
29602 this.xhr.onerror = function()
29604 _this.xhrOnError(_this.xhr);
29607 var formData = new FormData();
29609 formData.append('returnHTML', 'NO');
29611 formData.append('crop', crop);
29613 if(typeof(file.filename) != 'undefined'){
29614 formData.append('filename', file.filename);
29617 if(typeof(file.mimetype) != 'undefined'){
29618 formData.append('mimetype', file.mimetype);
29623 if(this.fireEvent('prepare', this, formData) != false){
29624 this.xhr.send(formData);
29634 * @class Roo.bootstrap.DocumentViewer
29635 * @extends Roo.bootstrap.Component
29636 * Bootstrap DocumentViewer class
29637 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29638 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29641 * Create a new DocumentViewer
29642 * @param {Object} config The config object
29645 Roo.bootstrap.DocumentViewer = function(config){
29646 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29651 * Fire after initEvent
29652 * @param {Roo.bootstrap.DocumentViewer} this
29658 * @param {Roo.bootstrap.DocumentViewer} this
29663 * Fire after download button
29664 * @param {Roo.bootstrap.DocumentViewer} this
29669 * Fire after trash button
29670 * @param {Roo.bootstrap.DocumentViewer} this
29677 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29679 showDownload : true,
29683 getAutoCreate : function()
29687 cls : 'roo-document-viewer',
29691 cls : 'roo-document-viewer-body',
29695 cls : 'roo-document-viewer-thumb',
29699 cls : 'roo-document-viewer-image'
29707 cls : 'roo-document-viewer-footer',
29710 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29714 cls : 'btn-group roo-document-viewer-download',
29718 cls : 'btn btn-default',
29719 html : '<i class="fa fa-download"></i>'
29725 cls : 'btn-group roo-document-viewer-trash',
29729 cls : 'btn btn-default',
29730 html : '<i class="fa fa-trash"></i>'
29743 initEvents : function()
29745 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29746 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29748 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29749 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29751 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29752 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29754 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29755 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29757 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29758 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29760 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29761 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29763 this.bodyEl.on('click', this.onClick, this);
29764 this.downloadBtn.on('click', this.onDownload, this);
29765 this.trashBtn.on('click', this.onTrash, this);
29767 this.downloadBtn.hide();
29768 this.trashBtn.hide();
29770 if(this.showDownload){
29771 this.downloadBtn.show();
29774 if(this.showTrash){
29775 this.trashBtn.show();
29778 if(!this.showDownload && !this.showTrash) {
29779 this.footerEl.hide();
29784 initial : function()
29786 this.fireEvent('initial', this);
29790 onClick : function(e)
29792 e.preventDefault();
29794 this.fireEvent('click', this);
29797 onDownload : function(e)
29799 e.preventDefault();
29801 this.fireEvent('download', this);
29804 onTrash : function(e)
29806 e.preventDefault();
29808 this.fireEvent('trash', this);
29820 * @class Roo.bootstrap.NavProgressBar
29821 * @extends Roo.bootstrap.Component
29822 * Bootstrap NavProgressBar class
29825 * Create a new nav progress bar
29826 * @param {Object} config The config object
29829 Roo.bootstrap.NavProgressBar = function(config){
29830 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29832 this.bullets = this.bullets || [];
29834 // Roo.bootstrap.NavProgressBar.register(this);
29838 * Fires when the active item changes
29839 * @param {Roo.bootstrap.NavProgressBar} this
29840 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29841 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29848 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29853 getAutoCreate : function()
29855 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29859 cls : 'roo-navigation-bar-group',
29863 cls : 'roo-navigation-top-bar'
29867 cls : 'roo-navigation-bullets-bar',
29871 cls : 'roo-navigation-bar'
29878 cls : 'roo-navigation-bottom-bar'
29888 initEvents: function()
29893 onRender : function(ct, position)
29895 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29897 if(this.bullets.length){
29898 Roo.each(this.bullets, function(b){
29907 addItem : function(cfg)
29909 var item = new Roo.bootstrap.NavProgressItem(cfg);
29911 item.parentId = this.id;
29912 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29915 var top = new Roo.bootstrap.Element({
29917 cls : 'roo-navigation-bar-text'
29920 var bottom = new Roo.bootstrap.Element({
29922 cls : 'roo-navigation-bar-text'
29925 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29926 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29928 var topText = new Roo.bootstrap.Element({
29930 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29933 var bottomText = new Roo.bootstrap.Element({
29935 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29938 topText.onRender(top.el, null);
29939 bottomText.onRender(bottom.el, null);
29942 item.bottomEl = bottom;
29945 this.barItems.push(item);
29950 getActive : function()
29952 var active = false;
29954 Roo.each(this.barItems, function(v){
29956 if (!v.isActive()) {
29968 setActiveItem : function(item)
29972 Roo.each(this.barItems, function(v){
29973 if (v.rid == item.rid) {
29977 if (v.isActive()) {
29978 v.setActive(false);
29983 item.setActive(true);
29985 this.fireEvent('changed', this, item, prev);
29988 getBarItem: function(rid)
29992 Roo.each(this.barItems, function(e) {
29993 if (e.rid != rid) {
30004 indexOfItem : function(item)
30008 Roo.each(this.barItems, function(v, i){
30010 if (v.rid != item.rid) {
30021 setActiveNext : function()
30023 var i = this.indexOfItem(this.getActive());
30025 if (i > this.barItems.length) {
30029 this.setActiveItem(this.barItems[i+1]);
30032 setActivePrev : function()
30034 var i = this.indexOfItem(this.getActive());
30040 this.setActiveItem(this.barItems[i-1]);
30043 format : function()
30045 if(!this.barItems.length){
30049 var width = 100 / this.barItems.length;
30051 Roo.each(this.barItems, function(i){
30052 i.el.setStyle('width', width + '%');
30053 i.topEl.el.setStyle('width', width + '%');
30054 i.bottomEl.el.setStyle('width', width + '%');
30063 * Nav Progress Item
30068 * @class Roo.bootstrap.NavProgressItem
30069 * @extends Roo.bootstrap.Component
30070 * Bootstrap NavProgressItem class
30071 * @cfg {String} rid the reference id
30072 * @cfg {Boolean} active (true|false) Is item active default false
30073 * @cfg {Boolean} disabled (true|false) Is item active default false
30074 * @cfg {String} html
30075 * @cfg {String} position (top|bottom) text position default bottom
30076 * @cfg {String} icon show icon instead of number
30079 * Create a new NavProgressItem
30080 * @param {Object} config The config object
30082 Roo.bootstrap.NavProgressItem = function(config){
30083 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30088 * The raw click event for the entire grid.
30089 * @param {Roo.bootstrap.NavProgressItem} this
30090 * @param {Roo.EventObject} e
30097 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30103 position : 'bottom',
30106 getAutoCreate : function()
30108 var iconCls = 'roo-navigation-bar-item-icon';
30110 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30114 cls: 'roo-navigation-bar-item',
30124 cfg.cls += ' active';
30127 cfg.cls += ' disabled';
30133 disable : function()
30135 this.setDisabled(true);
30138 enable : function()
30140 this.setDisabled(false);
30143 initEvents: function()
30145 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30147 this.iconEl.on('click', this.onClick, this);
30150 onClick : function(e)
30152 e.preventDefault();
30158 if(this.fireEvent('click', this, e) === false){
30162 this.parent().setActiveItem(this);
30165 isActive: function ()
30167 return this.active;
30170 setActive : function(state)
30172 if(this.active == state){
30176 this.active = state;
30179 this.el.addClass('active');
30183 this.el.removeClass('active');
30188 setDisabled : function(state)
30190 if(this.disabled == state){
30194 this.disabled = state;
30197 this.el.addClass('disabled');
30201 this.el.removeClass('disabled');
30204 tooltipEl : function()
30206 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30219 * @class Roo.bootstrap.FieldLabel
30220 * @extends Roo.bootstrap.Component
30221 * Bootstrap FieldLabel class
30222 * @cfg {String} html contents of the element
30223 * @cfg {String} tag tag of the element default label
30224 * @cfg {String} cls class of the element
30225 * @cfg {String} target label target
30226 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30227 * @cfg {String} invalidClass default "text-warning"
30228 * @cfg {String} validClass default "text-success"
30229 * @cfg {String} iconTooltip default "This field is required"
30230 * @cfg {String} indicatorpos (left|right) default left
30233 * Create a new FieldLabel
30234 * @param {Object} config The config object
30237 Roo.bootstrap.FieldLabel = function(config){
30238 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30243 * Fires after the field has been marked as invalid.
30244 * @param {Roo.form.FieldLabel} this
30245 * @param {String} msg The validation message
30250 * Fires after the field has been validated with no errors.
30251 * @param {Roo.form.FieldLabel} this
30257 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30264 invalidClass : 'has-warning',
30265 validClass : 'has-success',
30266 iconTooltip : 'This field is required',
30267 indicatorpos : 'left',
30269 getAutoCreate : function(){
30272 if (!this.allowBlank) {
30278 cls : 'roo-bootstrap-field-label ' + this.cls,
30283 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30284 tooltip : this.iconTooltip
30293 if(this.indicatorpos == 'right'){
30296 cls : 'roo-bootstrap-field-label ' + this.cls,
30305 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30306 tooltip : this.iconTooltip
30315 initEvents: function()
30317 Roo.bootstrap.Element.superclass.initEvents.call(this);
30319 this.indicator = this.indicatorEl();
30321 if(this.indicator){
30322 this.indicator.removeClass('visible');
30323 this.indicator.addClass('invisible');
30326 Roo.bootstrap.FieldLabel.register(this);
30329 indicatorEl : function()
30331 var indicator = this.el.select('i.roo-required-indicator',true).first();
30342 * Mark this field as valid
30344 markValid : function()
30346 if(this.indicator){
30347 this.indicator.removeClass('visible');
30348 this.indicator.addClass('invisible');
30351 this.el.removeClass(this.invalidClass);
30353 this.el.addClass(this.validClass);
30355 this.fireEvent('valid', this);
30359 * Mark this field as invalid
30360 * @param {String} msg The validation message
30362 markInvalid : function(msg)
30364 if(this.indicator){
30365 this.indicator.removeClass('invisible');
30366 this.indicator.addClass('visible');
30369 this.el.removeClass(this.validClass);
30371 this.el.addClass(this.invalidClass);
30373 this.fireEvent('invalid', this, msg);
30379 Roo.apply(Roo.bootstrap.FieldLabel, {
30384 * register a FieldLabel Group
30385 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30387 register : function(label)
30389 if(this.groups.hasOwnProperty(label.target)){
30393 this.groups[label.target] = label;
30397 * fetch a FieldLabel Group based on the target
30398 * @param {string} target
30399 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30401 get: function(target) {
30402 if (typeof(this.groups[target]) == 'undefined') {
30406 return this.groups[target] ;
30415 * page DateSplitField.
30421 * @class Roo.bootstrap.DateSplitField
30422 * @extends Roo.bootstrap.Component
30423 * Bootstrap DateSplitField class
30424 * @cfg {string} fieldLabel - the label associated
30425 * @cfg {Number} labelWidth set the width of label (0-12)
30426 * @cfg {String} labelAlign (top|left)
30427 * @cfg {Boolean} dayAllowBlank (true|false) default false
30428 * @cfg {Boolean} monthAllowBlank (true|false) default false
30429 * @cfg {Boolean} yearAllowBlank (true|false) default false
30430 * @cfg {string} dayPlaceholder
30431 * @cfg {string} monthPlaceholder
30432 * @cfg {string} yearPlaceholder
30433 * @cfg {string} dayFormat default 'd'
30434 * @cfg {string} monthFormat default 'm'
30435 * @cfg {string} yearFormat default 'Y'
30436 * @cfg {Number} labellg set the width of label (1-12)
30437 * @cfg {Number} labelmd set the width of label (1-12)
30438 * @cfg {Number} labelsm set the width of label (1-12)
30439 * @cfg {Number} labelxs set the width of label (1-12)
30443 * Create a new DateSplitField
30444 * @param {Object} config The config object
30447 Roo.bootstrap.DateSplitField = function(config){
30448 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30454 * getting the data of years
30455 * @param {Roo.bootstrap.DateSplitField} this
30456 * @param {Object} years
30461 * getting the data of days
30462 * @param {Roo.bootstrap.DateSplitField} this
30463 * @param {Object} days
30468 * Fires after the field has been marked as invalid.
30469 * @param {Roo.form.Field} this
30470 * @param {String} msg The validation message
30475 * Fires after the field has been validated with no errors.
30476 * @param {Roo.form.Field} this
30482 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30485 labelAlign : 'top',
30487 dayAllowBlank : false,
30488 monthAllowBlank : false,
30489 yearAllowBlank : false,
30490 dayPlaceholder : '',
30491 monthPlaceholder : '',
30492 yearPlaceholder : '',
30496 isFormField : true,
30502 getAutoCreate : function()
30506 cls : 'row roo-date-split-field-group',
30511 cls : 'form-hidden-field roo-date-split-field-group-value',
30517 var labelCls = 'col-md-12';
30518 var contentCls = 'col-md-4';
30520 if(this.fieldLabel){
30524 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30528 html : this.fieldLabel
30533 if(this.labelAlign == 'left'){
30535 if(this.labelWidth > 12){
30536 label.style = "width: " + this.labelWidth + 'px';
30539 if(this.labelWidth < 13 && this.labelmd == 0){
30540 this.labelmd = this.labelWidth;
30543 if(this.labellg > 0){
30544 labelCls = ' col-lg-' + this.labellg;
30545 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30548 if(this.labelmd > 0){
30549 labelCls = ' col-md-' + this.labelmd;
30550 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30553 if(this.labelsm > 0){
30554 labelCls = ' col-sm-' + this.labelsm;
30555 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30558 if(this.labelxs > 0){
30559 labelCls = ' col-xs-' + this.labelxs;
30560 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30564 label.cls += ' ' + labelCls;
30566 cfg.cn.push(label);
30569 Roo.each(['day', 'month', 'year'], function(t){
30572 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30579 inputEl: function ()
30581 return this.el.select('.roo-date-split-field-group-value', true).first();
30584 onRender : function(ct, position)
30588 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30590 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30592 this.dayField = new Roo.bootstrap.ComboBox({
30593 allowBlank : this.dayAllowBlank,
30594 alwaysQuery : true,
30595 displayField : 'value',
30598 forceSelection : true,
30600 placeholder : this.dayPlaceholder,
30601 selectOnFocus : true,
30602 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30603 triggerAction : 'all',
30605 valueField : 'value',
30606 store : new Roo.data.SimpleStore({
30607 data : (function() {
30609 _this.fireEvent('days', _this, days);
30612 fields : [ 'value' ]
30615 select : function (_self, record, index)
30617 _this.setValue(_this.getValue());
30622 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30624 this.monthField = new Roo.bootstrap.MonthField({
30625 after : '<i class=\"fa fa-calendar\"></i>',
30626 allowBlank : this.monthAllowBlank,
30627 placeholder : this.monthPlaceholder,
30630 render : function (_self)
30632 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30633 e.preventDefault();
30637 select : function (_self, oldvalue, newvalue)
30639 _this.setValue(_this.getValue());
30644 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30646 this.yearField = new Roo.bootstrap.ComboBox({
30647 allowBlank : this.yearAllowBlank,
30648 alwaysQuery : true,
30649 displayField : 'value',
30652 forceSelection : true,
30654 placeholder : this.yearPlaceholder,
30655 selectOnFocus : true,
30656 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30657 triggerAction : 'all',
30659 valueField : 'value',
30660 store : new Roo.data.SimpleStore({
30661 data : (function() {
30663 _this.fireEvent('years', _this, years);
30666 fields : [ 'value' ]
30669 select : function (_self, record, index)
30671 _this.setValue(_this.getValue());
30676 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30679 setValue : function(v, format)
30681 this.inputEl.dom.value = v;
30683 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30685 var d = Date.parseDate(v, f);
30692 this.setDay(d.format(this.dayFormat));
30693 this.setMonth(d.format(this.monthFormat));
30694 this.setYear(d.format(this.yearFormat));
30701 setDay : function(v)
30703 this.dayField.setValue(v);
30704 this.inputEl.dom.value = this.getValue();
30709 setMonth : function(v)
30711 this.monthField.setValue(v, true);
30712 this.inputEl.dom.value = this.getValue();
30717 setYear : function(v)
30719 this.yearField.setValue(v);
30720 this.inputEl.dom.value = this.getValue();
30725 getDay : function()
30727 return this.dayField.getValue();
30730 getMonth : function()
30732 return this.monthField.getValue();
30735 getYear : function()
30737 return this.yearField.getValue();
30740 getValue : function()
30742 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30744 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30754 this.inputEl.dom.value = '';
30759 validate : function()
30761 var d = this.dayField.validate();
30762 var m = this.monthField.validate();
30763 var y = this.yearField.validate();
30768 (!this.dayAllowBlank && !d) ||
30769 (!this.monthAllowBlank && !m) ||
30770 (!this.yearAllowBlank && !y)
30775 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30784 this.markInvalid();
30789 markValid : function()
30792 var label = this.el.select('label', true).first();
30793 var icon = this.el.select('i.fa-star', true).first();
30799 this.fireEvent('valid', this);
30803 * Mark this field as invalid
30804 * @param {String} msg The validation message
30806 markInvalid : function(msg)
30809 var label = this.el.select('label', true).first();
30810 var icon = this.el.select('i.fa-star', true).first();
30812 if(label && !icon){
30813 this.el.select('.roo-date-split-field-label', true).createChild({
30815 cls : 'text-danger fa fa-lg fa-star',
30816 tooltip : 'This field is required',
30817 style : 'margin-right:5px;'
30821 this.fireEvent('invalid', this, msg);
30824 clearInvalid : function()
30826 var label = this.el.select('label', true).first();
30827 var icon = this.el.select('i.fa-star', true).first();
30833 this.fireEvent('valid', this);
30836 getName: function()
30846 * http://masonry.desandro.com
30848 * The idea is to render all the bricks based on vertical width...
30850 * The original code extends 'outlayer' - we might need to use that....
30856 * @class Roo.bootstrap.LayoutMasonry
30857 * @extends Roo.bootstrap.Component
30858 * Bootstrap Layout Masonry class
30861 * Create a new Element
30862 * @param {Object} config The config object
30865 Roo.bootstrap.LayoutMasonry = function(config){
30867 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30871 Roo.bootstrap.LayoutMasonry.register(this);
30877 * Fire after layout the items
30878 * @param {Roo.bootstrap.LayoutMasonry} this
30879 * @param {Roo.EventObject} e
30886 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30889 * @cfg {Boolean} isLayoutInstant = no animation?
30891 isLayoutInstant : false, // needed?
30894 * @cfg {Number} boxWidth width of the columns
30899 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30904 * @cfg {Number} padWidth padding below box..
30909 * @cfg {Number} gutter gutter width..
30914 * @cfg {Number} maxCols maximum number of columns
30920 * @cfg {Boolean} isAutoInitial defalut true
30922 isAutoInitial : true,
30927 * @cfg {Boolean} isHorizontal defalut false
30929 isHorizontal : false,
30931 currentSize : null,
30937 bricks: null, //CompositeElement
30941 _isLayoutInited : false,
30943 // isAlternative : false, // only use for vertical layout...
30946 * @cfg {Number} alternativePadWidth padding below box..
30948 alternativePadWidth : 50,
30950 selectedBrick : [],
30952 getAutoCreate : function(){
30954 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30958 cls: 'blog-masonary-wrapper ' + this.cls,
30960 cls : 'mas-boxes masonary'
30967 getChildContainer: function( )
30969 if (this.boxesEl) {
30970 return this.boxesEl;
30973 this.boxesEl = this.el.select('.mas-boxes').first();
30975 return this.boxesEl;
30979 initEvents : function()
30983 if(this.isAutoInitial){
30984 Roo.log('hook children rendered');
30985 this.on('childrenrendered', function() {
30986 Roo.log('children rendered');
30992 initial : function()
30994 this.selectedBrick = [];
30996 this.currentSize = this.el.getBox(true);
30998 Roo.EventManager.onWindowResize(this.resize, this);
31000 if(!this.isAutoInitial){
31008 //this.layout.defer(500,this);
31012 resize : function()
31014 var cs = this.el.getBox(true);
31017 this.currentSize.width == cs.width &&
31018 this.currentSize.x == cs.x &&
31019 this.currentSize.height == cs.height &&
31020 this.currentSize.y == cs.y
31022 Roo.log("no change in with or X or Y");
31026 this.currentSize = cs;
31032 layout : function()
31034 this._resetLayout();
31036 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31038 this.layoutItems( isInstant );
31040 this._isLayoutInited = true;
31042 this.fireEvent('layout', this);
31046 _resetLayout : function()
31048 if(this.isHorizontal){
31049 this.horizontalMeasureColumns();
31053 this.verticalMeasureColumns();
31057 verticalMeasureColumns : function()
31059 this.getContainerWidth();
31061 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31062 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31066 var boxWidth = this.boxWidth + this.padWidth;
31068 if(this.containerWidth < this.boxWidth){
31069 boxWidth = this.containerWidth
31072 var containerWidth = this.containerWidth;
31074 var cols = Math.floor(containerWidth / boxWidth);
31076 this.cols = Math.max( cols, 1 );
31078 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31080 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31082 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31084 this.colWidth = boxWidth + avail - this.padWidth;
31086 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31087 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31090 horizontalMeasureColumns : function()
31092 this.getContainerWidth();
31094 var boxWidth = this.boxWidth;
31096 if(this.containerWidth < boxWidth){
31097 boxWidth = this.containerWidth;
31100 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31102 this.el.setHeight(boxWidth);
31106 getContainerWidth : function()
31108 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31111 layoutItems : function( isInstant )
31113 Roo.log(this.bricks);
31115 var items = Roo.apply([], this.bricks);
31117 if(this.isHorizontal){
31118 this._horizontalLayoutItems( items , isInstant );
31122 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31123 // this._verticalAlternativeLayoutItems( items , isInstant );
31127 this._verticalLayoutItems( items , isInstant );
31131 _verticalLayoutItems : function ( items , isInstant)
31133 if ( !items || !items.length ) {
31138 ['xs', 'xs', 'xs', 'tall'],
31139 ['xs', 'xs', 'tall'],
31140 ['xs', 'xs', 'sm'],
31141 ['xs', 'xs', 'xs'],
31147 ['sm', 'xs', 'xs'],
31151 ['tall', 'xs', 'xs', 'xs'],
31152 ['tall', 'xs', 'xs'],
31164 Roo.each(items, function(item, k){
31166 switch (item.size) {
31167 // these layouts take up a full box,
31178 boxes.push([item]);
31201 var filterPattern = function(box, length)
31209 var pattern = box.slice(0, length);
31213 Roo.each(pattern, function(i){
31214 format.push(i.size);
31217 Roo.each(standard, function(s){
31219 if(String(s) != String(format)){
31228 if(!match && length == 1){
31233 filterPattern(box, length - 1);
31237 queue.push(pattern);
31239 box = box.slice(length, box.length);
31241 filterPattern(box, 4);
31247 Roo.each(boxes, function(box, k){
31253 if(box.length == 1){
31258 filterPattern(box, 4);
31262 this._processVerticalLayoutQueue( queue, isInstant );
31266 // _verticalAlternativeLayoutItems : function( items , isInstant )
31268 // if ( !items || !items.length ) {
31272 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31276 _horizontalLayoutItems : function ( items , isInstant)
31278 if ( !items || !items.length || items.length < 3) {
31284 var eItems = items.slice(0, 3);
31286 items = items.slice(3, items.length);
31289 ['xs', 'xs', 'xs', 'wide'],
31290 ['xs', 'xs', 'wide'],
31291 ['xs', 'xs', 'sm'],
31292 ['xs', 'xs', 'xs'],
31298 ['sm', 'xs', 'xs'],
31302 ['wide', 'xs', 'xs', 'xs'],
31303 ['wide', 'xs', 'xs'],
31316 Roo.each(items, function(item, k){
31318 switch (item.size) {
31329 boxes.push([item]);
31353 var filterPattern = function(box, length)
31361 var pattern = box.slice(0, length);
31365 Roo.each(pattern, function(i){
31366 format.push(i.size);
31369 Roo.each(standard, function(s){
31371 if(String(s) != String(format)){
31380 if(!match && length == 1){
31385 filterPattern(box, length - 1);
31389 queue.push(pattern);
31391 box = box.slice(length, box.length);
31393 filterPattern(box, 4);
31399 Roo.each(boxes, function(box, k){
31405 if(box.length == 1){
31410 filterPattern(box, 4);
31417 var pos = this.el.getBox(true);
31421 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31423 var hit_end = false;
31425 Roo.each(queue, function(box){
31429 Roo.each(box, function(b){
31431 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31441 Roo.each(box, function(b){
31443 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31446 mx = Math.max(mx, b.x);
31450 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31454 Roo.each(box, function(b){
31456 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31470 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31473 /** Sets position of item in DOM
31474 * @param {Element} item
31475 * @param {Number} x - horizontal position
31476 * @param {Number} y - vertical position
31477 * @param {Boolean} isInstant - disables transitions
31479 _processVerticalLayoutQueue : function( queue, isInstant )
31481 var pos = this.el.getBox(true);
31486 for (var i = 0; i < this.cols; i++){
31490 Roo.each(queue, function(box, k){
31492 var col = k % this.cols;
31494 Roo.each(box, function(b,kk){
31496 b.el.position('absolute');
31498 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31499 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31501 if(b.size == 'md-left' || b.size == 'md-right'){
31502 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31503 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31506 b.el.setWidth(width);
31507 b.el.setHeight(height);
31509 b.el.select('iframe',true).setSize(width,height);
31513 for (var i = 0; i < this.cols; i++){
31515 if(maxY[i] < maxY[col]){
31520 col = Math.min(col, i);
31524 x = pos.x + col * (this.colWidth + this.padWidth);
31528 var positions = [];
31530 switch (box.length){
31532 positions = this.getVerticalOneBoxColPositions(x, y, box);
31535 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31538 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31541 positions = this.getVerticalFourBoxColPositions(x, y, box);
31547 Roo.each(box, function(b,kk){
31549 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31551 var sz = b.el.getSize();
31553 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31561 for (var i = 0; i < this.cols; i++){
31562 mY = Math.max(mY, maxY[i]);
31565 this.el.setHeight(mY - pos.y);
31569 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31571 // var pos = this.el.getBox(true);
31574 // var maxX = pos.right;
31576 // var maxHeight = 0;
31578 // Roo.each(items, function(item, k){
31582 // item.el.position('absolute');
31584 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31586 // item.el.setWidth(width);
31588 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31590 // item.el.setHeight(height);
31593 // item.el.setXY([x, y], isInstant ? false : true);
31595 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31598 // y = y + height + this.alternativePadWidth;
31600 // maxHeight = maxHeight + height + this.alternativePadWidth;
31604 // this.el.setHeight(maxHeight);
31608 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31610 var pos = this.el.getBox(true);
31615 var maxX = pos.right;
31617 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31619 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31621 Roo.each(queue, function(box, k){
31623 Roo.each(box, function(b, kk){
31625 b.el.position('absolute');
31627 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31628 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31630 if(b.size == 'md-left' || b.size == 'md-right'){
31631 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31632 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31635 b.el.setWidth(width);
31636 b.el.setHeight(height);
31644 var positions = [];
31646 switch (box.length){
31648 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31651 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31654 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31657 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31663 Roo.each(box, function(b,kk){
31665 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31667 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31675 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31677 Roo.each(eItems, function(b,k){
31679 b.size = (k == 0) ? 'sm' : 'xs';
31680 b.x = (k == 0) ? 2 : 1;
31681 b.y = (k == 0) ? 2 : 1;
31683 b.el.position('absolute');
31685 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31687 b.el.setWidth(width);
31689 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31691 b.el.setHeight(height);
31695 var positions = [];
31698 x : maxX - this.unitWidth * 2 - this.gutter,
31703 x : maxX - this.unitWidth,
31704 y : minY + (this.unitWidth + this.gutter) * 2
31708 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31712 Roo.each(eItems, function(b,k){
31714 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31720 getVerticalOneBoxColPositions : function(x, y, box)
31724 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31726 if(box[0].size == 'md-left'){
31730 if(box[0].size == 'md-right'){
31735 x : x + (this.unitWidth + this.gutter) * rand,
31742 getVerticalTwoBoxColPositions : function(x, y, box)
31746 if(box[0].size == 'xs'){
31750 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31754 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31768 x : x + (this.unitWidth + this.gutter) * 2,
31769 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31776 getVerticalThreeBoxColPositions : function(x, y, box)
31780 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31788 x : x + (this.unitWidth + this.gutter) * 1,
31793 x : x + (this.unitWidth + this.gutter) * 2,
31801 if(box[0].size == 'xs' && box[1].size == 'xs'){
31810 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31814 x : x + (this.unitWidth + this.gutter) * 1,
31828 x : x + (this.unitWidth + this.gutter) * 2,
31833 x : x + (this.unitWidth + this.gutter) * 2,
31834 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31841 getVerticalFourBoxColPositions : function(x, y, box)
31845 if(box[0].size == 'xs'){
31854 y : y + (this.unitHeight + this.gutter) * 1
31859 y : y + (this.unitHeight + this.gutter) * 2
31863 x : x + (this.unitWidth + this.gutter) * 1,
31877 x : x + (this.unitWidth + this.gutter) * 2,
31882 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31883 y : y + (this.unitHeight + this.gutter) * 1
31887 x : x + (this.unitWidth + this.gutter) * 2,
31888 y : y + (this.unitWidth + this.gutter) * 2
31895 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31899 if(box[0].size == 'md-left'){
31901 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31908 if(box[0].size == 'md-right'){
31910 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31911 y : minY + (this.unitWidth + this.gutter) * 1
31917 var rand = Math.floor(Math.random() * (4 - box[0].y));
31920 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31921 y : minY + (this.unitWidth + this.gutter) * rand
31928 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31932 if(box[0].size == 'xs'){
31935 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31940 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31941 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
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) * 2
31962 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31966 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31969 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31974 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31975 y : minY + (this.unitWidth + this.gutter) * 1
31979 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31980 y : minY + (this.unitWidth + this.gutter) * 2
31987 if(box[0].size == 'xs' && box[1].size == 'xs'){
31990 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31995 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32000 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32001 y : minY + (this.unitWidth + this.gutter) * 1
32009 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32014 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32015 y : minY + (this.unitWidth + this.gutter) * 2
32019 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32020 y : minY + (this.unitWidth + this.gutter) * 2
32027 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32031 if(box[0].size == 'xs'){
32034 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32039 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32044 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),
32049 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32050 y : minY + (this.unitWidth + this.gutter) * 1
32058 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32063 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32064 y : minY + (this.unitWidth + this.gutter) * 2
32068 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32069 y : minY + (this.unitWidth + this.gutter) * 2
32073 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),
32074 y : minY + (this.unitWidth + this.gutter) * 2
32082 * remove a Masonry Brick
32083 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32085 removeBrick : function(brick_id)
32091 for (var i = 0; i<this.bricks.length; i++) {
32092 if (this.bricks[i].id == brick_id) {
32093 this.bricks.splice(i,1);
32094 this.el.dom.removeChild(Roo.get(brick_id).dom);
32101 * adds a Masonry Brick
32102 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32104 addBrick : function(cfg)
32106 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32107 //this.register(cn);
32108 cn.parentId = this.id;
32109 cn.render(this.el);
32114 * register a Masonry Brick
32115 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32118 register : function(brick)
32120 this.bricks.push(brick);
32121 brick.masonryId = this.id;
32125 * clear all the Masonry Brick
32127 clearAll : function()
32130 //this.getChildContainer().dom.innerHTML = "";
32131 this.el.dom.innerHTML = '';
32134 getSelected : function()
32136 if (!this.selectedBrick) {
32140 return this.selectedBrick;
32144 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32148 * register a Masonry Layout
32149 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32152 register : function(layout)
32154 this.groups[layout.id] = layout;
32157 * fetch a Masonry Layout based on the masonry layout ID
32158 * @param {string} the masonry layout to add
32159 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32162 get: function(layout_id) {
32163 if (typeof(this.groups[layout_id]) == 'undefined') {
32166 return this.groups[layout_id] ;
32178 * http://masonry.desandro.com
32180 * The idea is to render all the bricks based on vertical width...
32182 * The original code extends 'outlayer' - we might need to use that....
32188 * @class Roo.bootstrap.LayoutMasonryAuto
32189 * @extends Roo.bootstrap.Component
32190 * Bootstrap Layout Masonry class
32193 * Create a new Element
32194 * @param {Object} config The config object
32197 Roo.bootstrap.LayoutMasonryAuto = function(config){
32198 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32201 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32204 * @cfg {Boolean} isFitWidth - resize the width..
32206 isFitWidth : false, // options..
32208 * @cfg {Boolean} isOriginLeft = left align?
32210 isOriginLeft : true,
32212 * @cfg {Boolean} isOriginTop = top align?
32214 isOriginTop : false,
32216 * @cfg {Boolean} isLayoutInstant = no animation?
32218 isLayoutInstant : false, // needed?
32220 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32222 isResizingContainer : true,
32224 * @cfg {Number} columnWidth width of the columns
32230 * @cfg {Number} maxCols maximum number of columns
32235 * @cfg {Number} padHeight padding below box..
32241 * @cfg {Boolean} isAutoInitial defalut true
32244 isAutoInitial : true,
32250 initialColumnWidth : 0,
32251 currentSize : null,
32253 colYs : null, // array.
32260 bricks: null, //CompositeElement
32261 cols : 0, // array?
32262 // element : null, // wrapped now this.el
32263 _isLayoutInited : null,
32266 getAutoCreate : function(){
32270 cls: 'blog-masonary-wrapper ' + this.cls,
32272 cls : 'mas-boxes masonary'
32279 getChildContainer: function( )
32281 if (this.boxesEl) {
32282 return this.boxesEl;
32285 this.boxesEl = this.el.select('.mas-boxes').first();
32287 return this.boxesEl;
32291 initEvents : function()
32295 if(this.isAutoInitial){
32296 Roo.log('hook children rendered');
32297 this.on('childrenrendered', function() {
32298 Roo.log('children rendered');
32305 initial : function()
32307 this.reloadItems();
32309 this.currentSize = this.el.getBox(true);
32311 /// was window resize... - let's see if this works..
32312 Roo.EventManager.onWindowResize(this.resize, this);
32314 if(!this.isAutoInitial){
32319 this.layout.defer(500,this);
32322 reloadItems: function()
32324 this.bricks = this.el.select('.masonry-brick', true);
32326 this.bricks.each(function(b) {
32327 //Roo.log(b.getSize());
32328 if (!b.attr('originalwidth')) {
32329 b.attr('originalwidth', b.getSize().width);
32334 Roo.log(this.bricks.elements.length);
32337 resize : function()
32340 var cs = this.el.getBox(true);
32342 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32343 Roo.log("no change in with or X");
32346 this.currentSize = cs;
32350 layout : function()
32353 this._resetLayout();
32354 //this._manageStamps();
32356 // don't animate first layout
32357 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32358 this.layoutItems( isInstant );
32360 // flag for initalized
32361 this._isLayoutInited = true;
32364 layoutItems : function( isInstant )
32366 //var items = this._getItemsForLayout( this.items );
32367 // original code supports filtering layout items.. we just ignore it..
32369 this._layoutItems( this.bricks , isInstant );
32371 this._postLayout();
32373 _layoutItems : function ( items , isInstant)
32375 //this.fireEvent( 'layout', this, items );
32378 if ( !items || !items.elements.length ) {
32379 // no items, emit event with empty array
32384 items.each(function(item) {
32385 Roo.log("layout item");
32387 // get x/y object from method
32388 var position = this._getItemLayoutPosition( item );
32390 position.item = item;
32391 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32392 queue.push( position );
32395 this._processLayoutQueue( queue );
32397 /** Sets position of item in DOM
32398 * @param {Element} item
32399 * @param {Number} x - horizontal position
32400 * @param {Number} y - vertical position
32401 * @param {Boolean} isInstant - disables transitions
32403 _processLayoutQueue : function( queue )
32405 for ( var i=0, len = queue.length; i < len; i++ ) {
32406 var obj = queue[i];
32407 obj.item.position('absolute');
32408 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32414 * Any logic you want to do after each layout,
32415 * i.e. size the container
32417 _postLayout : function()
32419 this.resizeContainer();
32422 resizeContainer : function()
32424 if ( !this.isResizingContainer ) {
32427 var size = this._getContainerSize();
32429 this.el.setSize(size.width,size.height);
32430 this.boxesEl.setSize(size.width,size.height);
32436 _resetLayout : function()
32438 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32439 this.colWidth = this.el.getWidth();
32440 //this.gutter = this.el.getWidth();
32442 this.measureColumns();
32448 this.colYs.push( 0 );
32454 measureColumns : function()
32456 this.getContainerWidth();
32457 // if columnWidth is 0, default to outerWidth of first item
32458 if ( !this.columnWidth ) {
32459 var firstItem = this.bricks.first();
32460 Roo.log(firstItem);
32461 this.columnWidth = this.containerWidth;
32462 if (firstItem && firstItem.attr('originalwidth') ) {
32463 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32465 // columnWidth fall back to item of first element
32466 Roo.log("set column width?");
32467 this.initialColumnWidth = this.columnWidth ;
32469 // if first elem has no width, default to size of container
32474 if (this.initialColumnWidth) {
32475 this.columnWidth = this.initialColumnWidth;
32480 // column width is fixed at the top - however if container width get's smaller we should
32483 // this bit calcs how man columns..
32485 var columnWidth = this.columnWidth += this.gutter;
32487 // calculate columns
32488 var containerWidth = this.containerWidth + this.gutter;
32490 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32491 // fix rounding errors, typically with gutters
32492 var excess = columnWidth - containerWidth % columnWidth;
32495 // if overshoot is less than a pixel, round up, otherwise floor it
32496 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32497 cols = Math[ mathMethod ]( cols );
32498 this.cols = Math.max( cols, 1 );
32499 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32501 // padding positioning..
32502 var totalColWidth = this.cols * this.columnWidth;
32503 var padavail = this.containerWidth - totalColWidth;
32504 // so for 2 columns - we need 3 'pads'
32506 var padNeeded = (1+this.cols) * this.padWidth;
32508 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32510 this.columnWidth += padExtra
32511 //this.padWidth = Math.floor(padavail / ( this.cols));
32513 // adjust colum width so that padding is fixed??
32515 // we have 3 columns ... total = width * 3
32516 // we have X left over... that should be used by
32518 //if (this.expandC) {
32526 getContainerWidth : function()
32528 /* // container is parent if fit width
32529 var container = this.isFitWidth ? this.element.parentNode : this.element;
32530 // check that this.size and size are there
32531 // IE8 triggers resize on body size change, so they might not be
32533 var size = getSize( container ); //FIXME
32534 this.containerWidth = size && size.innerWidth; //FIXME
32537 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32541 _getItemLayoutPosition : function( item ) // what is item?
32543 // we resize the item to our columnWidth..
32545 item.setWidth(this.columnWidth);
32546 item.autoBoxAdjust = false;
32548 var sz = item.getSize();
32550 // how many columns does this brick span
32551 var remainder = this.containerWidth % this.columnWidth;
32553 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32554 // round if off by 1 pixel, otherwise use ceil
32555 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32556 colSpan = Math.min( colSpan, this.cols );
32558 // normally this should be '1' as we dont' currently allow multi width columns..
32560 var colGroup = this._getColGroup( colSpan );
32561 // get the minimum Y value from the columns
32562 var minimumY = Math.min.apply( Math, colGroup );
32563 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32565 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32567 // position the brick
32569 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32570 y: this.currentSize.y + minimumY + this.padHeight
32574 // apply setHeight to necessary columns
32575 var setHeight = minimumY + sz.height + this.padHeight;
32576 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32578 var setSpan = this.cols + 1 - colGroup.length;
32579 for ( var i = 0; i < setSpan; i++ ) {
32580 this.colYs[ shortColIndex + i ] = setHeight ;
32587 * @param {Number} colSpan - number of columns the element spans
32588 * @returns {Array} colGroup
32590 _getColGroup : function( colSpan )
32592 if ( colSpan < 2 ) {
32593 // if brick spans only one column, use all the column Ys
32598 // how many different places could this brick fit horizontally
32599 var groupCount = this.cols + 1 - colSpan;
32600 // for each group potential horizontal position
32601 for ( var i = 0; i < groupCount; i++ ) {
32602 // make an array of colY values for that one group
32603 var groupColYs = this.colYs.slice( i, i + colSpan );
32604 // and get the max value of the array
32605 colGroup[i] = Math.max.apply( Math, groupColYs );
32610 _manageStamp : function( stamp )
32612 var stampSize = stamp.getSize();
32613 var offset = stamp.getBox();
32614 // get the columns that this stamp affects
32615 var firstX = this.isOriginLeft ? offset.x : offset.right;
32616 var lastX = firstX + stampSize.width;
32617 var firstCol = Math.floor( firstX / this.columnWidth );
32618 firstCol = Math.max( 0, firstCol );
32620 var lastCol = Math.floor( lastX / this.columnWidth );
32621 // lastCol should not go over if multiple of columnWidth #425
32622 lastCol -= lastX % this.columnWidth ? 0 : 1;
32623 lastCol = Math.min( this.cols - 1, lastCol );
32625 // set colYs to bottom of the stamp
32626 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32629 for ( var i = firstCol; i <= lastCol; i++ ) {
32630 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32635 _getContainerSize : function()
32637 this.maxY = Math.max.apply( Math, this.colYs );
32642 if ( this.isFitWidth ) {
32643 size.width = this._getContainerFitWidth();
32649 _getContainerFitWidth : function()
32651 var unusedCols = 0;
32652 // count unused columns
32655 if ( this.colYs[i] !== 0 ) {
32660 // fit container to columns that have been used
32661 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32664 needsResizeLayout : function()
32666 var previousWidth = this.containerWidth;
32667 this.getContainerWidth();
32668 return previousWidth !== this.containerWidth;
32683 * @class Roo.bootstrap.MasonryBrick
32684 * @extends Roo.bootstrap.Component
32685 * Bootstrap MasonryBrick class
32688 * Create a new MasonryBrick
32689 * @param {Object} config The config object
32692 Roo.bootstrap.MasonryBrick = function(config){
32694 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32696 Roo.bootstrap.MasonryBrick.register(this);
32702 * When a MasonryBrick is clcik
32703 * @param {Roo.bootstrap.MasonryBrick} this
32704 * @param {Roo.EventObject} e
32710 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32713 * @cfg {String} title
32717 * @cfg {String} html
32721 * @cfg {String} bgimage
32725 * @cfg {String} videourl
32729 * @cfg {String} cls
32733 * @cfg {String} href
32737 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32742 * @cfg {String} placetitle (center|bottom)
32747 * @cfg {Boolean} isFitContainer defalut true
32749 isFitContainer : true,
32752 * @cfg {Boolean} preventDefault defalut false
32754 preventDefault : false,
32757 * @cfg {Boolean} inverse defalut false
32759 maskInverse : false,
32761 getAutoCreate : function()
32763 if(!this.isFitContainer){
32764 return this.getSplitAutoCreate();
32767 var cls = 'masonry-brick masonry-brick-full';
32769 if(this.href.length){
32770 cls += ' masonry-brick-link';
32773 if(this.bgimage.length){
32774 cls += ' masonry-brick-image';
32777 if(this.maskInverse){
32778 cls += ' mask-inverse';
32781 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32782 cls += ' enable-mask';
32786 cls += ' masonry-' + this.size + '-brick';
32789 if(this.placetitle.length){
32791 switch (this.placetitle) {
32793 cls += ' masonry-center-title';
32796 cls += ' masonry-bottom-title';
32803 if(!this.html.length && !this.bgimage.length){
32804 cls += ' masonry-center-title';
32807 if(!this.html.length && this.bgimage.length){
32808 cls += ' masonry-bottom-title';
32813 cls += ' ' + this.cls;
32817 tag: (this.href.length) ? 'a' : 'div',
32822 cls: 'masonry-brick-mask'
32826 cls: 'masonry-brick-paragraph',
32832 if(this.href.length){
32833 cfg.href = this.href;
32836 var cn = cfg.cn[1].cn;
32838 if(this.title.length){
32841 cls: 'masonry-brick-title',
32846 if(this.html.length){
32849 cls: 'masonry-brick-text',
32854 if (!this.title.length && !this.html.length) {
32855 cfg.cn[1].cls += ' hide';
32858 if(this.bgimage.length){
32861 cls: 'masonry-brick-image-view',
32866 if(this.videourl.length){
32867 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32868 // youtube support only?
32871 cls: 'masonry-brick-image-view',
32874 allowfullscreen : true
32882 getSplitAutoCreate : function()
32884 var cls = 'masonry-brick masonry-brick-split';
32886 if(this.href.length){
32887 cls += ' masonry-brick-link';
32890 if(this.bgimage.length){
32891 cls += ' masonry-brick-image';
32895 cls += ' masonry-' + this.size + '-brick';
32898 switch (this.placetitle) {
32900 cls += ' masonry-center-title';
32903 cls += ' masonry-bottom-title';
32906 if(!this.bgimage.length){
32907 cls += ' masonry-center-title';
32910 if(this.bgimage.length){
32911 cls += ' masonry-bottom-title';
32917 cls += ' ' + this.cls;
32921 tag: (this.href.length) ? 'a' : 'div',
32926 cls: 'masonry-brick-split-head',
32930 cls: 'masonry-brick-paragraph',
32937 cls: 'masonry-brick-split-body',
32943 if(this.href.length){
32944 cfg.href = this.href;
32947 if(this.title.length){
32948 cfg.cn[0].cn[0].cn.push({
32950 cls: 'masonry-brick-title',
32955 if(this.html.length){
32956 cfg.cn[1].cn.push({
32958 cls: 'masonry-brick-text',
32963 if(this.bgimage.length){
32964 cfg.cn[0].cn.push({
32966 cls: 'masonry-brick-image-view',
32971 if(this.videourl.length){
32972 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32973 // youtube support only?
32974 cfg.cn[0].cn.cn.push({
32976 cls: 'masonry-brick-image-view',
32979 allowfullscreen : true
32986 initEvents: function()
32988 switch (this.size) {
33021 this.el.on('touchstart', this.onTouchStart, this);
33022 this.el.on('touchmove', this.onTouchMove, this);
33023 this.el.on('touchend', this.onTouchEnd, this);
33024 this.el.on('contextmenu', this.onContextMenu, this);
33026 this.el.on('mouseenter' ,this.enter, this);
33027 this.el.on('mouseleave', this.leave, this);
33028 this.el.on('click', this.onClick, this);
33031 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33032 this.parent().bricks.push(this);
33037 onClick: function(e, el)
33039 var time = this.endTimer - this.startTimer;
33040 // Roo.log(e.preventDefault());
33043 e.preventDefault();
33048 if(!this.preventDefault){
33052 e.preventDefault();
33054 if (this.activeClass != '') {
33055 this.selectBrick();
33058 this.fireEvent('click', this, e);
33061 enter: function(e, el)
33063 e.preventDefault();
33065 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33069 if(this.bgimage.length && this.html.length){
33070 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33074 leave: function(e, el)
33076 e.preventDefault();
33078 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33082 if(this.bgimage.length && this.html.length){
33083 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33087 onTouchStart: function(e, el)
33089 // e.preventDefault();
33091 this.touchmoved = false;
33093 if(!this.isFitContainer){
33097 if(!this.bgimage.length || !this.html.length){
33101 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33103 this.timer = new Date().getTime();
33107 onTouchMove: function(e, el)
33109 this.touchmoved = true;
33112 onContextMenu : function(e,el)
33114 e.preventDefault();
33115 e.stopPropagation();
33119 onTouchEnd: function(e, el)
33121 // e.preventDefault();
33123 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33130 if(!this.bgimage.length || !this.html.length){
33132 if(this.href.length){
33133 window.location.href = this.href;
33139 if(!this.isFitContainer){
33143 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33145 window.location.href = this.href;
33148 //selection on single brick only
33149 selectBrick : function() {
33151 if (!this.parentId) {
33155 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33156 var index = m.selectedBrick.indexOf(this.id);
33159 m.selectedBrick.splice(index,1);
33160 this.el.removeClass(this.activeClass);
33164 for(var i = 0; i < m.selectedBrick.length; i++) {
33165 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33166 b.el.removeClass(b.activeClass);
33169 m.selectedBrick = [];
33171 m.selectedBrick.push(this.id);
33172 this.el.addClass(this.activeClass);
33176 isSelected : function(){
33177 return this.el.hasClass(this.activeClass);
33182 Roo.apply(Roo.bootstrap.MasonryBrick, {
33185 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33187 * register a Masonry Brick
33188 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33191 register : function(brick)
33193 //this.groups[brick.id] = brick;
33194 this.groups.add(brick.id, brick);
33197 * fetch a masonry brick based on the masonry brick ID
33198 * @param {string} the masonry brick to add
33199 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33202 get: function(brick_id)
33204 // if (typeof(this.groups[brick_id]) == 'undefined') {
33207 // return this.groups[brick_id] ;
33209 if(this.groups.key(brick_id)) {
33210 return this.groups.key(brick_id);
33228 * @class Roo.bootstrap.Brick
33229 * @extends Roo.bootstrap.Component
33230 * Bootstrap Brick class
33233 * Create a new Brick
33234 * @param {Object} config The config object
33237 Roo.bootstrap.Brick = function(config){
33238 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33244 * When a Brick is click
33245 * @param {Roo.bootstrap.Brick} this
33246 * @param {Roo.EventObject} e
33252 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33255 * @cfg {String} title
33259 * @cfg {String} html
33263 * @cfg {String} bgimage
33267 * @cfg {String} cls
33271 * @cfg {String} href
33275 * @cfg {String} video
33279 * @cfg {Boolean} square
33283 getAutoCreate : function()
33285 var cls = 'roo-brick';
33287 if(this.href.length){
33288 cls += ' roo-brick-link';
33291 if(this.bgimage.length){
33292 cls += ' roo-brick-image';
33295 if(!this.html.length && !this.bgimage.length){
33296 cls += ' roo-brick-center-title';
33299 if(!this.html.length && this.bgimage.length){
33300 cls += ' roo-brick-bottom-title';
33304 cls += ' ' + this.cls;
33308 tag: (this.href.length) ? 'a' : 'div',
33313 cls: 'roo-brick-paragraph',
33319 if(this.href.length){
33320 cfg.href = this.href;
33323 var cn = cfg.cn[0].cn;
33325 if(this.title.length){
33328 cls: 'roo-brick-title',
33333 if(this.html.length){
33336 cls: 'roo-brick-text',
33343 if(this.bgimage.length){
33346 cls: 'roo-brick-image-view',
33354 initEvents: function()
33356 if(this.title.length || this.html.length){
33357 this.el.on('mouseenter' ,this.enter, this);
33358 this.el.on('mouseleave', this.leave, this);
33361 Roo.EventManager.onWindowResize(this.resize, this);
33363 if(this.bgimage.length){
33364 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33365 this.imageEl.on('load', this.onImageLoad, this);
33372 onImageLoad : function()
33377 resize : function()
33379 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33381 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33383 if(this.bgimage.length){
33384 var image = this.el.select('.roo-brick-image-view', true).first();
33386 image.setWidth(paragraph.getWidth());
33389 image.setHeight(paragraph.getWidth());
33392 this.el.setHeight(image.getHeight());
33393 paragraph.setHeight(image.getHeight());
33399 enter: function(e, el)
33401 e.preventDefault();
33403 if(this.bgimage.length){
33404 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33405 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33409 leave: function(e, el)
33411 e.preventDefault();
33413 if(this.bgimage.length){
33414 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33415 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33430 * @class Roo.bootstrap.NumberField
33431 * @extends Roo.bootstrap.Input
33432 * Bootstrap NumberField class
33438 * Create a new NumberField
33439 * @param {Object} config The config object
33442 Roo.bootstrap.NumberField = function(config){
33443 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33446 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33449 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33451 allowDecimals : true,
33453 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33455 decimalSeparator : ".",
33457 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33459 decimalPrecision : 2,
33461 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33463 allowNegative : true,
33466 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33470 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33472 minValue : Number.NEGATIVE_INFINITY,
33474 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33476 maxValue : Number.MAX_VALUE,
33478 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33480 minText : "The minimum value for this field is {0}",
33482 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33484 maxText : "The maximum value for this field is {0}",
33486 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33487 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33489 nanText : "{0} is not a valid number",
33491 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33493 thousandsDelimiter : false,
33495 * @cfg {String} valueAlign alignment of value
33497 valueAlign : "left",
33499 getAutoCreate : function()
33501 var hiddenInput = {
33505 cls: 'hidden-number-input'
33509 hiddenInput.name = this.name;
33514 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33516 this.name = hiddenInput.name;
33518 if(cfg.cn.length > 0) {
33519 cfg.cn.push(hiddenInput);
33526 initEvents : function()
33528 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33530 var allowed = "0123456789";
33532 if(this.allowDecimals){
33533 allowed += this.decimalSeparator;
33536 if(this.allowNegative){
33540 if(this.thousandsDelimiter) {
33544 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33546 var keyPress = function(e){
33548 var k = e.getKey();
33550 var c = e.getCharCode();
33553 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33554 allowed.indexOf(String.fromCharCode(c)) === -1
33560 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33564 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33569 this.el.on("keypress", keyPress, this);
33572 validateValue : function(value)
33575 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33579 var num = this.parseValue(value);
33582 this.markInvalid(String.format(this.nanText, value));
33586 if(num < this.minValue){
33587 this.markInvalid(String.format(this.minText, this.minValue));
33591 if(num > this.maxValue){
33592 this.markInvalid(String.format(this.maxText, this.maxValue));
33599 getValue : function()
33601 var v = this.hiddenEl().getValue();
33603 return this.fixPrecision(this.parseValue(v));
33606 parseValue : function(value)
33608 if(this.thousandsDelimiter) {
33610 r = new RegExp(",", "g");
33611 value = value.replace(r, "");
33614 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33615 return isNaN(value) ? '' : value;
33618 fixPrecision : function(value)
33620 if(this.thousandsDelimiter) {
33622 r = new RegExp(",", "g");
33623 value = value.replace(r, "");
33626 var nan = isNaN(value);
33628 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33629 return nan ? '' : value;
33631 return parseFloat(value).toFixed(this.decimalPrecision);
33634 setValue : function(v)
33636 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33642 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33644 this.inputEl().dom.value = (v == '') ? '' :
33645 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33647 if(!this.allowZero && v === '0') {
33648 this.hiddenEl().dom.value = '';
33649 this.inputEl().dom.value = '';
33656 decimalPrecisionFcn : function(v)
33658 return Math.floor(v);
33661 beforeBlur : function()
33663 var v = this.parseValue(this.getRawValue());
33665 if(v || v === 0 || v === ''){
33670 hiddenEl : function()
33672 return this.el.select('input.hidden-number-input',true).first();
33684 * @class Roo.bootstrap.DocumentSlider
33685 * @extends Roo.bootstrap.Component
33686 * Bootstrap DocumentSlider class
33689 * Create a new DocumentViewer
33690 * @param {Object} config The config object
33693 Roo.bootstrap.DocumentSlider = function(config){
33694 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33701 * Fire after initEvent
33702 * @param {Roo.bootstrap.DocumentSlider} this
33707 * Fire after update
33708 * @param {Roo.bootstrap.DocumentSlider} this
33714 * @param {Roo.bootstrap.DocumentSlider} this
33720 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33726 getAutoCreate : function()
33730 cls : 'roo-document-slider',
33734 cls : 'roo-document-slider-header',
33738 cls : 'roo-document-slider-header-title'
33744 cls : 'roo-document-slider-body',
33748 cls : 'roo-document-slider-prev',
33752 cls : 'fa fa-chevron-left'
33758 cls : 'roo-document-slider-thumb',
33762 cls : 'roo-document-slider-image'
33768 cls : 'roo-document-slider-next',
33772 cls : 'fa fa-chevron-right'
33784 initEvents : function()
33786 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33787 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33789 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33790 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33792 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33793 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33795 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33796 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33798 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33799 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33801 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33802 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33804 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33805 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33807 this.thumbEl.on('click', this.onClick, this);
33809 this.prevIndicator.on('click', this.prev, this);
33811 this.nextIndicator.on('click', this.next, this);
33815 initial : function()
33817 if(this.files.length){
33818 this.indicator = 1;
33822 this.fireEvent('initial', this);
33825 update : function()
33827 this.imageEl.attr('src', this.files[this.indicator - 1]);
33829 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33831 this.prevIndicator.show();
33833 if(this.indicator == 1){
33834 this.prevIndicator.hide();
33837 this.nextIndicator.show();
33839 if(this.indicator == this.files.length){
33840 this.nextIndicator.hide();
33843 this.thumbEl.scrollTo('top');
33845 this.fireEvent('update', this);
33848 onClick : function(e)
33850 e.preventDefault();
33852 this.fireEvent('click', this);
33857 e.preventDefault();
33859 this.indicator = Math.max(1, this.indicator - 1);
33866 e.preventDefault();
33868 this.indicator = Math.min(this.files.length, this.indicator + 1);
33882 * @class Roo.bootstrap.RadioSet
33883 * @extends Roo.bootstrap.Input
33884 * Bootstrap RadioSet class
33885 * @cfg {String} indicatorpos (left|right) default left
33886 * @cfg {Boolean} inline (true|false) inline the element (default true)
33887 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33889 * Create a new RadioSet
33890 * @param {Object} config The config object
33893 Roo.bootstrap.RadioSet = function(config){
33895 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33899 Roo.bootstrap.RadioSet.register(this);
33904 * Fires when the element is checked or unchecked.
33905 * @param {Roo.bootstrap.RadioSet} this This radio
33906 * @param {Roo.bootstrap.Radio} item The checked item
33911 * Fires when the element is click.
33912 * @param {Roo.bootstrap.RadioSet} this This radio set
33913 * @param {Roo.bootstrap.Radio} item The checked item
33914 * @param {Roo.EventObject} e The event object
33921 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33929 indicatorpos : 'left',
33931 getAutoCreate : function()
33935 cls : 'roo-radio-set-label',
33939 html : this.fieldLabel
33944 if(this.indicatorpos == 'left'){
33947 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33948 tooltip : 'This field is required'
33953 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33954 tooltip : 'This field is required'
33960 cls : 'roo-radio-set-items'
33963 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33965 if (align === 'left' && this.fieldLabel.length) {
33968 cls : "roo-radio-set-right",
33974 if(this.labelWidth > 12){
33975 label.style = "width: " + this.labelWidth + 'px';
33978 if(this.labelWidth < 13 && this.labelmd == 0){
33979 this.labelmd = this.labelWidth;
33982 if(this.labellg > 0){
33983 label.cls += ' col-lg-' + this.labellg;
33984 items.cls += ' col-lg-' + (12 - this.labellg);
33987 if(this.labelmd > 0){
33988 label.cls += ' col-md-' + this.labelmd;
33989 items.cls += ' col-md-' + (12 - this.labelmd);
33992 if(this.labelsm > 0){
33993 label.cls += ' col-sm-' + this.labelsm;
33994 items.cls += ' col-sm-' + (12 - this.labelsm);
33997 if(this.labelxs > 0){
33998 label.cls += ' col-xs-' + this.labelxs;
33999 items.cls += ' col-xs-' + (12 - this.labelxs);
34005 cls : 'roo-radio-set',
34009 cls : 'roo-radio-set-input',
34012 value : this.value ? this.value : ''
34019 if(this.weight.length){
34020 cfg.cls += ' roo-radio-' + this.weight;
34024 cfg.cls += ' roo-radio-set-inline';
34028 ['xs','sm','md','lg'].map(function(size){
34029 if (settings[size]) {
34030 cfg.cls += ' col-' + size + '-' + settings[size];
34038 initEvents : function()
34040 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34041 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34043 if(!this.fieldLabel.length){
34044 this.labelEl.hide();
34047 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34048 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34050 this.indicator = this.indicatorEl();
34052 if(this.indicator){
34053 this.indicator.addClass('invisible');
34056 this.originalValue = this.getValue();
34060 inputEl: function ()
34062 return this.el.select('.roo-radio-set-input', true).first();
34065 getChildContainer : function()
34067 return this.itemsEl;
34070 register : function(item)
34072 this.radioes.push(item);
34076 validate : function()
34078 if(this.getVisibilityEl().hasClass('hidden')){
34084 Roo.each(this.radioes, function(i){
34093 if(this.allowBlank) {
34097 if(this.disabled || valid){
34102 this.markInvalid();
34107 markValid : function()
34109 if(this.labelEl.isVisible(true)){
34110 this.indicatorEl().removeClass('visible');
34111 this.indicatorEl().addClass('invisible');
34114 this.el.removeClass([this.invalidClass, this.validClass]);
34115 this.el.addClass(this.validClass);
34117 this.fireEvent('valid', this);
34120 markInvalid : function(msg)
34122 if(this.allowBlank || this.disabled){
34126 if(this.labelEl.isVisible(true)){
34127 this.indicatorEl().removeClass('invisible');
34128 this.indicatorEl().addClass('visible');
34131 this.el.removeClass([this.invalidClass, this.validClass]);
34132 this.el.addClass(this.invalidClass);
34134 this.fireEvent('invalid', this, msg);
34138 setValue : function(v, suppressEvent)
34140 if(this.value === v){
34147 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34150 Roo.each(this.radioes, function(i){
34152 i.el.removeClass('checked');
34155 Roo.each(this.radioes, function(i){
34157 if(i.value === v || i.value.toString() === v.toString()){
34159 i.el.addClass('checked');
34161 if(suppressEvent !== true){
34162 this.fireEvent('check', this, i);
34173 clearInvalid : function(){
34175 if(!this.el || this.preventMark){
34179 this.el.removeClass([this.invalidClass]);
34181 this.fireEvent('valid', this);
34186 Roo.apply(Roo.bootstrap.RadioSet, {
34190 register : function(set)
34192 this.groups[set.name] = set;
34195 get: function(name)
34197 if (typeof(this.groups[name]) == 'undefined') {
34201 return this.groups[name] ;
34207 * Ext JS Library 1.1.1
34208 * Copyright(c) 2006-2007, Ext JS, LLC.
34210 * Originally Released Under LGPL - original licence link has changed is not relivant.
34213 * <script type="text/javascript">
34218 * @class Roo.bootstrap.SplitBar
34219 * @extends Roo.util.Observable
34220 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34224 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34225 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34226 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34227 split.minSize = 100;
34228 split.maxSize = 600;
34229 split.animate = true;
34230 split.on('moved', splitterMoved);
34233 * Create a new SplitBar
34234 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34235 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34236 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34237 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34238 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34239 position of the SplitBar).
34241 Roo.bootstrap.SplitBar = function(cfg){
34246 // dragElement : elm
34247 // resizingElement: el,
34249 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34250 // placement : Roo.bootstrap.SplitBar.LEFT ,
34251 // existingProxy ???
34254 this.el = Roo.get(cfg.dragElement, true);
34255 this.el.dom.unselectable = "on";
34257 this.resizingEl = Roo.get(cfg.resizingElement, true);
34261 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34262 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34265 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34268 * The minimum size of the resizing element. (Defaults to 0)
34274 * The maximum size of the resizing element. (Defaults to 2000)
34277 this.maxSize = 2000;
34280 * Whether to animate the transition to the new size
34283 this.animate = false;
34286 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34289 this.useShim = false;
34294 if(!cfg.existingProxy){
34296 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34298 this.proxy = Roo.get(cfg.existingProxy).dom;
34301 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34304 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34307 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34310 this.dragSpecs = {};
34313 * @private The adapter to use to positon and resize elements
34315 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34316 this.adapter.init(this);
34318 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34320 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34321 this.el.addClass("roo-splitbar-h");
34324 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34325 this.el.addClass("roo-splitbar-v");
34331 * Fires when the splitter is moved (alias for {@link #event-moved})
34332 * @param {Roo.bootstrap.SplitBar} this
34333 * @param {Number} newSize the new width or height
34338 * Fires when the splitter is moved
34339 * @param {Roo.bootstrap.SplitBar} this
34340 * @param {Number} newSize the new width or height
34344 * @event beforeresize
34345 * Fires before the splitter is dragged
34346 * @param {Roo.bootstrap.SplitBar} this
34348 "beforeresize" : true,
34350 "beforeapply" : true
34353 Roo.util.Observable.call(this);
34356 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34357 onStartProxyDrag : function(x, y){
34358 this.fireEvent("beforeresize", this);
34360 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34362 o.enableDisplayMode("block");
34363 // all splitbars share the same overlay
34364 Roo.bootstrap.SplitBar.prototype.overlay = o;
34366 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34367 this.overlay.show();
34368 Roo.get(this.proxy).setDisplayed("block");
34369 var size = this.adapter.getElementSize(this);
34370 this.activeMinSize = this.getMinimumSize();;
34371 this.activeMaxSize = this.getMaximumSize();;
34372 var c1 = size - this.activeMinSize;
34373 var c2 = Math.max(this.activeMaxSize - size, 0);
34374 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34375 this.dd.resetConstraints();
34376 this.dd.setXConstraint(
34377 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34378 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34380 this.dd.setYConstraint(0, 0);
34382 this.dd.resetConstraints();
34383 this.dd.setXConstraint(0, 0);
34384 this.dd.setYConstraint(
34385 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34386 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34389 this.dragSpecs.startSize = size;
34390 this.dragSpecs.startPoint = [x, y];
34391 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34395 * @private Called after the drag operation by the DDProxy
34397 onEndProxyDrag : function(e){
34398 Roo.get(this.proxy).setDisplayed(false);
34399 var endPoint = Roo.lib.Event.getXY(e);
34401 this.overlay.hide();
34404 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34405 newSize = this.dragSpecs.startSize +
34406 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34407 endPoint[0] - this.dragSpecs.startPoint[0] :
34408 this.dragSpecs.startPoint[0] - endPoint[0]
34411 newSize = this.dragSpecs.startSize +
34412 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34413 endPoint[1] - this.dragSpecs.startPoint[1] :
34414 this.dragSpecs.startPoint[1] - endPoint[1]
34417 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34418 if(newSize != this.dragSpecs.startSize){
34419 if(this.fireEvent('beforeapply', this, newSize) !== false){
34420 this.adapter.setElementSize(this, newSize);
34421 this.fireEvent("moved", this, newSize);
34422 this.fireEvent("resize", this, newSize);
34428 * Get the adapter this SplitBar uses
34429 * @return The adapter object
34431 getAdapter : function(){
34432 return this.adapter;
34436 * Set the adapter this SplitBar uses
34437 * @param {Object} adapter A SplitBar adapter object
34439 setAdapter : function(adapter){
34440 this.adapter = adapter;
34441 this.adapter.init(this);
34445 * Gets the minimum size for the resizing element
34446 * @return {Number} The minimum size
34448 getMinimumSize : function(){
34449 return this.minSize;
34453 * Sets the minimum size for the resizing element
34454 * @param {Number} minSize The minimum size
34456 setMinimumSize : function(minSize){
34457 this.minSize = minSize;
34461 * Gets the maximum size for the resizing element
34462 * @return {Number} The maximum size
34464 getMaximumSize : function(){
34465 return this.maxSize;
34469 * Sets the maximum size for the resizing element
34470 * @param {Number} maxSize The maximum size
34472 setMaximumSize : function(maxSize){
34473 this.maxSize = maxSize;
34477 * Sets the initialize size for the resizing element
34478 * @param {Number} size The initial size
34480 setCurrentSize : function(size){
34481 var oldAnimate = this.animate;
34482 this.animate = false;
34483 this.adapter.setElementSize(this, size);
34484 this.animate = oldAnimate;
34488 * Destroy this splitbar.
34489 * @param {Boolean} removeEl True to remove the element
34491 destroy : function(removeEl){
34493 this.shim.remove();
34496 this.proxy.parentNode.removeChild(this.proxy);
34504 * @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.
34506 Roo.bootstrap.SplitBar.createProxy = function(dir){
34507 var proxy = new Roo.Element(document.createElement("div"));
34508 proxy.unselectable();
34509 var cls = 'roo-splitbar-proxy';
34510 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34511 document.body.appendChild(proxy.dom);
34516 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34517 * Default Adapter. It assumes the splitter and resizing element are not positioned
34518 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34520 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34523 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34524 // do nothing for now
34525 init : function(s){
34529 * Called before drag operations to get the current size of the resizing element.
34530 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34532 getElementSize : function(s){
34533 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34534 return s.resizingEl.getWidth();
34536 return s.resizingEl.getHeight();
34541 * Called after drag operations to set the size of the resizing element.
34542 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34543 * @param {Number} newSize The new size to set
34544 * @param {Function} onComplete A function to be invoked when resizing is complete
34546 setElementSize : function(s, newSize, onComplete){
34547 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34549 s.resizingEl.setWidth(newSize);
34551 onComplete(s, newSize);
34554 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34559 s.resizingEl.setHeight(newSize);
34561 onComplete(s, newSize);
34564 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34571 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34572 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34573 * Adapter that moves the splitter element to align with the resized sizing element.
34574 * Used with an absolute positioned SplitBar.
34575 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34576 * document.body, make sure you assign an id to the body element.
34578 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34579 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34580 this.container = Roo.get(container);
34583 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34584 init : function(s){
34585 this.basic.init(s);
34588 getElementSize : function(s){
34589 return this.basic.getElementSize(s);
34592 setElementSize : function(s, newSize, onComplete){
34593 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34596 moveSplitter : function(s){
34597 var yes = Roo.bootstrap.SplitBar;
34598 switch(s.placement){
34600 s.el.setX(s.resizingEl.getRight());
34603 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34606 s.el.setY(s.resizingEl.getBottom());
34609 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34616 * Orientation constant - Create a vertical SplitBar
34620 Roo.bootstrap.SplitBar.VERTICAL = 1;
34623 * Orientation constant - Create a horizontal SplitBar
34627 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34630 * Placement constant - The resizing element is to the left of the splitter element
34634 Roo.bootstrap.SplitBar.LEFT = 1;
34637 * Placement constant - The resizing element is to the right of the splitter element
34641 Roo.bootstrap.SplitBar.RIGHT = 2;
34644 * Placement constant - The resizing element is positioned above the splitter element
34648 Roo.bootstrap.SplitBar.TOP = 3;
34651 * Placement constant - The resizing element is positioned under splitter element
34655 Roo.bootstrap.SplitBar.BOTTOM = 4;
34656 Roo.namespace("Roo.bootstrap.layout");/*
34658 * Ext JS Library 1.1.1
34659 * Copyright(c) 2006-2007, Ext JS, LLC.
34661 * Originally Released Under LGPL - original licence link has changed is not relivant.
34664 * <script type="text/javascript">
34668 * @class Roo.bootstrap.layout.Manager
34669 * @extends Roo.bootstrap.Component
34670 * Base class for layout managers.
34672 Roo.bootstrap.layout.Manager = function(config)
34674 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34680 /** false to disable window resize monitoring @type Boolean */
34681 this.monitorWindowResize = true;
34686 * Fires when a layout is performed.
34687 * @param {Roo.LayoutManager} this
34691 * @event regionresized
34692 * Fires when the user resizes a region.
34693 * @param {Roo.LayoutRegion} region The resized region
34694 * @param {Number} newSize The new size (width for east/west, height for north/south)
34696 "regionresized" : true,
34698 * @event regioncollapsed
34699 * Fires when a region is collapsed.
34700 * @param {Roo.LayoutRegion} region The collapsed region
34702 "regioncollapsed" : true,
34704 * @event regionexpanded
34705 * Fires when a region is expanded.
34706 * @param {Roo.LayoutRegion} region The expanded region
34708 "regionexpanded" : true
34710 this.updating = false;
34713 this.el = Roo.get(config.el);
34719 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34724 monitorWindowResize : true,
34730 onRender : function(ct, position)
34733 this.el = Roo.get(ct);
34736 //this.fireEvent('render',this);
34740 initEvents: function()
34744 // ie scrollbar fix
34745 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34746 document.body.scroll = "no";
34747 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34748 this.el.position('relative');
34750 this.id = this.el.id;
34751 this.el.addClass("roo-layout-container");
34752 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34753 if(this.el.dom != document.body ) {
34754 this.el.on('resize', this.layout,this);
34755 this.el.on('show', this.layout,this);
34761 * Returns true if this layout is currently being updated
34762 * @return {Boolean}
34764 isUpdating : function(){
34765 return this.updating;
34769 * Suspend the LayoutManager from doing auto-layouts while
34770 * making multiple add or remove calls
34772 beginUpdate : function(){
34773 this.updating = true;
34777 * Restore auto-layouts and optionally disable the manager from performing a layout
34778 * @param {Boolean} noLayout true to disable a layout update
34780 endUpdate : function(noLayout){
34781 this.updating = false;
34787 layout: function(){
34791 onRegionResized : function(region, newSize){
34792 this.fireEvent("regionresized", region, newSize);
34796 onRegionCollapsed : function(region){
34797 this.fireEvent("regioncollapsed", region);
34800 onRegionExpanded : function(region){
34801 this.fireEvent("regionexpanded", region);
34805 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34806 * performs box-model adjustments.
34807 * @return {Object} The size as an object {width: (the width), height: (the height)}
34809 getViewSize : function()
34812 if(this.el.dom != document.body){
34813 size = this.el.getSize();
34815 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34817 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34818 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34823 * Returns the Element this layout is bound to.
34824 * @return {Roo.Element}
34826 getEl : function(){
34831 * Returns the specified region.
34832 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34833 * @return {Roo.LayoutRegion}
34835 getRegion : function(target){
34836 return this.regions[target.toLowerCase()];
34839 onWindowResize : function(){
34840 if(this.monitorWindowResize){
34847 * Ext JS Library 1.1.1
34848 * Copyright(c) 2006-2007, Ext JS, LLC.
34850 * Originally Released Under LGPL - original licence link has changed is not relivant.
34853 * <script type="text/javascript">
34856 * @class Roo.bootstrap.layout.Border
34857 * @extends Roo.bootstrap.layout.Manager
34858 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34859 * please see: examples/bootstrap/nested.html<br><br>
34861 <b>The container the layout is rendered into can be either the body element or any other element.
34862 If it is not the body element, the container needs to either be an absolute positioned element,
34863 or you will need to add "position:relative" to the css of the container. You will also need to specify
34864 the container size if it is not the body element.</b>
34867 * Create a new Border
34868 * @param {Object} config Configuration options
34870 Roo.bootstrap.layout.Border = function(config){
34871 config = config || {};
34872 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34876 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34877 if(config[region]){
34878 config[region].region = region;
34879 this.addRegion(config[region]);
34885 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34887 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34889 * Creates and adds a new region if it doesn't already exist.
34890 * @param {String} target The target region key (north, south, east, west or center).
34891 * @param {Object} config The regions config object
34892 * @return {BorderLayoutRegion} The new region
34894 addRegion : function(config)
34896 if(!this.regions[config.region]){
34897 var r = this.factory(config);
34898 this.bindRegion(r);
34900 return this.regions[config.region];
34904 bindRegion : function(r){
34905 this.regions[r.config.region] = r;
34907 r.on("visibilitychange", this.layout, this);
34908 r.on("paneladded", this.layout, this);
34909 r.on("panelremoved", this.layout, this);
34910 r.on("invalidated", this.layout, this);
34911 r.on("resized", this.onRegionResized, this);
34912 r.on("collapsed", this.onRegionCollapsed, this);
34913 r.on("expanded", this.onRegionExpanded, this);
34917 * Performs a layout update.
34919 layout : function()
34921 if(this.updating) {
34925 // render all the rebions if they have not been done alreayd?
34926 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34927 if(this.regions[region] && !this.regions[region].bodyEl){
34928 this.regions[region].onRender(this.el)
34932 var size = this.getViewSize();
34933 var w = size.width;
34934 var h = size.height;
34939 //var x = 0, y = 0;
34941 var rs = this.regions;
34942 var north = rs["north"];
34943 var south = rs["south"];
34944 var west = rs["west"];
34945 var east = rs["east"];
34946 var center = rs["center"];
34947 //if(this.hideOnLayout){ // not supported anymore
34948 //c.el.setStyle("display", "none");
34950 if(north && north.isVisible()){
34951 var b = north.getBox();
34952 var m = north.getMargins();
34953 b.width = w - (m.left+m.right);
34956 centerY = b.height + b.y + m.bottom;
34957 centerH -= centerY;
34958 north.updateBox(this.safeBox(b));
34960 if(south && south.isVisible()){
34961 var b = south.getBox();
34962 var m = south.getMargins();
34963 b.width = w - (m.left+m.right);
34965 var totalHeight = (b.height + m.top + m.bottom);
34966 b.y = h - totalHeight + m.top;
34967 centerH -= totalHeight;
34968 south.updateBox(this.safeBox(b));
34970 if(west && west.isVisible()){
34971 var b = west.getBox();
34972 var m = west.getMargins();
34973 b.height = centerH - (m.top+m.bottom);
34975 b.y = centerY + m.top;
34976 var totalWidth = (b.width + m.left + m.right);
34977 centerX += totalWidth;
34978 centerW -= totalWidth;
34979 west.updateBox(this.safeBox(b));
34981 if(east && east.isVisible()){
34982 var b = east.getBox();
34983 var m = east.getMargins();
34984 b.height = centerH - (m.top+m.bottom);
34985 var totalWidth = (b.width + m.left + m.right);
34986 b.x = w - totalWidth + m.left;
34987 b.y = centerY + m.top;
34988 centerW -= totalWidth;
34989 east.updateBox(this.safeBox(b));
34992 var m = center.getMargins();
34994 x: centerX + m.left,
34995 y: centerY + m.top,
34996 width: centerW - (m.left+m.right),
34997 height: centerH - (m.top+m.bottom)
34999 //if(this.hideOnLayout){
35000 //center.el.setStyle("display", "block");
35002 center.updateBox(this.safeBox(centerBox));
35005 this.fireEvent("layout", this);
35009 safeBox : function(box){
35010 box.width = Math.max(0, box.width);
35011 box.height = Math.max(0, box.height);
35016 * Adds a ContentPanel (or subclass) to this layout.
35017 * @param {String} target The target region key (north, south, east, west or center).
35018 * @param {Roo.ContentPanel} panel The panel to add
35019 * @return {Roo.ContentPanel} The added panel
35021 add : function(target, panel){
35023 target = target.toLowerCase();
35024 return this.regions[target].add(panel);
35028 * Remove a ContentPanel (or subclass) to this layout.
35029 * @param {String} target The target region key (north, south, east, west or center).
35030 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35031 * @return {Roo.ContentPanel} The removed panel
35033 remove : function(target, panel){
35034 target = target.toLowerCase();
35035 return this.regions[target].remove(panel);
35039 * Searches all regions for a panel with the specified id
35040 * @param {String} panelId
35041 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35043 findPanel : function(panelId){
35044 var rs = this.regions;
35045 for(var target in rs){
35046 if(typeof rs[target] != "function"){
35047 var p = rs[target].getPanel(panelId);
35057 * Searches all regions for a panel with the specified id and activates (shows) it.
35058 * @param {String/ContentPanel} panelId The panels id or the panel itself
35059 * @return {Roo.ContentPanel} The shown panel or null
35061 showPanel : function(panelId) {
35062 var rs = this.regions;
35063 for(var target in rs){
35064 var r = rs[target];
35065 if(typeof r != "function"){
35066 if(r.hasPanel(panelId)){
35067 return r.showPanel(panelId);
35075 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35076 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35079 restoreState : function(provider){
35081 provider = Roo.state.Manager;
35083 var sm = new Roo.LayoutStateManager();
35084 sm.init(this, provider);
35090 * Adds a xtype elements to the layout.
35094 xtype : 'ContentPanel',
35101 xtype : 'NestedLayoutPanel',
35107 items : [ ... list of content panels or nested layout panels.. ]
35111 * @param {Object} cfg Xtype definition of item to add.
35113 addxtype : function(cfg)
35115 // basically accepts a pannel...
35116 // can accept a layout region..!?!?
35117 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35120 // theory? children can only be panels??
35122 //if (!cfg.xtype.match(/Panel$/)) {
35127 if (typeof(cfg.region) == 'undefined') {
35128 Roo.log("Failed to add Panel, region was not set");
35132 var region = cfg.region;
35138 xitems = cfg.items;
35145 case 'Content': // ContentPanel (el, cfg)
35146 case 'Scroll': // ContentPanel (el, cfg)
35148 cfg.autoCreate = true;
35149 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35151 // var el = this.el.createChild();
35152 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35155 this.add(region, ret);
35159 case 'TreePanel': // our new panel!
35160 cfg.el = this.el.createChild();
35161 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35162 this.add(region, ret);
35167 // create a new Layout (which is a Border Layout...
35169 var clayout = cfg.layout;
35170 clayout.el = this.el.createChild();
35171 clayout.items = clayout.items || [];
35175 // replace this exitems with the clayout ones..
35176 xitems = clayout.items;
35178 // force background off if it's in center...
35179 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35180 cfg.background = false;
35182 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35185 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35186 //console.log('adding nested layout panel ' + cfg.toSource());
35187 this.add(region, ret);
35188 nb = {}; /// find first...
35193 // needs grid and region
35195 //var el = this.getRegion(region).el.createChild();
35197 *var el = this.el.createChild();
35198 // create the grid first...
35199 cfg.grid.container = el;
35200 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35203 if (region == 'center' && this.active ) {
35204 cfg.background = false;
35207 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35209 this.add(region, ret);
35211 if (cfg.background) {
35212 // render grid on panel activation (if panel background)
35213 ret.on('activate', function(gp) {
35214 if (!gp.grid.rendered) {
35215 // gp.grid.render(el);
35219 // cfg.grid.render(el);
35225 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35226 // it was the old xcomponent building that caused this before.
35227 // espeically if border is the top element in the tree.
35237 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35239 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35240 this.add(region, ret);
35244 throw "Can not add '" + cfg.xtype + "' to Border";
35250 this.beginUpdate();
35254 Roo.each(xitems, function(i) {
35255 region = nb && i.region ? i.region : false;
35257 var add = ret.addxtype(i);
35260 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35261 if (!i.background) {
35262 abn[region] = nb[region] ;
35269 // make the last non-background panel active..
35270 //if (nb) { Roo.log(abn); }
35273 for(var r in abn) {
35274 region = this.getRegion(r);
35276 // tried using nb[r], but it does not work..
35278 region.showPanel(abn[r]);
35289 factory : function(cfg)
35292 var validRegions = Roo.bootstrap.layout.Border.regions;
35294 var target = cfg.region;
35297 var r = Roo.bootstrap.layout;
35301 return new r.North(cfg);
35303 return new r.South(cfg);
35305 return new r.East(cfg);
35307 return new r.West(cfg);
35309 return new r.Center(cfg);
35311 throw 'Layout region "'+target+'" not supported.';
35318 * Ext JS Library 1.1.1
35319 * Copyright(c) 2006-2007, Ext JS, LLC.
35321 * Originally Released Under LGPL - original licence link has changed is not relivant.
35324 * <script type="text/javascript">
35328 * @class Roo.bootstrap.layout.Basic
35329 * @extends Roo.util.Observable
35330 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35331 * and does not have a titlebar, tabs or any other features. All it does is size and position
35332 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35333 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35334 * @cfg {string} region the region that it inhabits..
35335 * @cfg {bool} skipConfig skip config?
35339 Roo.bootstrap.layout.Basic = function(config){
35341 this.mgr = config.mgr;
35343 this.position = config.region;
35345 var skipConfig = config.skipConfig;
35349 * @scope Roo.BasicLayoutRegion
35353 * @event beforeremove
35354 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35355 * @param {Roo.LayoutRegion} this
35356 * @param {Roo.ContentPanel} panel The panel
35357 * @param {Object} e The cancel event object
35359 "beforeremove" : true,
35361 * @event invalidated
35362 * Fires when the layout for this region is changed.
35363 * @param {Roo.LayoutRegion} this
35365 "invalidated" : true,
35367 * @event visibilitychange
35368 * Fires when this region is shown or hidden
35369 * @param {Roo.LayoutRegion} this
35370 * @param {Boolean} visibility true or false
35372 "visibilitychange" : true,
35374 * @event paneladded
35375 * Fires when a panel is added.
35376 * @param {Roo.LayoutRegion} this
35377 * @param {Roo.ContentPanel} panel The panel
35379 "paneladded" : true,
35381 * @event panelremoved
35382 * Fires when a panel is removed.
35383 * @param {Roo.LayoutRegion} this
35384 * @param {Roo.ContentPanel} panel The panel
35386 "panelremoved" : true,
35388 * @event beforecollapse
35389 * Fires when this region before collapse.
35390 * @param {Roo.LayoutRegion} this
35392 "beforecollapse" : true,
35395 * Fires when this region is collapsed.
35396 * @param {Roo.LayoutRegion} this
35398 "collapsed" : true,
35401 * Fires when this region is expanded.
35402 * @param {Roo.LayoutRegion} this
35407 * Fires when this region is slid into view.
35408 * @param {Roo.LayoutRegion} this
35410 "slideshow" : true,
35413 * Fires when this region slides out of view.
35414 * @param {Roo.LayoutRegion} this
35416 "slidehide" : true,
35418 * @event panelactivated
35419 * Fires when a panel is activated.
35420 * @param {Roo.LayoutRegion} this
35421 * @param {Roo.ContentPanel} panel The activated panel
35423 "panelactivated" : true,
35426 * Fires when the user resizes this region.
35427 * @param {Roo.LayoutRegion} this
35428 * @param {Number} newSize The new size (width for east/west, height for north/south)
35432 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35433 this.panels = new Roo.util.MixedCollection();
35434 this.panels.getKey = this.getPanelId.createDelegate(this);
35436 this.activePanel = null;
35437 // ensure listeners are added...
35439 if (config.listeners || config.events) {
35440 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35441 listeners : config.listeners || {},
35442 events : config.events || {}
35446 if(skipConfig !== true){
35447 this.applyConfig(config);
35451 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35453 getPanelId : function(p){
35457 applyConfig : function(config){
35458 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35459 this.config = config;
35464 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35465 * the width, for horizontal (north, south) the height.
35466 * @param {Number} newSize The new width or height
35468 resizeTo : function(newSize){
35469 var el = this.el ? this.el :
35470 (this.activePanel ? this.activePanel.getEl() : null);
35472 switch(this.position){
35475 el.setWidth(newSize);
35476 this.fireEvent("resized", this, newSize);
35480 el.setHeight(newSize);
35481 this.fireEvent("resized", this, newSize);
35487 getBox : function(){
35488 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35491 getMargins : function(){
35492 return this.margins;
35495 updateBox : function(box){
35497 var el = this.activePanel.getEl();
35498 el.dom.style.left = box.x + "px";
35499 el.dom.style.top = box.y + "px";
35500 this.activePanel.setSize(box.width, box.height);
35504 * Returns the container element for this region.
35505 * @return {Roo.Element}
35507 getEl : function(){
35508 return this.activePanel;
35512 * Returns true if this region is currently visible.
35513 * @return {Boolean}
35515 isVisible : function(){
35516 return this.activePanel ? true : false;
35519 setActivePanel : function(panel){
35520 panel = this.getPanel(panel);
35521 if(this.activePanel && this.activePanel != panel){
35522 this.activePanel.setActiveState(false);
35523 this.activePanel.getEl().setLeftTop(-10000,-10000);
35525 this.activePanel = panel;
35526 panel.setActiveState(true);
35528 panel.setSize(this.box.width, this.box.height);
35530 this.fireEvent("panelactivated", this, panel);
35531 this.fireEvent("invalidated");
35535 * Show the specified panel.
35536 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35537 * @return {Roo.ContentPanel} The shown panel or null
35539 showPanel : function(panel){
35540 panel = this.getPanel(panel);
35542 this.setActivePanel(panel);
35548 * Get the active panel for this region.
35549 * @return {Roo.ContentPanel} The active panel or null
35551 getActivePanel : function(){
35552 return this.activePanel;
35556 * Add the passed ContentPanel(s)
35557 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35558 * @return {Roo.ContentPanel} The panel added (if only one was added)
35560 add : function(panel){
35561 if(arguments.length > 1){
35562 for(var i = 0, len = arguments.length; i < len; i++) {
35563 this.add(arguments[i]);
35567 if(this.hasPanel(panel)){
35568 this.showPanel(panel);
35571 var el = panel.getEl();
35572 if(el.dom.parentNode != this.mgr.el.dom){
35573 this.mgr.el.dom.appendChild(el.dom);
35575 if(panel.setRegion){
35576 panel.setRegion(this);
35578 this.panels.add(panel);
35579 el.setStyle("position", "absolute");
35580 if(!panel.background){
35581 this.setActivePanel(panel);
35582 if(this.config.initialSize && this.panels.getCount()==1){
35583 this.resizeTo(this.config.initialSize);
35586 this.fireEvent("paneladded", this, panel);
35591 * Returns true if the panel is in this region.
35592 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35593 * @return {Boolean}
35595 hasPanel : function(panel){
35596 if(typeof panel == "object"){ // must be panel obj
35597 panel = panel.getId();
35599 return this.getPanel(panel) ? true : false;
35603 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35604 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35605 * @param {Boolean} preservePanel Overrides the config preservePanel option
35606 * @return {Roo.ContentPanel} The panel that was removed
35608 remove : function(panel, preservePanel){
35609 panel = this.getPanel(panel);
35614 this.fireEvent("beforeremove", this, panel, e);
35615 if(e.cancel === true){
35618 var panelId = panel.getId();
35619 this.panels.removeKey(panelId);
35624 * Returns the panel specified or null if it's not in this region.
35625 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35626 * @return {Roo.ContentPanel}
35628 getPanel : function(id){
35629 if(typeof id == "object"){ // must be panel obj
35632 return this.panels.get(id);
35636 * Returns this regions position (north/south/east/west/center).
35639 getPosition: function(){
35640 return this.position;
35644 * Ext JS Library 1.1.1
35645 * Copyright(c) 2006-2007, Ext JS, LLC.
35647 * Originally Released Under LGPL - original licence link has changed is not relivant.
35650 * <script type="text/javascript">
35654 * @class Roo.bootstrap.layout.Region
35655 * @extends Roo.bootstrap.layout.Basic
35656 * This class represents a region in a layout manager.
35658 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35659 * @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})
35660 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35661 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35662 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35663 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35664 * @cfg {String} title The title for the region (overrides panel titles)
35665 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35666 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35667 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35668 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35669 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35670 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35671 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35672 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35673 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35674 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35676 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35677 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35678 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35679 * @cfg {Number} width For East/West panels
35680 * @cfg {Number} height For North/South panels
35681 * @cfg {Boolean} split To show the splitter
35682 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35684 * @cfg {string} cls Extra CSS classes to add to region
35686 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35687 * @cfg {string} region the region that it inhabits..
35690 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35691 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35693 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35694 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35695 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35697 Roo.bootstrap.layout.Region = function(config)
35699 this.applyConfig(config);
35701 var mgr = config.mgr;
35702 var pos = config.region;
35703 config.skipConfig = true;
35704 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35707 this.onRender(mgr.el);
35710 this.visible = true;
35711 this.collapsed = false;
35712 this.unrendered_panels = [];
35715 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35717 position: '', // set by wrapper (eg. north/south etc..)
35718 unrendered_panels : null, // unrendered panels.
35719 createBody : function(){
35720 /** This region's body element
35721 * @type Roo.Element */
35722 this.bodyEl = this.el.createChild({
35724 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35728 onRender: function(ctr, pos)
35730 var dh = Roo.DomHelper;
35731 /** This region's container element
35732 * @type Roo.Element */
35733 this.el = dh.append(ctr.dom, {
35735 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35737 /** This region's title element
35738 * @type Roo.Element */
35740 this.titleEl = dh.append(this.el.dom,
35743 unselectable: "on",
35744 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35746 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35747 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35750 this.titleEl.enableDisplayMode();
35751 /** This region's title text element
35752 * @type HTMLElement */
35753 this.titleTextEl = this.titleEl.dom.firstChild;
35754 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35756 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35757 this.closeBtn.enableDisplayMode();
35758 this.closeBtn.on("click", this.closeClicked, this);
35759 this.closeBtn.hide();
35761 this.createBody(this.config);
35762 if(this.config.hideWhenEmpty){
35764 this.on("paneladded", this.validateVisibility, this);
35765 this.on("panelremoved", this.validateVisibility, this);
35767 if(this.autoScroll){
35768 this.bodyEl.setStyle("overflow", "auto");
35770 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35772 //if(c.titlebar !== false){
35773 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35774 this.titleEl.hide();
35776 this.titleEl.show();
35777 if(this.config.title){
35778 this.titleTextEl.innerHTML = this.config.title;
35782 if(this.config.collapsed){
35783 this.collapse(true);
35785 if(this.config.hidden){
35789 if (this.unrendered_panels && this.unrendered_panels.length) {
35790 for (var i =0;i< this.unrendered_panels.length; i++) {
35791 this.add(this.unrendered_panels[i]);
35793 this.unrendered_panels = null;
35799 applyConfig : function(c)
35802 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35803 var dh = Roo.DomHelper;
35804 if(c.titlebar !== false){
35805 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35806 this.collapseBtn.on("click", this.collapse, this);
35807 this.collapseBtn.enableDisplayMode();
35809 if(c.showPin === true || this.showPin){
35810 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35811 this.stickBtn.enableDisplayMode();
35812 this.stickBtn.on("click", this.expand, this);
35813 this.stickBtn.hide();
35818 /** This region's collapsed element
35819 * @type Roo.Element */
35822 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35823 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35826 if(c.floatable !== false){
35827 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35828 this.collapsedEl.on("click", this.collapseClick, this);
35831 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35832 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35833 id: "message", unselectable: "on", style:{"float":"left"}});
35834 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35836 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35837 this.expandBtn.on("click", this.expand, this);
35841 if(this.collapseBtn){
35842 this.collapseBtn.setVisible(c.collapsible == true);
35845 this.cmargins = c.cmargins || this.cmargins ||
35846 (this.position == "west" || this.position == "east" ?
35847 {top: 0, left: 2, right:2, bottom: 0} :
35848 {top: 2, left: 0, right:0, bottom: 2});
35850 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35853 this.bottomTabs = c.tabPosition != "top";
35855 this.autoScroll = c.autoScroll || false;
35860 this.duration = c.duration || .30;
35861 this.slideDuration = c.slideDuration || .45;
35866 * Returns true if this region is currently visible.
35867 * @return {Boolean}
35869 isVisible : function(){
35870 return this.visible;
35874 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35875 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35877 //setCollapsedTitle : function(title){
35878 // title = title || " ";
35879 // if(this.collapsedTitleTextEl){
35880 // this.collapsedTitleTextEl.innerHTML = title;
35884 getBox : function(){
35886 // if(!this.collapsed){
35887 b = this.el.getBox(false, true);
35889 // b = this.collapsedEl.getBox(false, true);
35894 getMargins : function(){
35895 return this.margins;
35896 //return this.collapsed ? this.cmargins : this.margins;
35899 highlight : function(){
35900 this.el.addClass("x-layout-panel-dragover");
35903 unhighlight : function(){
35904 this.el.removeClass("x-layout-panel-dragover");
35907 updateBox : function(box)
35909 if (!this.bodyEl) {
35910 return; // not rendered yet..
35914 if(!this.collapsed){
35915 this.el.dom.style.left = box.x + "px";
35916 this.el.dom.style.top = box.y + "px";
35917 this.updateBody(box.width, box.height);
35919 this.collapsedEl.dom.style.left = box.x + "px";
35920 this.collapsedEl.dom.style.top = box.y + "px";
35921 this.collapsedEl.setSize(box.width, box.height);
35924 this.tabs.autoSizeTabs();
35928 updateBody : function(w, h)
35931 this.el.setWidth(w);
35932 w -= this.el.getBorderWidth("rl");
35933 if(this.config.adjustments){
35934 w += this.config.adjustments[0];
35937 if(h !== null && h > 0){
35938 this.el.setHeight(h);
35939 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35940 h -= this.el.getBorderWidth("tb");
35941 if(this.config.adjustments){
35942 h += this.config.adjustments[1];
35944 this.bodyEl.setHeight(h);
35946 h = this.tabs.syncHeight(h);
35949 if(this.panelSize){
35950 w = w !== null ? w : this.panelSize.width;
35951 h = h !== null ? h : this.panelSize.height;
35953 if(this.activePanel){
35954 var el = this.activePanel.getEl();
35955 w = w !== null ? w : el.getWidth();
35956 h = h !== null ? h : el.getHeight();
35957 this.panelSize = {width: w, height: h};
35958 this.activePanel.setSize(w, h);
35960 if(Roo.isIE && this.tabs){
35961 this.tabs.el.repaint();
35966 * Returns the container element for this region.
35967 * @return {Roo.Element}
35969 getEl : function(){
35974 * Hides this region.
35977 //if(!this.collapsed){
35978 this.el.dom.style.left = "-2000px";
35981 // this.collapsedEl.dom.style.left = "-2000px";
35982 // this.collapsedEl.hide();
35984 this.visible = false;
35985 this.fireEvent("visibilitychange", this, false);
35989 * Shows this region if it was previously hidden.
35992 //if(!this.collapsed){
35995 // this.collapsedEl.show();
35997 this.visible = true;
35998 this.fireEvent("visibilitychange", this, true);
36001 closeClicked : function(){
36002 if(this.activePanel){
36003 this.remove(this.activePanel);
36007 collapseClick : function(e){
36009 e.stopPropagation();
36012 e.stopPropagation();
36018 * Collapses this region.
36019 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36022 collapse : function(skipAnim, skipCheck = false){
36023 if(this.collapsed) {
36027 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36029 this.collapsed = true;
36031 this.split.el.hide();
36033 if(this.config.animate && skipAnim !== true){
36034 this.fireEvent("invalidated", this);
36035 this.animateCollapse();
36037 this.el.setLocation(-20000,-20000);
36039 this.collapsedEl.show();
36040 this.fireEvent("collapsed", this);
36041 this.fireEvent("invalidated", this);
36047 animateCollapse : function(){
36052 * Expands this region if it was previously collapsed.
36053 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36054 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36057 expand : function(e, skipAnim){
36059 e.stopPropagation();
36061 if(!this.collapsed || this.el.hasActiveFx()) {
36065 this.afterSlideIn();
36068 this.collapsed = false;
36069 if(this.config.animate && skipAnim !== true){
36070 this.animateExpand();
36074 this.split.el.show();
36076 this.collapsedEl.setLocation(-2000,-2000);
36077 this.collapsedEl.hide();
36078 this.fireEvent("invalidated", this);
36079 this.fireEvent("expanded", this);
36083 animateExpand : function(){
36087 initTabs : function()
36089 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36091 var ts = new Roo.bootstrap.panel.Tabs({
36092 el: this.bodyEl.dom,
36093 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36094 disableTooltips: this.config.disableTabTips,
36095 toolbar : this.config.toolbar
36098 if(this.config.hideTabs){
36099 ts.stripWrap.setDisplayed(false);
36102 ts.resizeTabs = this.config.resizeTabs === true;
36103 ts.minTabWidth = this.config.minTabWidth || 40;
36104 ts.maxTabWidth = this.config.maxTabWidth || 250;
36105 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36106 ts.monitorResize = false;
36107 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36108 ts.bodyEl.addClass('roo-layout-tabs-body');
36109 this.panels.each(this.initPanelAsTab, this);
36112 initPanelAsTab : function(panel){
36113 var ti = this.tabs.addTab(
36117 this.config.closeOnTab && panel.isClosable(),
36120 if(panel.tabTip !== undefined){
36121 ti.setTooltip(panel.tabTip);
36123 ti.on("activate", function(){
36124 this.setActivePanel(panel);
36127 if(this.config.closeOnTab){
36128 ti.on("beforeclose", function(t, e){
36130 this.remove(panel);
36134 panel.tabItem = ti;
36139 updatePanelTitle : function(panel, title)
36141 if(this.activePanel == panel){
36142 this.updateTitle(title);
36145 var ti = this.tabs.getTab(panel.getEl().id);
36147 if(panel.tabTip !== undefined){
36148 ti.setTooltip(panel.tabTip);
36153 updateTitle : function(title){
36154 if(this.titleTextEl && !this.config.title){
36155 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36159 setActivePanel : function(panel)
36161 panel = this.getPanel(panel);
36162 if(this.activePanel && this.activePanel != panel){
36163 if(this.activePanel.setActiveState(false) === false){
36167 this.activePanel = panel;
36168 panel.setActiveState(true);
36169 if(this.panelSize){
36170 panel.setSize(this.panelSize.width, this.panelSize.height);
36173 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36175 this.updateTitle(panel.getTitle());
36177 this.fireEvent("invalidated", this);
36179 this.fireEvent("panelactivated", this, panel);
36183 * Shows the specified panel.
36184 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36185 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36187 showPanel : function(panel)
36189 panel = this.getPanel(panel);
36192 var tab = this.tabs.getTab(panel.getEl().id);
36193 if(tab.isHidden()){
36194 this.tabs.unhideTab(tab.id);
36198 this.setActivePanel(panel);
36205 * Get the active panel for this region.
36206 * @return {Roo.ContentPanel} The active panel or null
36208 getActivePanel : function(){
36209 return this.activePanel;
36212 validateVisibility : function(){
36213 if(this.panels.getCount() < 1){
36214 this.updateTitle(" ");
36215 this.closeBtn.hide();
36218 if(!this.isVisible()){
36225 * Adds the passed ContentPanel(s) to this region.
36226 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36227 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36229 add : function(panel)
36231 if(arguments.length > 1){
36232 for(var i = 0, len = arguments.length; i < len; i++) {
36233 this.add(arguments[i]);
36238 // if we have not been rendered yet, then we can not really do much of this..
36239 if (!this.bodyEl) {
36240 this.unrendered_panels.push(panel);
36247 if(this.hasPanel(panel)){
36248 this.showPanel(panel);
36251 panel.setRegion(this);
36252 this.panels.add(panel);
36253 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36254 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36255 // and hide them... ???
36256 this.bodyEl.dom.appendChild(panel.getEl().dom);
36257 if(panel.background !== true){
36258 this.setActivePanel(panel);
36260 this.fireEvent("paneladded", this, panel);
36267 this.initPanelAsTab(panel);
36271 if(panel.background !== true){
36272 this.tabs.activate(panel.getEl().id);
36274 this.fireEvent("paneladded", this, panel);
36279 * Hides the tab for the specified panel.
36280 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36282 hidePanel : function(panel){
36283 if(this.tabs && (panel = this.getPanel(panel))){
36284 this.tabs.hideTab(panel.getEl().id);
36289 * Unhides the tab for a previously hidden panel.
36290 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36292 unhidePanel : function(panel){
36293 if(this.tabs && (panel = this.getPanel(panel))){
36294 this.tabs.unhideTab(panel.getEl().id);
36298 clearPanels : function(){
36299 while(this.panels.getCount() > 0){
36300 this.remove(this.panels.first());
36305 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36306 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36307 * @param {Boolean} preservePanel Overrides the config preservePanel option
36308 * @return {Roo.ContentPanel} The panel that was removed
36310 remove : function(panel, preservePanel)
36312 panel = this.getPanel(panel);
36317 this.fireEvent("beforeremove", this, panel, e);
36318 if(e.cancel === true){
36321 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36322 var panelId = panel.getId();
36323 this.panels.removeKey(panelId);
36325 document.body.appendChild(panel.getEl().dom);
36328 this.tabs.removeTab(panel.getEl().id);
36329 }else if (!preservePanel){
36330 this.bodyEl.dom.removeChild(panel.getEl().dom);
36332 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36333 var p = this.panels.first();
36334 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36335 tempEl.appendChild(p.getEl().dom);
36336 this.bodyEl.update("");
36337 this.bodyEl.dom.appendChild(p.getEl().dom);
36339 this.updateTitle(p.getTitle());
36341 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36342 this.setActivePanel(p);
36344 panel.setRegion(null);
36345 if(this.activePanel == panel){
36346 this.activePanel = null;
36348 if(this.config.autoDestroy !== false && preservePanel !== true){
36349 try{panel.destroy();}catch(e){}
36351 this.fireEvent("panelremoved", this, panel);
36356 * Returns the TabPanel component used by this region
36357 * @return {Roo.TabPanel}
36359 getTabs : function(){
36363 createTool : function(parentEl, className){
36364 var btn = Roo.DomHelper.append(parentEl, {
36366 cls: "x-layout-tools-button",
36369 cls: "roo-layout-tools-button-inner " + className,
36373 btn.addClassOnOver("roo-layout-tools-button-over");
36378 * Ext JS Library 1.1.1
36379 * Copyright(c) 2006-2007, Ext JS, LLC.
36381 * Originally Released Under LGPL - original licence link has changed is not relivant.
36384 * <script type="text/javascript">
36390 * @class Roo.SplitLayoutRegion
36391 * @extends Roo.LayoutRegion
36392 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36394 Roo.bootstrap.layout.Split = function(config){
36395 this.cursor = config.cursor;
36396 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36399 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36401 splitTip : "Drag to resize.",
36402 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36403 useSplitTips : false,
36405 applyConfig : function(config){
36406 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36409 onRender : function(ctr,pos) {
36411 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36412 if(!this.config.split){
36417 var splitEl = Roo.DomHelper.append(ctr.dom, {
36419 id: this.el.id + "-split",
36420 cls: "roo-layout-split roo-layout-split-"+this.position,
36423 /** The SplitBar for this region
36424 * @type Roo.SplitBar */
36425 // does not exist yet...
36426 Roo.log([this.position, this.orientation]);
36428 this.split = new Roo.bootstrap.SplitBar({
36429 dragElement : splitEl,
36430 resizingElement: this.el,
36431 orientation : this.orientation
36434 this.split.on("moved", this.onSplitMove, this);
36435 this.split.useShim = this.config.useShim === true;
36436 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36437 if(this.useSplitTips){
36438 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36440 //if(config.collapsible){
36441 // this.split.el.on("dblclick", this.collapse, this);
36444 if(typeof this.config.minSize != "undefined"){
36445 this.split.minSize = this.config.minSize;
36447 if(typeof this.config.maxSize != "undefined"){
36448 this.split.maxSize = this.config.maxSize;
36450 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36451 this.hideSplitter();
36456 getHMaxSize : function(){
36457 var cmax = this.config.maxSize || 10000;
36458 var center = this.mgr.getRegion("center");
36459 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36462 getVMaxSize : function(){
36463 var cmax = this.config.maxSize || 10000;
36464 var center = this.mgr.getRegion("center");
36465 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36468 onSplitMove : function(split, newSize){
36469 this.fireEvent("resized", this, newSize);
36473 * Returns the {@link Roo.SplitBar} for this region.
36474 * @return {Roo.SplitBar}
36476 getSplitBar : function(){
36481 this.hideSplitter();
36482 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36485 hideSplitter : function(){
36487 this.split.el.setLocation(-2000,-2000);
36488 this.split.el.hide();
36494 this.split.el.show();
36496 Roo.bootstrap.layout.Split.superclass.show.call(this);
36499 beforeSlide: function(){
36500 if(Roo.isGecko){// firefox overflow auto bug workaround
36501 this.bodyEl.clip();
36503 this.tabs.bodyEl.clip();
36505 if(this.activePanel){
36506 this.activePanel.getEl().clip();
36508 if(this.activePanel.beforeSlide){
36509 this.activePanel.beforeSlide();
36515 afterSlide : function(){
36516 if(Roo.isGecko){// firefox overflow auto bug workaround
36517 this.bodyEl.unclip();
36519 this.tabs.bodyEl.unclip();
36521 if(this.activePanel){
36522 this.activePanel.getEl().unclip();
36523 if(this.activePanel.afterSlide){
36524 this.activePanel.afterSlide();
36530 initAutoHide : function(){
36531 if(this.autoHide !== false){
36532 if(!this.autoHideHd){
36533 var st = new Roo.util.DelayedTask(this.slideIn, this);
36534 this.autoHideHd = {
36535 "mouseout": function(e){
36536 if(!e.within(this.el, true)){
36540 "mouseover" : function(e){
36546 this.el.on(this.autoHideHd);
36550 clearAutoHide : function(){
36551 if(this.autoHide !== false){
36552 this.el.un("mouseout", this.autoHideHd.mouseout);
36553 this.el.un("mouseover", this.autoHideHd.mouseover);
36557 clearMonitor : function(){
36558 Roo.get(document).un("click", this.slideInIf, this);
36561 // these names are backwards but not changed for compat
36562 slideOut : function(){
36563 if(this.isSlid || this.el.hasActiveFx()){
36566 this.isSlid = true;
36567 if(this.collapseBtn){
36568 this.collapseBtn.hide();
36570 this.closeBtnState = this.closeBtn.getStyle('display');
36571 this.closeBtn.hide();
36573 this.stickBtn.show();
36576 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36577 this.beforeSlide();
36578 this.el.setStyle("z-index", 10001);
36579 this.el.slideIn(this.getSlideAnchor(), {
36580 callback: function(){
36582 this.initAutoHide();
36583 Roo.get(document).on("click", this.slideInIf, this);
36584 this.fireEvent("slideshow", this);
36591 afterSlideIn : function(){
36592 this.clearAutoHide();
36593 this.isSlid = false;
36594 this.clearMonitor();
36595 this.el.setStyle("z-index", "");
36596 if(this.collapseBtn){
36597 this.collapseBtn.show();
36599 this.closeBtn.setStyle('display', this.closeBtnState);
36601 this.stickBtn.hide();
36603 this.fireEvent("slidehide", this);
36606 slideIn : function(cb){
36607 if(!this.isSlid || this.el.hasActiveFx()){
36611 this.isSlid = false;
36612 this.beforeSlide();
36613 this.el.slideOut(this.getSlideAnchor(), {
36614 callback: function(){
36615 this.el.setLeftTop(-10000, -10000);
36617 this.afterSlideIn();
36625 slideInIf : function(e){
36626 if(!e.within(this.el)){
36631 animateCollapse : function(){
36632 this.beforeSlide();
36633 this.el.setStyle("z-index", 20000);
36634 var anchor = this.getSlideAnchor();
36635 this.el.slideOut(anchor, {
36636 callback : function(){
36637 this.el.setStyle("z-index", "");
36638 this.collapsedEl.slideIn(anchor, {duration:.3});
36640 this.el.setLocation(-10000,-10000);
36642 this.fireEvent("collapsed", this);
36649 animateExpand : function(){
36650 this.beforeSlide();
36651 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36652 this.el.setStyle("z-index", 20000);
36653 this.collapsedEl.hide({
36656 this.el.slideIn(this.getSlideAnchor(), {
36657 callback : function(){
36658 this.el.setStyle("z-index", "");
36661 this.split.el.show();
36663 this.fireEvent("invalidated", this);
36664 this.fireEvent("expanded", this);
36692 getAnchor : function(){
36693 return this.anchors[this.position];
36696 getCollapseAnchor : function(){
36697 return this.canchors[this.position];
36700 getSlideAnchor : function(){
36701 return this.sanchors[this.position];
36704 getAlignAdj : function(){
36705 var cm = this.cmargins;
36706 switch(this.position){
36722 getExpandAdj : function(){
36723 var c = this.collapsedEl, cm = this.cmargins;
36724 switch(this.position){
36726 return [-(cm.right+c.getWidth()+cm.left), 0];
36729 return [cm.right+c.getWidth()+cm.left, 0];
36732 return [0, -(cm.top+cm.bottom+c.getHeight())];
36735 return [0, cm.top+cm.bottom+c.getHeight()];
36741 * Ext JS Library 1.1.1
36742 * Copyright(c) 2006-2007, Ext JS, LLC.
36744 * Originally Released Under LGPL - original licence link has changed is not relivant.
36747 * <script type="text/javascript">
36750 * These classes are private internal classes
36752 Roo.bootstrap.layout.Center = function(config){
36753 config.region = "center";
36754 Roo.bootstrap.layout.Region.call(this, config);
36755 this.visible = true;
36756 this.minWidth = config.minWidth || 20;
36757 this.minHeight = config.minHeight || 20;
36760 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36762 // center panel can't be hidden
36766 // center panel can't be hidden
36769 getMinWidth: function(){
36770 return this.minWidth;
36773 getMinHeight: function(){
36774 return this.minHeight;
36787 Roo.bootstrap.layout.North = function(config)
36789 config.region = 'north';
36790 config.cursor = 'n-resize';
36792 Roo.bootstrap.layout.Split.call(this, config);
36796 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36797 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36798 this.split.el.addClass("roo-layout-split-v");
36800 var size = config.initialSize || config.height;
36801 if(typeof size != "undefined"){
36802 this.el.setHeight(size);
36805 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36807 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36811 getBox : function(){
36812 if(this.collapsed){
36813 return this.collapsedEl.getBox();
36815 var box = this.el.getBox();
36817 box.height += this.split.el.getHeight();
36822 updateBox : function(box){
36823 if(this.split && !this.collapsed){
36824 box.height -= this.split.el.getHeight();
36825 this.split.el.setLeft(box.x);
36826 this.split.el.setTop(box.y+box.height);
36827 this.split.el.setWidth(box.width);
36829 if(this.collapsed){
36830 this.updateBody(box.width, null);
36832 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36840 Roo.bootstrap.layout.South = function(config){
36841 config.region = 'south';
36842 config.cursor = 's-resize';
36843 Roo.bootstrap.layout.Split.call(this, config);
36845 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36846 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36847 this.split.el.addClass("roo-layout-split-v");
36849 var size = config.initialSize || config.height;
36850 if(typeof size != "undefined"){
36851 this.el.setHeight(size);
36855 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36856 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36857 getBox : function(){
36858 if(this.collapsed){
36859 return this.collapsedEl.getBox();
36861 var box = this.el.getBox();
36863 var sh = this.split.el.getHeight();
36870 updateBox : function(box){
36871 if(this.split && !this.collapsed){
36872 var sh = this.split.el.getHeight();
36875 this.split.el.setLeft(box.x);
36876 this.split.el.setTop(box.y-sh);
36877 this.split.el.setWidth(box.width);
36879 if(this.collapsed){
36880 this.updateBody(box.width, null);
36882 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36886 Roo.bootstrap.layout.East = function(config){
36887 config.region = "east";
36888 config.cursor = "e-resize";
36889 Roo.bootstrap.layout.Split.call(this, config);
36891 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36892 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36893 this.split.el.addClass("roo-layout-split-h");
36895 var size = config.initialSize || config.width;
36896 if(typeof size != "undefined"){
36897 this.el.setWidth(size);
36900 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36901 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36902 getBox : function(){
36903 if(this.collapsed){
36904 return this.collapsedEl.getBox();
36906 var box = this.el.getBox();
36908 var sw = this.split.el.getWidth();
36915 updateBox : function(box){
36916 if(this.split && !this.collapsed){
36917 var sw = this.split.el.getWidth();
36919 this.split.el.setLeft(box.x);
36920 this.split.el.setTop(box.y);
36921 this.split.el.setHeight(box.height);
36924 if(this.collapsed){
36925 this.updateBody(null, box.height);
36927 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36931 Roo.bootstrap.layout.West = function(config){
36932 config.region = "west";
36933 config.cursor = "w-resize";
36935 Roo.bootstrap.layout.Split.call(this, config);
36937 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36938 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36939 this.split.el.addClass("roo-layout-split-h");
36943 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36944 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36946 onRender: function(ctr, pos)
36948 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36949 var size = this.config.initialSize || this.config.width;
36950 if(typeof size != "undefined"){
36951 this.el.setWidth(size);
36955 getBox : function(){
36956 if(this.collapsed){
36957 return this.collapsedEl.getBox();
36959 var box = this.el.getBox();
36961 box.width += this.split.el.getWidth();
36966 updateBox : function(box){
36967 if(this.split && !this.collapsed){
36968 var sw = this.split.el.getWidth();
36970 this.split.el.setLeft(box.x+box.width);
36971 this.split.el.setTop(box.y);
36972 this.split.el.setHeight(box.height);
36974 if(this.collapsed){
36975 this.updateBody(null, box.height);
36977 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36980 Roo.namespace("Roo.bootstrap.panel");/*
36982 * Ext JS Library 1.1.1
36983 * Copyright(c) 2006-2007, Ext JS, LLC.
36985 * Originally Released Under LGPL - original licence link has changed is not relivant.
36988 * <script type="text/javascript">
36991 * @class Roo.ContentPanel
36992 * @extends Roo.util.Observable
36993 * A basic ContentPanel element.
36994 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36995 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36996 * @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
36997 * @cfg {Boolean} closable True if the panel can be closed/removed
36998 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36999 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37000 * @cfg {Toolbar} toolbar A toolbar for this panel
37001 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37002 * @cfg {String} title The title for this panel
37003 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37004 * @cfg {String} url Calls {@link #setUrl} with this value
37005 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37006 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37007 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37008 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37009 * @cfg {Boolean} badges render the badges
37012 * Create a new ContentPanel.
37013 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37014 * @param {String/Object} config A string to set only the title or a config object
37015 * @param {String} content (optional) Set the HTML content for this panel
37016 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37018 Roo.bootstrap.panel.Content = function( config){
37020 this.tpl = config.tpl || false;
37022 var el = config.el;
37023 var content = config.content;
37025 if(config.autoCreate){ // xtype is available if this is called from factory
37028 this.el = Roo.get(el);
37029 if(!this.el && config && config.autoCreate){
37030 if(typeof config.autoCreate == "object"){
37031 if(!config.autoCreate.id){
37032 config.autoCreate.id = config.id||el;
37034 this.el = Roo.DomHelper.append(document.body,
37035 config.autoCreate, true);
37037 var elcfg = { tag: "div",
37038 cls: "roo-layout-inactive-content",
37042 elcfg.html = config.html;
37046 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37049 this.closable = false;
37050 this.loaded = false;
37051 this.active = false;
37054 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37056 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37058 this.wrapEl = this.el; //this.el.wrap();
37060 if (config.toolbar.items) {
37061 ti = config.toolbar.items ;
37062 delete config.toolbar.items ;
37066 this.toolbar.render(this.wrapEl, 'before');
37067 for(var i =0;i < ti.length;i++) {
37068 // Roo.log(['add child', items[i]]);
37069 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37071 this.toolbar.items = nitems;
37072 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37073 delete config.toolbar;
37077 // xtype created footer. - not sure if will work as we normally have to render first..
37078 if (this.footer && !this.footer.el && this.footer.xtype) {
37079 if (!this.wrapEl) {
37080 this.wrapEl = this.el.wrap();
37083 this.footer.container = this.wrapEl.createChild();
37085 this.footer = Roo.factory(this.footer, Roo);
37090 if(typeof config == "string"){
37091 this.title = config;
37093 Roo.apply(this, config);
37097 this.resizeEl = Roo.get(this.resizeEl, true);
37099 this.resizeEl = this.el;
37101 // handle view.xtype
37109 * Fires when this panel is activated.
37110 * @param {Roo.ContentPanel} this
37114 * @event deactivate
37115 * Fires when this panel is activated.
37116 * @param {Roo.ContentPanel} this
37118 "deactivate" : true,
37122 * Fires when this panel is resized if fitToFrame is true.
37123 * @param {Roo.ContentPanel} this
37124 * @param {Number} width The width after any component adjustments
37125 * @param {Number} height The height after any component adjustments
37131 * Fires when this tab is created
37132 * @param {Roo.ContentPanel} this
37143 if(this.autoScroll){
37144 this.resizeEl.setStyle("overflow", "auto");
37146 // fix randome scrolling
37147 //this.el.on('scroll', function() {
37148 // Roo.log('fix random scolling');
37149 // this.scrollTo('top',0);
37152 content = content || this.content;
37154 this.setContent(content);
37156 if(config && config.url){
37157 this.setUrl(this.url, this.params, this.loadOnce);
37162 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37164 if (this.view && typeof(this.view.xtype) != 'undefined') {
37165 this.view.el = this.el.appendChild(document.createElement("div"));
37166 this.view = Roo.factory(this.view);
37167 this.view.render && this.view.render(false, '');
37171 this.fireEvent('render', this);
37174 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37178 setRegion : function(region){
37179 this.region = region;
37180 this.setActiveClass(region && !this.background);
37184 setActiveClass: function(state)
37187 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37188 this.el.setStyle('position','relative');
37190 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37191 this.el.setStyle('position', 'absolute');
37196 * Returns the toolbar for this Panel if one was configured.
37197 * @return {Roo.Toolbar}
37199 getToolbar : function(){
37200 return this.toolbar;
37203 setActiveState : function(active)
37205 this.active = active;
37206 this.setActiveClass(active);
37208 if(this.fireEvent("deactivate", this) === false){
37213 this.fireEvent("activate", this);
37217 * Updates this panel's element
37218 * @param {String} content The new content
37219 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37221 setContent : function(content, loadScripts){
37222 this.el.update(content, loadScripts);
37225 ignoreResize : function(w, h){
37226 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37229 this.lastSize = {width: w, height: h};
37234 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37235 * @return {Roo.UpdateManager} The UpdateManager
37237 getUpdateManager : function(){
37238 return this.el.getUpdateManager();
37241 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37242 * @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:
37245 url: "your-url.php",
37246 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37247 callback: yourFunction,
37248 scope: yourObject, //(optional scope)
37251 text: "Loading...",
37256 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37257 * 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.
37258 * @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}
37259 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37260 * @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.
37261 * @return {Roo.ContentPanel} this
37264 var um = this.el.getUpdateManager();
37265 um.update.apply(um, arguments);
37271 * 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.
37272 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37273 * @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)
37274 * @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)
37275 * @return {Roo.UpdateManager} The UpdateManager
37277 setUrl : function(url, params, loadOnce){
37278 if(this.refreshDelegate){
37279 this.removeListener("activate", this.refreshDelegate);
37281 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37282 this.on("activate", this.refreshDelegate);
37283 return this.el.getUpdateManager();
37286 _handleRefresh : function(url, params, loadOnce){
37287 if(!loadOnce || !this.loaded){
37288 var updater = this.el.getUpdateManager();
37289 updater.update(url, params, this._setLoaded.createDelegate(this));
37293 _setLoaded : function(){
37294 this.loaded = true;
37298 * Returns this panel's id
37301 getId : function(){
37306 * Returns this panel's element - used by regiosn to add.
37307 * @return {Roo.Element}
37309 getEl : function(){
37310 return this.wrapEl || this.el;
37315 adjustForComponents : function(width, height)
37317 //Roo.log('adjustForComponents ');
37318 if(this.resizeEl != this.el){
37319 width -= this.el.getFrameWidth('lr');
37320 height -= this.el.getFrameWidth('tb');
37323 var te = this.toolbar.getEl();
37324 te.setWidth(width);
37325 height -= te.getHeight();
37328 var te = this.footer.getEl();
37329 te.setWidth(width);
37330 height -= te.getHeight();
37334 if(this.adjustments){
37335 width += this.adjustments[0];
37336 height += this.adjustments[1];
37338 return {"width": width, "height": height};
37341 setSize : function(width, height){
37342 if(this.fitToFrame && !this.ignoreResize(width, height)){
37343 if(this.fitContainer && this.resizeEl != this.el){
37344 this.el.setSize(width, height);
37346 var size = this.adjustForComponents(width, height);
37347 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37348 this.fireEvent('resize', this, size.width, size.height);
37353 * Returns this panel's title
37356 getTitle : function(){
37358 if (typeof(this.title) != 'object') {
37363 for (var k in this.title) {
37364 if (!this.title.hasOwnProperty(k)) {
37368 if (k.indexOf('-') >= 0) {
37369 var s = k.split('-');
37370 for (var i = 0; i<s.length; i++) {
37371 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37374 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37381 * Set this panel's title
37382 * @param {String} title
37384 setTitle : function(title){
37385 this.title = title;
37387 this.region.updatePanelTitle(this, title);
37392 * Returns true is this panel was configured to be closable
37393 * @return {Boolean}
37395 isClosable : function(){
37396 return this.closable;
37399 beforeSlide : function(){
37401 this.resizeEl.clip();
37404 afterSlide : function(){
37406 this.resizeEl.unclip();
37410 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37411 * Will fail silently if the {@link #setUrl} method has not been called.
37412 * This does not activate the panel, just updates its content.
37414 refresh : function(){
37415 if(this.refreshDelegate){
37416 this.loaded = false;
37417 this.refreshDelegate();
37422 * Destroys this panel
37424 destroy : function(){
37425 this.el.removeAllListeners();
37426 var tempEl = document.createElement("span");
37427 tempEl.appendChild(this.el.dom);
37428 tempEl.innerHTML = "";
37434 * form - if the content panel contains a form - this is a reference to it.
37435 * @type {Roo.form.Form}
37439 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37440 * This contains a reference to it.
37446 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37456 * @param {Object} cfg Xtype definition of item to add.
37460 getChildContainer: function () {
37461 return this.getEl();
37466 var ret = new Roo.factory(cfg);
37471 if (cfg.xtype.match(/^Form$/)) {
37474 //if (this.footer) {
37475 // el = this.footer.container.insertSibling(false, 'before');
37477 el = this.el.createChild();
37480 this.form = new Roo.form.Form(cfg);
37483 if ( this.form.allItems.length) {
37484 this.form.render(el.dom);
37488 // should only have one of theses..
37489 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37490 // views.. should not be just added - used named prop 'view''
37492 cfg.el = this.el.appendChild(document.createElement("div"));
37495 var ret = new Roo.factory(cfg);
37497 ret.render && ret.render(false, ''); // render blank..
37507 * @class Roo.bootstrap.panel.Grid
37508 * @extends Roo.bootstrap.panel.Content
37510 * Create a new GridPanel.
37511 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37512 * @param {Object} config A the config object
37518 Roo.bootstrap.panel.Grid = function(config)
37522 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37523 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37525 config.el = this.wrapper;
37526 //this.el = this.wrapper;
37528 if (config.container) {
37529 // ctor'ed from a Border/panel.grid
37532 this.wrapper.setStyle("overflow", "hidden");
37533 this.wrapper.addClass('roo-grid-container');
37538 if(config.toolbar){
37539 var tool_el = this.wrapper.createChild();
37540 this.toolbar = Roo.factory(config.toolbar);
37542 if (config.toolbar.items) {
37543 ti = config.toolbar.items ;
37544 delete config.toolbar.items ;
37548 this.toolbar.render(tool_el);
37549 for(var i =0;i < ti.length;i++) {
37550 // Roo.log(['add child', items[i]]);
37551 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37553 this.toolbar.items = nitems;
37555 delete config.toolbar;
37558 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37559 config.grid.scrollBody = true;;
37560 config.grid.monitorWindowResize = false; // turn off autosizing
37561 config.grid.autoHeight = false;
37562 config.grid.autoWidth = false;
37564 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37566 if (config.background) {
37567 // render grid on panel activation (if panel background)
37568 this.on('activate', function(gp) {
37569 if (!gp.grid.rendered) {
37570 gp.grid.render(this.wrapper);
37571 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37576 this.grid.render(this.wrapper);
37577 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37580 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37581 // ??? needed ??? config.el = this.wrapper;
37586 // xtype created footer. - not sure if will work as we normally have to render first..
37587 if (this.footer && !this.footer.el && this.footer.xtype) {
37589 var ctr = this.grid.getView().getFooterPanel(true);
37590 this.footer.dataSource = this.grid.dataSource;
37591 this.footer = Roo.factory(this.footer, Roo);
37592 this.footer.render(ctr);
37602 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37603 getId : function(){
37604 return this.grid.id;
37608 * Returns the grid for this panel
37609 * @return {Roo.bootstrap.Table}
37611 getGrid : function(){
37615 setSize : function(width, height){
37616 if(!this.ignoreResize(width, height)){
37617 var grid = this.grid;
37618 var size = this.adjustForComponents(width, height);
37619 var gridel = grid.getGridEl();
37620 gridel.setSize(size.width, size.height);
37622 var thd = grid.getGridEl().select('thead',true).first();
37623 var tbd = grid.getGridEl().select('tbody', true).first();
37625 tbd.setSize(width, height - thd.getHeight());
37634 beforeSlide : function(){
37635 this.grid.getView().scroller.clip();
37638 afterSlide : function(){
37639 this.grid.getView().scroller.unclip();
37642 destroy : function(){
37643 this.grid.destroy();
37645 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37650 * @class Roo.bootstrap.panel.Nest
37651 * @extends Roo.bootstrap.panel.Content
37653 * Create a new Panel, that can contain a layout.Border.
37656 * @param {Roo.BorderLayout} layout The layout for this panel
37657 * @param {String/Object} config A string to set only the title or a config object
37659 Roo.bootstrap.panel.Nest = function(config)
37661 // construct with only one argument..
37662 /* FIXME - implement nicer consturctors
37663 if (layout.layout) {
37665 layout = config.layout;
37666 delete config.layout;
37668 if (layout.xtype && !layout.getEl) {
37669 // then layout needs constructing..
37670 layout = Roo.factory(layout, Roo);
37674 config.el = config.layout.getEl();
37676 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37678 config.layout.monitorWindowResize = false; // turn off autosizing
37679 this.layout = config.layout;
37680 this.layout.getEl().addClass("roo-layout-nested-layout");
37687 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37689 setSize : function(width, height){
37690 if(!this.ignoreResize(width, height)){
37691 var size = this.adjustForComponents(width, height);
37692 var el = this.layout.getEl();
37693 if (size.height < 1) {
37694 el.setWidth(size.width);
37696 el.setSize(size.width, size.height);
37698 var touch = el.dom.offsetWidth;
37699 this.layout.layout();
37700 // ie requires a double layout on the first pass
37701 if(Roo.isIE && !this.initialized){
37702 this.initialized = true;
37703 this.layout.layout();
37708 // activate all subpanels if not currently active..
37710 setActiveState : function(active){
37711 this.active = active;
37712 this.setActiveClass(active);
37715 this.fireEvent("deactivate", this);
37719 this.fireEvent("activate", this);
37720 // not sure if this should happen before or after..
37721 if (!this.layout) {
37722 return; // should not happen..
37725 for (var r in this.layout.regions) {
37726 reg = this.layout.getRegion(r);
37727 if (reg.getActivePanel()) {
37728 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37729 reg.setActivePanel(reg.getActivePanel());
37732 if (!reg.panels.length) {
37735 reg.showPanel(reg.getPanel(0));
37744 * Returns the nested BorderLayout for this panel
37745 * @return {Roo.BorderLayout}
37747 getLayout : function(){
37748 return this.layout;
37752 * Adds a xtype elements to the layout of the nested panel
37756 xtype : 'ContentPanel',
37763 xtype : 'NestedLayoutPanel',
37769 items : [ ... list of content panels or nested layout panels.. ]
37773 * @param {Object} cfg Xtype definition of item to add.
37775 addxtype : function(cfg) {
37776 return this.layout.addxtype(cfg);
37781 * Ext JS Library 1.1.1
37782 * Copyright(c) 2006-2007, Ext JS, LLC.
37784 * Originally Released Under LGPL - original licence link has changed is not relivant.
37787 * <script type="text/javascript">
37790 * @class Roo.TabPanel
37791 * @extends Roo.util.Observable
37792 * A lightweight tab container.
37796 // basic tabs 1, built from existing content
37797 var tabs = new Roo.TabPanel("tabs1");
37798 tabs.addTab("script", "View Script");
37799 tabs.addTab("markup", "View Markup");
37800 tabs.activate("script");
37802 // more advanced tabs, built from javascript
37803 var jtabs = new Roo.TabPanel("jtabs");
37804 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37806 // set up the UpdateManager
37807 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37808 var updater = tab2.getUpdateManager();
37809 updater.setDefaultUrl("ajax1.htm");
37810 tab2.on('activate', updater.refresh, updater, true);
37812 // Use setUrl for Ajax loading
37813 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37814 tab3.setUrl("ajax2.htm", null, true);
37817 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37820 jtabs.activate("jtabs-1");
37823 * Create a new TabPanel.
37824 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37825 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37827 Roo.bootstrap.panel.Tabs = function(config){
37829 * The container element for this TabPanel.
37830 * @type Roo.Element
37832 this.el = Roo.get(config.el);
37835 if(typeof config == "boolean"){
37836 this.tabPosition = config ? "bottom" : "top";
37838 Roo.apply(this, config);
37842 if(this.tabPosition == "bottom"){
37843 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37844 this.el.addClass("roo-tabs-bottom");
37846 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37847 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37848 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37850 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37852 if(this.tabPosition != "bottom"){
37853 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37854 * @type Roo.Element
37856 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37857 this.el.addClass("roo-tabs-top");
37861 this.bodyEl.setStyle("position", "relative");
37863 this.active = null;
37864 this.activateDelegate = this.activate.createDelegate(this);
37869 * Fires when the active tab changes
37870 * @param {Roo.TabPanel} this
37871 * @param {Roo.TabPanelItem} activePanel The new active tab
37875 * @event beforetabchange
37876 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37877 * @param {Roo.TabPanel} this
37878 * @param {Object} e Set cancel to true on this object to cancel the tab change
37879 * @param {Roo.TabPanelItem} tab The tab being changed to
37881 "beforetabchange" : true
37884 Roo.EventManager.onWindowResize(this.onResize, this);
37885 this.cpad = this.el.getPadding("lr");
37886 this.hiddenCount = 0;
37889 // toolbar on the tabbar support...
37890 if (this.toolbar) {
37891 alert("no toolbar support yet");
37892 this.toolbar = false;
37894 var tcfg = this.toolbar;
37895 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37896 this.toolbar = new Roo.Toolbar(tcfg);
37897 if (Roo.isSafari) {
37898 var tbl = tcfg.container.child('table', true);
37899 tbl.setAttribute('width', '100%');
37907 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37910 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37912 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37914 tabPosition : "top",
37916 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37918 currentTabWidth : 0,
37920 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37924 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37928 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37930 preferredTabWidth : 175,
37932 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37934 resizeTabs : false,
37936 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37938 monitorResize : true,
37940 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37945 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37946 * @param {String} id The id of the div to use <b>or create</b>
37947 * @param {String} text The text for the tab
37948 * @param {String} content (optional) Content to put in the TabPanelItem body
37949 * @param {Boolean} closable (optional) True to create a close icon on the tab
37950 * @return {Roo.TabPanelItem} The created TabPanelItem
37952 addTab : function(id, text, content, closable, tpl)
37954 var item = new Roo.bootstrap.panel.TabItem({
37958 closable : closable,
37961 this.addTabItem(item);
37963 item.setContent(content);
37969 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37970 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37971 * @return {Roo.TabPanelItem}
37973 getTab : function(id){
37974 return this.items[id];
37978 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37979 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37981 hideTab : function(id){
37982 var t = this.items[id];
37985 this.hiddenCount++;
37986 this.autoSizeTabs();
37991 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37992 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37994 unhideTab : function(id){
37995 var t = this.items[id];
37997 t.setHidden(false);
37998 this.hiddenCount--;
37999 this.autoSizeTabs();
38004 * Adds an existing {@link Roo.TabPanelItem}.
38005 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38007 addTabItem : function(item){
38008 this.items[item.id] = item;
38009 this.items.push(item);
38010 // if(this.resizeTabs){
38011 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38012 // this.autoSizeTabs();
38014 // item.autoSize();
38019 * Removes a {@link Roo.TabPanelItem}.
38020 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38022 removeTab : function(id){
38023 var items = this.items;
38024 var tab = items[id];
38025 if(!tab) { return; }
38026 var index = items.indexOf(tab);
38027 if(this.active == tab && items.length > 1){
38028 var newTab = this.getNextAvailable(index);
38033 this.stripEl.dom.removeChild(tab.pnode.dom);
38034 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38035 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38037 items.splice(index, 1);
38038 delete this.items[tab.id];
38039 tab.fireEvent("close", tab);
38040 tab.purgeListeners();
38041 this.autoSizeTabs();
38044 getNextAvailable : function(start){
38045 var items = this.items;
38047 // look for a next tab that will slide over to
38048 // replace the one being removed
38049 while(index < items.length){
38050 var item = items[++index];
38051 if(item && !item.isHidden()){
38055 // if one isn't found select the previous tab (on the left)
38058 var item = items[--index];
38059 if(item && !item.isHidden()){
38067 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38068 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38070 disableTab : function(id){
38071 var tab = this.items[id];
38072 if(tab && this.active != tab){
38078 * Enables a {@link Roo.TabPanelItem} that is disabled.
38079 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38081 enableTab : function(id){
38082 var tab = this.items[id];
38087 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38088 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38089 * @return {Roo.TabPanelItem} The TabPanelItem.
38091 activate : function(id){
38092 var tab = this.items[id];
38096 if(tab == this.active || tab.disabled){
38100 this.fireEvent("beforetabchange", this, e, tab);
38101 if(e.cancel !== true && !tab.disabled){
38103 this.active.hide();
38105 this.active = this.items[id];
38106 this.active.show();
38107 this.fireEvent("tabchange", this, this.active);
38113 * Gets the active {@link Roo.TabPanelItem}.
38114 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38116 getActiveTab : function(){
38117 return this.active;
38121 * Updates the tab body element to fit the height of the container element
38122 * for overflow scrolling
38123 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38125 syncHeight : function(targetHeight){
38126 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38127 var bm = this.bodyEl.getMargins();
38128 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38129 this.bodyEl.setHeight(newHeight);
38133 onResize : function(){
38134 if(this.monitorResize){
38135 this.autoSizeTabs();
38140 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38142 beginUpdate : function(){
38143 this.updating = true;
38147 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38149 endUpdate : function(){
38150 this.updating = false;
38151 this.autoSizeTabs();
38155 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38157 autoSizeTabs : function(){
38158 var count = this.items.length;
38159 var vcount = count - this.hiddenCount;
38160 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38163 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38164 var availWidth = Math.floor(w / vcount);
38165 var b = this.stripBody;
38166 if(b.getWidth() > w){
38167 var tabs = this.items;
38168 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38169 if(availWidth < this.minTabWidth){
38170 /*if(!this.sleft){ // incomplete scrolling code
38171 this.createScrollButtons();
38174 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38177 if(this.currentTabWidth < this.preferredTabWidth){
38178 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38184 * Returns the number of tabs in this TabPanel.
38187 getCount : function(){
38188 return this.items.length;
38192 * Resizes all the tabs to the passed width
38193 * @param {Number} The new width
38195 setTabWidth : function(width){
38196 this.currentTabWidth = width;
38197 for(var i = 0, len = this.items.length; i < len; i++) {
38198 if(!this.items[i].isHidden()) {
38199 this.items[i].setWidth(width);
38205 * Destroys this TabPanel
38206 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38208 destroy : function(removeEl){
38209 Roo.EventManager.removeResizeListener(this.onResize, this);
38210 for(var i = 0, len = this.items.length; i < len; i++){
38211 this.items[i].purgeListeners();
38213 if(removeEl === true){
38214 this.el.update("");
38219 createStrip : function(container)
38221 var strip = document.createElement("nav");
38222 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38223 container.appendChild(strip);
38227 createStripList : function(strip)
38229 // div wrapper for retard IE
38230 // returns the "tr" element.
38231 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38232 //'<div class="x-tabs-strip-wrap">'+
38233 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38234 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38235 return strip.firstChild; //.firstChild.firstChild.firstChild;
38237 createBody : function(container)
38239 var body = document.createElement("div");
38240 Roo.id(body, "tab-body");
38241 //Roo.fly(body).addClass("x-tabs-body");
38242 Roo.fly(body).addClass("tab-content");
38243 container.appendChild(body);
38246 createItemBody :function(bodyEl, id){
38247 var body = Roo.getDom(id);
38249 body = document.createElement("div");
38252 //Roo.fly(body).addClass("x-tabs-item-body");
38253 Roo.fly(body).addClass("tab-pane");
38254 bodyEl.insertBefore(body, bodyEl.firstChild);
38258 createStripElements : function(stripEl, text, closable, tpl)
38260 var td = document.createElement("li"); // was td..
38263 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38266 stripEl.appendChild(td);
38268 td.className = "x-tabs-closable";
38269 if(!this.closeTpl){
38270 this.closeTpl = new Roo.Template(
38271 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38272 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38273 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38276 var el = this.closeTpl.overwrite(td, {"text": text});
38277 var close = el.getElementsByTagName("div")[0];
38278 var inner = el.getElementsByTagName("em")[0];
38279 return {"el": el, "close": close, "inner": inner};
38282 // not sure what this is..
38283 // if(!this.tabTpl){
38284 //this.tabTpl = new Roo.Template(
38285 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38286 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38288 // this.tabTpl = new Roo.Template(
38289 // '<a href="#">' +
38290 // '<span unselectable="on"' +
38291 // (this.disableTooltips ? '' : ' title="{text}"') +
38292 // ' >{text}</span></a>'
38298 var template = tpl || this.tabTpl || false;
38302 template = new Roo.Template(
38304 '<span unselectable="on"' +
38305 (this.disableTooltips ? '' : ' title="{text}"') +
38306 ' >{text}</span></a>'
38310 switch (typeof(template)) {
38314 template = new Roo.Template(template);
38320 var el = template.overwrite(td, {"text": text});
38322 var inner = el.getElementsByTagName("span")[0];
38324 return {"el": el, "inner": inner};
38332 * @class Roo.TabPanelItem
38333 * @extends Roo.util.Observable
38334 * Represents an individual item (tab plus body) in a TabPanel.
38335 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38336 * @param {String} id The id of this TabPanelItem
38337 * @param {String} text The text for the tab of this TabPanelItem
38338 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38340 Roo.bootstrap.panel.TabItem = function(config){
38342 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38343 * @type Roo.TabPanel
38345 this.tabPanel = config.panel;
38347 * The id for this TabPanelItem
38350 this.id = config.id;
38352 this.disabled = false;
38354 this.text = config.text;
38356 this.loaded = false;
38357 this.closable = config.closable;
38360 * The body element for this TabPanelItem.
38361 * @type Roo.Element
38363 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38364 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38365 this.bodyEl.setStyle("display", "block");
38366 this.bodyEl.setStyle("zoom", "1");
38367 //this.hideAction();
38369 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38371 this.el = Roo.get(els.el);
38372 this.inner = Roo.get(els.inner, true);
38373 this.textEl = Roo.get(this.el.dom.firstChild, true);
38374 this.pnode = Roo.get(els.el.parentNode, true);
38375 // this.el.on("mousedown", this.onTabMouseDown, this);
38376 this.el.on("click", this.onTabClick, this);
38378 if(config.closable){
38379 var c = Roo.get(els.close, true);
38380 c.dom.title = this.closeText;
38381 c.addClassOnOver("close-over");
38382 c.on("click", this.closeClick, this);
38388 * Fires when this tab becomes the active tab.
38389 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38390 * @param {Roo.TabPanelItem} this
38394 * @event beforeclose
38395 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38396 * @param {Roo.TabPanelItem} this
38397 * @param {Object} e Set cancel to true on this object to cancel the close.
38399 "beforeclose": true,
38402 * Fires when this tab is closed.
38403 * @param {Roo.TabPanelItem} this
38407 * @event deactivate
38408 * Fires when this tab is no longer the active tab.
38409 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38410 * @param {Roo.TabPanelItem} this
38412 "deactivate" : true
38414 this.hidden = false;
38416 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38419 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38421 purgeListeners : function(){
38422 Roo.util.Observable.prototype.purgeListeners.call(this);
38423 this.el.removeAllListeners();
38426 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38429 this.pnode.addClass("active");
38432 this.tabPanel.stripWrap.repaint();
38434 this.fireEvent("activate", this.tabPanel, this);
38438 * Returns true if this tab is the active tab.
38439 * @return {Boolean}
38441 isActive : function(){
38442 return this.tabPanel.getActiveTab() == this;
38446 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38449 this.pnode.removeClass("active");
38451 this.fireEvent("deactivate", this.tabPanel, this);
38454 hideAction : function(){
38455 this.bodyEl.hide();
38456 this.bodyEl.setStyle("position", "absolute");
38457 this.bodyEl.setLeft("-20000px");
38458 this.bodyEl.setTop("-20000px");
38461 showAction : function(){
38462 this.bodyEl.setStyle("position", "relative");
38463 this.bodyEl.setTop("");
38464 this.bodyEl.setLeft("");
38465 this.bodyEl.show();
38469 * Set the tooltip for the tab.
38470 * @param {String} tooltip The tab's tooltip
38472 setTooltip : function(text){
38473 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38474 this.textEl.dom.qtip = text;
38475 this.textEl.dom.removeAttribute('title');
38477 this.textEl.dom.title = text;
38481 onTabClick : function(e){
38482 e.preventDefault();
38483 this.tabPanel.activate(this.id);
38486 onTabMouseDown : function(e){
38487 e.preventDefault();
38488 this.tabPanel.activate(this.id);
38491 getWidth : function(){
38492 return this.inner.getWidth();
38495 setWidth : function(width){
38496 var iwidth = width - this.pnode.getPadding("lr");
38497 this.inner.setWidth(iwidth);
38498 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38499 this.pnode.setWidth(width);
38503 * Show or hide the tab
38504 * @param {Boolean} hidden True to hide or false to show.
38506 setHidden : function(hidden){
38507 this.hidden = hidden;
38508 this.pnode.setStyle("display", hidden ? "none" : "");
38512 * Returns true if this tab is "hidden"
38513 * @return {Boolean}
38515 isHidden : function(){
38516 return this.hidden;
38520 * Returns the text for this tab
38523 getText : function(){
38527 autoSize : function(){
38528 //this.el.beginMeasure();
38529 this.textEl.setWidth(1);
38531 * #2804 [new] Tabs in Roojs
38532 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38534 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38535 //this.el.endMeasure();
38539 * Sets the text for the tab (Note: this also sets the tooltip text)
38540 * @param {String} text The tab's text and tooltip
38542 setText : function(text){
38544 this.textEl.update(text);
38545 this.setTooltip(text);
38546 //if(!this.tabPanel.resizeTabs){
38547 // this.autoSize();
38551 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38553 activate : function(){
38554 this.tabPanel.activate(this.id);
38558 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38560 disable : function(){
38561 if(this.tabPanel.active != this){
38562 this.disabled = true;
38563 this.pnode.addClass("disabled");
38568 * Enables this TabPanelItem if it was previously disabled.
38570 enable : function(){
38571 this.disabled = false;
38572 this.pnode.removeClass("disabled");
38576 * Sets the content for this TabPanelItem.
38577 * @param {String} content The content
38578 * @param {Boolean} loadScripts true to look for and load scripts
38580 setContent : function(content, loadScripts){
38581 this.bodyEl.update(content, loadScripts);
38585 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38586 * @return {Roo.UpdateManager} The UpdateManager
38588 getUpdateManager : function(){
38589 return this.bodyEl.getUpdateManager();
38593 * Set a URL to be used to load the content for this TabPanelItem.
38594 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38595 * @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)
38596 * @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)
38597 * @return {Roo.UpdateManager} The UpdateManager
38599 setUrl : function(url, params, loadOnce){
38600 if(this.refreshDelegate){
38601 this.un('activate', this.refreshDelegate);
38603 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38604 this.on("activate", this.refreshDelegate);
38605 return this.bodyEl.getUpdateManager();
38609 _handleRefresh : function(url, params, loadOnce){
38610 if(!loadOnce || !this.loaded){
38611 var updater = this.bodyEl.getUpdateManager();
38612 updater.update(url, params, this._setLoaded.createDelegate(this));
38617 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38618 * Will fail silently if the setUrl method has not been called.
38619 * This does not activate the panel, just updates its content.
38621 refresh : function(){
38622 if(this.refreshDelegate){
38623 this.loaded = false;
38624 this.refreshDelegate();
38629 _setLoaded : function(){
38630 this.loaded = true;
38634 closeClick : function(e){
38637 this.fireEvent("beforeclose", this, o);
38638 if(o.cancel !== true){
38639 this.tabPanel.removeTab(this.id);
38643 * The text displayed in the tooltip for the close icon.
38646 closeText : "Close this tab"
38649 * This script refer to:
38650 * Title: International Telephone Input
38651 * Author: Jack O'Connor
38652 * Code version: v12.1.12
38653 * Availability: https://github.com/jackocnr/intl-tel-input.git
38656 Roo.bootstrap.PhoneInputData = function() {
38659 "Afghanistan (افغانستان)",
38664 "Albania (Shqipëri)",
38669 "Algeria (الجزائر)",
38694 "Antigua and Barbuda",
38704 "Armenia (Հայաստան)",
38720 "Austria (Österreich)",
38725 "Azerbaijan (Azərbaycan)",
38735 "Bahrain (البحرين)",
38740 "Bangladesh (বাংলাদেশ)",
38750 "Belarus (Беларусь)",
38755 "Belgium (België)",
38785 "Bosnia and Herzegovina (Босна и Херцеговина)",
38800 "British Indian Ocean Territory",
38805 "British Virgin Islands",
38815 "Bulgaria (България)",
38825 "Burundi (Uburundi)",
38830 "Cambodia (កម្ពុជា)",
38835 "Cameroon (Cameroun)",
38844 ["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"]
38847 "Cape Verde (Kabu Verdi)",
38852 "Caribbean Netherlands",
38863 "Central African Republic (République centrafricaine)",
38883 "Christmas Island",
38889 "Cocos (Keeling) Islands",
38900 "Comoros (جزر القمر)",
38905 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38910 "Congo (Republic) (Congo-Brazzaville)",
38930 "Croatia (Hrvatska)",
38951 "Czech Republic (Česká republika)",
38956 "Denmark (Danmark)",
38971 "Dominican Republic (República Dominicana)",
38975 ["809", "829", "849"]
38993 "Equatorial Guinea (Guinea Ecuatorial)",
39013 "Falkland Islands (Islas Malvinas)",
39018 "Faroe Islands (Føroyar)",
39039 "French Guiana (Guyane française)",
39044 "French Polynesia (Polynésie française)",
39059 "Georgia (საქართველო)",
39064 "Germany (Deutschland)",
39084 "Greenland (Kalaallit Nunaat)",
39121 "Guinea-Bissau (Guiné Bissau)",
39146 "Hungary (Magyarország)",
39151 "Iceland (Ísland)",
39171 "Iraq (العراق)",
39187 "Israel (ישראל)",
39214 "Jordan (الأردن)",
39219 "Kazakhstan (Казахстан)",
39240 "Kuwait (الكويت)",
39245 "Kyrgyzstan (Кыргызстан)",
39255 "Latvia (Latvija)",
39260 "Lebanon (لبنان)",
39275 "Libya (ليبيا)",
39285 "Lithuania (Lietuva)",
39300 "Macedonia (FYROM) (Македонија)",
39305 "Madagascar (Madagasikara)",
39335 "Marshall Islands",
39345 "Mauritania (موريتانيا)",
39350 "Mauritius (Moris)",
39371 "Moldova (Republica Moldova)",
39381 "Mongolia (Монгол)",
39386 "Montenegro (Crna Gora)",
39396 "Morocco (المغرب)",
39402 "Mozambique (Moçambique)",
39407 "Myanmar (Burma) (မြန်မာ)",
39412 "Namibia (Namibië)",
39427 "Netherlands (Nederland)",
39432 "New Caledonia (Nouvelle-Calédonie)",
39467 "North Korea (조선 민주주의 인민 공화국)",
39472 "Northern Mariana Islands",
39488 "Pakistan (پاکستان)",
39498 "Palestine (فلسطين)",
39508 "Papua New Guinea",
39550 "Réunion (La Réunion)",
39556 "Romania (România)",
39572 "Saint Barthélemy",
39583 "Saint Kitts and Nevis",
39593 "Saint Martin (Saint-Martin (partie française))",
39599 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39604 "Saint Vincent and the Grenadines",
39619 "São Tomé and Príncipe (São Tomé e Príncipe)",
39624 "Saudi Arabia (المملكة العربية السعودية)",
39629 "Senegal (Sénégal)",
39659 "Slovakia (Slovensko)",
39664 "Slovenia (Slovenija)",
39674 "Somalia (Soomaaliya)",
39684 "South Korea (대한민국)",
39689 "South Sudan (جنوب السودان)",
39699 "Sri Lanka (ශ්රී ලංකාව)",
39704 "Sudan (السودان)",
39714 "Svalbard and Jan Mayen",
39725 "Sweden (Sverige)",
39730 "Switzerland (Schweiz)",
39735 "Syria (سوريا)",
39780 "Trinidad and Tobago",
39785 "Tunisia (تونس)",
39790 "Turkey (Türkiye)",
39800 "Turks and Caicos Islands",
39810 "U.S. Virgin Islands",
39820 "Ukraine (Україна)",
39825 "United Arab Emirates (الإمارات العربية المتحدة)",
39847 "Uzbekistan (Oʻzbekiston)",
39857 "Vatican City (Città del Vaticano)",
39868 "Vietnam (Việt Nam)",
39873 "Wallis and Futuna (Wallis-et-Futuna)",
39878 "Western Sahara (الصحراء الغربية)",
39884 "Yemen (اليمن)",
39908 * This script refer to:
39909 * Title: International Telephone Input
39910 * Author: Jack O'Connor
39911 * Code version: v12.1.12
39912 * Availability: https://github.com/jackocnr/intl-tel-input.git
39916 * @class Roo.bootstrap.PhoneInput
39917 * @extends Roo.bootstrap.TriggerField
39918 * An input with International dial-code selection
39920 * @cfg {String} defaultDialCode default '+852'
39921 * @cfg {Array} preferedCountries default []
39924 * Create a new PhoneInput.
39925 * @param {Object} config Configuration options
39928 Roo.bootstrap.PhoneInput = function(config) {
39929 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39932 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39934 listWidth: undefined,
39936 selectedClass: 'active',
39938 invalidClass : "has-warning",
39940 validClass: 'has-success',
39942 allowed: '0123456789',
39947 * @cfg {String} defaultDialCode The default dial code when initializing the input
39949 defaultDialCode: '+852',
39952 * @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
39954 preferedCountries: false,
39956 getAutoCreate : function()
39958 var data = Roo.bootstrap.PhoneInputData();
39959 var align = this.labelAlign || this.parentLabelAlign();
39962 this.allCountries = [];
39963 this.dialCodeMapping = [];
39965 for (var i = 0; i < data.length; i++) {
39967 this.allCountries[i] = {
39971 priority: c[3] || 0,
39972 areaCodes: c[4] || null
39974 this.dialCodeMapping[c[2]] = {
39977 priority: c[3] || 0,
39978 areaCodes: c[4] || null
39990 // type: 'number', -- do not use number - we get the flaky up/down arrows.
39991 maxlength: this.max_length,
39992 cls : 'form-control tel-input',
39993 autocomplete: 'new-password'
39996 var hiddenInput = {
39999 cls: 'hidden-tel-input'
40003 hiddenInput.name = this.name;
40006 if (this.disabled) {
40007 input.disabled = true;
40010 var flag_container = {
40027 cls: this.hasFeedback ? 'has-feedback' : '',
40033 cls: 'dial-code-holder',
40040 cls: 'roo-select2-container input-group',
40047 if (this.fieldLabel.length) {
40050 tooltip: 'This field is required'
40056 cls: 'control-label',
40062 html: this.fieldLabel
40065 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40071 if(this.indicatorpos == 'right') {
40072 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40079 if(align == 'left') {
40087 if(this.labelWidth > 12){
40088 label.style = "width: " + this.labelWidth + 'px';
40090 if(this.labelWidth < 13 && this.labelmd == 0){
40091 this.labelmd = this.labelWidth;
40093 if(this.labellg > 0){
40094 label.cls += ' col-lg-' + this.labellg;
40095 input.cls += ' col-lg-' + (12 - this.labellg);
40097 if(this.labelmd > 0){
40098 label.cls += ' col-md-' + this.labelmd;
40099 container.cls += ' col-md-' + (12 - this.labelmd);
40101 if(this.labelsm > 0){
40102 label.cls += ' col-sm-' + this.labelsm;
40103 container.cls += ' col-sm-' + (12 - this.labelsm);
40105 if(this.labelxs > 0){
40106 label.cls += ' col-xs-' + this.labelxs;
40107 container.cls += ' col-xs-' + (12 - this.labelxs);
40117 var settings = this;
40119 ['xs','sm','md','lg'].map(function(size){
40120 if (settings[size]) {
40121 cfg.cls += ' col-' + size + '-' + settings[size];
40125 this.store = new Roo.data.Store({
40126 proxy : new Roo.data.MemoryProxy({}),
40127 reader : new Roo.data.JsonReader({
40138 'name' : 'dialCode',
40142 'name' : 'priority',
40146 'name' : 'areaCodes',
40153 if(!this.preferedCountries) {
40154 this.preferedCountries = [
40161 var p = this.preferedCountries.reverse();
40164 for (var i = 0; i < p.length; i++) {
40165 for (var j = 0; j < this.allCountries.length; j++) {
40166 if(this.allCountries[j].iso2 == p[i]) {
40167 var t = this.allCountries[j];
40168 this.allCountries.splice(j,1);
40169 this.allCountries.unshift(t);
40175 this.store.proxy.data = {
40177 data: this.allCountries
40183 initEvents : function()
40186 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40188 this.indicator = this.indicatorEl();
40189 this.flag = this.flagEl();
40190 this.dialCodeHolder = this.dialCodeHolderEl();
40192 this.trigger = this.el.select('div.flag-box',true).first();
40193 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40198 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40199 _this.list.setWidth(lw);
40202 this.list.on('mouseover', this.onViewOver, this);
40203 this.list.on('mousemove', this.onViewMove, this);
40204 this.inputEl().on("keyup", this.onKeyUp, this);
40205 this.inputEl().on("keypress", this.onKeyPress, this);
40207 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40209 this.view = new Roo.View(this.list, this.tpl, {
40210 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40213 this.view.on('click', this.onViewClick, this);
40214 this.setValue(this.defaultDialCode);
40217 onTriggerClick : function(e)
40219 Roo.log('trigger click');
40224 if(this.isExpanded()){
40226 this.hasFocus = false;
40228 this.store.load({});
40229 this.hasFocus = true;
40234 isExpanded : function()
40236 return this.list.isVisible();
40239 collapse : function()
40241 if(!this.isExpanded()){
40245 Roo.get(document).un('mousedown', this.collapseIf, this);
40246 Roo.get(document).un('mousewheel', this.collapseIf, this);
40247 this.fireEvent('collapse', this);
40251 expand : function()
40255 if(this.isExpanded() || !this.hasFocus){
40259 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40260 this.list.setWidth(lw);
40263 this.restrictHeight();
40265 Roo.get(document).on('mousedown', this.collapseIf, this);
40266 Roo.get(document).on('mousewheel', this.collapseIf, this);
40268 this.fireEvent('expand', this);
40271 restrictHeight : function()
40273 this.list.alignTo(this.inputEl(), this.listAlign);
40274 this.list.alignTo(this.inputEl(), this.listAlign);
40277 onViewOver : function(e, t)
40279 if(this.inKeyMode){
40282 var item = this.view.findItemFromChild(t);
40285 var index = this.view.indexOf(item);
40286 this.select(index, false);
40291 onViewClick : function(view, doFocus, el, e)
40293 var index = this.view.getSelectedIndexes()[0];
40295 var r = this.store.getAt(index);
40298 this.onSelect(r, index);
40300 if(doFocus !== false && !this.blockFocus){
40301 this.inputEl().focus();
40305 onViewMove : function(e, t)
40307 this.inKeyMode = false;
40310 select : function(index, scrollIntoView)
40312 this.selectedIndex = index;
40313 this.view.select(index);
40314 if(scrollIntoView !== false){
40315 var el = this.view.getNode(index);
40317 this.list.scrollChildIntoView(el, false);
40322 createList : function()
40324 this.list = Roo.get(document.body).createChild({
40326 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40327 style: 'display:none'
40330 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40333 collapseIf : function(e)
40335 var in_combo = e.within(this.el);
40336 var in_list = e.within(this.list);
40337 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40339 if (in_combo || in_list || is_list) {
40345 onSelect : function(record, index)
40347 if(this.fireEvent('beforeselect', this, record, index) !== false){
40349 this.setFlagClass(record.data.iso2);
40350 this.setDialCode(record.data.dialCode);
40351 this.hasFocus = false;
40353 this.fireEvent('select', this, record, index);
40357 flagEl : function()
40359 var flag = this.el.select('div.flag',true).first();
40366 dialCodeHolderEl : function()
40368 var d = this.el.select('input.dial-code-holder',true).first();
40375 setDialCode : function(v)
40377 this.dialCodeHolder.dom.value = '+'+v;
40380 setFlagClass : function(n)
40382 this.flag.dom.className = 'flag '+n;
40385 getValue : function()
40387 var v = this.inputEl().getValue();
40388 if(this.dialCodeHolder) {
40389 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40394 setValue : function(v)
40396 var d = this.getDialCode(v);
40398 //invalid dial code
40399 if(v.length == 0 || !d || d.length == 0) {
40401 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40402 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40408 this.setFlagClass(this.dialCodeMapping[d].iso2);
40409 this.setDialCode(d);
40410 this.inputEl().dom.value = v.replace('+'+d,'');
40411 this.hiddenEl().dom.value = this.getValue();
40416 getDialCode : function(v)
40420 if (v.length == 0) {
40421 return this.dialCodeHolder.dom.value;
40425 if (v.charAt(0) != "+") {
40428 var numericChars = "";
40429 for (var i = 1; i < v.length; i++) {
40430 var c = v.charAt(i);
40433 if (this.dialCodeMapping[numericChars]) {
40434 dialCode = v.substr(1, i);
40436 if (numericChars.length == 4) {
40446 this.setValue(this.defaultDialCode);
40450 hiddenEl : function()
40452 return this.el.select('input.hidden-tel-input',true).first();
40455 // after setting val
40456 onKeyUp : function(e){
40457 this.setValue(this.getValue());
40460 onKeyPress : function(e){
40461 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40468 * @class Roo.bootstrap.MoneyField
40469 * @extends Roo.bootstrap.ComboBox
40470 * Bootstrap MoneyField class
40473 * Create a new MoneyField.
40474 * @param {Object} config Configuration options
40477 Roo.bootstrap.MoneyField = function(config) {
40479 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40483 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40486 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40488 allowDecimals : true,
40490 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40492 decimalSeparator : ".",
40494 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40496 decimalPrecision : 0,
40498 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40500 allowNegative : true,
40502 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40506 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40508 minValue : Number.NEGATIVE_INFINITY,
40510 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40512 maxValue : Number.MAX_VALUE,
40514 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40516 minText : "The minimum value for this field is {0}",
40518 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40520 maxText : "The maximum value for this field is {0}",
40522 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40523 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40525 nanText : "{0} is not a valid number",
40527 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40531 * @cfg {String} defaults currency of the MoneyField
40532 * value should be in lkey
40534 defaultCurrency : false,
40536 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40538 thousandsDelimiter : false,
40540 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40551 getAutoCreate : function()
40553 var align = this.labelAlign || this.parentLabelAlign();
40565 cls : 'form-control roo-money-amount-input',
40566 autocomplete: 'new-password'
40569 var hiddenInput = {
40573 cls: 'hidden-number-input'
40576 if(this.max_length) {
40577 input.maxlength = this.max_length;
40581 hiddenInput.name = this.name;
40584 if (this.disabled) {
40585 input.disabled = true;
40588 var clg = 12 - this.inputlg;
40589 var cmd = 12 - this.inputmd;
40590 var csm = 12 - this.inputsm;
40591 var cxs = 12 - this.inputxs;
40595 cls : 'row roo-money-field',
40599 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40603 cls: 'roo-select2-container input-group',
40607 cls : 'form-control roo-money-currency-input',
40608 autocomplete: 'new-password',
40610 name : this.currencyName
40614 cls : 'input-group-addon',
40628 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40632 cls: this.hasFeedback ? 'has-feedback' : '',
40643 if (this.fieldLabel.length) {
40646 tooltip: 'This field is required'
40652 cls: 'control-label',
40658 html: this.fieldLabel
40661 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40667 if(this.indicatorpos == 'right') {
40668 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40675 if(align == 'left') {
40683 if(this.labelWidth > 12){
40684 label.style = "width: " + this.labelWidth + 'px';
40686 if(this.labelWidth < 13 && this.labelmd == 0){
40687 this.labelmd = this.labelWidth;
40689 if(this.labellg > 0){
40690 label.cls += ' col-lg-' + this.labellg;
40691 input.cls += ' col-lg-' + (12 - this.labellg);
40693 if(this.labelmd > 0){
40694 label.cls += ' col-md-' + this.labelmd;
40695 container.cls += ' col-md-' + (12 - this.labelmd);
40697 if(this.labelsm > 0){
40698 label.cls += ' col-sm-' + this.labelsm;
40699 container.cls += ' col-sm-' + (12 - this.labelsm);
40701 if(this.labelxs > 0){
40702 label.cls += ' col-xs-' + this.labelxs;
40703 container.cls += ' col-xs-' + (12 - this.labelxs);
40714 var settings = this;
40716 ['xs','sm','md','lg'].map(function(size){
40717 if (settings[size]) {
40718 cfg.cls += ' col-' + size + '-' + settings[size];
40725 initEvents : function()
40727 this.indicator = this.indicatorEl();
40729 this.initCurrencyEvent();
40731 this.initNumberEvent();
40734 initCurrencyEvent : function()
40737 throw "can not find store for combo";
40740 this.store = Roo.factory(this.store, Roo.data);
40741 this.store.parent = this;
40745 this.triggerEl = this.el.select('.input-group-addon', true).first();
40747 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40752 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40753 _this.list.setWidth(lw);
40756 this.list.on('mouseover', this.onViewOver, this);
40757 this.list.on('mousemove', this.onViewMove, this);
40758 this.list.on('scroll', this.onViewScroll, this);
40761 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40764 this.view = new Roo.View(this.list, this.tpl, {
40765 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40768 this.view.on('click', this.onViewClick, this);
40770 this.store.on('beforeload', this.onBeforeLoad, this);
40771 this.store.on('load', this.onLoad, this);
40772 this.store.on('loadexception', this.onLoadException, this);
40774 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40775 "up" : function(e){
40776 this.inKeyMode = true;
40780 "down" : function(e){
40781 if(!this.isExpanded()){
40782 this.onTriggerClick();
40784 this.inKeyMode = true;
40789 "enter" : function(e){
40792 if(this.fireEvent("specialkey", this, e)){
40793 this.onViewClick(false);
40799 "esc" : function(e){
40803 "tab" : function(e){
40806 if(this.fireEvent("specialkey", this, e)){
40807 this.onViewClick(false);
40815 doRelay : function(foo, bar, hname){
40816 if(hname == 'down' || this.scope.isExpanded()){
40817 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40825 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40829 initNumberEvent : function(e)
40831 this.inputEl().on("keydown" , this.fireKey, this);
40832 this.inputEl().on("focus", this.onFocus, this);
40833 this.inputEl().on("blur", this.onBlur, this);
40835 this.inputEl().relayEvent('keyup', this);
40837 if(this.indicator){
40838 this.indicator.addClass('invisible');
40841 this.originalValue = this.getValue();
40843 if(this.validationEvent == 'keyup'){
40844 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40845 this.inputEl().on('keyup', this.filterValidation, this);
40847 else if(this.validationEvent !== false){
40848 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40851 if(this.selectOnFocus){
40852 this.on("focus", this.preFocus, this);
40855 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40856 this.inputEl().on("keypress", this.filterKeys, this);
40858 this.inputEl().relayEvent('keypress', this);
40861 var allowed = "0123456789";
40863 if(this.allowDecimals){
40864 allowed += this.decimalSeparator;
40867 if(this.allowNegative){
40871 if(this.thousandsDelimiter) {
40875 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40877 var keyPress = function(e){
40879 var k = e.getKey();
40881 var c = e.getCharCode();
40884 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40885 allowed.indexOf(String.fromCharCode(c)) === -1
40891 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40895 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40900 this.inputEl().on("keypress", keyPress, this);
40904 onTriggerClick : function(e)
40911 this.loadNext = false;
40913 if(this.isExpanded()){
40918 this.hasFocus = true;
40920 if(this.triggerAction == 'all') {
40921 this.doQuery(this.allQuery, true);
40925 this.doQuery(this.getRawValue());
40928 getCurrency : function()
40930 var v = this.currencyEl().getValue();
40935 restrictHeight : function()
40937 this.list.alignTo(this.currencyEl(), this.listAlign);
40938 this.list.alignTo(this.currencyEl(), this.listAlign);
40941 onViewClick : function(view, doFocus, el, e)
40943 var index = this.view.getSelectedIndexes()[0];
40945 var r = this.store.getAt(index);
40948 this.onSelect(r, index);
40952 onSelect : function(record, index){
40954 if(this.fireEvent('beforeselect', this, record, index) !== false){
40956 this.setFromCurrencyData(index > -1 ? record.data : false);
40960 this.fireEvent('select', this, record, index);
40964 setFromCurrencyData : function(o)
40968 this.lastCurrency = o;
40970 if (this.currencyField) {
40971 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40973 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40976 this.lastSelectionText = currency;
40978 //setting default currency
40979 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40980 this.setCurrency(this.defaultCurrency);
40984 this.setCurrency(currency);
40987 setFromData : function(o)
40991 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40993 this.setFromCurrencyData(c);
40998 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41000 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41003 this.setValue(value);
41007 setCurrency : function(v)
41009 this.currencyValue = v;
41012 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41017 setValue : function(v)
41019 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41025 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41027 this.inputEl().dom.value = (v == '') ? '' :
41028 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41030 if(!this.allowZero && v === '0') {
41031 this.hiddenEl().dom.value = '';
41032 this.inputEl().dom.value = '';
41039 getRawValue : function()
41041 var v = this.inputEl().getValue();
41046 getValue : function()
41048 return this.fixPrecision(this.parseValue(this.getRawValue()));
41051 parseValue : function(value)
41053 if(this.thousandsDelimiter) {
41055 r = new RegExp(",", "g");
41056 value = value.replace(r, "");
41059 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41060 return isNaN(value) ? '' : value;
41064 fixPrecision : function(value)
41066 if(this.thousandsDelimiter) {
41068 r = new RegExp(",", "g");
41069 value = value.replace(r, "");
41072 var nan = isNaN(value);
41074 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41075 return nan ? '' : value;
41077 return parseFloat(value).toFixed(this.decimalPrecision);
41080 decimalPrecisionFcn : function(v)
41082 return Math.floor(v);
41085 validateValue : function(value)
41087 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41091 var num = this.parseValue(value);
41094 this.markInvalid(String.format(this.nanText, value));
41098 if(num < this.minValue){
41099 this.markInvalid(String.format(this.minText, this.minValue));
41103 if(num > this.maxValue){
41104 this.markInvalid(String.format(this.maxText, this.maxValue));
41111 validate : function()
41113 if(this.disabled || this.allowBlank){
41118 var currency = this.getCurrency();
41120 if(this.validateValue(this.getRawValue()) && currency.length){
41125 this.markInvalid();
41129 getName: function()
41134 beforeBlur : function()
41140 var v = this.parseValue(this.getRawValue());
41147 onBlur : function()
41151 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41152 //this.el.removeClass(this.focusClass);
41155 this.hasFocus = false;
41157 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41161 var v = this.getValue();
41163 if(String(v) !== String(this.startValue)){
41164 this.fireEvent('change', this, v, this.startValue);
41167 this.fireEvent("blur", this);
41170 inputEl : function()
41172 return this.el.select('.roo-money-amount-input', true).first();
41175 currencyEl : function()
41177 return this.el.select('.roo-money-currency-input', true).first();
41180 hiddenEl : function()
41182 return this.el.select('input.hidden-number-input',true).first();