4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
105 cfg.id = this.id || Roo.id();
107 // fill in the extra attributes
108 if (this.xattr && typeof(this.xattr) =='object') {
109 for (var i in this.xattr) {
110 cfg[i] = this.xattr[i];
115 cfg.dataId = this.dataId;
119 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
122 if (this.style) { // fixme needs to support more complex style data.
123 cfg.style = this.style;
127 cfg.name = this.name;
130 this.el = ct.createChild(cfg, position);
133 this.tooltipEl().attr('tooltip', this.tooltip);
136 if(this.tabIndex !== undefined){
137 this.el.dom.setAttribute('tabIndex', this.tabIndex);
144 * Fetch the element to add children to
145 * @return {Roo.Element} defaults to this.el
147 getChildContainer : function()
152 * Fetch the element to display the tooltip on.
153 * @return {Roo.Element} defaults to this.el
155 tooltipEl : function()
160 addxtype : function(tree,cntr)
164 cn = Roo.factory(tree);
165 //Roo.log(['addxtype', cn]);
167 cn.parentType = this.xtype; //??
168 cn.parentId = this.id;
170 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171 if (typeof(cn.container_method) == 'string') {
172 cntr = cn.container_method;
176 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
178 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
180 var build_from_html = Roo.XComponent.build_from_html;
182 var is_body = (tree.xtype == 'Body') ;
184 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186 var self_cntr_el = Roo.get(this[cntr](false));
188 // do not try and build conditional elements
189 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
193 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195 return this.addxtypeChild(tree,cntr, is_body);
198 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
201 return this.addxtypeChild(Roo.apply({}, tree),cntr);
204 Roo.log('skipping render');
210 if (!build_from_html) {
214 // this i think handles overlaying multiple children of the same type
215 // with the sam eelement.. - which might be buggy..
217 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
223 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
227 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
234 addxtypeChild : function (tree, cntr, is_body)
236 Roo.debug && Roo.log('addxtypeChild:' + cntr);
238 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
241 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242 (typeof(tree['flexy:foreach']) != 'undefined');
246 skip_children = false;
247 // render the element if it's not BODY.
250 cn = Roo.factory(tree);
252 cn.parentType = this.xtype; //??
253 cn.parentId = this.id;
255 var build_from_html = Roo.XComponent.build_from_html;
258 // does the container contain child eleemnts with 'xtype' attributes.
259 // that match this xtype..
260 // note - when we render we create these as well..
261 // so we should check to see if body has xtype set.
262 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
264 var self_cntr_el = Roo.get(this[cntr](false));
265 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
267 //Roo.log(Roo.XComponent.build_from_html);
268 //Roo.log("got echild:");
271 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272 // and are not displayed -this causes this to use up the wrong element when matching.
273 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
276 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
283 //echild.dom.removeAttribute('xtype');
285 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286 Roo.debug && Roo.log(self_cntr_el);
287 Roo.debug && Roo.log(echild);
288 Roo.debug && Roo.log(cn);
294 // if object has flexy:if - then it may or may not be rendered.
295 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
296 // skip a flexy if element.
297 Roo.debug && Roo.log('skipping render');
298 Roo.debug && Roo.log(tree);
300 Roo.debug && Roo.log('skipping all children');
301 skip_children = true;
306 // actually if flexy:foreach is found, we really want to create
307 // multiple copies here...
309 //Roo.log(this[cntr]());
310 // some elements do not have render methods.. like the layouts...
311 cn.render && cn.render(this[cntr](true));
313 // then add the element..
321 if (typeof (tree.menu) != 'undefined') {
322 tree.menu.parentType = cn.xtype;
323 tree.menu.triggerEl = cn.el;
324 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
328 if (!tree.items || !tree.items.length) {
330 //Roo.log(["no children", this]);
335 var items = tree.items;
338 //Roo.log(items.length);
340 if (!skip_children) {
341 for(var i =0;i < items.length;i++) {
342 // Roo.log(['add child', items[i]]);
343 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
349 //Roo.log("fire childrenrendered");
351 cn.fireEvent('childrenrendered', this);
356 * Show a component - removes 'hidden' class
361 this.el.removeClass('hidden');
365 * Hide a component - adds 'hidden' class
369 if (this.el && !this.el.hasClass('hidden')) {
370 this.el.addClass('hidden');
384 * @class Roo.bootstrap.Body
385 * @extends Roo.bootstrap.Component
386 * Bootstrap Body class
390 * @param {Object} config The config object
393 Roo.bootstrap.Body = function(config){
395 config = config || {};
397 Roo.bootstrap.Body.superclass.constructor.call(this, config);
398 this.el = Roo.get(config.el ? config.el : document.body );
399 if (this.cls && this.cls.length) {
400 Roo.get(document.body).addClass(this.cls);
404 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
406 is_body : true,// just to make sure it's constructed?
411 onRender : function(ct, position)
413 /* Roo.log("Roo.bootstrap.Body - onRender");
414 if (this.cls && this.cls.length) {
415 Roo.get(document.body).addClass(this.cls);
434 * @class Roo.bootstrap.ButtonGroup
435 * @extends Roo.bootstrap.Component
436 * Bootstrap ButtonGroup class
437 * @cfg {String} size lg | sm | xs (default empty normal)
438 * @cfg {String} align vertical | justified (default none)
439 * @cfg {String} direction up | down (default down)
440 * @cfg {Boolean} toolbar false | true
441 * @cfg {Boolean} btn true | false
446 * @param {Object} config The config object
449 Roo.bootstrap.ButtonGroup = function(config){
450 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
453 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
461 getAutoCreate : function(){
467 cfg.html = this.html || cfg.html;
478 if (['vertical','justified'].indexOf(this.align)!==-1) {
479 cfg.cls = 'btn-group-' + this.align;
481 if (this.align == 'justified') {
482 console.log(this.items);
486 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
487 cfg.cls += ' btn-group-' + this.size;
490 if (this.direction == 'up') {
491 cfg.cls += ' dropup' ;
507 * @class Roo.bootstrap.Button
508 * @extends Roo.bootstrap.Component
509 * Bootstrap Button class
510 * @cfg {String} html The button content
511 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
512 * @cfg {String} size ( lg | sm | xs)
513 * @cfg {String} tag ( a | input | submit)
514 * @cfg {String} href empty or href
515 * @cfg {Boolean} disabled default false;
516 * @cfg {Boolean} isClose default false;
517 * @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)
518 * @cfg {String} badge text for badge
519 * @cfg {String} theme default
520 * @cfg {Boolean} inverse
521 * @cfg {Boolean} toggle
522 * @cfg {String} ontext text for on toggle state
523 * @cfg {String} offtext text for off toggle state
524 * @cfg {Boolean} defaulton
525 * @cfg {Boolean} preventDefault default true
526 * @cfg {Boolean} removeClass remove the standard class..
527 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
530 * Create a new button
531 * @param {Object} config The config object
535 Roo.bootstrap.Button = function(config){
536 Roo.bootstrap.Button.superclass.constructor.call(this, config);
541 * When a butotn is pressed
542 * @param {Roo.bootstrap.Button} this
543 * @param {Roo.EventObject} e
548 * After the button has been toggles
549 * @param {Roo.EventObject} e
550 * @param {boolean} pressed (also available as button.pressed)
556 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
574 preventDefault: true,
583 getAutoCreate : function(){
591 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
592 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
597 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
599 if (this.toggle == true) {
602 cls: 'slider-frame roo-button',
607 'data-off-text':'OFF',
608 cls: 'slider-button',
614 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
615 cfg.cls += ' '+this.weight;
624 cfg["aria-hidden"] = true;
626 cfg.html = "×";
632 if (this.theme==='default') {
633 cfg.cls = 'btn roo-button';
635 //if (this.parentType != 'Navbar') {
636 this.weight = this.weight.length ? this.weight : 'default';
638 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
640 cfg.cls += ' btn-' + this.weight;
642 } else if (this.theme==='glow') {
645 cfg.cls = 'btn-glow roo-button';
647 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
649 cfg.cls += ' ' + this.weight;
655 this.cls += ' inverse';
660 cfg.cls += ' active';
664 cfg.disabled = 'disabled';
668 Roo.log('changing to ul' );
670 this.glyphicon = 'caret';
673 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
675 //gsRoo.log(this.parentType);
676 if (this.parentType === 'Navbar' && !this.parent().bar) {
677 Roo.log('changing to li?');
686 href : this.href || '#'
689 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
690 cfg.cls += ' dropdown';
697 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
699 if (this.glyphicon) {
700 cfg.html = ' ' + cfg.html;
705 cls: 'glyphicon glyphicon-' + this.glyphicon
715 // cfg.cls='btn roo-button';
719 var value = cfg.html;
724 cls: 'glyphicon glyphicon-' + this.glyphicon,
743 cfg.cls += ' dropdown';
744 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
747 if (cfg.tag !== 'a' && this.href !== '') {
748 throw "Tag must be a to set href.";
749 } else if (this.href.length > 0) {
750 cfg.href = this.href;
753 if(this.removeClass){
758 cfg.target = this.target;
763 initEvents: function() {
764 // Roo.log('init events?');
765 // Roo.log(this.el.dom);
768 if (typeof (this.menu) != 'undefined') {
769 this.menu.parentType = this.xtype;
770 this.menu.triggerEl = this.el;
771 this.addxtype(Roo.apply({}, this.menu));
775 if (this.el.hasClass('roo-button')) {
776 this.el.on('click', this.onClick, this);
778 this.el.select('.roo-button').on('click', this.onClick, this);
781 if(this.removeClass){
782 this.el.on('click', this.onClick, this);
785 this.el.enableDisplayMode();
788 onClick : function(e)
795 Roo.log('button on click ');
796 if(this.preventDefault){
799 if (this.pressed === true || this.pressed === false) {
800 this.pressed = !this.pressed;
801 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
802 this.fireEvent('toggle', this, e, this.pressed);
806 this.fireEvent('click', this, e);
810 * Enables this button
814 this.disabled = false;
815 this.el.removeClass('disabled');
819 * Disable this button
823 this.disabled = true;
824 this.el.addClass('disabled');
827 * sets the active state on/off,
828 * @param {Boolean} state (optional) Force a particular state
830 setActive : function(v) {
832 this.el[v ? 'addClass' : 'removeClass']('active');
835 * toggles the current active state
837 toggleActive : function()
839 var active = this.el.hasClass('active');
840 this.setActive(!active);
844 setText : function(str)
846 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
850 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
873 * @class Roo.bootstrap.Column
874 * @extends Roo.bootstrap.Component
875 * Bootstrap Column class
876 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
877 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
878 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
879 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
880 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
881 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
882 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
883 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
886 * @cfg {Boolean} hidden (true|false) hide the element
887 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
888 * @cfg {String} fa (ban|check|...) font awesome icon
889 * @cfg {Number} fasize (1|2|....) font awsome size
891 * @cfg {String} icon (info-sign|check|...) glyphicon name
893 * @cfg {String} html content of column.
896 * Create a new Column
897 * @param {Object} config The config object
900 Roo.bootstrap.Column = function(config){
901 Roo.bootstrap.Column.superclass.constructor.call(this, config);
904 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
922 getAutoCreate : function(){
923 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
931 ['xs','sm','md','lg'].map(function(size){
932 //Roo.log( size + ':' + settings[size]);
934 if (settings[size+'off'] !== false) {
935 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
938 if (settings[size] === false) {
942 if (!settings[size]) { // 0 = hidden
943 cfg.cls += ' hidden-' + size;
946 cfg.cls += ' col-' + size + '-' + settings[size];
951 cfg.cls += ' hidden';
954 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
955 cfg.cls +=' alert alert-' + this.alert;
959 if (this.html.length) {
960 cfg.html = this.html;
964 if (this.fasize > 1) {
965 fasize = ' fa-' + this.fasize + 'x';
967 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
972 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
991 * @class Roo.bootstrap.Container
992 * @extends Roo.bootstrap.Component
993 * Bootstrap Container class
994 * @cfg {Boolean} jumbotron is it a jumbotron element
995 * @cfg {String} html content of element
996 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
997 * @cfg {String} panel (primary|success|info|warning|danger) render as panel - type - primary/success.....
998 * @cfg {String} header content of header (for panel)
999 * @cfg {String} footer content of footer (for panel)
1000 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1001 * @cfg {String} tag (header|aside|section) type of HTML tag.
1002 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1003 * @cfg {String} fa font awesome icon
1004 * @cfg {String} icon (info-sign|check|...) glyphicon name
1005 * @cfg {Boolean} hidden (true|false) hide the element
1006 * @cfg {Boolean} expandable (true|false) default false
1007 * @cfg {Boolean} expanded (true|false) default true
1008 * @cfg {String} rheader contet on the right of header
1009 * @cfg {Boolean} clickable (true|false) default false
1013 * Create a new Container
1014 * @param {Object} config The config object
1017 Roo.bootstrap.Container = function(config){
1018 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1024 * After the panel has been expand
1026 * @param {Roo.bootstrap.Container} this
1031 * After the panel has been collapsed
1033 * @param {Roo.bootstrap.Container} this
1038 * When a element is chick
1039 * @param {Roo.bootstrap.Container} this
1040 * @param {Roo.EventObject} e
1046 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1064 getChildContainer : function() {
1070 if (this.panel.length) {
1071 return this.el.select('.panel-body',true).first();
1078 getAutoCreate : function(){
1081 tag : this.tag || 'div',
1085 if (this.jumbotron) {
1086 cfg.cls = 'jumbotron';
1091 // - this is applied by the parent..
1093 // cfg.cls = this.cls + '';
1096 if (this.sticky.length) {
1098 var bd = Roo.get(document.body);
1099 if (!bd.hasClass('bootstrap-sticky')) {
1100 bd.addClass('bootstrap-sticky');
1101 Roo.select('html',true).setStyle('height', '100%');
1104 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1108 if (this.well.length) {
1109 switch (this.well) {
1112 cfg.cls +=' well well-' +this.well;
1121 cfg.cls += ' hidden';
1125 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1126 cfg.cls +=' alert alert-' + this.alert;
1131 if (this.panel.length) {
1132 cfg.cls += ' panel panel-' + this.panel;
1134 if (this.header.length) {
1138 if(this.expandable){
1140 cfg.cls = cfg.cls + ' expandable';
1144 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1152 cls : 'panel-title',
1153 html : (this.expandable ? ' ' : '') + this.header
1157 cls: 'panel-header-right',
1163 cls : 'panel-heading',
1164 style : this.expandable ? 'cursor: pointer' : '',
1172 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1177 if (this.footer.length) {
1179 cls : 'panel-footer',
1188 body.html = this.html || cfg.html;
1189 // prefix with the icons..
1191 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1194 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1199 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1200 cfg.cls = 'container';
1206 initEvents: function()
1208 if(this.expandable){
1209 var headerEl = this.headerEl();
1212 headerEl.on('click', this.onToggleClick, this);
1217 this.el.on('click', this.onClick, this);
1222 onToggleClick : function()
1224 var headerEl = this.headerEl();
1240 if(this.fireEvent('expand', this)) {
1242 this.expanded = true;
1244 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1246 this.el.select('.panel-body',true).first().removeClass('hide');
1248 var toggleEl = this.toggleEl();
1254 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1259 collapse : function()
1261 if(this.fireEvent('collapse', this)) {
1263 this.expanded = false;
1265 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1266 this.el.select('.panel-body',true).first().addClass('hide');
1268 var toggleEl = this.toggleEl();
1274 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1278 toggleEl : function()
1280 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1284 return this.el.select('.panel-heading .fa',true).first();
1287 headerEl : function()
1289 if(!this.el || !this.panel.length || !this.header.length){
1293 return this.el.select('.panel-heading',true).first()
1296 titleEl : function()
1298 if(!this.el || !this.panel.length || !this.header.length){
1302 return this.el.select('.panel-title',true).first();
1305 setTitle : function(v)
1307 var titleEl = this.titleEl();
1313 titleEl.dom.innerHTML = v;
1316 getTitle : function()
1319 var titleEl = this.titleEl();
1325 return titleEl.dom.innerHTML;
1328 setRightTitle : function(v)
1330 var t = this.el.select('.panel-header-right',true).first();
1336 t.dom.innerHTML = v;
1339 onClick : function(e)
1343 this.fireEvent('click', this, e);
1357 * @class Roo.bootstrap.Img
1358 * @extends Roo.bootstrap.Component
1359 * Bootstrap Img class
1360 * @cfg {Boolean} imgResponsive false | true
1361 * @cfg {String} border rounded | circle | thumbnail
1362 * @cfg {String} src image source
1363 * @cfg {String} alt image alternative text
1364 * @cfg {String} href a tag href
1365 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1366 * @cfg {String} xsUrl xs image source
1367 * @cfg {String} smUrl sm image source
1368 * @cfg {String} mdUrl md image source
1369 * @cfg {String} lgUrl lg image source
1372 * Create a new Input
1373 * @param {Object} config The config object
1376 Roo.bootstrap.Img = function(config){
1377 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1383 * The img click event for the img.
1384 * @param {Roo.EventObject} e
1390 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1392 imgResponsive: true,
1402 getAutoCreate : function()
1404 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1405 return this.createSingleImg();
1410 cls: 'roo-image-responsive-group',
1415 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1417 if(!_this[size + 'Url']){
1423 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1424 html: _this.html || cfg.html,
1425 src: _this[size + 'Url']
1428 img.cls += ' roo-image-responsive-' + size;
1430 var s = ['xs', 'sm', 'md', 'lg'];
1432 s.splice(s.indexOf(size), 1);
1434 Roo.each(s, function(ss){
1435 img.cls += ' hidden-' + ss;
1438 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1439 cfg.cls += ' img-' + _this.border;
1443 cfg.alt = _this.alt;
1456 a.target = _this.target;
1460 cfg.cn.push((_this.href) ? a : img);
1467 createSingleImg : function()
1471 cls: (this.imgResponsive) ? 'img-responsive' : '',
1473 src : 'about:blank' // just incase src get's set to undefined?!?
1476 cfg.html = this.html || cfg.html;
1478 cfg.src = this.src || cfg.src;
1480 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1481 cfg.cls += ' img-' + this.border;
1498 a.target = this.target;
1503 return (this.href) ? a : cfg;
1506 initEvents: function()
1509 this.el.on('click', this.onClick, this);
1514 onClick : function(e)
1516 Roo.log('img onclick');
1517 this.fireEvent('click', this, e);
1520 * Sets the url of the image - used to update it
1521 * @param {String} url the url of the image
1524 setSrc : function(url)
1528 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1529 this.el.dom.src = url;
1533 this.el.select('img', true).first().dom.src = url;
1549 * @class Roo.bootstrap.Link
1550 * @extends Roo.bootstrap.Component
1551 * Bootstrap Link Class
1552 * @cfg {String} alt image alternative text
1553 * @cfg {String} href a tag href
1554 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1555 * @cfg {String} html the content of the link.
1556 * @cfg {String} anchor name for the anchor link
1557 * @cfg {String} fa - favicon
1559 * @cfg {Boolean} preventDefault (true | false) default false
1563 * Create a new Input
1564 * @param {Object} config The config object
1567 Roo.bootstrap.Link = function(config){
1568 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1574 * The img click event for the img.
1575 * @param {Roo.EventObject} e
1581 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1585 preventDefault: false,
1591 getAutoCreate : function()
1593 var html = this.html || '';
1595 if (this.fa !== false) {
1596 html = '<i class="fa fa-' + this.fa + '"></i>';
1601 // anchor's do not require html/href...
1602 if (this.anchor === false) {
1604 cfg.href = this.href || '#';
1606 cfg.name = this.anchor;
1607 if (this.html !== false || this.fa !== false) {
1610 if (this.href !== false) {
1611 cfg.href = this.href;
1615 if(this.alt !== false){
1620 if(this.target !== false) {
1621 cfg.target = this.target;
1627 initEvents: function() {
1629 if(!this.href || this.preventDefault){
1630 this.el.on('click', this.onClick, this);
1634 onClick : function(e)
1636 if(this.preventDefault){
1639 //Roo.log('img onclick');
1640 this.fireEvent('click', this, e);
1653 * @class Roo.bootstrap.Header
1654 * @extends Roo.bootstrap.Component
1655 * Bootstrap Header class
1656 * @cfg {String} html content of header
1657 * @cfg {Number} level (1|2|3|4|5|6) default 1
1660 * Create a new Header
1661 * @param {Object} config The config object
1665 Roo.bootstrap.Header = function(config){
1666 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1669 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1677 getAutoCreate : function(){
1682 tag: 'h' + (1 *this.level),
1683 html: this.html || ''
1695 * Ext JS Library 1.1.1
1696 * Copyright(c) 2006-2007, Ext JS, LLC.
1698 * Originally Released Under LGPL - original licence link has changed is not relivant.
1701 * <script type="text/javascript">
1705 * @class Roo.bootstrap.MenuMgr
1706 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1709 Roo.bootstrap.MenuMgr = function(){
1710 var menus, active, groups = {}, attached = false, lastShow = new Date();
1712 // private - called when first menu is created
1715 active = new Roo.util.MixedCollection();
1716 Roo.get(document).addKeyListener(27, function(){
1717 if(active.length > 0){
1725 if(active && active.length > 0){
1726 var c = active.clone();
1736 if(active.length < 1){
1737 Roo.get(document).un("mouseup", onMouseDown);
1745 var last = active.last();
1746 lastShow = new Date();
1749 Roo.get(document).on("mouseup", onMouseDown);
1754 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1755 m.parentMenu.activeChild = m;
1756 }else if(last && last.isVisible()){
1757 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1762 function onBeforeHide(m){
1764 m.activeChild.hide();
1766 if(m.autoHideTimer){
1767 clearTimeout(m.autoHideTimer);
1768 delete m.autoHideTimer;
1773 function onBeforeShow(m){
1774 var pm = m.parentMenu;
1775 if(!pm && !m.allowOtherMenus){
1777 }else if(pm && pm.activeChild && active != m){
1778 pm.activeChild.hide();
1782 // private this should really trigger on mouseup..
1783 function onMouseDown(e){
1784 Roo.log("on Mouse Up");
1786 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1787 Roo.log("MenuManager hideAll");
1796 function onBeforeCheck(mi, state){
1798 var g = groups[mi.group];
1799 for(var i = 0, l = g.length; i < l; i++){
1801 g[i].setChecked(false);
1810 * Hides all menus that are currently visible
1812 hideAll : function(){
1817 register : function(menu){
1821 menus[menu.id] = menu;
1822 menu.on("beforehide", onBeforeHide);
1823 menu.on("hide", onHide);
1824 menu.on("beforeshow", onBeforeShow);
1825 menu.on("show", onShow);
1827 if(g && menu.events["checkchange"]){
1831 groups[g].push(menu);
1832 menu.on("checkchange", onCheck);
1837 * Returns a {@link Roo.menu.Menu} object
1838 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1839 * be used to generate and return a new Menu instance.
1841 get : function(menu){
1842 if(typeof menu == "string"){ // menu id
1844 }else if(menu.events){ // menu instance
1847 /*else if(typeof menu.length == 'number'){ // array of menu items?
1848 return new Roo.bootstrap.Menu({items:menu});
1849 }else{ // otherwise, must be a config
1850 return new Roo.bootstrap.Menu(menu);
1857 unregister : function(menu){
1858 delete menus[menu.id];
1859 menu.un("beforehide", onBeforeHide);
1860 menu.un("hide", onHide);
1861 menu.un("beforeshow", onBeforeShow);
1862 menu.un("show", onShow);
1864 if(g && menu.events["checkchange"]){
1865 groups[g].remove(menu);
1866 menu.un("checkchange", onCheck);
1871 registerCheckable : function(menuItem){
1872 var g = menuItem.group;
1877 groups[g].push(menuItem);
1878 menuItem.on("beforecheckchange", onBeforeCheck);
1883 unregisterCheckable : function(menuItem){
1884 var g = menuItem.group;
1886 groups[g].remove(menuItem);
1887 menuItem.un("beforecheckchange", onBeforeCheck);
1899 * @class Roo.bootstrap.Menu
1900 * @extends Roo.bootstrap.Component
1901 * Bootstrap Menu class - container for MenuItems
1902 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1903 * @cfg {bool} hidden if the menu should be hidden when rendered.
1904 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1905 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1909 * @param {Object} config The config object
1913 Roo.bootstrap.Menu = function(config){
1914 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1915 if (this.registerMenu && this.type != 'treeview') {
1916 Roo.bootstrap.MenuMgr.register(this);
1921 * Fires before this menu is displayed
1922 * @param {Roo.menu.Menu} this
1927 * Fires before this menu is hidden
1928 * @param {Roo.menu.Menu} this
1933 * Fires after this menu is displayed
1934 * @param {Roo.menu.Menu} this
1939 * Fires after this menu is hidden
1940 * @param {Roo.menu.Menu} this
1945 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1946 * @param {Roo.menu.Menu} this
1947 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1948 * @param {Roo.EventObject} e
1953 * Fires when the mouse is hovering over this menu
1954 * @param {Roo.menu.Menu} this
1955 * @param {Roo.EventObject} e
1956 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1961 * Fires when the mouse exits this menu
1962 * @param {Roo.menu.Menu} this
1963 * @param {Roo.EventObject} e
1964 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1969 * Fires when a menu item contained in this menu is clicked
1970 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1971 * @param {Roo.EventObject} e
1975 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1978 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1982 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1985 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1987 registerMenu : true,
1989 menuItems :false, // stores the menu items..
1999 getChildContainer : function() {
2003 getAutoCreate : function(){
2005 //if (['right'].indexOf(this.align)!==-1) {
2006 // cfg.cn[1].cls += ' pull-right'
2012 cls : 'dropdown-menu' ,
2013 style : 'z-index:1000'
2017 if (this.type === 'submenu') {
2018 cfg.cls = 'submenu active';
2020 if (this.type === 'treeview') {
2021 cfg.cls = 'treeview-menu';
2026 initEvents : function() {
2028 // Roo.log("ADD event");
2029 // Roo.log(this.triggerEl.dom);
2031 this.triggerEl.on('click', this.onTriggerClick, this);
2033 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2035 this.triggerEl.addClass('dropdown-toggle');
2038 this.el.on('touchstart' , this.onTouch, this);
2040 this.el.on('click' , this.onClick, this);
2042 this.el.on("mouseover", this.onMouseOver, this);
2043 this.el.on("mouseout", this.onMouseOut, this);
2047 findTargetItem : function(e)
2049 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2053 //Roo.log(t); Roo.log(t.id);
2055 //Roo.log(this.menuitems);
2056 return this.menuitems.get(t.id);
2058 //return this.items.get(t.menuItemId);
2064 onTouch : function(e)
2066 Roo.log("menu.onTouch");
2067 //e.stopEvent(); this make the user popdown broken
2071 onClick : function(e)
2073 Roo.log("menu.onClick");
2075 var t = this.findTargetItem(e);
2076 if(!t || t.isContainer){
2081 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2082 if(t == this.activeItem && t.shouldDeactivate(e)){
2083 this.activeItem.deactivate();
2084 delete this.activeItem;
2088 this.setActiveItem(t, true);
2096 Roo.log('pass click event');
2100 this.fireEvent("click", this, t, e);
2104 (function() { _this.hide(); }).defer(100);
2107 onMouseOver : function(e){
2108 var t = this.findTargetItem(e);
2111 // if(t.canActivate && !t.disabled){
2112 // this.setActiveItem(t, true);
2116 this.fireEvent("mouseover", this, e, t);
2118 isVisible : function(){
2119 return !this.hidden;
2121 onMouseOut : function(e){
2122 var t = this.findTargetItem(e);
2125 // if(t == this.activeItem && t.shouldDeactivate(e)){
2126 // this.activeItem.deactivate();
2127 // delete this.activeItem;
2130 this.fireEvent("mouseout", this, e, t);
2135 * Displays this menu relative to another element
2136 * @param {String/HTMLElement/Roo.Element} element The element to align to
2137 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2138 * the element (defaults to this.defaultAlign)
2139 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2141 show : function(el, pos, parentMenu){
2142 this.parentMenu = parentMenu;
2146 this.fireEvent("beforeshow", this);
2147 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2150 * Displays this menu at a specific xy position
2151 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2152 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2154 showAt : function(xy, parentMenu, /* private: */_e){
2155 this.parentMenu = parentMenu;
2160 this.fireEvent("beforeshow", this);
2161 //xy = this.el.adjustForConstraints(xy);
2165 this.hideMenuItems();
2166 this.hidden = false;
2167 this.triggerEl.addClass('open');
2169 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2170 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2173 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2178 this.fireEvent("show", this);
2184 this.doFocus.defer(50, this);
2188 doFocus : function(){
2190 this.focusEl.focus();
2195 * Hides this menu and optionally all parent menus
2196 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2198 hide : function(deep)
2201 this.hideMenuItems();
2202 if(this.el && this.isVisible()){
2203 this.fireEvent("beforehide", this);
2204 if(this.activeItem){
2205 this.activeItem.deactivate();
2206 this.activeItem = null;
2208 this.triggerEl.removeClass('open');;
2210 this.fireEvent("hide", this);
2212 if(deep === true && this.parentMenu){
2213 this.parentMenu.hide(true);
2217 onTriggerClick : function(e)
2219 Roo.log('trigger click');
2221 var target = e.getTarget();
2223 Roo.log(target.nodeName.toLowerCase());
2225 if(target.nodeName.toLowerCase() === 'i'){
2231 onTriggerPress : function(e)
2233 Roo.log('trigger press');
2234 //Roo.log(e.getTarget());
2235 // Roo.log(this.triggerEl.dom);
2237 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2238 var pel = Roo.get(e.getTarget());
2239 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2240 Roo.log('is treeview or dropdown?');
2244 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2248 if (this.isVisible()) {
2253 this.show(this.triggerEl, false, false);
2256 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2263 hideMenuItems : function()
2265 Roo.log("hide Menu Items");
2269 //$(backdrop).remove()
2270 this.el.select('.open',true).each(function(aa) {
2272 aa.removeClass('open');
2273 //var parent = getParent($(this))
2274 //var relatedTarget = { relatedTarget: this }
2276 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2277 //if (e.isDefaultPrevented()) return
2278 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2281 addxtypeChild : function (tree, cntr) {
2282 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2284 this.menuitems.add(comp);
2305 * @class Roo.bootstrap.MenuItem
2306 * @extends Roo.bootstrap.Component
2307 * Bootstrap MenuItem class
2308 * @cfg {String} html the menu label
2309 * @cfg {String} href the link
2310 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2311 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2312 * @cfg {Boolean} active used on sidebars to highlight active itesm
2313 * @cfg {String} fa favicon to show on left of menu item.
2314 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2318 * Create a new MenuItem
2319 * @param {Object} config The config object
2323 Roo.bootstrap.MenuItem = function(config){
2324 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2329 * The raw click event for the entire grid.
2330 * @param {Roo.bootstrap.MenuItem} this
2331 * @param {Roo.EventObject} e
2337 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2341 preventDefault: false,
2342 isContainer : false,
2346 getAutoCreate : function(){
2348 if(this.isContainer){
2351 cls: 'dropdown-menu-item'
2365 if (this.fa !== false) {
2368 cls : 'fa fa-' + this.fa
2377 cls: 'dropdown-menu-item',
2380 if (this.parent().type == 'treeview') {
2381 cfg.cls = 'treeview-menu';
2384 cfg.cls += ' active';
2389 anc.href = this.href || cfg.cn[0].href ;
2390 ctag.html = this.html || cfg.cn[0].html ;
2394 initEvents: function()
2396 if (this.parent().type == 'treeview') {
2397 this.el.select('a').on('click', this.onClick, this);
2400 this.menu.parentType = this.xtype;
2401 this.menu.triggerEl = this.el;
2402 this.menu = this.addxtype(Roo.apply({}, this.menu));
2406 onClick : function(e)
2408 Roo.log('item on click ');
2410 if(this.preventDefault){
2413 //this.parent().hideMenuItems();
2415 this.fireEvent('click', this, e);
2434 * @class Roo.bootstrap.MenuSeparator
2435 * @extends Roo.bootstrap.Component
2436 * Bootstrap MenuSeparator class
2439 * Create a new MenuItem
2440 * @param {Object} config The config object
2444 Roo.bootstrap.MenuSeparator = function(config){
2445 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2448 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2450 getAutoCreate : function(){
2469 * @class Roo.bootstrap.Modal
2470 * @extends Roo.bootstrap.Component
2471 * Bootstrap Modal class
2472 * @cfg {String} title Title of dialog
2473 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2474 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2475 * @cfg {Boolean} specificTitle default false
2476 * @cfg {Array} buttons Array of buttons or standard button set..
2477 * @cfg {String} buttonPosition (left|right|center) default right
2478 * @cfg {Boolean} animate default true
2479 * @cfg {Boolean} allow_close default true
2480 * @cfg {Boolean} fitwindow default false
2481 * @cfg {String} size (sm|lg) default empty
2485 * Create a new Modal Dialog
2486 * @param {Object} config The config object
2489 Roo.bootstrap.Modal = function(config){
2490 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2495 * The raw btnclick event for the button
2496 * @param {Roo.EventObject} e
2500 this.buttons = this.buttons || [];
2503 this.tmpl = Roo.factory(this.tmpl);
2508 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2510 title : 'test dialog',
2520 specificTitle: false,
2522 buttonPosition: 'right',
2541 onRender : function(ct, position)
2543 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2546 var cfg = Roo.apply({}, this.getAutoCreate());
2549 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2551 //if (!cfg.name.length) {
2555 cfg.cls += ' ' + this.cls;
2558 cfg.style = this.style;
2560 this.el = Roo.get(document.body).createChild(cfg, position);
2562 //var type = this.el.dom.type;
2565 if(this.tabIndex !== undefined){
2566 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2569 this.dialogEl = this.el.select('.modal-dialog',true).first();
2570 this.bodyEl = this.el.select('.modal-body',true).first();
2571 this.closeEl = this.el.select('.modal-header .close', true).first();
2572 this.headerEl = this.el.select('.modal-header',true).first();
2573 this.titleEl = this.el.select('.modal-title',true).first();
2574 this.footerEl = this.el.select('.modal-footer',true).first();
2576 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2577 this.maskEl.enableDisplayMode("block");
2579 //this.el.addClass("x-dlg-modal");
2581 if (this.buttons.length) {
2582 Roo.each(this.buttons, function(bb) {
2583 var b = Roo.apply({}, bb);
2584 b.xns = b.xns || Roo.bootstrap;
2585 b.xtype = b.xtype || 'Button';
2586 if (typeof(b.listeners) == 'undefined') {
2587 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2590 var btn = Roo.factory(b);
2592 btn.render(this.el.select('.modal-footer div').first());
2596 // render the children.
2599 if(typeof(this.items) != 'undefined'){
2600 var items = this.items;
2603 for(var i =0;i < items.length;i++) {
2604 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2608 this.items = nitems;
2610 // where are these used - they used to be body/close/footer
2614 //this.el.addClass([this.fieldClass, this.cls]);
2618 getAutoCreate : function(){
2623 html : this.html || ''
2628 cls : 'modal-title',
2632 if(this.specificTitle){
2638 if (this.allow_close) {
2650 if(this.size.length){
2651 size = 'modal-' + this.size;
2656 style : 'display: none',
2659 cls: "modal-dialog " + size,
2662 cls : "modal-content",
2665 cls : 'modal-header',
2670 cls : 'modal-footer',
2674 cls: 'btn-' + this.buttonPosition
2691 modal.cls += ' fade';
2697 getChildContainer : function() {
2702 getButtonContainer : function() {
2703 return this.el.select('.modal-footer div',true).first();
2706 initEvents : function()
2708 if (this.allow_close) {
2709 this.closeEl.on('click', this.hide, this);
2711 Roo.EventManager.onWindowResize(this.resize, this, true);
2718 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2719 if (this.fitwindow) {
2720 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2721 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2726 setSize : function(w,h)
2736 if (!this.rendered) {
2740 this.el.setStyle('display', 'block');
2742 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2745 this.el.addClass('in');
2748 this.el.addClass('in');
2752 // not sure how we can show data in here..
2754 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2757 Roo.get(document.body).addClass("x-body-masked");
2759 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2760 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2765 this.fireEvent('show', this);
2767 // set zindex here - otherwise it appears to be ignored...
2768 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2771 this.items.forEach( function(e) {
2772 e.layout ? e.layout() : false;
2780 if(this.fireEvent("beforehide", this) !== false){
2782 Roo.get(document.body).removeClass("x-body-masked");
2783 this.el.removeClass('in');
2784 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2786 if(this.animate){ // why
2788 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2790 this.el.setStyle('display', 'none');
2792 this.fireEvent('hide', this);
2796 addButton : function(str, cb)
2800 var b = Roo.apply({}, { html : str } );
2801 b.xns = b.xns || Roo.bootstrap;
2802 b.xtype = b.xtype || 'Button';
2803 if (typeof(b.listeners) == 'undefined') {
2804 b.listeners = { click : cb.createDelegate(this) };
2807 var btn = Roo.factory(b);
2809 btn.render(this.el.select('.modal-footer div').first());
2815 setDefaultButton : function(btn)
2817 //this.el.select('.modal-footer').()
2821 resizeTo: function(w,h)
2825 this.dialogEl.setWidth(w);
2826 if (this.diff === false) {
2827 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2830 this.bodyEl.setHeight(h-this.diff);
2834 setContentSize : function(w, h)
2838 onButtonClick: function(btn,e)
2841 this.fireEvent('btnclick', btn.name, e);
2844 * Set the title of the Dialog
2845 * @param {String} str new Title
2847 setTitle: function(str) {
2848 this.titleEl.dom.innerHTML = str;
2851 * Set the body of the Dialog
2852 * @param {String} str new Title
2854 setBody: function(str) {
2855 this.bodyEl.dom.innerHTML = str;
2858 * Set the body of the Dialog using the template
2859 * @param {Obj} data - apply this data to the template and replace the body contents.
2861 applyBody: function(obj)
2864 Roo.log("Error - using apply Body without a template");
2867 this.tmpl.overwrite(this.bodyEl, obj);
2873 Roo.apply(Roo.bootstrap.Modal, {
2875 * Button config that displays a single OK button
2884 * Button config that displays Yes and No buttons
2900 * Button config that displays OK and Cancel buttons
2915 * Button config that displays Yes, No and Cancel buttons
2939 * messagebox - can be used as a replace
2943 * @class Roo.MessageBox
2944 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2948 Roo.Msg.alert('Status', 'Changes saved successfully.');
2950 // Prompt for user data:
2951 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2953 // process text value...
2957 // Show a dialog using config options:
2959 title:'Save Changes?',
2960 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2961 buttons: Roo.Msg.YESNOCANCEL,
2968 Roo.bootstrap.MessageBox = function(){
2969 var dlg, opt, mask, waitTimer;
2970 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2971 var buttons, activeTextEl, bwidth;
2975 var handleButton = function(button){
2977 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2981 var handleHide = function(){
2983 dlg.el.removeClass(opt.cls);
2986 // Roo.TaskMgr.stop(waitTimer);
2987 // waitTimer = null;
2992 var updateButtons = function(b){
2995 buttons["ok"].hide();
2996 buttons["cancel"].hide();
2997 buttons["yes"].hide();
2998 buttons["no"].hide();
2999 //dlg.footer.dom.style.display = 'none';
3002 dlg.footerEl.dom.style.display = '';
3003 for(var k in buttons){
3004 if(typeof buttons[k] != "function"){
3007 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3008 width += buttons[k].el.getWidth()+15;
3018 var handleEsc = function(d, k, e){
3019 if(opt && opt.closable !== false){
3029 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3030 * @return {Roo.BasicDialog} The BasicDialog element
3032 getDialog : function(){
3034 dlg = new Roo.bootstrap.Modal( {
3037 //constraintoviewport:false,
3039 //collapsible : false,
3044 //buttonAlign:"center",
3045 closeClick : function(){
3046 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3049 handleButton("cancel");
3054 dlg.on("hide", handleHide);
3056 //dlg.addKeyListener(27, handleEsc);
3058 this.buttons = buttons;
3059 var bt = this.buttonText;
3060 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3061 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3062 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3063 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3065 bodyEl = dlg.bodyEl.createChild({
3067 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3068 '<textarea class="roo-mb-textarea"></textarea>' +
3069 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3071 msgEl = bodyEl.dom.firstChild;
3072 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3073 textboxEl.enableDisplayMode();
3074 textboxEl.addKeyListener([10,13], function(){
3075 if(dlg.isVisible() && opt && opt.buttons){
3078 }else if(opt.buttons.yes){
3079 handleButton("yes");
3083 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3084 textareaEl.enableDisplayMode();
3085 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3086 progressEl.enableDisplayMode();
3088 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3089 //var pf = progressEl.dom.firstChild;
3091 //pp = Roo.get(pf.firstChild);
3092 //pp.setHeight(pf.offsetHeight);
3100 * Updates the message box body text
3101 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3102 * the XHTML-compliant non-breaking space character '&#160;')
3103 * @return {Roo.MessageBox} This message box
3105 updateText : function(text)
3107 if(!dlg.isVisible() && !opt.width){
3108 dlg.dialogEl.setWidth(this.maxWidth);
3109 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3111 msgEl.innerHTML = text || ' ';
3113 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3114 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3116 Math.min(opt.width || cw , this.maxWidth),
3117 Math.max(opt.minWidth || this.minWidth, bwidth)
3120 activeTextEl.setWidth(w);
3122 if(dlg.isVisible()){
3123 dlg.fixedcenter = false;
3125 // to big, make it scroll. = But as usual stupid IE does not support
3128 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3129 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3130 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3132 bodyEl.dom.style.height = '';
3133 bodyEl.dom.style.overflowY = '';
3136 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3138 bodyEl.dom.style.overflowX = '';
3141 dlg.setContentSize(w, bodyEl.getHeight());
3142 if(dlg.isVisible()){
3143 dlg.fixedcenter = true;
3149 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3150 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3151 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3152 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3153 * @return {Roo.MessageBox} This message box
3155 updateProgress : function(value, text){
3157 this.updateText(text);
3159 if (pp) { // weird bug on my firefox - for some reason this is not defined
3160 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3166 * Returns true if the message box is currently displayed
3167 * @return {Boolean} True if the message box is visible, else false
3169 isVisible : function(){
3170 return dlg && dlg.isVisible();
3174 * Hides the message box if it is displayed
3177 if(this.isVisible()){
3183 * Displays a new message box, or reinitializes an existing message box, based on the config options
3184 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3185 * The following config object properties are supported:
3187 Property Type Description
3188 ---------- --------------- ------------------------------------------------------------------------------------
3189 animEl String/Element An id or Element from which the message box should animate as it opens and
3190 closes (defaults to undefined)
3191 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3192 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3193 closable Boolean False to hide the top-right close button (defaults to true). Note that
3194 progress and wait dialogs will ignore this property and always hide the
3195 close button as they can only be closed programmatically.
3196 cls String A custom CSS class to apply to the message box element
3197 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3198 displayed (defaults to 75)
3199 fn Function A callback function to execute after closing the dialog. The arguments to the
3200 function will be btn (the name of the button that was clicked, if applicable,
3201 e.g. "ok"), and text (the value of the active text field, if applicable).
3202 Progress and wait dialogs will ignore this option since they do not respond to
3203 user actions and can only be closed programmatically, so any required function
3204 should be called by the same code after it closes the dialog.
3205 icon String A CSS class that provides a background image to be used as an icon for
3206 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3207 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3208 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3209 modal Boolean False to allow user interaction with the page while the message box is
3210 displayed (defaults to true)
3211 msg String A string that will replace the existing message box body text (defaults
3212 to the XHTML-compliant non-breaking space character ' ')
3213 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3214 progress Boolean True to display a progress bar (defaults to false)
3215 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3216 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3217 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3218 title String The title text
3219 value String The string value to set into the active textbox element if displayed
3220 wait Boolean True to display a progress bar (defaults to false)
3221 width Number The width of the dialog in pixels
3228 msg: 'Please enter your address:',
3230 buttons: Roo.MessageBox.OKCANCEL,
3233 animEl: 'addAddressBtn'
3236 * @param {Object} config Configuration options
3237 * @return {Roo.MessageBox} This message box
3239 show : function(options)
3242 // this causes nightmares if you show one dialog after another
3243 // especially on callbacks..
3245 if(this.isVisible()){
3248 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3249 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3250 Roo.log("New Dialog Message:" + options.msg )
3251 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3252 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3255 var d = this.getDialog();
3257 d.setTitle(opt.title || " ");
3258 d.closeEl.setDisplayed(opt.closable !== false);
3259 activeTextEl = textboxEl;
3260 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3265 textareaEl.setHeight(typeof opt.multiline == "number" ?
3266 opt.multiline : this.defaultTextHeight);
3267 activeTextEl = textareaEl;
3276 progressEl.setDisplayed(opt.progress === true);
3277 this.updateProgress(0);
3278 activeTextEl.dom.value = opt.value || "";
3280 dlg.setDefaultButton(activeTextEl);
3282 var bs = opt.buttons;
3286 }else if(bs && bs.yes){
3287 db = buttons["yes"];
3289 dlg.setDefaultButton(db);
3291 bwidth = updateButtons(opt.buttons);
3292 this.updateText(opt.msg);
3294 d.el.addClass(opt.cls);
3296 d.proxyDrag = opt.proxyDrag === true;
3297 d.modal = opt.modal !== false;
3298 d.mask = opt.modal !== false ? mask : false;
3300 // force it to the end of the z-index stack so it gets a cursor in FF
3301 document.body.appendChild(dlg.el.dom);
3302 d.animateTarget = null;
3303 d.show(options.animEl);
3309 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3310 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3311 * and closing the message box when the process is complete.
3312 * @param {String} title The title bar text
3313 * @param {String} msg The message box body text
3314 * @return {Roo.MessageBox} This message box
3316 progress : function(title, msg){
3323 minWidth: this.minProgressWidth,
3330 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3331 * If a callback function is passed it will be called after the user clicks the button, and the
3332 * id of the button that was clicked will be passed as the only parameter to the callback
3333 * (could also be the top-right close button).
3334 * @param {String} title The title bar text
3335 * @param {String} msg The message box body text
3336 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3337 * @param {Object} scope (optional) The scope of the callback function
3338 * @return {Roo.MessageBox} This message box
3340 alert : function(title, msg, fn, scope)
3355 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3356 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3357 * You are responsible for closing the message box when the process is complete.
3358 * @param {String} msg The message box body text
3359 * @param {String} title (optional) The title bar text
3360 * @return {Roo.MessageBox} This message box
3362 wait : function(msg, title){
3373 waitTimer = Roo.TaskMgr.start({
3375 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3383 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3384 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3385 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3386 * @param {String} title The title bar text
3387 * @param {String} msg The message box body text
3388 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3389 * @param {Object} scope (optional) The scope of the callback function
3390 * @return {Roo.MessageBox} This message box
3392 confirm : function(title, msg, fn, scope){
3396 buttons: this.YESNO,
3405 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3406 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3407 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3408 * (could also be the top-right close button) and the text that was entered will be passed as the two
3409 * parameters to the callback.
3410 * @param {String} title The title bar text
3411 * @param {String} msg The message box body text
3412 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3413 * @param {Object} scope (optional) The scope of the callback function
3414 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3415 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3416 * @return {Roo.MessageBox} This message box
3418 prompt : function(title, msg, fn, scope, multiline){
3422 buttons: this.OKCANCEL,
3427 multiline: multiline,
3434 * Button config that displays a single OK button
3439 * Button config that displays Yes and No buttons
3442 YESNO : {yes:true, no:true},
3444 * Button config that displays OK and Cancel buttons
3447 OKCANCEL : {ok:true, cancel:true},
3449 * Button config that displays Yes, No and Cancel buttons
3452 YESNOCANCEL : {yes:true, no:true, cancel:true},
3455 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3458 defaultTextHeight : 75,
3460 * The maximum width in pixels of the message box (defaults to 600)
3465 * The minimum width in pixels of the message box (defaults to 100)
3470 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3471 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3474 minProgressWidth : 250,
3476 * An object containing the default button text strings that can be overriden for localized language support.
3477 * Supported properties are: ok, cancel, yes and no.
3478 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3491 * Shorthand for {@link Roo.MessageBox}
3493 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3494 Roo.Msg = Roo.Msg || Roo.MessageBox;
3503 * @class Roo.bootstrap.Navbar
3504 * @extends Roo.bootstrap.Component
3505 * Bootstrap Navbar class
3508 * Create a new Navbar
3509 * @param {Object} config The config object
3513 Roo.bootstrap.Navbar = function(config){
3514 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3518 * @event beforetoggle
3519 * Fire before toggle the menu
3520 * @param {Roo.EventObject} e
3522 "beforetoggle" : true
3526 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3535 getAutoCreate : function(){
3538 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3542 initEvents :function ()
3544 //Roo.log(this.el.select('.navbar-toggle',true));
3545 this.el.select('.navbar-toggle',true).on('click', function() {
3546 if(this.fireEvent('beforetoggle', this) !== false){
3547 this.el.select('.navbar-collapse',true).toggleClass('in');
3557 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3559 var size = this.el.getSize();
3560 this.maskEl.setSize(size.width, size.height);
3561 this.maskEl.enableDisplayMode("block");
3570 getChildContainer : function()
3572 if (this.el.select('.collapse').getCount()) {
3573 return this.el.select('.collapse',true).first();
3606 * @class Roo.bootstrap.NavSimplebar
3607 * @extends Roo.bootstrap.Navbar
3608 * Bootstrap Sidebar class
3610 * @cfg {Boolean} inverse is inverted color
3612 * @cfg {String} type (nav | pills | tabs)
3613 * @cfg {Boolean} arrangement stacked | justified
3614 * @cfg {String} align (left | right) alignment
3616 * @cfg {Boolean} main (true|false) main nav bar? default false
3617 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3619 * @cfg {String} tag (header|footer|nav|div) default is nav
3625 * Create a new Sidebar
3626 * @param {Object} config The config object
3630 Roo.bootstrap.NavSimplebar = function(config){
3631 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3634 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3650 getAutoCreate : function(){
3654 tag : this.tag || 'div',
3667 this.type = this.type || 'nav';
3668 if (['tabs','pills'].indexOf(this.type)!==-1) {
3669 cfg.cn[0].cls += ' nav-' + this.type
3673 if (this.type!=='nav') {
3674 Roo.log('nav type must be nav/tabs/pills')
3676 cfg.cn[0].cls += ' navbar-nav'
3682 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3683 cfg.cn[0].cls += ' nav-' + this.arrangement;
3687 if (this.align === 'right') {
3688 cfg.cn[0].cls += ' navbar-right';
3692 cfg.cls += ' navbar-inverse';
3719 * @class Roo.bootstrap.NavHeaderbar
3720 * @extends Roo.bootstrap.NavSimplebar
3721 * Bootstrap Sidebar class
3723 * @cfg {String} brand what is brand
3724 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3725 * @cfg {String} brand_href href of the brand
3726 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3727 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3728 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3729 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3732 * Create a new Sidebar
3733 * @param {Object} config The config object
3737 Roo.bootstrap.NavHeaderbar = function(config){
3738 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3742 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3749 desktopCenter : false,
3752 getAutoCreate : function(){
3755 tag: this.nav || 'nav',
3762 if (this.desktopCenter) {
3763 cn.push({cls : 'container', cn : []});
3770 cls: 'navbar-header',
3775 cls: 'navbar-toggle',
3776 'data-toggle': 'collapse',
3781 html: 'Toggle navigation'
3803 cls: 'collapse navbar-collapse',
3807 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3809 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3810 cfg.cls += ' navbar-' + this.position;
3812 // tag can override this..
3814 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3817 if (this.brand !== '') {
3820 href: this.brand_href ? this.brand_href : '#',
3821 cls: 'navbar-brand',
3829 cfg.cls += ' main-nav';
3837 getHeaderChildContainer : function()
3839 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3840 return this.el.select('.navbar-header',true).first();
3843 return this.getChildContainer();
3847 initEvents : function()
3849 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3851 if (this.autohide) {
3856 Roo.get(document).on('scroll',function(e) {
3857 var ns = Roo.get(document).getScroll().top;
3858 var os = prevScroll;
3862 ft.removeClass('slideDown');
3863 ft.addClass('slideUp');
3866 ft.removeClass('slideUp');
3867 ft.addClass('slideDown');
3888 * @class Roo.bootstrap.NavSidebar
3889 * @extends Roo.bootstrap.Navbar
3890 * Bootstrap Sidebar class
3893 * Create a new Sidebar
3894 * @param {Object} config The config object
3898 Roo.bootstrap.NavSidebar = function(config){
3899 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3902 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3904 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3906 getAutoCreate : function(){
3911 cls: 'sidebar sidebar-nav'
3933 * @class Roo.bootstrap.NavGroup
3934 * @extends Roo.bootstrap.Component
3935 * Bootstrap NavGroup class
3936 * @cfg {String} align (left|right)
3937 * @cfg {Boolean} inverse
3938 * @cfg {String} type (nav|pills|tab) default nav
3939 * @cfg {String} navId - reference Id for navbar.
3943 * Create a new nav group
3944 * @param {Object} config The config object
3947 Roo.bootstrap.NavGroup = function(config){
3948 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3951 Roo.bootstrap.NavGroup.register(this);
3955 * Fires when the active item changes
3956 * @param {Roo.bootstrap.NavGroup} this
3957 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3958 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3965 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3976 getAutoCreate : function()
3978 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3985 if (['tabs','pills'].indexOf(this.type)!==-1) {
3986 cfg.cls += ' nav-' + this.type
3988 if (this.type!=='nav') {
3989 Roo.log('nav type must be nav/tabs/pills')
3991 cfg.cls += ' navbar-nav'
3994 if (this.parent().sidebar) {
3997 cls: 'dashboard-menu sidebar-menu'
4003 if (this.form === true) {
4009 if (this.align === 'right') {
4010 cfg.cls += ' navbar-right';
4012 cfg.cls += ' navbar-left';
4016 if (this.align === 'right') {
4017 cfg.cls += ' navbar-right';
4021 cfg.cls += ' navbar-inverse';
4029 * sets the active Navigation item
4030 * @param {Roo.bootstrap.NavItem} the new current navitem
4032 setActiveItem : function(item)
4035 Roo.each(this.navItems, function(v){
4040 v.setActive(false, true);
4047 item.setActive(true, true);
4048 this.fireEvent('changed', this, item, prev);
4053 * gets the active Navigation item
4054 * @return {Roo.bootstrap.NavItem} the current navitem
4056 getActive : function()
4060 Roo.each(this.navItems, function(v){
4071 indexOfNav : function()
4075 Roo.each(this.navItems, function(v,i){
4086 * adds a Navigation item
4087 * @param {Roo.bootstrap.NavItem} the navitem to add
4089 addItem : function(cfg)
4091 var cn = new Roo.bootstrap.NavItem(cfg);
4093 cn.parentId = this.id;
4094 cn.onRender(this.el, null);
4098 * register a Navigation item
4099 * @param {Roo.bootstrap.NavItem} the navitem to add
4101 register : function(item)
4103 this.navItems.push( item);
4104 item.navId = this.navId;
4109 * clear all the Navigation item
4112 clearAll : function()
4115 this.el.dom.innerHTML = '';
4118 getNavItem: function(tabId)
4121 Roo.each(this.navItems, function(e) {
4122 if (e.tabId == tabId) {
4132 setActiveNext : function()
4134 var i = this.indexOfNav(this.getActive());
4135 if (i > this.navItems.length) {
4138 this.setActiveItem(this.navItems[i+1]);
4140 setActivePrev : function()
4142 var i = this.indexOfNav(this.getActive());
4146 this.setActiveItem(this.navItems[i-1]);
4148 clearWasActive : function(except) {
4149 Roo.each(this.navItems, function(e) {
4150 if (e.tabId != except.tabId && e.was_active) {
4151 e.was_active = false;
4158 getWasActive : function ()
4161 Roo.each(this.navItems, function(e) {
4176 Roo.apply(Roo.bootstrap.NavGroup, {
4180 * register a Navigation Group
4181 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4183 register : function(navgrp)
4185 this.groups[navgrp.navId] = navgrp;
4189 * fetch a Navigation Group based on the navigation ID
4190 * @param {string} the navgroup to add
4191 * @returns {Roo.bootstrap.NavGroup} the navgroup
4193 get: function(navId) {
4194 if (typeof(this.groups[navId]) == 'undefined') {
4196 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4198 return this.groups[navId] ;
4213 * @class Roo.bootstrap.NavItem
4214 * @extends Roo.bootstrap.Component
4215 * Bootstrap Navbar.NavItem class
4216 * @cfg {String} href link to
4217 * @cfg {String} html content of button
4218 * @cfg {String} badge text inside badge
4219 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4220 * @cfg {String} glyphicon name of glyphicon
4221 * @cfg {String} icon name of font awesome icon
4222 * @cfg {Boolean} active Is item active
4223 * @cfg {Boolean} disabled Is item disabled
4225 * @cfg {Boolean} preventDefault (true | false) default false
4226 * @cfg {String} tabId the tab that this item activates.
4227 * @cfg {String} tagtype (a|span) render as a href or span?
4228 * @cfg {Boolean} animateRef (true|false) link to element default false
4231 * Create a new Navbar Item
4232 * @param {Object} config The config object
4234 Roo.bootstrap.NavItem = function(config){
4235 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4240 * The raw click event for the entire grid.
4241 * @param {Roo.EventObject} e
4246 * Fires when the active item active state changes
4247 * @param {Roo.bootstrap.NavItem} this
4248 * @param {boolean} state the new state
4254 * Fires when scroll to element
4255 * @param {Roo.bootstrap.NavItem} this
4256 * @param {Object} options
4257 * @param {Roo.EventObject} e
4265 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4273 preventDefault : false,
4280 getAutoCreate : function(){
4289 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4291 if (this.disabled) {
4292 cfg.cls += ' disabled';
4295 if (this.href || this.html || this.glyphicon || this.icon) {
4299 href : this.href || "#",
4300 html: this.html || ''
4305 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4308 if(this.glyphicon) {
4309 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4314 cfg.cn[0].html += " <span class='caret'></span>";
4318 if (this.badge !== '') {
4320 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4328 initEvents: function()
4330 if (typeof (this.menu) != 'undefined') {
4331 this.menu.parentType = this.xtype;
4332 this.menu.triggerEl = this.el;
4333 this.menu = this.addxtype(Roo.apply({}, this.menu));
4336 this.el.select('a',true).on('click', this.onClick, this);
4338 if(this.tagtype == 'span'){
4339 this.el.select('span',true).on('click', this.onClick, this);
4342 // at this point parent should be available..
4343 this.parent().register(this);
4346 onClick : function(e)
4348 if (e.getTarget('.dropdown-menu-item')) {
4349 // did you click on a menu itemm.... - then don't trigger onclick..
4354 this.preventDefault ||
4357 Roo.log("NavItem - prevent Default?");
4361 if (this.disabled) {
4365 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4366 if (tg && tg.transition) {
4367 Roo.log("waiting for the transitionend");
4373 //Roo.log("fire event clicked");
4374 if(this.fireEvent('click', this, e) === false){
4378 if(this.tagtype == 'span'){
4382 //Roo.log(this.href);
4383 var ael = this.el.select('a',true).first();
4386 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4387 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4388 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4389 return; // ignore... - it's a 'hash' to another page.
4391 Roo.log("NavItem - prevent Default?");
4393 this.scrollToElement(e);
4397 var p = this.parent();
4399 if (['tabs','pills'].indexOf(p.type)!==-1) {
4400 if (typeof(p.setActiveItem) !== 'undefined') {
4401 p.setActiveItem(this);
4405 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4406 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4407 // remove the collapsed menu expand...
4408 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4412 isActive: function () {
4415 setActive : function(state, fire, is_was_active)
4417 if (this.active && !state && this.navId) {
4418 this.was_active = true;
4419 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4421 nv.clearWasActive(this);
4425 this.active = state;
4428 this.el.removeClass('active');
4429 } else if (!this.el.hasClass('active')) {
4430 this.el.addClass('active');
4433 this.fireEvent('changed', this, state);
4436 // show a panel if it's registered and related..
4438 if (!this.navId || !this.tabId || !state || is_was_active) {
4442 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4446 var pan = tg.getPanelByName(this.tabId);
4450 // if we can not flip to new panel - go back to old nav highlight..
4451 if (false == tg.showPanel(pan)) {
4452 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4454 var onav = nv.getWasActive();
4456 onav.setActive(true, false, true);
4465 // this should not be here...
4466 setDisabled : function(state)
4468 this.disabled = state;
4470 this.el.removeClass('disabled');
4471 } else if (!this.el.hasClass('disabled')) {
4472 this.el.addClass('disabled');
4478 * Fetch the element to display the tooltip on.
4479 * @return {Roo.Element} defaults to this.el
4481 tooltipEl : function()
4483 return this.el.select('' + this.tagtype + '', true).first();
4486 scrollToElement : function(e)
4488 var c = document.body;
4491 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4493 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4494 c = document.documentElement;
4497 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4503 var o = target.calcOffsetsTo(c);
4510 this.fireEvent('scrollto', this, options, e);
4512 Roo.get(c).scrollTo('top', options.value, true);
4525 * <span> icon </span>
4526 * <span> text </span>
4527 * <span>badge </span>
4531 * @class Roo.bootstrap.NavSidebarItem
4532 * @extends Roo.bootstrap.NavItem
4533 * Bootstrap Navbar.NavSidebarItem class
4534 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4535 * {bool} open is the menu open
4537 * Create a new Navbar Button
4538 * @param {Object} config The config object
4540 Roo.bootstrap.NavSidebarItem = function(config){
4541 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4546 * The raw click event for the entire grid.
4547 * @param {Roo.EventObject} e
4552 * Fires when the active item active state changes
4553 * @param {Roo.bootstrap.NavSidebarItem} this
4554 * @param {boolean} state the new state
4562 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4564 badgeWeight : 'default',
4568 getAutoCreate : function(){
4573 href : this.href || '#',
4585 html : this.html || ''
4590 cfg.cls += ' active';
4593 if (this.disabled) {
4594 cfg.cls += ' disabled';
4597 cfg.cls += ' open x-open';
4600 if (this.glyphicon || this.icon) {
4601 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4602 a.cn.push({ tag : 'i', cls : c }) ;
4607 if (this.badge !== '') {
4609 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4613 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4614 a.cls += 'dropdown-toggle treeview' ;
4622 initEvents : function()
4624 if (typeof (this.menu) != 'undefined') {
4625 this.menu.parentType = this.xtype;
4626 this.menu.triggerEl = this.el;
4627 this.menu = this.addxtype(Roo.apply({}, this.menu));
4630 this.el.on('click', this.onClick, this);
4633 if(this.badge !== ''){
4635 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4640 onClick : function(e)
4647 if(this.preventDefault){
4651 this.fireEvent('click', this);
4654 disable : function()
4656 this.setDisabled(true);
4661 this.setDisabled(false);
4664 setDisabled : function(state)
4666 if(this.disabled == state){
4670 this.disabled = state;
4673 this.el.addClass('disabled');
4677 this.el.removeClass('disabled');
4682 setActive : function(state)
4684 if(this.active == state){
4688 this.active = state;
4691 this.el.addClass('active');
4695 this.el.removeClass('active');
4700 isActive: function ()
4705 setBadge : function(str)
4711 this.badgeEl.dom.innerHTML = str;
4728 * @class Roo.bootstrap.Row
4729 * @extends Roo.bootstrap.Component
4730 * Bootstrap Row class (contains columns...)
4734 * @param {Object} config The config object
4737 Roo.bootstrap.Row = function(config){
4738 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4741 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4743 getAutoCreate : function(){
4762 * @class Roo.bootstrap.Element
4763 * @extends Roo.bootstrap.Component
4764 * Bootstrap Element class
4765 * @cfg {String} html contents of the element
4766 * @cfg {String} tag tag of the element
4767 * @cfg {String} cls class of the element
4768 * @cfg {Boolean} preventDefault (true|false) default false
4769 * @cfg {Boolean} clickable (true|false) default false
4772 * Create a new Element
4773 * @param {Object} config The config object
4776 Roo.bootstrap.Element = function(config){
4777 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4783 * When a element is chick
4784 * @param {Roo.bootstrap.Element} this
4785 * @param {Roo.EventObject} e
4791 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4796 preventDefault: false,
4799 getAutoCreate : function(){
4810 initEvents: function()
4812 Roo.bootstrap.Element.superclass.initEvents.call(this);
4815 this.el.on('click', this.onClick, this);
4820 onClick : function(e)
4822 if(this.preventDefault){
4826 this.fireEvent('click', this, e);
4829 getValue : function()
4831 return this.el.dom.innerHTML;
4834 setValue : function(value)
4836 this.el.dom.innerHTML = value;
4851 * @class Roo.bootstrap.Pagination
4852 * @extends Roo.bootstrap.Component
4853 * Bootstrap Pagination class
4854 * @cfg {String} size xs | sm | md | lg
4855 * @cfg {Boolean} inverse false | true
4858 * Create a new Pagination
4859 * @param {Object} config The config object
4862 Roo.bootstrap.Pagination = function(config){
4863 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4866 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4872 getAutoCreate : function(){
4878 cfg.cls += ' inverse';
4884 cfg.cls += " " + this.cls;
4902 * @class Roo.bootstrap.PaginationItem
4903 * @extends Roo.bootstrap.Component
4904 * Bootstrap PaginationItem class
4905 * @cfg {String} html text
4906 * @cfg {String} href the link
4907 * @cfg {Boolean} preventDefault (true | false) default true
4908 * @cfg {Boolean} active (true | false) default false
4909 * @cfg {Boolean} disabled default false
4913 * Create a new PaginationItem
4914 * @param {Object} config The config object
4918 Roo.bootstrap.PaginationItem = function(config){
4919 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4924 * The raw click event for the entire grid.
4925 * @param {Roo.EventObject} e
4931 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4935 preventDefault: true,
4940 getAutoCreate : function(){
4946 href : this.href ? this.href : '#',
4947 html : this.html ? this.html : ''
4957 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4961 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4967 initEvents: function() {
4969 this.el.on('click', this.onClick, this);
4972 onClick : function(e)
4974 Roo.log('PaginationItem on click ');
4975 if(this.preventDefault){
4983 this.fireEvent('click', this, e);
4999 * @class Roo.bootstrap.Slider
5000 * @extends Roo.bootstrap.Component
5001 * Bootstrap Slider class
5004 * Create a new Slider
5005 * @param {Object} config The config object
5008 Roo.bootstrap.Slider = function(config){
5009 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5012 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5014 getAutoCreate : function(){
5018 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5022 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5034 * Ext JS Library 1.1.1
5035 * Copyright(c) 2006-2007, Ext JS, LLC.
5037 * Originally Released Under LGPL - original licence link has changed is not relivant.
5040 * <script type="text/javascript">
5045 * @class Roo.grid.ColumnModel
5046 * @extends Roo.util.Observable
5047 * This is the default implementation of a ColumnModel used by the Grid. It defines
5048 * the columns in the grid.
5051 var colModel = new Roo.grid.ColumnModel([
5052 {header: "Ticker", width: 60, sortable: true, locked: true},
5053 {header: "Company Name", width: 150, sortable: true},
5054 {header: "Market Cap.", width: 100, sortable: true},
5055 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5056 {header: "Employees", width: 100, sortable: true, resizable: false}
5061 * The config options listed for this class are options which may appear in each
5062 * individual column definition.
5063 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5065 * @param {Object} config An Array of column config objects. See this class's
5066 * config objects for details.
5068 Roo.grid.ColumnModel = function(config){
5070 * The config passed into the constructor
5072 this.config = config;
5075 // if no id, create one
5076 // if the column does not have a dataIndex mapping,
5077 // map it to the order it is in the config
5078 for(var i = 0, len = config.length; i < len; i++){
5080 if(typeof c.dataIndex == "undefined"){
5083 if(typeof c.renderer == "string"){
5084 c.renderer = Roo.util.Format[c.renderer];
5086 if(typeof c.id == "undefined"){
5089 if(c.editor && c.editor.xtype){
5090 c.editor = Roo.factory(c.editor, Roo.grid);
5092 if(c.editor && c.editor.isFormField){
5093 c.editor = new Roo.grid.GridEditor(c.editor);
5095 this.lookup[c.id] = c;
5099 * The width of columns which have no width specified (defaults to 100)
5102 this.defaultWidth = 100;
5105 * Default sortable of columns which have no sortable specified (defaults to false)
5108 this.defaultSortable = false;
5112 * @event widthchange
5113 * Fires when the width of a column changes.
5114 * @param {ColumnModel} this
5115 * @param {Number} columnIndex The column index
5116 * @param {Number} newWidth The new width
5118 "widthchange": true,
5120 * @event headerchange
5121 * Fires when the text of a header changes.
5122 * @param {ColumnModel} this
5123 * @param {Number} columnIndex The column index
5124 * @param {Number} newText The new header text
5126 "headerchange": true,
5128 * @event hiddenchange
5129 * Fires when a column is hidden or "unhidden".
5130 * @param {ColumnModel} this
5131 * @param {Number} columnIndex The column index
5132 * @param {Boolean} hidden true if hidden, false otherwise
5134 "hiddenchange": true,
5136 * @event columnmoved
5137 * Fires when a column is moved.
5138 * @param {ColumnModel} this
5139 * @param {Number} oldIndex
5140 * @param {Number} newIndex
5142 "columnmoved" : true,
5144 * @event columlockchange
5145 * Fires when a column's locked state is changed
5146 * @param {ColumnModel} this
5147 * @param {Number} colIndex
5148 * @param {Boolean} locked true if locked
5150 "columnlockchange" : true
5152 Roo.grid.ColumnModel.superclass.constructor.call(this);
5154 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5156 * @cfg {String} header The header text to display in the Grid view.
5159 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5160 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5161 * specified, the column's index is used as an index into the Record's data Array.
5164 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5165 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5168 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5169 * Defaults to the value of the {@link #defaultSortable} property.
5170 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5173 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5176 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5179 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5182 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5185 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5186 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5187 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5188 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5191 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5194 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5197 * @cfg {String} cursor (Optional)
5200 * @cfg {String} tooltip (Optional)
5203 * @cfg {Number} xs (Optional)
5206 * @cfg {Number} sm (Optional)
5209 * @cfg {Number} md (Optional)
5212 * @cfg {Number} lg (Optional)
5215 * Returns the id of the column at the specified index.
5216 * @param {Number} index The column index
5217 * @return {String} the id
5219 getColumnId : function(index){
5220 return this.config[index].id;
5224 * Returns the column for a specified id.
5225 * @param {String} id The column id
5226 * @return {Object} the column
5228 getColumnById : function(id){
5229 return this.lookup[id];
5234 * Returns the column for a specified dataIndex.
5235 * @param {String} dataIndex The column dataIndex
5236 * @return {Object|Boolean} the column or false if not found
5238 getColumnByDataIndex: function(dataIndex){
5239 var index = this.findColumnIndex(dataIndex);
5240 return index > -1 ? this.config[index] : false;
5244 * Returns the index for a specified column id.
5245 * @param {String} id The column id
5246 * @return {Number} the index, or -1 if not found
5248 getIndexById : function(id){
5249 for(var i = 0, len = this.config.length; i < len; i++){
5250 if(this.config[i].id == id){
5258 * Returns the index for a specified column dataIndex.
5259 * @param {String} dataIndex The column dataIndex
5260 * @return {Number} the index, or -1 if not found
5263 findColumnIndex : function(dataIndex){
5264 for(var i = 0, len = this.config.length; i < len; i++){
5265 if(this.config[i].dataIndex == dataIndex){
5273 moveColumn : function(oldIndex, newIndex){
5274 var c = this.config[oldIndex];
5275 this.config.splice(oldIndex, 1);
5276 this.config.splice(newIndex, 0, c);
5277 this.dataMap = null;
5278 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5281 isLocked : function(colIndex){
5282 return this.config[colIndex].locked === true;
5285 setLocked : function(colIndex, value, suppressEvent){
5286 if(this.isLocked(colIndex) == value){
5289 this.config[colIndex].locked = value;
5291 this.fireEvent("columnlockchange", this, colIndex, value);
5295 getTotalLockedWidth : function(){
5297 for(var i = 0; i < this.config.length; i++){
5298 if(this.isLocked(i) && !this.isHidden(i)){
5299 this.totalWidth += this.getColumnWidth(i);
5305 getLockedCount : function(){
5306 for(var i = 0, len = this.config.length; i < len; i++){
5307 if(!this.isLocked(i)){
5312 return this.config.length;
5316 * Returns the number of columns.
5319 getColumnCount : function(visibleOnly){
5320 if(visibleOnly === true){
5322 for(var i = 0, len = this.config.length; i < len; i++){
5323 if(!this.isHidden(i)){
5329 return this.config.length;
5333 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5334 * @param {Function} fn
5335 * @param {Object} scope (optional)
5336 * @return {Array} result
5338 getColumnsBy : function(fn, scope){
5340 for(var i = 0, len = this.config.length; i < len; i++){
5341 var c = this.config[i];
5342 if(fn.call(scope||this, c, i) === true){
5350 * Returns true if the specified column is sortable.
5351 * @param {Number} col The column index
5354 isSortable : function(col){
5355 if(typeof this.config[col].sortable == "undefined"){
5356 return this.defaultSortable;
5358 return this.config[col].sortable;
5362 * Returns the rendering (formatting) function defined for the column.
5363 * @param {Number} col The column index.
5364 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5366 getRenderer : function(col){
5367 if(!this.config[col].renderer){
5368 return Roo.grid.ColumnModel.defaultRenderer;
5370 return this.config[col].renderer;
5374 * Sets the rendering (formatting) function for a column.
5375 * @param {Number} col The column index
5376 * @param {Function} fn The function to use to process the cell's raw data
5377 * to return HTML markup for the grid view. The render function is called with
5378 * the following parameters:<ul>
5379 * <li>Data value.</li>
5380 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5381 * <li>css A CSS style string to apply to the table cell.</li>
5382 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5383 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5384 * <li>Row index</li>
5385 * <li>Column index</li>
5386 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5388 setRenderer : function(col, fn){
5389 this.config[col].renderer = fn;
5393 * Returns the width for the specified column.
5394 * @param {Number} col The column index
5397 getColumnWidth : function(col){
5398 return this.config[col].width * 1 || this.defaultWidth;
5402 * Sets the width for a column.
5403 * @param {Number} col The column index
5404 * @param {Number} width The new width
5406 setColumnWidth : function(col, width, suppressEvent){
5407 this.config[col].width = width;
5408 this.totalWidth = null;
5410 this.fireEvent("widthchange", this, col, width);
5415 * Returns the total width of all columns.
5416 * @param {Boolean} includeHidden True to include hidden column widths
5419 getTotalWidth : function(includeHidden){
5420 if(!this.totalWidth){
5421 this.totalWidth = 0;
5422 for(var i = 0, len = this.config.length; i < len; i++){
5423 if(includeHidden || !this.isHidden(i)){
5424 this.totalWidth += this.getColumnWidth(i);
5428 return this.totalWidth;
5432 * Returns the header for the specified column.
5433 * @param {Number} col The column index
5436 getColumnHeader : function(col){
5437 return this.config[col].header;
5441 * Sets the header for a column.
5442 * @param {Number} col The column index
5443 * @param {String} header The new header
5445 setColumnHeader : function(col, header){
5446 this.config[col].header = header;
5447 this.fireEvent("headerchange", this, col, header);
5451 * Returns the tooltip for the specified column.
5452 * @param {Number} col The column index
5455 getColumnTooltip : function(col){
5456 return this.config[col].tooltip;
5459 * Sets the tooltip for a column.
5460 * @param {Number} col The column index
5461 * @param {String} tooltip The new tooltip
5463 setColumnTooltip : function(col, tooltip){
5464 this.config[col].tooltip = tooltip;
5468 * Returns the dataIndex for the specified column.
5469 * @param {Number} col The column index
5472 getDataIndex : function(col){
5473 return this.config[col].dataIndex;
5477 * Sets the dataIndex for a column.
5478 * @param {Number} col The column index
5479 * @param {Number} dataIndex The new dataIndex
5481 setDataIndex : function(col, dataIndex){
5482 this.config[col].dataIndex = dataIndex;
5488 * Returns true if the cell is editable.
5489 * @param {Number} colIndex The column index
5490 * @param {Number} rowIndex The row index - this is nto actually used..?
5493 isCellEditable : function(colIndex, rowIndex){
5494 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5498 * Returns the editor defined for the cell/column.
5499 * return false or null to disable editing.
5500 * @param {Number} colIndex The column index
5501 * @param {Number} rowIndex The row index
5504 getCellEditor : function(colIndex, rowIndex){
5505 return this.config[colIndex].editor;
5509 * Sets if a column is editable.
5510 * @param {Number} col The column index
5511 * @param {Boolean} editable True if the column is editable
5513 setEditable : function(col, editable){
5514 this.config[col].editable = editable;
5519 * Returns true if the column is hidden.
5520 * @param {Number} colIndex The column index
5523 isHidden : function(colIndex){
5524 return this.config[colIndex].hidden;
5529 * Returns true if the column width cannot be changed
5531 isFixed : function(colIndex){
5532 return this.config[colIndex].fixed;
5536 * Returns true if the column can be resized
5539 isResizable : function(colIndex){
5540 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5543 * Sets if a column is hidden.
5544 * @param {Number} colIndex The column index
5545 * @param {Boolean} hidden True if the column is hidden
5547 setHidden : function(colIndex, hidden){
5548 this.config[colIndex].hidden = hidden;
5549 this.totalWidth = null;
5550 this.fireEvent("hiddenchange", this, colIndex, hidden);
5554 * Sets the editor for a column.
5555 * @param {Number} col The column index
5556 * @param {Object} editor The editor object
5558 setEditor : function(col, editor){
5559 this.config[col].editor = editor;
5563 Roo.grid.ColumnModel.defaultRenderer = function(value)
5565 if(typeof value == "object") {
5568 if(typeof value == "string" && value.length < 1){
5572 return String.format("{0}", value);
5575 // Alias for backwards compatibility
5576 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5579 * Ext JS Library 1.1.1
5580 * Copyright(c) 2006-2007, Ext JS, LLC.
5582 * Originally Released Under LGPL - original licence link has changed is not relivant.
5585 * <script type="text/javascript">
5589 * @class Roo.LoadMask
5590 * A simple utility class for generically masking elements while loading data. If the element being masked has
5591 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5592 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5593 * element's UpdateManager load indicator and will be destroyed after the initial load.
5595 * Create a new LoadMask
5596 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5597 * @param {Object} config The config object
5599 Roo.LoadMask = function(el, config){
5600 this.el = Roo.get(el);
5601 Roo.apply(this, config);
5603 this.store.on('beforeload', this.onBeforeLoad, this);
5604 this.store.on('load', this.onLoad, this);
5605 this.store.on('loadexception', this.onLoadException, this);
5606 this.removeMask = false;
5608 var um = this.el.getUpdateManager();
5609 um.showLoadIndicator = false; // disable the default indicator
5610 um.on('beforeupdate', this.onBeforeLoad, this);
5611 um.on('update', this.onLoad, this);
5612 um.on('failure', this.onLoad, this);
5613 this.removeMask = true;
5617 Roo.LoadMask.prototype = {
5619 * @cfg {Boolean} removeMask
5620 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5621 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5625 * The text to display in a centered loading message box (defaults to 'Loading...')
5629 * @cfg {String} msgCls
5630 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5632 msgCls : 'x-mask-loading',
5635 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5641 * Disables the mask to prevent it from being displayed
5643 disable : function(){
5644 this.disabled = true;
5648 * Enables the mask so that it can be displayed
5650 enable : function(){
5651 this.disabled = false;
5654 onLoadException : function()
5658 if (typeof(arguments[3]) != 'undefined') {
5659 Roo.MessageBox.alert("Error loading",arguments[3]);
5663 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5664 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5673 this.el.unmask(this.removeMask);
5678 this.el.unmask(this.removeMask);
5682 onBeforeLoad : function(){
5684 this.el.mask(this.msg, this.msgCls);
5689 destroy : function(){
5691 this.store.un('beforeload', this.onBeforeLoad, this);
5692 this.store.un('load', this.onLoad, this);
5693 this.store.un('loadexception', this.onLoadException, this);
5695 var um = this.el.getUpdateManager();
5696 um.un('beforeupdate', this.onBeforeLoad, this);
5697 um.un('update', this.onLoad, this);
5698 um.un('failure', this.onLoad, this);
5709 * @class Roo.bootstrap.Table
5710 * @extends Roo.bootstrap.Component
5711 * Bootstrap Table class
5712 * @cfg {String} cls table class
5713 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5714 * @cfg {String} bgcolor Specifies the background color for a table
5715 * @cfg {Number} border Specifies whether the table cells should have borders or not
5716 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5717 * @cfg {Number} cellspacing Specifies the space between cells
5718 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5719 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5720 * @cfg {String} sortable Specifies that the table should be sortable
5721 * @cfg {String} summary Specifies a summary of the content of a table
5722 * @cfg {Number} width Specifies the width of a table
5723 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5725 * @cfg {boolean} striped Should the rows be alternative striped
5726 * @cfg {boolean} bordered Add borders to the table
5727 * @cfg {boolean} hover Add hover highlighting
5728 * @cfg {boolean} condensed Format condensed
5729 * @cfg {boolean} responsive Format condensed
5730 * @cfg {Boolean} loadMask (true|false) default false
5731 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5732 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5733 * @cfg {Boolean} rowSelection (true|false) default false
5734 * @cfg {Boolean} cellSelection (true|false) default false
5735 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5736 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5740 * Create a new Table
5741 * @param {Object} config The config object
5744 Roo.bootstrap.Table = function(config){
5745 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5750 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5751 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5752 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5753 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5755 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5757 this.sm.grid = this;
5758 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5759 this.sm = this.selModel;
5760 this.sm.xmodule = this.xmodule || false;
5763 if (this.cm && typeof(this.cm.config) == 'undefined') {
5764 this.colModel = new Roo.grid.ColumnModel(this.cm);
5765 this.cm = this.colModel;
5766 this.cm.xmodule = this.xmodule || false;
5769 this.store= Roo.factory(this.store, Roo.data);
5770 this.ds = this.store;
5771 this.ds.xmodule = this.xmodule || false;
5774 if (this.footer && this.store) {
5775 this.footer.dataSource = this.ds;
5776 this.footer = Roo.factory(this.footer);
5783 * Fires when a cell is clicked
5784 * @param {Roo.bootstrap.Table} this
5785 * @param {Roo.Element} el
5786 * @param {Number} rowIndex
5787 * @param {Number} columnIndex
5788 * @param {Roo.EventObject} e
5792 * @event celldblclick
5793 * Fires when a cell is double clicked
5794 * @param {Roo.bootstrap.Table} this
5795 * @param {Roo.Element} el
5796 * @param {Number} rowIndex
5797 * @param {Number} columnIndex
5798 * @param {Roo.EventObject} e
5800 "celldblclick" : true,
5803 * Fires when a row is clicked
5804 * @param {Roo.bootstrap.Table} this
5805 * @param {Roo.Element} el
5806 * @param {Number} rowIndex
5807 * @param {Roo.EventObject} e
5811 * @event rowdblclick
5812 * Fires when a row is double clicked
5813 * @param {Roo.bootstrap.Table} this
5814 * @param {Roo.Element} el
5815 * @param {Number} rowIndex
5816 * @param {Roo.EventObject} e
5818 "rowdblclick" : true,
5821 * Fires when a mouseover occur
5822 * @param {Roo.bootstrap.Table} this
5823 * @param {Roo.Element} el
5824 * @param {Number} rowIndex
5825 * @param {Number} columnIndex
5826 * @param {Roo.EventObject} e
5831 * Fires when a mouseout occur
5832 * @param {Roo.bootstrap.Table} this
5833 * @param {Roo.Element} el
5834 * @param {Number} rowIndex
5835 * @param {Number} columnIndex
5836 * @param {Roo.EventObject} e
5841 * Fires when a row is rendered, so you can change add a style to it.
5842 * @param {Roo.bootstrap.Table} this
5843 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5847 * @event rowsrendered
5848 * Fires when all the rows have been rendered
5849 * @param {Roo.bootstrap.Table} this
5851 'rowsrendered' : true,
5853 * @event contextmenu
5854 * The raw contextmenu event for the entire grid.
5855 * @param {Roo.EventObject} e
5857 "contextmenu" : true,
5859 * @event rowcontextmenu
5860 * Fires when a row is right clicked
5861 * @param {Roo.bootstrap.Table} this
5862 * @param {Number} rowIndex
5863 * @param {Roo.EventObject} e
5865 "rowcontextmenu" : true,
5867 * @event cellcontextmenu
5868 * Fires when a cell is right clicked
5869 * @param {Roo.bootstrap.Table} this
5870 * @param {Number} rowIndex
5871 * @param {Number} cellIndex
5872 * @param {Roo.EventObject} e
5874 "cellcontextmenu" : true,
5876 * @event headercontextmenu
5877 * Fires when a header is right clicked
5878 * @param {Roo.bootstrap.Table} this
5879 * @param {Number} columnIndex
5880 * @param {Roo.EventObject} e
5882 "headercontextmenu" : true
5886 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5912 rowSelection : false,
5913 cellSelection : false,
5916 // Roo.Element - the tbody
5918 // Roo.Element - thead element
5921 container: false, // used by gridpanel...
5923 getAutoCreate : function()
5925 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5932 if (this.scrollBody) {
5933 cfg.cls += ' table-body-fixed';
5936 cfg.cls += ' table-striped';
5940 cfg.cls += ' table-hover';
5942 if (this.bordered) {
5943 cfg.cls += ' table-bordered';
5945 if (this.condensed) {
5946 cfg.cls += ' table-condensed';
5948 if (this.responsive) {
5949 cfg.cls += ' table-responsive';
5953 cfg.cls+= ' ' +this.cls;
5956 // this lot should be simplifed...
5959 cfg.align=this.align;
5962 cfg.bgcolor=this.bgcolor;
5965 cfg.border=this.border;
5967 if (this.cellpadding) {
5968 cfg.cellpadding=this.cellpadding;
5970 if (this.cellspacing) {
5971 cfg.cellspacing=this.cellspacing;
5974 cfg.frame=this.frame;
5977 cfg.rules=this.rules;
5979 if (this.sortable) {
5980 cfg.sortable=this.sortable;
5983 cfg.summary=this.summary;
5986 cfg.width=this.width;
5989 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5992 if(this.store || this.cm){
5993 if(this.headerShow){
5994 cfg.cn.push(this.renderHeader());
5997 cfg.cn.push(this.renderBody());
5999 if(this.footerShow){
6000 cfg.cn.push(this.renderFooter());
6002 // where does this come from?
6003 //cfg.cls+= ' TableGrid';
6006 return { cn : [ cfg ] };
6009 initEvents : function()
6011 if(!this.store || !this.cm){
6014 if (this.selModel) {
6015 this.selModel.initEvents();
6019 //Roo.log('initEvents with ds!!!!');
6021 this.mainBody = this.el.select('tbody', true).first();
6022 this.mainHead = this.el.select('thead', true).first();
6029 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6030 e.on('click', _this.sort, _this);
6033 this.mainBody.on("click", this.onClick, this);
6034 this.mainBody.on("dblclick", this.onDblClick, this);
6036 // why is this done????? = it breaks dialogs??
6037 //this.parent().el.setStyle('position', 'relative');
6041 this.footer.parentId = this.id;
6042 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6045 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6047 this.store.on('load', this.onLoad, this);
6048 this.store.on('beforeload', this.onBeforeLoad, this);
6049 this.store.on('update', this.onUpdate, this);
6050 this.store.on('add', this.onAdd, this);
6051 this.store.on("clear", this.clear, this);
6053 this.el.on("contextmenu", this.onContextMenu, this);
6055 this.mainBody.on('scroll', this.onBodyScroll, this);
6060 onContextMenu : function(e, t)
6062 this.processEvent("contextmenu", e);
6065 processEvent : function(name, e)
6067 if (name != 'touchstart' ) {
6068 this.fireEvent(name, e);
6071 var t = e.getTarget();
6073 var cell = Roo.get(t);
6079 if(cell.findParent('tfoot', false, true)){
6083 if(cell.findParent('thead', false, true)){
6085 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6086 cell = Roo.get(t).findParent('th', false, true);
6088 Roo.log("failed to find th in thead?");
6089 Roo.log(e.getTarget());
6094 var cellIndex = cell.dom.cellIndex;
6096 var ename = name == 'touchstart' ? 'click' : name;
6097 this.fireEvent("header" + ename, this, cellIndex, e);
6102 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6103 cell = Roo.get(t).findParent('td', false, true);
6105 Roo.log("failed to find th in tbody?");
6106 Roo.log(e.getTarget());
6111 var row = cell.findParent('tr', false, true);
6112 var cellIndex = cell.dom.cellIndex;
6113 var rowIndex = row.dom.rowIndex - 1;
6117 this.fireEvent("row" + name, this, rowIndex, e);
6121 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6127 onMouseover : function(e, el)
6129 var cell = Roo.get(el);
6135 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6136 cell = cell.findParent('td', false, true);
6139 var row = cell.findParent('tr', false, true);
6140 var cellIndex = cell.dom.cellIndex;
6141 var rowIndex = row.dom.rowIndex - 1; // start from 0
6143 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6147 onMouseout : function(e, el)
6149 var cell = Roo.get(el);
6155 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6156 cell = cell.findParent('td', false, true);
6159 var row = cell.findParent('tr', false, true);
6160 var cellIndex = cell.dom.cellIndex;
6161 var rowIndex = row.dom.rowIndex - 1; // start from 0
6163 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6167 onClick : function(e, el)
6169 var cell = Roo.get(el);
6171 if(!cell || (!this.cellSelection && !this.rowSelection)){
6175 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6176 cell = cell.findParent('td', false, true);
6179 if(!cell || typeof(cell) == 'undefined'){
6183 var row = cell.findParent('tr', false, true);
6185 if(!row || typeof(row) == 'undefined'){
6189 var cellIndex = cell.dom.cellIndex;
6190 var rowIndex = this.getRowIndex(row);
6192 // why??? - should these not be based on SelectionModel?
6193 if(this.cellSelection){
6194 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6197 if(this.rowSelection){
6198 this.fireEvent('rowclick', this, row, rowIndex, e);
6204 onDblClick : function(e,el)
6206 var cell = Roo.get(el);
6208 if(!cell || (!this.cellSelection && !this.rowSelection)){
6212 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6213 cell = cell.findParent('td', false, true);
6216 if(!cell || typeof(cell) == 'undefined'){
6220 var row = cell.findParent('tr', false, true);
6222 if(!row || typeof(row) == 'undefined'){
6226 var cellIndex = cell.dom.cellIndex;
6227 var rowIndex = this.getRowIndex(row);
6229 if(this.cellSelection){
6230 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6233 if(this.rowSelection){
6234 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6238 sort : function(e,el)
6240 var col = Roo.get(el);
6242 if(!col.hasClass('sortable')){
6246 var sort = col.attr('sort');
6249 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6253 this.store.sortInfo = {field : sort, direction : dir};
6256 Roo.log("calling footer first");
6257 this.footer.onClick('first');
6260 this.store.load({ params : { start : 0 } });
6264 renderHeader : function()
6272 this.totalWidth = 0;
6274 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6276 var config = cm.config[i];
6281 html: cm.getColumnHeader(i)
6286 if(typeof(config.sortable) != 'undefined' && config.sortable){
6288 c.html = '<i class="glyphicon"></i>' + c.html;
6291 if(typeof(config.lgHeader) != 'undefined'){
6292 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6295 if(typeof(config.mdHeader) != 'undefined'){
6296 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6299 if(typeof(config.smHeader) != 'undefined'){
6300 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6303 if(typeof(config.xsHeader) != 'undefined'){
6304 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6311 if(typeof(config.tooltip) != 'undefined'){
6312 c.tooltip = config.tooltip;
6315 if(typeof(config.colspan) != 'undefined'){
6316 c.colspan = config.colspan;
6319 if(typeof(config.hidden) != 'undefined' && config.hidden){
6320 c.style += ' display:none;';
6323 if(typeof(config.dataIndex) != 'undefined'){
6324 c.sort = config.dataIndex;
6329 if(typeof(config.align) != 'undefined' && config.align.length){
6330 c.style += ' text-align:' + config.align + ';';
6333 if(typeof(config.width) != 'undefined'){
6334 c.style += ' width:' + config.width + 'px;';
6335 this.totalWidth += config.width;
6337 this.totalWidth += 100; // assume minimum of 100 per column?
6340 if(typeof(config.cls) != 'undefined'){
6341 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6344 ['xs','sm','md','lg'].map(function(size){
6346 if(typeof(config[size]) == 'undefined'){
6350 if (!config[size]) { // 0 = hidden
6351 c.cls += ' hidden-' + size;
6355 c.cls += ' col-' + size + '-' + config[size];
6365 renderBody : function()
6375 colspan : this.cm.getColumnCount()
6385 renderFooter : function()
6395 colspan : this.cm.getColumnCount()
6409 // Roo.log('ds onload');
6414 var ds = this.store;
6416 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6417 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6418 if (_this.store.sortInfo) {
6420 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6421 e.select('i', true).addClass(['glyphicon-arrow-up']);
6424 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6425 e.select('i', true).addClass(['glyphicon-arrow-down']);
6430 var tbody = this.mainBody;
6432 if(ds.getCount() > 0){
6433 ds.data.each(function(d,rowIndex){
6434 var row = this.renderRow(cm, ds, rowIndex);
6436 tbody.createChild(row);
6440 if(row.cellObjects.length){
6441 Roo.each(row.cellObjects, function(r){
6442 _this.renderCellObject(r);
6449 Roo.each(this.el.select('tbody td', true).elements, function(e){
6450 e.on('mouseover', _this.onMouseover, _this);
6453 Roo.each(this.el.select('tbody td', true).elements, function(e){
6454 e.on('mouseout', _this.onMouseout, _this);
6456 this.fireEvent('rowsrendered', this);
6457 //if(this.loadMask){
6458 // this.maskEl.hide();
6465 onUpdate : function(ds,record)
6467 this.refreshRow(record);
6471 onRemove : function(ds, record, index, isUpdate){
6472 if(isUpdate !== true){
6473 this.fireEvent("beforerowremoved", this, index, record);
6475 var bt = this.mainBody.dom;
6477 var rows = this.el.select('tbody > tr', true).elements;
6479 if(typeof(rows[index]) != 'undefined'){
6480 bt.removeChild(rows[index].dom);
6483 // if(bt.rows[index]){
6484 // bt.removeChild(bt.rows[index]);
6487 if(isUpdate !== true){
6488 //this.stripeRows(index);
6489 //this.syncRowHeights(index, index);
6491 this.fireEvent("rowremoved", this, index, record);
6495 onAdd : function(ds, records, rowIndex)
6497 //Roo.log('on Add called');
6498 // - note this does not handle multiple adding very well..
6499 var bt = this.mainBody.dom;
6500 for (var i =0 ; i < records.length;i++) {
6501 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6502 //Roo.log(records[i]);
6503 //Roo.log(this.store.getAt(rowIndex+i));
6504 this.insertRow(this.store, rowIndex + i, false);
6511 refreshRow : function(record){
6512 var ds = this.store, index;
6513 if(typeof record == 'number'){
6515 record = ds.getAt(index);
6517 index = ds.indexOf(record);
6519 this.insertRow(ds, index, true);
6521 this.onRemove(ds, record, index+1, true);
6523 //this.syncRowHeights(index, index);
6525 this.fireEvent("rowupdated", this, index, record);
6528 insertRow : function(dm, rowIndex, isUpdate){
6531 this.fireEvent("beforerowsinserted", this, rowIndex);
6533 //var s = this.getScrollState();
6534 var row = this.renderRow(this.cm, this.store, rowIndex);
6535 // insert before rowIndex..
6536 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6540 if(row.cellObjects.length){
6541 Roo.each(row.cellObjects, function(r){
6542 _this.renderCellObject(r);
6547 this.fireEvent("rowsinserted", this, rowIndex);
6548 //this.syncRowHeights(firstRow, lastRow);
6549 //this.stripeRows(firstRow);
6556 getRowDom : function(rowIndex)
6558 var rows = this.el.select('tbody > tr', true).elements;
6560 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6563 // returns the object tree for a tr..
6566 renderRow : function(cm, ds, rowIndex)
6569 var d = ds.getAt(rowIndex);
6576 var cellObjects = [];
6578 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6579 var config = cm.config[i];
6581 var renderer = cm.getRenderer(i);
6585 if(typeof(renderer) !== 'undefined'){
6586 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6588 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6589 // and are rendered into the cells after the row is rendered - using the id for the element.
6591 if(typeof(value) === 'object'){
6601 rowIndex : rowIndex,
6606 this.fireEvent('rowclass', this, rowcfg);
6610 cls : rowcfg.rowClass,
6612 html: (typeof(value) === 'object') ? '' : value
6619 if(typeof(config.colspan) != 'undefined'){
6620 td.colspan = config.colspan;
6623 if(typeof(config.hidden) != 'undefined' && config.hidden){
6624 td.style += ' display:none;';
6627 if(typeof(config.align) != 'undefined' && config.align.length){
6628 td.style += ' text-align:' + config.align + ';';
6631 if(typeof(config.width) != 'undefined'){
6632 td.style += ' width:' + config.width + 'px;';
6635 if(typeof(config.cursor) != 'undefined'){
6636 td.style += ' cursor:' + config.cursor + ';';
6639 if(typeof(config.cls) != 'undefined'){
6640 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6643 ['xs','sm','md','lg'].map(function(size){
6645 if(typeof(config[size]) == 'undefined'){
6649 if (!config[size]) { // 0 = hidden
6650 td.cls += ' hidden-' + size;
6654 td.cls += ' col-' + size + '-' + config[size];
6662 row.cellObjects = cellObjects;
6670 onBeforeLoad : function()
6672 //Roo.log('ds onBeforeLoad');
6676 //if(this.loadMask){
6677 // this.maskEl.show();
6685 this.el.select('tbody', true).first().dom.innerHTML = '';
6688 * Show or hide a row.
6689 * @param {Number} rowIndex to show or hide
6690 * @param {Boolean} state hide
6692 setRowVisibility : function(rowIndex, state)
6694 var bt = this.mainBody.dom;
6696 var rows = this.el.select('tbody > tr', true).elements;
6698 if(typeof(rows[rowIndex]) == 'undefined'){
6701 rows[rowIndex].dom.style.display = state ? '' : 'none';
6705 getSelectionModel : function(){
6707 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6709 return this.selModel;
6712 * Render the Roo.bootstrap object from renderder
6714 renderCellObject : function(r)
6718 var t = r.cfg.render(r.container);
6721 Roo.each(r.cfg.cn, function(c){
6723 container: t.getChildContainer(),
6726 _this.renderCellObject(child);
6731 getRowIndex : function(row)
6735 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6746 * Returns the grid's underlying element = used by panel.Grid
6747 * @return {Element} The element
6749 getGridEl : function(){
6753 * Forces a resize - used by panel.Grid
6754 * @return {Element} The element
6756 autoSize : function()
6758 //var ctr = Roo.get(this.container.dom.parentElement);
6759 var ctr = Roo.get(this.el.dom);
6761 var thd = this.getGridEl().select('thead',true).first();
6762 var tbd = this.getGridEl().select('tbody', true).first();
6763 var tfd = this.getGridEl().select('tfoot', true).first();
6765 var cw = ctr.getWidth();
6769 tbd.setSize(ctr.getWidth(),
6770 ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6772 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6775 cw = Math.max(cw, this.totalWidth);
6776 this.getGridEl().select('tr',true).setWidth(cw);
6777 // resize 'expandable coloumn?
6779 return; // we doe not have a view in this design..
6782 onBodyScroll: function()
6785 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6786 this.mainHead.setStyle({
6787 'position' : 'relative',
6788 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6805 * @class Roo.bootstrap.TableCell
6806 * @extends Roo.bootstrap.Component
6807 * Bootstrap TableCell class
6808 * @cfg {String} html cell contain text
6809 * @cfg {String} cls cell class
6810 * @cfg {String} tag cell tag (td|th) default td
6811 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6812 * @cfg {String} align Aligns the content in a cell
6813 * @cfg {String} axis Categorizes cells
6814 * @cfg {String} bgcolor Specifies the background color of a cell
6815 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6816 * @cfg {Number} colspan Specifies the number of columns a cell should span
6817 * @cfg {String} headers Specifies one or more header cells a cell is related to
6818 * @cfg {Number} height Sets the height of a cell
6819 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6820 * @cfg {Number} rowspan Sets the number of rows a cell should span
6821 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6822 * @cfg {String} valign Vertical aligns the content in a cell
6823 * @cfg {Number} width Specifies the width of a cell
6826 * Create a new TableCell
6827 * @param {Object} config The config object
6830 Roo.bootstrap.TableCell = function(config){
6831 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6834 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6854 getAutoCreate : function(){
6855 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6875 cfg.align=this.align
6881 cfg.bgcolor=this.bgcolor
6884 cfg.charoff=this.charoff
6887 cfg.colspan=this.colspan
6890 cfg.headers=this.headers
6893 cfg.height=this.height
6896 cfg.nowrap=this.nowrap
6899 cfg.rowspan=this.rowspan
6902 cfg.scope=this.scope
6905 cfg.valign=this.valign
6908 cfg.width=this.width
6927 * @class Roo.bootstrap.TableRow
6928 * @extends Roo.bootstrap.Component
6929 * Bootstrap TableRow class
6930 * @cfg {String} cls row class
6931 * @cfg {String} align Aligns the content in a table row
6932 * @cfg {String} bgcolor Specifies a background color for a table row
6933 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6934 * @cfg {String} valign Vertical aligns the content in a table row
6937 * Create a new TableRow
6938 * @param {Object} config The config object
6941 Roo.bootstrap.TableRow = function(config){
6942 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6945 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6953 getAutoCreate : function(){
6954 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6964 cfg.align = this.align;
6967 cfg.bgcolor = this.bgcolor;
6970 cfg.charoff = this.charoff;
6973 cfg.valign = this.valign;
6991 * @class Roo.bootstrap.TableBody
6992 * @extends Roo.bootstrap.Component
6993 * Bootstrap TableBody class
6994 * @cfg {String} cls element class
6995 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6996 * @cfg {String} align Aligns the content inside the element
6997 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6998 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7001 * Create a new TableBody
7002 * @param {Object} config The config object
7005 Roo.bootstrap.TableBody = function(config){
7006 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7009 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7017 getAutoCreate : function(){
7018 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7032 cfg.align = this.align;
7035 cfg.charoff = this.charoff;
7038 cfg.valign = this.valign;
7045 // initEvents : function()
7052 // this.store = Roo.factory(this.store, Roo.data);
7053 // this.store.on('load', this.onLoad, this);
7055 // this.store.load();
7059 // onLoad: function ()
7061 // this.fireEvent('load', this);
7071 * Ext JS Library 1.1.1
7072 * Copyright(c) 2006-2007, Ext JS, LLC.
7074 * Originally Released Under LGPL - original licence link has changed is not relivant.
7077 * <script type="text/javascript">
7080 // as we use this in bootstrap.
7081 Roo.namespace('Roo.form');
7083 * @class Roo.form.Action
7084 * Internal Class used to handle form actions
7086 * @param {Roo.form.BasicForm} el The form element or its id
7087 * @param {Object} config Configuration options
7092 // define the action interface
7093 Roo.form.Action = function(form, options){
7095 this.options = options || {};
7098 * Client Validation Failed
7101 Roo.form.Action.CLIENT_INVALID = 'client';
7103 * Server Validation Failed
7106 Roo.form.Action.SERVER_INVALID = 'server';
7108 * Connect to Server Failed
7111 Roo.form.Action.CONNECT_FAILURE = 'connect';
7113 * Reading Data from Server Failed
7116 Roo.form.Action.LOAD_FAILURE = 'load';
7118 Roo.form.Action.prototype = {
7120 failureType : undefined,
7121 response : undefined,
7125 run : function(options){
7130 success : function(response){
7135 handleResponse : function(response){
7139 // default connection failure
7140 failure : function(response){
7142 this.response = response;
7143 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7144 this.form.afterAction(this, false);
7147 processResponse : function(response){
7148 this.response = response;
7149 if(!response.responseText){
7152 this.result = this.handleResponse(response);
7156 // utility functions used internally
7157 getUrl : function(appendParams){
7158 var url = this.options.url || this.form.url || this.form.el.dom.action;
7160 var p = this.getParams();
7162 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7168 getMethod : function(){
7169 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7172 getParams : function(){
7173 var bp = this.form.baseParams;
7174 var p = this.options.params;
7176 if(typeof p == "object"){
7177 p = Roo.urlEncode(Roo.applyIf(p, bp));
7178 }else if(typeof p == 'string' && bp){
7179 p += '&' + Roo.urlEncode(bp);
7182 p = Roo.urlEncode(bp);
7187 createCallback : function(){
7189 success: this.success,
7190 failure: this.failure,
7192 timeout: (this.form.timeout*1000),
7193 upload: this.form.fileUpload ? this.success : undefined
7198 Roo.form.Action.Submit = function(form, options){
7199 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7202 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7205 haveProgress : false,
7206 uploadComplete : false,
7208 // uploadProgress indicator.
7209 uploadProgress : function()
7211 if (!this.form.progressUrl) {
7215 if (!this.haveProgress) {
7216 Roo.MessageBox.progress("Uploading", "Uploading");
7218 if (this.uploadComplete) {
7219 Roo.MessageBox.hide();
7223 this.haveProgress = true;
7225 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7227 var c = new Roo.data.Connection();
7229 url : this.form.progressUrl,
7234 success : function(req){
7235 //console.log(data);
7239 rdata = Roo.decode(req.responseText)
7241 Roo.log("Invalid data from server..");
7245 if (!rdata || !rdata.success) {
7247 Roo.MessageBox.alert(Roo.encode(rdata));
7250 var data = rdata.data;
7252 if (this.uploadComplete) {
7253 Roo.MessageBox.hide();
7258 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7259 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7262 this.uploadProgress.defer(2000,this);
7265 failure: function(data) {
7266 Roo.log('progress url failed ');
7277 // run get Values on the form, so it syncs any secondary forms.
7278 this.form.getValues();
7280 var o = this.options;
7281 var method = this.getMethod();
7282 var isPost = method == 'POST';
7283 if(o.clientValidation === false || this.form.isValid()){
7285 if (this.form.progressUrl) {
7286 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7287 (new Date() * 1) + '' + Math.random());
7292 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7293 form:this.form.el.dom,
7294 url:this.getUrl(!isPost),
7296 params:isPost ? this.getParams() : null,
7297 isUpload: this.form.fileUpload
7300 this.uploadProgress();
7302 }else if (o.clientValidation !== false){ // client validation failed
7303 this.failureType = Roo.form.Action.CLIENT_INVALID;
7304 this.form.afterAction(this, false);
7308 success : function(response)
7310 this.uploadComplete= true;
7311 if (this.haveProgress) {
7312 Roo.MessageBox.hide();
7316 var result = this.processResponse(response);
7317 if(result === true || result.success){
7318 this.form.afterAction(this, true);
7322 this.form.markInvalid(result.errors);
7323 this.failureType = Roo.form.Action.SERVER_INVALID;
7325 this.form.afterAction(this, false);
7327 failure : function(response)
7329 this.uploadComplete= true;
7330 if (this.haveProgress) {
7331 Roo.MessageBox.hide();
7334 this.response = response;
7335 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7336 this.form.afterAction(this, false);
7339 handleResponse : function(response){
7340 if(this.form.errorReader){
7341 var rs = this.form.errorReader.read(response);
7344 for(var i = 0, len = rs.records.length; i < len; i++) {
7345 var r = rs.records[i];
7349 if(errors.length < 1){
7353 success : rs.success,
7359 ret = Roo.decode(response.responseText);
7363 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7373 Roo.form.Action.Load = function(form, options){
7374 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7375 this.reader = this.form.reader;
7378 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7383 Roo.Ajax.request(Roo.apply(
7384 this.createCallback(), {
7385 method:this.getMethod(),
7386 url:this.getUrl(false),
7387 params:this.getParams()
7391 success : function(response){
7393 var result = this.processResponse(response);
7394 if(result === true || !result.success || !result.data){
7395 this.failureType = Roo.form.Action.LOAD_FAILURE;
7396 this.form.afterAction(this, false);
7399 this.form.clearInvalid();
7400 this.form.setValues(result.data);
7401 this.form.afterAction(this, true);
7404 handleResponse : function(response){
7405 if(this.form.reader){
7406 var rs = this.form.reader.read(response);
7407 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7409 success : rs.success,
7413 return Roo.decode(response.responseText);
7417 Roo.form.Action.ACTION_TYPES = {
7418 'load' : Roo.form.Action.Load,
7419 'submit' : Roo.form.Action.Submit
7428 * @class Roo.bootstrap.Form
7429 * @extends Roo.bootstrap.Component
7430 * Bootstrap Form class
7431 * @cfg {String} method GET | POST (default POST)
7432 * @cfg {String} labelAlign top | left (default top)
7433 * @cfg {String} align left | right - for navbars
7434 * @cfg {Boolean} loadMask load mask when submit (default true)
7439 * @param {Object} config The config object
7443 Roo.bootstrap.Form = function(config){
7444 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7447 * @event clientvalidation
7448 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7449 * @param {Form} this
7450 * @param {Boolean} valid true if the form has passed client-side validation
7452 clientvalidation: true,
7454 * @event beforeaction
7455 * Fires before any action is performed. Return false to cancel the action.
7456 * @param {Form} this
7457 * @param {Action} action The action to be performed
7461 * @event actionfailed
7462 * Fires when an action fails.
7463 * @param {Form} this
7464 * @param {Action} action The action that failed
7466 actionfailed : true,
7468 * @event actioncomplete
7469 * Fires when an action is completed.
7470 * @param {Form} this
7471 * @param {Action} action The action that completed
7473 actioncomplete : true
7478 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7481 * @cfg {String} method
7482 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7487 * The URL to use for form actions if one isn't supplied in the action options.
7490 * @cfg {Boolean} fileUpload
7491 * Set to true if this form is a file upload.
7495 * @cfg {Object} baseParams
7496 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7500 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7504 * @cfg {Sting} align (left|right) for navbar forms
7509 activeAction : null,
7512 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7513 * element by passing it or its id or mask the form itself by passing in true.
7516 waitMsgTarget : false,
7520 getAutoCreate : function(){
7524 method : this.method || 'POST',
7525 id : this.id || Roo.id(),
7528 if (this.parent().xtype.match(/^Nav/)) {
7529 cfg.cls = 'navbar-form navbar-' + this.align;
7533 if (this.labelAlign == 'left' ) {
7534 cfg.cls += ' form-horizontal';
7540 initEvents : function()
7542 this.el.on('submit', this.onSubmit, this);
7543 // this was added as random key presses on the form where triggering form submit.
7544 this.el.on('keypress', function(e) {
7545 if (e.getCharCode() != 13) {
7548 // we might need to allow it for textareas.. and some other items.
7549 // check e.getTarget().
7551 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7555 Roo.log("keypress blocked");
7563 onSubmit : function(e){
7568 * Returns true if client-side validation on the form is successful.
7571 isValid : function(){
7572 var items = this.getItems();
7574 items.each(function(f){
7583 * Returns true if any fields in this form have changed since their original load.
7586 isDirty : function(){
7588 var items = this.getItems();
7589 items.each(function(f){
7599 * Performs a predefined action (submit or load) or custom actions you define on this form.
7600 * @param {String} actionName The name of the action type
7601 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7602 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7603 * accept other config options):
7605 Property Type Description
7606 ---------------- --------------- ----------------------------------------------------------------------------------
7607 url String The url for the action (defaults to the form's url)
7608 method String The form method to use (defaults to the form's method, or POST if not defined)
7609 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7610 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7611 validate the form on the client (defaults to false)
7613 * @return {BasicForm} this
7615 doAction : function(action, options){
7616 if(typeof action == 'string'){
7617 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7619 if(this.fireEvent('beforeaction', this, action) !== false){
7620 this.beforeAction(action);
7621 action.run.defer(100, action);
7627 beforeAction : function(action){
7628 var o = action.options;
7631 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7633 // not really supported yet.. ??
7635 //if(this.waitMsgTarget === true){
7636 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7637 //}else if(this.waitMsgTarget){
7638 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7639 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7641 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7647 afterAction : function(action, success){
7648 this.activeAction = null;
7649 var o = action.options;
7651 //if(this.waitMsgTarget === true){
7653 //}else if(this.waitMsgTarget){
7654 // this.waitMsgTarget.unmask();
7656 // Roo.MessageBox.updateProgress(1);
7657 // Roo.MessageBox.hide();
7664 Roo.callback(o.success, o.scope, [this, action]);
7665 this.fireEvent('actioncomplete', this, action);
7669 // failure condition..
7670 // we have a scenario where updates need confirming.
7671 // eg. if a locking scenario exists..
7672 // we look for { errors : { needs_confirm : true }} in the response.
7674 (typeof(action.result) != 'undefined') &&
7675 (typeof(action.result.errors) != 'undefined') &&
7676 (typeof(action.result.errors.needs_confirm) != 'undefined')
7679 Roo.log("not supported yet");
7682 Roo.MessageBox.confirm(
7683 "Change requires confirmation",
7684 action.result.errorMsg,
7689 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7699 Roo.callback(o.failure, o.scope, [this, action]);
7700 // show an error message if no failed handler is set..
7701 if (!this.hasListener('actionfailed')) {
7702 Roo.log("need to add dialog support");
7704 Roo.MessageBox.alert("Error",
7705 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7706 action.result.errorMsg :
7707 "Saving Failed, please check your entries or try again"
7712 this.fireEvent('actionfailed', this, action);
7717 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7718 * @param {String} id The value to search for
7721 findField : function(id){
7722 var items = this.getItems();
7723 var field = items.get(id);
7725 items.each(function(f){
7726 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7733 return field || null;
7736 * Mark fields in this form invalid in bulk.
7737 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7738 * @return {BasicForm} this
7740 markInvalid : function(errors){
7741 if(errors instanceof Array){
7742 for(var i = 0, len = errors.length; i < len; i++){
7743 var fieldError = errors[i];
7744 var f = this.findField(fieldError.id);
7746 f.markInvalid(fieldError.msg);
7752 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7753 field.markInvalid(errors[id]);
7757 //Roo.each(this.childForms || [], function (f) {
7758 // f.markInvalid(errors);
7765 * Set values for fields in this form in bulk.
7766 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7767 * @return {BasicForm} this
7769 setValues : function(values){
7770 if(values instanceof Array){ // array of objects
7771 for(var i = 0, len = values.length; i < len; i++){
7773 var f = this.findField(v.id);
7775 f.setValue(v.value);
7776 if(this.trackResetOnLoad){
7777 f.originalValue = f.getValue();
7781 }else{ // object hash
7784 if(typeof values[id] != 'function' && (field = this.findField(id))){
7786 if (field.setFromData &&
7788 field.displayField &&
7789 // combos' with local stores can
7790 // be queried via setValue()
7791 // to set their value..
7792 (field.store && !field.store.isLocal)
7796 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7797 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7798 field.setFromData(sd);
7801 field.setValue(values[id]);
7805 if(this.trackResetOnLoad){
7806 field.originalValue = field.getValue();
7812 //Roo.each(this.childForms || [], function (f) {
7813 // f.setValues(values);
7820 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7821 * they are returned as an array.
7822 * @param {Boolean} asString
7825 getValues : function(asString){
7826 //if (this.childForms) {
7827 // copy values from the child forms
7828 // Roo.each(this.childForms, function (f) {
7829 // this.setValues(f.getValues());
7835 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7836 if(asString === true){
7839 return Roo.urlDecode(fs);
7843 * Returns the fields in this form as an object with key/value pairs.
7844 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7847 getFieldValues : function(with_hidden)
7849 var items = this.getItems();
7851 items.each(function(f){
7855 var v = f.getValue();
7856 if (f.inputType =='radio') {
7857 if (typeof(ret[f.getName()]) == 'undefined') {
7858 ret[f.getName()] = ''; // empty..
7861 if (!f.el.dom.checked) {
7869 // not sure if this supported any more..
7870 if ((typeof(v) == 'object') && f.getRawValue) {
7871 v = f.getRawValue() ; // dates..
7873 // combo boxes where name != hiddenName...
7874 if (f.name != f.getName()) {
7875 ret[f.name] = f.getRawValue();
7877 ret[f.getName()] = v;
7884 * Clears all invalid messages in this form.
7885 * @return {BasicForm} this
7887 clearInvalid : function(){
7888 var items = this.getItems();
7890 items.each(function(f){
7901 * @return {BasicForm} this
7904 var items = this.getItems();
7905 items.each(function(f){
7909 Roo.each(this.childForms || [], function (f) {
7916 getItems : function()
7918 var r=new Roo.util.MixedCollection(false, function(o){
7919 return o.id || (o.id = Roo.id());
7921 var iter = function(el) {
7928 Roo.each(el.items,function(e) {
7946 * Ext JS Library 1.1.1
7947 * Copyright(c) 2006-2007, Ext JS, LLC.
7949 * Originally Released Under LGPL - original licence link has changed is not relivant.
7952 * <script type="text/javascript">
7955 * @class Roo.form.VTypes
7956 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7959 Roo.form.VTypes = function(){
7960 // closure these in so they are only created once.
7961 var alpha = /^[a-zA-Z_]+$/;
7962 var alphanum = /^[a-zA-Z0-9_]+$/;
7963 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7964 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7966 // All these messages and functions are configurable
7969 * The function used to validate email addresses
7970 * @param {String} value The email address
7972 'email' : function(v){
7973 return email.test(v);
7976 * The error text to display when the email validation function returns false
7979 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7981 * The keystroke filter mask to be applied on email input
7984 'emailMask' : /[a-z0-9_\.\-@]/i,
7987 * The function used to validate URLs
7988 * @param {String} value The URL
7990 'url' : function(v){
7994 * The error text to display when the url validation function returns false
7997 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8000 * The function used to validate alpha values
8001 * @param {String} value The value
8003 'alpha' : function(v){
8004 return alpha.test(v);
8007 * The error text to display when the alpha validation function returns false
8010 'alphaText' : 'This field should only contain letters and _',
8012 * The keystroke filter mask to be applied on alpha input
8015 'alphaMask' : /[a-z_]/i,
8018 * The function used to validate alphanumeric values
8019 * @param {String} value The value
8021 'alphanum' : function(v){
8022 return alphanum.test(v);
8025 * The error text to display when the alphanumeric validation function returns false
8028 'alphanumText' : 'This field should only contain letters, numbers and _',
8030 * The keystroke filter mask to be applied on alphanumeric input
8033 'alphanumMask' : /[a-z0-9_]/i
8043 * @class Roo.bootstrap.Input
8044 * @extends Roo.bootstrap.Component
8045 * Bootstrap Input class
8046 * @cfg {Boolean} disabled is it disabled
8047 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8048 * @cfg {String} name name of the input
8049 * @cfg {string} fieldLabel - the label associated
8050 * @cfg {string} placeholder - placeholder to put in text.
8051 * @cfg {string} before - input group add on before
8052 * @cfg {string} after - input group add on after
8053 * @cfg {string} size - (lg|sm) or leave empty..
8054 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8055 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8056 * @cfg {Number} md colspan out of 12 for computer-sized screens
8057 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8058 * @cfg {string} value default value of the input
8059 * @cfg {Number} labelWidth set the width of label (0-12)
8060 * @cfg {String} labelAlign (top|left)
8061 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8062 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8063 * @cfg {String} indicatorpos (left|right) default left
8065 * @cfg {String} align (left|center|right) Default left
8066 * @cfg {Boolean} forceFeedback (true|false) Default false
8072 * Create a new Input
8073 * @param {Object} config The config object
8076 Roo.bootstrap.Input = function(config){
8077 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8082 * Fires when this field receives input focus.
8083 * @param {Roo.form.Field} this
8088 * Fires when this field loses input focus.
8089 * @param {Roo.form.Field} this
8094 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8095 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8096 * @param {Roo.form.Field} this
8097 * @param {Roo.EventObject} e The event object
8102 * Fires just before the field blurs if the field value has changed.
8103 * @param {Roo.form.Field} this
8104 * @param {Mixed} newValue The new value
8105 * @param {Mixed} oldValue The original value
8110 * Fires after the field has been marked as invalid.
8111 * @param {Roo.form.Field} this
8112 * @param {String} msg The validation message
8117 * Fires after the field has been validated with no errors.
8118 * @param {Roo.form.Field} this
8123 * Fires after the key up
8124 * @param {Roo.form.Field} this
8125 * @param {Roo.EventObject} e The event Object
8131 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8133 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8134 automatic validation (defaults to "keyup").
8136 validationEvent : "keyup",
8138 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8140 validateOnBlur : true,
8142 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8144 validationDelay : 250,
8146 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8148 focusClass : "x-form-focus", // not needed???
8152 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8154 invalidClass : "has-warning",
8157 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8159 validClass : "has-success",
8162 * @cfg {Boolean} hasFeedback (true|false) default true
8167 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8169 invalidFeedbackClass : "glyphicon-warning-sign",
8172 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8174 validFeedbackClass : "glyphicon-ok",
8177 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8179 selectOnFocus : false,
8182 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8186 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8191 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8193 disableKeyFilter : false,
8196 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8200 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8204 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8206 blankText : "This field is required",
8209 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8213 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8215 maxLength : Number.MAX_VALUE,
8217 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8219 minLengthText : "The minimum length for this field is {0}",
8221 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8223 maxLengthText : "The maximum length for this field is {0}",
8227 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8228 * If available, this function will be called only after the basic validators all return true, and will be passed the
8229 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8233 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8234 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8235 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8239 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8243 autocomplete: false,
8262 formatedValue : false,
8263 forceFeedback : false,
8265 indicatorpos : 'left',
8267 parentLabelAlign : function()
8270 while (parent.parent()) {
8271 parent = parent.parent();
8272 if (typeof(parent.labelAlign) !='undefined') {
8273 return parent.labelAlign;
8280 getAutoCreate : function()
8282 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8288 if(this.inputType != 'hidden'){
8289 cfg.cls = 'form-group' //input-group
8295 type : this.inputType,
8297 cls : 'form-control',
8298 placeholder : this.placeholder || '',
8299 autocomplete : this.autocomplete || 'new-password'
8303 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8306 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8307 input.maxLength = this.maxLength;
8310 if (this.disabled) {
8311 input.disabled=true;
8314 if (this.readOnly) {
8315 input.readonly=true;
8319 input.name = this.name;
8323 input.cls += ' input-' + this.size;
8327 ['xs','sm','md','lg'].map(function(size){
8328 if (settings[size]) {
8329 cfg.cls += ' col-' + size + '-' + settings[size];
8333 var inputblock = input;
8337 cls: 'glyphicon form-control-feedback'
8340 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8343 cls : 'has-feedback',
8351 if (this.before || this.after) {
8354 cls : 'input-group',
8358 if (this.before && typeof(this.before) == 'string') {
8360 inputblock.cn.push({
8362 cls : 'roo-input-before input-group-addon',
8366 if (this.before && typeof(this.before) == 'object') {
8367 this.before = Roo.factory(this.before);
8369 inputblock.cn.push({
8371 cls : 'roo-input-before input-group-' +
8372 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8376 inputblock.cn.push(input);
8378 if (this.after && typeof(this.after) == 'string') {
8379 inputblock.cn.push({
8381 cls : 'roo-input-after input-group-addon',
8385 if (this.after && typeof(this.after) == 'object') {
8386 this.after = Roo.factory(this.after);
8388 inputblock.cn.push({
8390 cls : 'roo-input-after input-group-' +
8391 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8395 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8396 inputblock.cls += ' has-feedback';
8397 inputblock.cn.push(feedback);
8401 if (align ==='left' && this.fieldLabel.length) {
8406 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8407 tooltip : 'This field is required'
8412 cls : 'control-label col-sm-' + this.labelWidth,
8413 html : this.fieldLabel
8417 cls : "col-sm-" + (12 - this.labelWidth),
8425 if(this.indicatorpos == 'right'){
8430 cls : 'control-label col-sm-' + this.labelWidth,
8431 html : this.fieldLabel
8436 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8437 tooltip : 'This field is required'
8440 cls : "col-sm-" + (12 - this.labelWidth),
8449 } else if ( this.fieldLabel.length) {
8454 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8455 tooltip : 'This field is required'
8459 //cls : 'input-group-addon',
8460 html : this.fieldLabel
8468 if(this.indicatorpos == 'right'){
8473 //cls : 'input-group-addon',
8474 html : this.fieldLabel
8479 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8480 tooltip : 'This field is required'
8500 if (this.parentType === 'Navbar' && this.parent().bar) {
8501 cfg.cls += ' navbar-form';
8504 if (this.parentType === 'NavGroup') {
8505 cfg.cls += ' navbar-form';
8513 * return the real input element.
8515 inputEl: function ()
8517 return this.el.select('input.form-control',true).first();
8520 tooltipEl : function()
8522 return this.inputEl();
8525 indicatorEl : function()
8527 var indicator = this.el.select('i.roo-required-indicator',true).first();
8537 setDisabled : function(v)
8539 var i = this.inputEl().dom;
8541 i.removeAttribute('disabled');
8545 i.setAttribute('disabled','true');
8547 initEvents : function()
8550 this.inputEl().on("keydown" , this.fireKey, this);
8551 this.inputEl().on("focus", this.onFocus, this);
8552 this.inputEl().on("blur", this.onBlur, this);
8554 this.inputEl().relayEvent('keyup', this);
8556 this.indicator = this.indicatorEl();
8559 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8560 this.indicator.hide();
8563 // reference to original value for reset
8564 this.originalValue = this.getValue();
8565 //Roo.form.TextField.superclass.initEvents.call(this);
8566 if(this.validationEvent == 'keyup'){
8567 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8568 this.inputEl().on('keyup', this.filterValidation, this);
8570 else if(this.validationEvent !== false){
8571 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8574 if(this.selectOnFocus){
8575 this.on("focus", this.preFocus, this);
8578 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8579 this.inputEl().on("keypress", this.filterKeys, this);
8581 this.inputEl().relayEvent('keypress', this);
8584 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8585 this.el.on("click", this.autoSize, this);
8588 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8589 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8592 if (typeof(this.before) == 'object') {
8593 this.before.render(this.el.select('.roo-input-before',true).first());
8595 if (typeof(this.after) == 'object') {
8596 this.after.render(this.el.select('.roo-input-after',true).first());
8601 filterValidation : function(e){
8602 if(!e.isNavKeyPress()){
8603 this.validationTask.delay(this.validationDelay);
8607 * Validates the field value
8608 * @return {Boolean} True if the value is valid, else false
8610 validate : function(){
8611 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8612 if(this.disabled || this.validateValue(this.getRawValue())){
8623 * Validates a value according to the field's validation rules and marks the field as invalid
8624 * if the validation fails
8625 * @param {Mixed} value The value to validate
8626 * @return {Boolean} True if the value is valid, else false
8628 validateValue : function(value){
8629 if(value.length < 1) { // if it's blank
8630 if(this.allowBlank){
8636 if(value.length < this.minLength){
8639 if(value.length > this.maxLength){
8643 var vt = Roo.form.VTypes;
8644 if(!vt[this.vtype](value, this)){
8648 if(typeof this.validator == "function"){
8649 var msg = this.validator(value);
8655 if(this.regex && !this.regex.test(value)){
8665 fireKey : function(e){
8666 //Roo.log('field ' + e.getKey());
8667 if(e.isNavKeyPress()){
8668 this.fireEvent("specialkey", this, e);
8671 focus : function (selectText){
8673 this.inputEl().focus();
8674 if(selectText === true){
8675 this.inputEl().dom.select();
8681 onFocus : function(){
8682 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8683 // this.el.addClass(this.focusClass);
8686 this.hasFocus = true;
8687 this.startValue = this.getValue();
8688 this.fireEvent("focus", this);
8692 beforeBlur : Roo.emptyFn,
8696 onBlur : function(){
8698 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8699 //this.el.removeClass(this.focusClass);
8701 this.hasFocus = false;
8702 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8705 var v = this.getValue();
8706 if(String(v) !== String(this.startValue)){
8707 this.fireEvent('change', this, v, this.startValue);
8709 this.fireEvent("blur", this);
8713 * Resets the current field value to the originally loaded value and clears any validation messages
8716 this.setValue(this.originalValue);
8720 * Returns the name of the field
8721 * @return {Mixed} name The name field
8723 getName: function(){
8727 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8728 * @return {Mixed} value The field value
8730 getValue : function(){
8732 var v = this.inputEl().getValue();
8737 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8738 * @return {Mixed} value The field value
8740 getRawValue : function(){
8741 var v = this.inputEl().getValue();
8747 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8748 * @param {Mixed} value The value to set
8750 setRawValue : function(v){
8751 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8754 selectText : function(start, end){
8755 var v = this.getRawValue();
8757 start = start === undefined ? 0 : start;
8758 end = end === undefined ? v.length : end;
8759 var d = this.inputEl().dom;
8760 if(d.setSelectionRange){
8761 d.setSelectionRange(start, end);
8762 }else if(d.createTextRange){
8763 var range = d.createTextRange();
8764 range.moveStart("character", start);
8765 range.moveEnd("character", v.length-end);
8772 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8773 * @param {Mixed} value The value to set
8775 setValue : function(v){
8778 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8784 processValue : function(value){
8785 if(this.stripCharsRe){
8786 var newValue = value.replace(this.stripCharsRe, '');
8787 if(newValue !== value){
8788 this.setRawValue(newValue);
8795 preFocus : function(){
8797 if(this.selectOnFocus){
8798 this.inputEl().dom.select();
8801 filterKeys : function(e){
8803 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8806 var c = e.getCharCode(), cc = String.fromCharCode(c);
8807 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8810 if(!this.maskRe.test(cc)){
8815 * Clear any invalid styles/messages for this field
8817 clearInvalid : function(){
8819 if(!this.el || this.preventMark){ // not rendered
8824 this.indicator.hide();
8827 this.el.removeClass(this.invalidClass);
8829 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8831 var feedback = this.el.select('.form-control-feedback', true).first();
8834 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8839 this.fireEvent('valid', this);
8843 * Mark this field as valid
8845 markValid : function()
8847 if(!this.el || this.preventMark){ // not rendered
8851 this.el.removeClass([this.invalidClass, this.validClass]);
8853 var feedback = this.el.select('.form-control-feedback', true).first();
8856 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8863 if(this.allowBlank && !this.getRawValue().length){
8868 this.indicator.hide();
8871 this.el.addClass(this.validClass);
8873 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8875 var feedback = this.el.select('.form-control-feedback', true).first();
8878 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8879 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8884 this.fireEvent('valid', this);
8888 * Mark this field as invalid
8889 * @param {String} msg The validation message
8891 markInvalid : function(msg)
8893 if(!this.el || this.preventMark){ // not rendered
8897 this.el.removeClass([this.invalidClass, this.validClass]);
8899 var feedback = this.el.select('.form-control-feedback', true).first();
8902 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8909 if(this.allowBlank && !this.getRawValue().length){
8914 this.indicator.show();
8917 this.el.addClass(this.invalidClass);
8919 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8921 var feedback = this.el.select('.form-control-feedback', true).first();
8924 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8926 if(this.getValue().length || this.forceFeedback){
8927 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8934 this.fireEvent('invalid', this, msg);
8937 SafariOnKeyDown : function(event)
8939 // this is a workaround for a password hang bug on chrome/ webkit.
8940 if (this.inputEl().dom.type != 'password') {
8944 var isSelectAll = false;
8946 if(this.inputEl().dom.selectionEnd > 0){
8947 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8949 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8950 event.preventDefault();
8955 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
8957 event.preventDefault();
8958 // this is very hacky as keydown always get's upper case.
8960 var cc = String.fromCharCode(event.getCharCode());
8961 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8965 adjustWidth : function(tag, w){
8966 tag = tag.toLowerCase();
8967 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8968 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8972 if(tag == 'textarea'){
8975 }else if(Roo.isOpera){
8979 if(tag == 'textarea'){
8998 * @class Roo.bootstrap.TextArea
8999 * @extends Roo.bootstrap.Input
9000 * Bootstrap TextArea class
9001 * @cfg {Number} cols Specifies the visible width of a text area
9002 * @cfg {Number} rows Specifies the visible number of lines in a text area
9003 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9004 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9005 * @cfg {string} html text
9008 * Create a new TextArea
9009 * @param {Object} config The config object
9012 Roo.bootstrap.TextArea = function(config){
9013 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9017 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9027 getAutoCreate : function(){
9029 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9040 value : this.value || '',
9041 html: this.html || '',
9042 cls : 'form-control',
9043 placeholder : this.placeholder || ''
9047 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9048 input.maxLength = this.maxLength;
9052 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9056 input.cols = this.cols;
9059 if (this.readOnly) {
9060 input.readonly = true;
9064 input.name = this.name;
9068 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9072 ['xs','sm','md','lg'].map(function(size){
9073 if (settings[size]) {
9074 cfg.cls += ' col-' + size + '-' + settings[size];
9078 var inputblock = input;
9080 if(this.hasFeedback && !this.allowBlank){
9084 cls: 'glyphicon form-control-feedback'
9088 cls : 'has-feedback',
9097 if (this.before || this.after) {
9100 cls : 'input-group',
9104 inputblock.cn.push({
9106 cls : 'input-group-addon',
9111 inputblock.cn.push(input);
9113 if(this.hasFeedback && !this.allowBlank){
9114 inputblock.cls += ' has-feedback';
9115 inputblock.cn.push(feedback);
9119 inputblock.cn.push({
9121 cls : 'input-group-addon',
9128 if (align ==='left' && this.fieldLabel.length) {
9129 // Roo.log("left and has label");
9135 cls : 'control-label col-sm-' + this.labelWidth,
9136 html : this.fieldLabel
9140 cls : "col-sm-" + (12 - this.labelWidth),
9147 } else if ( this.fieldLabel.length) {
9148 // Roo.log(" label");
9153 //cls : 'input-group-addon',
9154 html : this.fieldLabel
9164 // Roo.log(" no label && no align");
9174 if (this.disabled) {
9175 input.disabled=true;
9182 * return the real textarea element.
9184 inputEl: function ()
9186 return this.el.select('textarea.form-control',true).first();
9190 * Clear any invalid styles/messages for this field
9192 clearInvalid : function()
9195 if(!this.el || this.preventMark){ // not rendered
9199 var label = this.el.select('label', true).first();
9200 var icon = this.el.select('i.fa-star', true).first();
9206 this.el.removeClass(this.invalidClass);
9208 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9210 var feedback = this.el.select('.form-control-feedback', true).first();
9213 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9218 this.fireEvent('valid', this);
9222 * Mark this field as valid
9224 markValid : function()
9226 if(!this.el || this.preventMark){ // not rendered
9230 this.el.removeClass([this.invalidClass, this.validClass]);
9232 var feedback = this.el.select('.form-control-feedback', true).first();
9235 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9238 if(this.disabled || this.allowBlank){
9242 var label = this.el.select('label', true).first();
9243 var icon = this.el.select('i.fa-star', true).first();
9249 this.el.addClass(this.validClass);
9251 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9253 var feedback = this.el.select('.form-control-feedback', true).first();
9256 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9257 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9262 this.fireEvent('valid', this);
9266 * Mark this field as invalid
9267 * @param {String} msg The validation message
9269 markInvalid : function(msg)
9271 if(!this.el || this.preventMark){ // not rendered
9275 this.el.removeClass([this.invalidClass, this.validClass]);
9277 var feedback = this.el.select('.form-control-feedback', true).first();
9280 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9283 if(this.disabled || this.allowBlank){
9287 var label = this.el.select('label', true).first();
9288 var icon = this.el.select('i.fa-star', true).first();
9290 if(!this.getValue().length && label && !icon){
9291 this.el.createChild({
9293 cls : 'text-danger fa fa-lg fa-star',
9294 tooltip : 'This field is required',
9295 style : 'margin-right:5px;'
9299 this.el.addClass(this.invalidClass);
9301 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9303 var feedback = this.el.select('.form-control-feedback', true).first();
9306 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9308 if(this.getValue().length || this.forceFeedback){
9309 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9316 this.fireEvent('invalid', this, msg);
9324 * trigger field - base class for combo..
9329 * @class Roo.bootstrap.TriggerField
9330 * @extends Roo.bootstrap.Input
9331 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9332 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9333 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9334 * for which you can provide a custom implementation. For example:
9336 var trigger = new Roo.bootstrap.TriggerField();
9337 trigger.onTriggerClick = myTriggerFn;
9338 trigger.applyTo('my-field');
9341 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9342 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9343 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9344 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9345 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9348 * Create a new TriggerField.
9349 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9350 * to the base TextField)
9352 Roo.bootstrap.TriggerField = function(config){
9353 this.mimicing = false;
9354 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9357 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9359 * @cfg {String} triggerClass A CSS class to apply to the trigger
9362 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9367 * @cfg {Boolean} removable (true|false) special filter default false
9371 /** @cfg {Boolean} grow @hide */
9372 /** @cfg {Number} growMin @hide */
9373 /** @cfg {Number} growMax @hide */
9379 autoSize: Roo.emptyFn,
9386 actionMode : 'wrap',
9391 getAutoCreate : function(){
9393 var align = this.labelAlign || this.parentLabelAlign();
9398 cls: 'form-group' //input-group
9405 type : this.inputType,
9406 cls : 'form-control',
9407 autocomplete: 'new-password',
9408 placeholder : this.placeholder || ''
9412 input.name = this.name;
9415 input.cls += ' input-' + this.size;
9418 if (this.disabled) {
9419 input.disabled=true;
9422 var inputblock = input;
9424 if(this.hasFeedback && !this.allowBlank){
9428 cls: 'glyphicon form-control-feedback'
9431 if(this.removable && !this.editable && !this.tickable){
9433 cls : 'has-feedback',
9439 cls : 'roo-combo-removable-btn close'
9446 cls : 'has-feedback',
9455 if(this.removable && !this.editable && !this.tickable){
9457 cls : 'roo-removable',
9463 cls : 'roo-combo-removable-btn close'
9470 if (this.before || this.after) {
9473 cls : 'input-group',
9477 inputblock.cn.push({
9479 cls : 'input-group-addon',
9484 inputblock.cn.push(input);
9486 if(this.hasFeedback && !this.allowBlank){
9487 inputblock.cls += ' has-feedback';
9488 inputblock.cn.push(feedback);
9492 inputblock.cn.push({
9494 cls : 'input-group-addon',
9507 cls: 'form-hidden-field'
9521 cls: 'form-hidden-field'
9525 cls: 'roo-select2-choices',
9529 cls: 'roo-select2-search-field',
9542 cls: 'roo-select2-container input-group',
9547 // cls: 'typeahead typeahead-long dropdown-menu',
9548 // style: 'display:none'
9553 if(!this.multiple && this.showToggleBtn){
9559 if (this.caret != false) {
9562 cls: 'fa fa-' + this.caret
9569 cls : 'input-group-addon btn dropdown-toggle',
9574 cls: 'combobox-clear',
9588 combobox.cls += ' roo-select2-container-multi';
9591 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9593 // Roo.log("left and has label");
9597 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9598 tooltip : 'This field is required'
9603 cls : 'control-label col-sm-' + this.labelWidth,
9604 html : this.fieldLabel
9608 cls : "col-sm-" + (12 - this.labelWidth),
9616 if(this.indicatorpos == 'right'){
9621 cls : 'control-label col-sm-' + this.labelWidth,
9622 html : this.fieldLabel
9627 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9628 tooltip : 'This field is required'
9631 cls : "col-sm-" + (12 - this.labelWidth),
9640 } else if ( this.fieldLabel.length) {
9641 // Roo.log(" label");
9645 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9646 tooltip : 'This field is required'
9650 //cls : 'input-group-addon',
9651 html : this.fieldLabel
9659 if(this.indicatorpos == 'right'){
9664 //cls : 'input-group-addon',
9665 html : this.fieldLabel
9670 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9671 tooltip : 'This field is required'
9682 // Roo.log(" no label && no align");
9689 ['xs','sm','md','lg'].map(function(size){
9690 if (settings[size]) {
9691 cfg.cls += ' col-' + size + '-' + settings[size];
9702 onResize : function(w, h){
9703 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9704 // if(typeof w == 'number'){
9705 // var x = w - this.trigger.getWidth();
9706 // this.inputEl().setWidth(this.adjustWidth('input', x));
9707 // this.trigger.setStyle('left', x+'px');
9712 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9715 getResizeEl : function(){
9716 return this.inputEl();
9720 getPositionEl : function(){
9721 return this.inputEl();
9725 alignErrorIcon : function(){
9726 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9730 initEvents : function(){
9734 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9735 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9736 if(!this.multiple && this.showToggleBtn){
9737 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9738 if(this.hideTrigger){
9739 this.trigger.setDisplayed(false);
9741 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9745 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9748 if(this.removable && !this.editable && !this.tickable){
9749 var close = this.closeTriggerEl();
9752 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9753 close.on('click', this.removeBtnClick, this, close);
9757 //this.trigger.addClassOnOver('x-form-trigger-over');
9758 //this.trigger.addClassOnClick('x-form-trigger-click');
9761 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9765 closeTriggerEl : function()
9767 var close = this.el.select('.roo-combo-removable-btn', true).first();
9768 return close ? close : false;
9771 removeBtnClick : function(e, h, el)
9775 if(this.fireEvent("remove", this) !== false){
9777 this.fireEvent("afterremove", this)
9781 createList : function()
9783 this.list = Roo.get(document.body).createChild({
9785 cls: 'typeahead typeahead-long dropdown-menu',
9786 style: 'display:none'
9789 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9794 initTrigger : function(){
9799 onDestroy : function(){
9801 this.trigger.removeAllListeners();
9802 // this.trigger.remove();
9805 // this.wrap.remove();
9807 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9811 onFocus : function(){
9812 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9815 this.wrap.addClass('x-trigger-wrap-focus');
9816 this.mimicing = true;
9817 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9818 if(this.monitorTab){
9819 this.el.on("keydown", this.checkTab, this);
9826 checkTab : function(e){
9827 if(e.getKey() == e.TAB){
9833 onBlur : function(){
9838 mimicBlur : function(e, t){
9840 if(!this.wrap.contains(t) && this.validateBlur()){
9847 triggerBlur : function(){
9848 this.mimicing = false;
9849 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9850 if(this.monitorTab){
9851 this.el.un("keydown", this.checkTab, this);
9853 //this.wrap.removeClass('x-trigger-wrap-focus');
9854 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9858 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9859 validateBlur : function(e, t){
9864 onDisable : function(){
9865 this.inputEl().dom.disabled = true;
9866 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9868 // this.wrap.addClass('x-item-disabled');
9873 onEnable : function(){
9874 this.inputEl().dom.disabled = false;
9875 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9877 // this.el.removeClass('x-item-disabled');
9882 onShow : function(){
9883 var ae = this.getActionEl();
9886 ae.dom.style.display = '';
9887 ae.dom.style.visibility = 'visible';
9893 onHide : function(){
9894 var ae = this.getActionEl();
9895 ae.dom.style.display = 'none';
9899 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9900 * by an implementing function.
9902 * @param {EventObject} e
9904 onTriggerClick : Roo.emptyFn
9908 * Ext JS Library 1.1.1
9909 * Copyright(c) 2006-2007, Ext JS, LLC.
9911 * Originally Released Under LGPL - original licence link has changed is not relivant.
9914 * <script type="text/javascript">
9919 * @class Roo.data.SortTypes
9921 * Defines the default sorting (casting?) comparison functions used when sorting data.
9923 Roo.data.SortTypes = {
9925 * Default sort that does nothing
9926 * @param {Mixed} s The value being converted
9927 * @return {Mixed} The comparison value
9934 * The regular expression used to strip tags
9938 stripTagsRE : /<\/?[^>]+>/gi,
9941 * Strips all HTML tags to sort on text only
9942 * @param {Mixed} s The value being converted
9943 * @return {String} The comparison value
9945 asText : function(s){
9946 return String(s).replace(this.stripTagsRE, "");
9950 * Strips all HTML tags to sort on text only - Case insensitive
9951 * @param {Mixed} s The value being converted
9952 * @return {String} The comparison value
9954 asUCText : function(s){
9955 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9959 * Case insensitive string
9960 * @param {Mixed} s The value being converted
9961 * @return {String} The comparison value
9963 asUCString : function(s) {
9964 return String(s).toUpperCase();
9969 * @param {Mixed} s The value being converted
9970 * @return {Number} The comparison value
9972 asDate : function(s) {
9976 if(s instanceof Date){
9979 return Date.parse(String(s));
9984 * @param {Mixed} s The value being converted
9985 * @return {Float} The comparison value
9987 asFloat : function(s) {
9988 var val = parseFloat(String(s).replace(/,/g, ""));
9997 * @param {Mixed} s The value being converted
9998 * @return {Number} The comparison value
10000 asInt : function(s) {
10001 var val = parseInt(String(s).replace(/,/g, ""));
10009 * Ext JS Library 1.1.1
10010 * Copyright(c) 2006-2007, Ext JS, LLC.
10012 * Originally Released Under LGPL - original licence link has changed is not relivant.
10015 * <script type="text/javascript">
10019 * @class Roo.data.Record
10020 * Instances of this class encapsulate both record <em>definition</em> information, and record
10021 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10022 * to access Records cached in an {@link Roo.data.Store} object.<br>
10024 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10025 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10028 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10030 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10031 * {@link #create}. The parameters are the same.
10032 * @param {Array} data An associative Array of data values keyed by the field name.
10033 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10034 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10035 * not specified an integer id is generated.
10037 Roo.data.Record = function(data, id){
10038 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10043 * Generate a constructor for a specific record layout.
10044 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10045 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10046 * Each field definition object may contain the following properties: <ul>
10047 * <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,
10048 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10049 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10050 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10051 * is being used, then this is a string containing the javascript expression to reference the data relative to
10052 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10053 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10054 * this may be omitted.</p></li>
10055 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10056 * <ul><li>auto (Default, implies no conversion)</li>
10061 * <li>date</li></ul></p></li>
10062 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10063 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10064 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10065 * by the Reader into an object that will be stored in the Record. It is passed the
10066 * following parameters:<ul>
10067 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10069 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10071 * <br>usage:<br><pre><code>
10072 var TopicRecord = Roo.data.Record.create(
10073 {name: 'title', mapping: 'topic_title'},
10074 {name: 'author', mapping: 'username'},
10075 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10076 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10077 {name: 'lastPoster', mapping: 'user2'},
10078 {name: 'excerpt', mapping: 'post_text'}
10081 var myNewRecord = new TopicRecord({
10082 title: 'Do my job please',
10085 lastPost: new Date(),
10086 lastPoster: 'Animal',
10087 excerpt: 'No way dude!'
10089 myStore.add(myNewRecord);
10094 Roo.data.Record.create = function(o){
10095 var f = function(){
10096 f.superclass.constructor.apply(this, arguments);
10098 Roo.extend(f, Roo.data.Record);
10099 var p = f.prototype;
10100 p.fields = new Roo.util.MixedCollection(false, function(field){
10103 for(var i = 0, len = o.length; i < len; i++){
10104 p.fields.add(new Roo.data.Field(o[i]));
10106 f.getField = function(name){
10107 return p.fields.get(name);
10112 Roo.data.Record.AUTO_ID = 1000;
10113 Roo.data.Record.EDIT = 'edit';
10114 Roo.data.Record.REJECT = 'reject';
10115 Roo.data.Record.COMMIT = 'commit';
10117 Roo.data.Record.prototype = {
10119 * Readonly flag - true if this record has been modified.
10128 join : function(store){
10129 this.store = store;
10133 * Set the named field to the specified value.
10134 * @param {String} name The name of the field to set.
10135 * @param {Object} value The value to set the field to.
10137 set : function(name, value){
10138 if(this.data[name] == value){
10142 if(!this.modified){
10143 this.modified = {};
10145 if(typeof this.modified[name] == 'undefined'){
10146 this.modified[name] = this.data[name];
10148 this.data[name] = value;
10149 if(!this.editing && this.store){
10150 this.store.afterEdit(this);
10155 * Get the value of the named field.
10156 * @param {String} name The name of the field to get the value of.
10157 * @return {Object} The value of the field.
10159 get : function(name){
10160 return this.data[name];
10164 beginEdit : function(){
10165 this.editing = true;
10166 this.modified = {};
10170 cancelEdit : function(){
10171 this.editing = false;
10172 delete this.modified;
10176 endEdit : function(){
10177 this.editing = false;
10178 if(this.dirty && this.store){
10179 this.store.afterEdit(this);
10184 * Usually called by the {@link Roo.data.Store} which owns the Record.
10185 * Rejects all changes made to the Record since either creation, or the last commit operation.
10186 * Modified fields are reverted to their original values.
10188 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10189 * of reject operations.
10191 reject : function(){
10192 var m = this.modified;
10194 if(typeof m[n] != "function"){
10195 this.data[n] = m[n];
10198 this.dirty = false;
10199 delete this.modified;
10200 this.editing = false;
10202 this.store.afterReject(this);
10207 * Usually called by the {@link Roo.data.Store} which owns the Record.
10208 * Commits all changes made to the Record since either creation, or the last commit operation.
10210 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10211 * of commit operations.
10213 commit : function(){
10214 this.dirty = false;
10215 delete this.modified;
10216 this.editing = false;
10218 this.store.afterCommit(this);
10223 hasError : function(){
10224 return this.error != null;
10228 clearError : function(){
10233 * Creates a copy of this record.
10234 * @param {String} id (optional) A new record id if you don't want to use this record's id
10237 copy : function(newId) {
10238 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10242 * Ext JS Library 1.1.1
10243 * Copyright(c) 2006-2007, Ext JS, LLC.
10245 * Originally Released Under LGPL - original licence link has changed is not relivant.
10248 * <script type="text/javascript">
10254 * @class Roo.data.Store
10255 * @extends Roo.util.Observable
10256 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10257 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10259 * 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
10260 * has no knowledge of the format of the data returned by the Proxy.<br>
10262 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10263 * instances from the data object. These records are cached and made available through accessor functions.
10265 * Creates a new Store.
10266 * @param {Object} config A config object containing the objects needed for the Store to access data,
10267 * and read the data into Records.
10269 Roo.data.Store = function(config){
10270 this.data = new Roo.util.MixedCollection(false);
10271 this.data.getKey = function(o){
10274 this.baseParams = {};
10276 this.paramNames = {
10281 "multisort" : "_multisort"
10284 if(config && config.data){
10285 this.inlineData = config.data;
10286 delete config.data;
10289 Roo.apply(this, config);
10291 if(this.reader){ // reader passed
10292 this.reader = Roo.factory(this.reader, Roo.data);
10293 this.reader.xmodule = this.xmodule || false;
10294 if(!this.recordType){
10295 this.recordType = this.reader.recordType;
10297 if(this.reader.onMetaChange){
10298 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10302 if(this.recordType){
10303 this.fields = this.recordType.prototype.fields;
10305 this.modified = [];
10309 * @event datachanged
10310 * Fires when the data cache has changed, and a widget which is using this Store
10311 * as a Record cache should refresh its view.
10312 * @param {Store} this
10314 datachanged : true,
10316 * @event metachange
10317 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10318 * @param {Store} this
10319 * @param {Object} meta The JSON metadata
10324 * Fires when Records have been added to the Store
10325 * @param {Store} this
10326 * @param {Roo.data.Record[]} records The array of Records added
10327 * @param {Number} index The index at which the record(s) were added
10332 * Fires when a Record has been removed from the Store
10333 * @param {Store} this
10334 * @param {Roo.data.Record} record The Record that was removed
10335 * @param {Number} index The index at which the record was removed
10340 * Fires when a Record has been updated
10341 * @param {Store} this
10342 * @param {Roo.data.Record} record The Record that was updated
10343 * @param {String} operation The update operation being performed. Value may be one of:
10345 Roo.data.Record.EDIT
10346 Roo.data.Record.REJECT
10347 Roo.data.Record.COMMIT
10353 * Fires when the data cache has been cleared.
10354 * @param {Store} this
10358 * @event beforeload
10359 * Fires before a request is made for a new data object. If the beforeload handler returns false
10360 * the load action will be canceled.
10361 * @param {Store} this
10362 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10366 * @event beforeloadadd
10367 * Fires after a new set of Records has been loaded.
10368 * @param {Store} this
10369 * @param {Roo.data.Record[]} records The Records that were loaded
10370 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10372 beforeloadadd : true,
10375 * Fires after a new set of Records has been loaded, before they are added to the store.
10376 * @param {Store} this
10377 * @param {Roo.data.Record[]} records The Records that were loaded
10378 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10379 * @params {Object} return from reader
10383 * @event loadexception
10384 * Fires if an exception occurs in the Proxy during loading.
10385 * Called with the signature of the Proxy's "loadexception" event.
10386 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10389 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10390 * @param {Object} load options
10391 * @param {Object} jsonData from your request (normally this contains the Exception)
10393 loadexception : true
10397 this.proxy = Roo.factory(this.proxy, Roo.data);
10398 this.proxy.xmodule = this.xmodule || false;
10399 this.relayEvents(this.proxy, ["loadexception"]);
10401 this.sortToggle = {};
10402 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10404 Roo.data.Store.superclass.constructor.call(this);
10406 if(this.inlineData){
10407 this.loadData(this.inlineData);
10408 delete this.inlineData;
10412 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10414 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10415 * without a remote query - used by combo/forms at present.
10419 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10422 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10425 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10426 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10429 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10430 * on any HTTP request
10433 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10436 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10440 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10441 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10443 remoteSort : false,
10446 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10447 * loaded or when a record is removed. (defaults to false).
10449 pruneModifiedRecords : false,
10452 lastOptions : null,
10455 * Add Records to the Store and fires the add event.
10456 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10458 add : function(records){
10459 records = [].concat(records);
10460 for(var i = 0, len = records.length; i < len; i++){
10461 records[i].join(this);
10463 var index = this.data.length;
10464 this.data.addAll(records);
10465 this.fireEvent("add", this, records, index);
10469 * Remove a Record from the Store and fires the remove event.
10470 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10472 remove : function(record){
10473 var index = this.data.indexOf(record);
10474 this.data.removeAt(index);
10475 if(this.pruneModifiedRecords){
10476 this.modified.remove(record);
10478 this.fireEvent("remove", this, record, index);
10482 * Remove all Records from the Store and fires the clear event.
10484 removeAll : function(){
10486 if(this.pruneModifiedRecords){
10487 this.modified = [];
10489 this.fireEvent("clear", this);
10493 * Inserts Records to the Store at the given index and fires the add event.
10494 * @param {Number} index The start index at which to insert the passed Records.
10495 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10497 insert : function(index, records){
10498 records = [].concat(records);
10499 for(var i = 0, len = records.length; i < len; i++){
10500 this.data.insert(index, records[i]);
10501 records[i].join(this);
10503 this.fireEvent("add", this, records, index);
10507 * Get the index within the cache of the passed Record.
10508 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10509 * @return {Number} The index of the passed Record. Returns -1 if not found.
10511 indexOf : function(record){
10512 return this.data.indexOf(record);
10516 * Get the index within the cache of the Record with the passed id.
10517 * @param {String} id The id of the Record to find.
10518 * @return {Number} The index of the Record. Returns -1 if not found.
10520 indexOfId : function(id){
10521 return this.data.indexOfKey(id);
10525 * Get the Record with the specified id.
10526 * @param {String} id The id of the Record to find.
10527 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10529 getById : function(id){
10530 return this.data.key(id);
10534 * Get the Record at the specified index.
10535 * @param {Number} index The index of the Record to find.
10536 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10538 getAt : function(index){
10539 return this.data.itemAt(index);
10543 * Returns a range of Records between specified indices.
10544 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10545 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10546 * @return {Roo.data.Record[]} An array of Records
10548 getRange : function(start, end){
10549 return this.data.getRange(start, end);
10553 storeOptions : function(o){
10554 o = Roo.apply({}, o);
10557 this.lastOptions = o;
10561 * Loads the Record cache from the configured Proxy using the configured Reader.
10563 * If using remote paging, then the first load call must specify the <em>start</em>
10564 * and <em>limit</em> properties in the options.params property to establish the initial
10565 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10567 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10568 * and this call will return before the new data has been loaded. Perform any post-processing
10569 * in a callback function, or in a "load" event handler.</strong>
10571 * @param {Object} options An object containing properties which control loading options:<ul>
10572 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10573 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10574 * passed the following arguments:<ul>
10575 * <li>r : Roo.data.Record[]</li>
10576 * <li>options: Options object from the load call</li>
10577 * <li>success: Boolean success indicator</li></ul></li>
10578 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10579 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10582 load : function(options){
10583 options = options || {};
10584 if(this.fireEvent("beforeload", this, options) !== false){
10585 this.storeOptions(options);
10586 var p = Roo.apply(options.params || {}, this.baseParams);
10587 // if meta was not loaded from remote source.. try requesting it.
10588 if (!this.reader.metaFromRemote) {
10589 p._requestMeta = 1;
10591 if(this.sortInfo && this.remoteSort){
10592 var pn = this.paramNames;
10593 p[pn["sort"]] = this.sortInfo.field;
10594 p[pn["dir"]] = this.sortInfo.direction;
10596 if (this.multiSort) {
10597 var pn = this.paramNames;
10598 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10601 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10606 * Reloads the Record cache from the configured Proxy using the configured Reader and
10607 * the options from the last load operation performed.
10608 * @param {Object} options (optional) An object containing properties which may override the options
10609 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10610 * the most recently used options are reused).
10612 reload : function(options){
10613 this.load(Roo.applyIf(options||{}, this.lastOptions));
10617 // Called as a callback by the Reader during a load operation.
10618 loadRecords : function(o, options, success){
10619 if(!o || success === false){
10620 if(success !== false){
10621 this.fireEvent("load", this, [], options, o);
10623 if(options.callback){
10624 options.callback.call(options.scope || this, [], options, false);
10628 // if data returned failure - throw an exception.
10629 if (o.success === false) {
10630 // show a message if no listener is registered.
10631 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10632 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10634 // loadmask wil be hooked into this..
10635 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10638 var r = o.records, t = o.totalRecords || r.length;
10640 this.fireEvent("beforeloadadd", this, r, options, o);
10642 if(!options || options.add !== true){
10643 if(this.pruneModifiedRecords){
10644 this.modified = [];
10646 for(var i = 0, len = r.length; i < len; i++){
10650 this.data = this.snapshot;
10651 delete this.snapshot;
10654 this.data.addAll(r);
10655 this.totalLength = t;
10657 this.fireEvent("datachanged", this);
10659 this.totalLength = Math.max(t, this.data.length+r.length);
10662 this.fireEvent("load", this, r, options, o);
10663 if(options.callback){
10664 options.callback.call(options.scope || this, r, options, true);
10670 * Loads data from a passed data block. A Reader which understands the format of the data
10671 * must have been configured in the constructor.
10672 * @param {Object} data The data block from which to read the Records. The format of the data expected
10673 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10674 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10676 loadData : function(o, append){
10677 var r = this.reader.readRecords(o);
10678 this.loadRecords(r, {add: append}, true);
10682 * Gets the number of cached records.
10684 * <em>If using paging, this may not be the total size of the dataset. If the data object
10685 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10686 * the data set size</em>
10688 getCount : function(){
10689 return this.data.length || 0;
10693 * Gets the total number of records in the dataset as returned by the server.
10695 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10696 * the dataset size</em>
10698 getTotalCount : function(){
10699 return this.totalLength || 0;
10703 * Returns the sort state of the Store as an object with two properties:
10705 field {String} The name of the field by which the Records are sorted
10706 direction {String} The sort order, "ASC" or "DESC"
10709 getSortState : function(){
10710 return this.sortInfo;
10714 applySort : function(){
10715 if(this.sortInfo && !this.remoteSort){
10716 var s = this.sortInfo, f = s.field;
10717 var st = this.fields.get(f).sortType;
10718 var fn = function(r1, r2){
10719 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10720 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10722 this.data.sort(s.direction, fn);
10723 if(this.snapshot && this.snapshot != this.data){
10724 this.snapshot.sort(s.direction, fn);
10730 * Sets the default sort column and order to be used by the next load operation.
10731 * @param {String} fieldName The name of the field to sort by.
10732 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10734 setDefaultSort : function(field, dir){
10735 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10739 * Sort the Records.
10740 * If remote sorting is used, the sort is performed on the server, and the cache is
10741 * reloaded. If local sorting is used, the cache is sorted internally.
10742 * @param {String} fieldName The name of the field to sort by.
10743 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10745 sort : function(fieldName, dir){
10746 var f = this.fields.get(fieldName);
10748 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10750 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10751 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10756 this.sortToggle[f.name] = dir;
10757 this.sortInfo = {field: f.name, direction: dir};
10758 if(!this.remoteSort){
10760 this.fireEvent("datachanged", this);
10762 this.load(this.lastOptions);
10767 * Calls the specified function for each of the Records in the cache.
10768 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10769 * Returning <em>false</em> aborts and exits the iteration.
10770 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10772 each : function(fn, scope){
10773 this.data.each(fn, scope);
10777 * Gets all records modified since the last commit. Modified records are persisted across load operations
10778 * (e.g., during paging).
10779 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10781 getModifiedRecords : function(){
10782 return this.modified;
10786 createFilterFn : function(property, value, anyMatch){
10787 if(!value.exec){ // not a regex
10788 value = String(value);
10789 if(value.length == 0){
10792 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10794 return function(r){
10795 return value.test(r.data[property]);
10800 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10801 * @param {String} property A field on your records
10802 * @param {Number} start The record index to start at (defaults to 0)
10803 * @param {Number} end The last record index to include (defaults to length - 1)
10804 * @return {Number} The sum
10806 sum : function(property, start, end){
10807 var rs = this.data.items, v = 0;
10808 start = start || 0;
10809 end = (end || end === 0) ? end : rs.length-1;
10811 for(var i = start; i <= end; i++){
10812 v += (rs[i].data[property] || 0);
10818 * Filter the records by a specified property.
10819 * @param {String} field A field on your records
10820 * @param {String/RegExp} value Either a string that the field
10821 * should start with or a RegExp to test against the field
10822 * @param {Boolean} anyMatch True to match any part not just the beginning
10824 filter : function(property, value, anyMatch){
10825 var fn = this.createFilterFn(property, value, anyMatch);
10826 return fn ? this.filterBy(fn) : this.clearFilter();
10830 * Filter by a function. The specified function will be called with each
10831 * record in this data source. If the function returns true the record is included,
10832 * otherwise it is filtered.
10833 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10834 * @param {Object} scope (optional) The scope of the function (defaults to this)
10836 filterBy : function(fn, scope){
10837 this.snapshot = this.snapshot || this.data;
10838 this.data = this.queryBy(fn, scope||this);
10839 this.fireEvent("datachanged", this);
10843 * Query the records by a specified property.
10844 * @param {String} field A field on your records
10845 * @param {String/RegExp} value Either a string that the field
10846 * should start with or a RegExp to test against the field
10847 * @param {Boolean} anyMatch True to match any part not just the beginning
10848 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10850 query : function(property, value, anyMatch){
10851 var fn = this.createFilterFn(property, value, anyMatch);
10852 return fn ? this.queryBy(fn) : this.data.clone();
10856 * Query by a function. The specified function will be called with each
10857 * record in this data source. If the function returns true the record is included
10859 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10860 * @param {Object} scope (optional) The scope of the function (defaults to this)
10861 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10863 queryBy : function(fn, scope){
10864 var data = this.snapshot || this.data;
10865 return data.filterBy(fn, scope||this);
10869 * Collects unique values for a particular dataIndex from this store.
10870 * @param {String} dataIndex The property to collect
10871 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10872 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10873 * @return {Array} An array of the unique values
10875 collect : function(dataIndex, allowNull, bypassFilter){
10876 var d = (bypassFilter === true && this.snapshot) ?
10877 this.snapshot.items : this.data.items;
10878 var v, sv, r = [], l = {};
10879 for(var i = 0, len = d.length; i < len; i++){
10880 v = d[i].data[dataIndex];
10882 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10891 * Revert to a view of the Record cache with no filtering applied.
10892 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10894 clearFilter : function(suppressEvent){
10895 if(this.snapshot && this.snapshot != this.data){
10896 this.data = this.snapshot;
10897 delete this.snapshot;
10898 if(suppressEvent !== true){
10899 this.fireEvent("datachanged", this);
10905 afterEdit : function(record){
10906 if(this.modified.indexOf(record) == -1){
10907 this.modified.push(record);
10909 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10913 afterReject : function(record){
10914 this.modified.remove(record);
10915 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10919 afterCommit : function(record){
10920 this.modified.remove(record);
10921 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10925 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10926 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10928 commitChanges : function(){
10929 var m = this.modified.slice(0);
10930 this.modified = [];
10931 for(var i = 0, len = m.length; i < len; i++){
10937 * Cancel outstanding changes on all changed records.
10939 rejectChanges : function(){
10940 var m = this.modified.slice(0);
10941 this.modified = [];
10942 for(var i = 0, len = m.length; i < len; i++){
10947 onMetaChange : function(meta, rtype, o){
10948 this.recordType = rtype;
10949 this.fields = rtype.prototype.fields;
10950 delete this.snapshot;
10951 this.sortInfo = meta.sortInfo || this.sortInfo;
10952 this.modified = [];
10953 this.fireEvent('metachange', this, this.reader.meta);
10956 moveIndex : function(data, type)
10958 var index = this.indexOf(data);
10960 var newIndex = index + type;
10964 this.insert(newIndex, data);
10969 * Ext JS Library 1.1.1
10970 * Copyright(c) 2006-2007, Ext JS, LLC.
10972 * Originally Released Under LGPL - original licence link has changed is not relivant.
10975 * <script type="text/javascript">
10979 * @class Roo.data.SimpleStore
10980 * @extends Roo.data.Store
10981 * Small helper class to make creating Stores from Array data easier.
10982 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10983 * @cfg {Array} fields An array of field definition objects, or field name strings.
10984 * @cfg {Array} data The multi-dimensional array of data
10986 * @param {Object} config
10988 Roo.data.SimpleStore = function(config){
10989 Roo.data.SimpleStore.superclass.constructor.call(this, {
10991 reader: new Roo.data.ArrayReader({
10994 Roo.data.Record.create(config.fields)
10996 proxy : new Roo.data.MemoryProxy(config.data)
11000 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11002 * Ext JS Library 1.1.1
11003 * Copyright(c) 2006-2007, Ext JS, LLC.
11005 * Originally Released Under LGPL - original licence link has changed is not relivant.
11008 * <script type="text/javascript">
11013 * @extends Roo.data.Store
11014 * @class Roo.data.JsonStore
11015 * Small helper class to make creating Stores for JSON data easier. <br/>
11017 var store = new Roo.data.JsonStore({
11018 url: 'get-images.php',
11020 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11023 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11024 * JsonReader and HttpProxy (unless inline data is provided).</b>
11025 * @cfg {Array} fields An array of field definition objects, or field name strings.
11027 * @param {Object} config
11029 Roo.data.JsonStore = function(c){
11030 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11031 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11032 reader: new Roo.data.JsonReader(c, c.fields)
11035 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11037 * Ext JS Library 1.1.1
11038 * Copyright(c) 2006-2007, Ext JS, LLC.
11040 * Originally Released Under LGPL - original licence link has changed is not relivant.
11043 * <script type="text/javascript">
11047 Roo.data.Field = function(config){
11048 if(typeof config == "string"){
11049 config = {name: config};
11051 Roo.apply(this, config);
11054 this.type = "auto";
11057 var st = Roo.data.SortTypes;
11058 // named sortTypes are supported, here we look them up
11059 if(typeof this.sortType == "string"){
11060 this.sortType = st[this.sortType];
11063 // set default sortType for strings and dates
11064 if(!this.sortType){
11067 this.sortType = st.asUCString;
11070 this.sortType = st.asDate;
11073 this.sortType = st.none;
11078 var stripRe = /[\$,%]/g;
11080 // prebuilt conversion function for this field, instead of
11081 // switching every time we're reading a value
11083 var cv, dateFormat = this.dateFormat;
11088 cv = function(v){ return v; };
11091 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11095 return v !== undefined && v !== null && v !== '' ?
11096 parseInt(String(v).replace(stripRe, ""), 10) : '';
11101 return v !== undefined && v !== null && v !== '' ?
11102 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11107 cv = function(v){ return v === true || v === "true" || v == 1; };
11114 if(v instanceof Date){
11118 if(dateFormat == "timestamp"){
11119 return new Date(v*1000);
11121 return Date.parseDate(v, dateFormat);
11123 var parsed = Date.parse(v);
11124 return parsed ? new Date(parsed) : null;
11133 Roo.data.Field.prototype = {
11141 * Ext JS Library 1.1.1
11142 * Copyright(c) 2006-2007, Ext JS, LLC.
11144 * Originally Released Under LGPL - original licence link has changed is not relivant.
11147 * <script type="text/javascript">
11150 // Base class for reading structured data from a data source. This class is intended to be
11151 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11154 * @class Roo.data.DataReader
11155 * Base class for reading structured data from a data source. This class is intended to be
11156 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11159 Roo.data.DataReader = function(meta, recordType){
11163 this.recordType = recordType instanceof Array ?
11164 Roo.data.Record.create(recordType) : recordType;
11167 Roo.data.DataReader.prototype = {
11169 * Create an empty record
11170 * @param {Object} data (optional) - overlay some values
11171 * @return {Roo.data.Record} record created.
11173 newRow : function(d) {
11175 this.recordType.prototype.fields.each(function(c) {
11177 case 'int' : da[c.name] = 0; break;
11178 case 'date' : da[c.name] = new Date(); break;
11179 case 'float' : da[c.name] = 0.0; break;
11180 case 'boolean' : da[c.name] = false; break;
11181 default : da[c.name] = ""; break;
11185 return new this.recordType(Roo.apply(da, d));
11190 * Ext JS Library 1.1.1
11191 * Copyright(c) 2006-2007, Ext JS, LLC.
11193 * Originally Released Under LGPL - original licence link has changed is not relivant.
11196 * <script type="text/javascript">
11200 * @class Roo.data.DataProxy
11201 * @extends Roo.data.Observable
11202 * This class is an abstract base class for implementations which provide retrieval of
11203 * unformatted data objects.<br>
11205 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11206 * (of the appropriate type which knows how to parse the data object) to provide a block of
11207 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11209 * Custom implementations must implement the load method as described in
11210 * {@link Roo.data.HttpProxy#load}.
11212 Roo.data.DataProxy = function(){
11215 * @event beforeload
11216 * Fires before a network request is made to retrieve a data object.
11217 * @param {Object} This DataProxy object.
11218 * @param {Object} params The params parameter to the load function.
11223 * Fires before the load method's callback is called.
11224 * @param {Object} This DataProxy object.
11225 * @param {Object} o The data object.
11226 * @param {Object} arg The callback argument object passed to the load function.
11230 * @event loadexception
11231 * Fires if an Exception occurs during data retrieval.
11232 * @param {Object} This DataProxy object.
11233 * @param {Object} o The data object.
11234 * @param {Object} arg The callback argument object passed to the load function.
11235 * @param {Object} e The Exception.
11237 loadexception : true
11239 Roo.data.DataProxy.superclass.constructor.call(this);
11242 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11245 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11249 * Ext JS Library 1.1.1
11250 * Copyright(c) 2006-2007, Ext JS, LLC.
11252 * Originally Released Under LGPL - original licence link has changed is not relivant.
11255 * <script type="text/javascript">
11258 * @class Roo.data.MemoryProxy
11259 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11260 * to the Reader when its load method is called.
11262 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11264 Roo.data.MemoryProxy = function(data){
11268 Roo.data.MemoryProxy.superclass.constructor.call(this);
11272 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11275 * Load data from the requested source (in this case an in-memory
11276 * data object passed to the constructor), read the data object into
11277 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11278 * process that block using the passed callback.
11279 * @param {Object} params This parameter is not used by the MemoryProxy class.
11280 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11281 * object into a block of Roo.data.Records.
11282 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11283 * The function must be passed <ul>
11284 * <li>The Record block object</li>
11285 * <li>The "arg" argument from the load function</li>
11286 * <li>A boolean success indicator</li>
11288 * @param {Object} scope The scope in which to call the callback
11289 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11291 load : function(params, reader, callback, scope, arg){
11292 params = params || {};
11295 result = reader.readRecords(this.data);
11297 this.fireEvent("loadexception", this, arg, null, e);
11298 callback.call(scope, null, arg, false);
11301 callback.call(scope, result, arg, true);
11305 update : function(params, records){
11310 * Ext JS Library 1.1.1
11311 * Copyright(c) 2006-2007, Ext JS, LLC.
11313 * Originally Released Under LGPL - original licence link has changed is not relivant.
11316 * <script type="text/javascript">
11319 * @class Roo.data.HttpProxy
11320 * @extends Roo.data.DataProxy
11321 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11322 * configured to reference a certain URL.<br><br>
11324 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11325 * from which the running page was served.<br><br>
11327 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11329 * Be aware that to enable the browser to parse an XML document, the server must set
11330 * the Content-Type header in the HTTP response to "text/xml".
11332 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11333 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11334 * will be used to make the request.
11336 Roo.data.HttpProxy = function(conn){
11337 Roo.data.HttpProxy.superclass.constructor.call(this);
11338 // is conn a conn config or a real conn?
11340 this.useAjax = !conn || !conn.events;
11344 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11345 // thse are take from connection...
11348 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11351 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11352 * extra parameters to each request made by this object. (defaults to undefined)
11355 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11356 * to each request made by this object. (defaults to undefined)
11359 * @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)
11362 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11365 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11371 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11375 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11376 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11377 * a finer-grained basis than the DataProxy events.
11379 getConnection : function(){
11380 return this.useAjax ? Roo.Ajax : this.conn;
11384 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11385 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11386 * process that block using the passed callback.
11387 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11388 * for the request to the remote server.
11389 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11390 * object into a block of Roo.data.Records.
11391 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11392 * The function must be passed <ul>
11393 * <li>The Record block object</li>
11394 * <li>The "arg" argument from the load function</li>
11395 * <li>A boolean success indicator</li>
11397 * @param {Object} scope The scope in which to call the callback
11398 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11400 load : function(params, reader, callback, scope, arg){
11401 if(this.fireEvent("beforeload", this, params) !== false){
11403 params : params || {},
11405 callback : callback,
11410 callback : this.loadResponse,
11414 Roo.applyIf(o, this.conn);
11415 if(this.activeRequest){
11416 Roo.Ajax.abort(this.activeRequest);
11418 this.activeRequest = Roo.Ajax.request(o);
11420 this.conn.request(o);
11423 callback.call(scope||this, null, arg, false);
11428 loadResponse : function(o, success, response){
11429 delete this.activeRequest;
11431 this.fireEvent("loadexception", this, o, response);
11432 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11437 result = o.reader.read(response);
11439 this.fireEvent("loadexception", this, o, response, e);
11440 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11444 this.fireEvent("load", this, o, o.request.arg);
11445 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11449 update : function(dataSet){
11454 updateResponse : function(dataSet){
11459 * Ext JS Library 1.1.1
11460 * Copyright(c) 2006-2007, Ext JS, LLC.
11462 * Originally Released Under LGPL - original licence link has changed is not relivant.
11465 * <script type="text/javascript">
11469 * @class Roo.data.ScriptTagProxy
11470 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11471 * other than the originating domain of the running page.<br><br>
11473 * <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
11474 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11476 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11477 * source code that is used as the source inside a <script> tag.<br><br>
11479 * In order for the browser to process the returned data, the server must wrap the data object
11480 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11481 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11482 * depending on whether the callback name was passed:
11485 boolean scriptTag = false;
11486 String cb = request.getParameter("callback");
11489 response.setContentType("text/javascript");
11491 response.setContentType("application/x-json");
11493 Writer out = response.getWriter();
11495 out.write(cb + "(");
11497 out.print(dataBlock.toJsonString());
11504 * @param {Object} config A configuration object.
11506 Roo.data.ScriptTagProxy = function(config){
11507 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11508 Roo.apply(this, config);
11509 this.head = document.getElementsByTagName("head")[0];
11512 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11514 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11516 * @cfg {String} url The URL from which to request the data object.
11519 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11523 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11524 * the server the name of the callback function set up by the load call to process the returned data object.
11525 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11526 * javascript output which calls this named function passing the data object as its only parameter.
11528 callbackParam : "callback",
11530 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11531 * name to the request.
11536 * Load data from the configured URL, read the data object into
11537 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11538 * process that block using the passed callback.
11539 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11540 * for the request to the remote server.
11541 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11542 * object into a block of Roo.data.Records.
11543 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11544 * The function must be passed <ul>
11545 * <li>The Record block object</li>
11546 * <li>The "arg" argument from the load function</li>
11547 * <li>A boolean success indicator</li>
11549 * @param {Object} scope The scope in which to call the callback
11550 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11552 load : function(params, reader, callback, scope, arg){
11553 if(this.fireEvent("beforeload", this, params) !== false){
11555 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11557 var url = this.url;
11558 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11560 url += "&_dc=" + (new Date().getTime());
11562 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11565 cb : "stcCallback"+transId,
11566 scriptId : "stcScript"+transId,
11570 callback : callback,
11576 window[trans.cb] = function(o){
11577 conn.handleResponse(o, trans);
11580 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11582 if(this.autoAbort !== false){
11586 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11588 var script = document.createElement("script");
11589 script.setAttribute("src", url);
11590 script.setAttribute("type", "text/javascript");
11591 script.setAttribute("id", trans.scriptId);
11592 this.head.appendChild(script);
11594 this.trans = trans;
11596 callback.call(scope||this, null, arg, false);
11601 isLoading : function(){
11602 return this.trans ? true : false;
11606 * Abort the current server request.
11608 abort : function(){
11609 if(this.isLoading()){
11610 this.destroyTrans(this.trans);
11615 destroyTrans : function(trans, isLoaded){
11616 this.head.removeChild(document.getElementById(trans.scriptId));
11617 clearTimeout(trans.timeoutId);
11619 window[trans.cb] = undefined;
11621 delete window[trans.cb];
11624 // if hasn't been loaded, wait for load to remove it to prevent script error
11625 window[trans.cb] = function(){
11626 window[trans.cb] = undefined;
11628 delete window[trans.cb];
11635 handleResponse : function(o, trans){
11636 this.trans = false;
11637 this.destroyTrans(trans, true);
11640 result = trans.reader.readRecords(o);
11642 this.fireEvent("loadexception", this, o, trans.arg, e);
11643 trans.callback.call(trans.scope||window, null, trans.arg, false);
11646 this.fireEvent("load", this, o, trans.arg);
11647 trans.callback.call(trans.scope||window, result, trans.arg, true);
11651 handleFailure : function(trans){
11652 this.trans = false;
11653 this.destroyTrans(trans, false);
11654 this.fireEvent("loadexception", this, null, trans.arg);
11655 trans.callback.call(trans.scope||window, null, trans.arg, false);
11659 * Ext JS Library 1.1.1
11660 * Copyright(c) 2006-2007, Ext JS, LLC.
11662 * Originally Released Under LGPL - original licence link has changed is not relivant.
11665 * <script type="text/javascript">
11669 * @class Roo.data.JsonReader
11670 * @extends Roo.data.DataReader
11671 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11672 * based on mappings in a provided Roo.data.Record constructor.
11674 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11675 * in the reply previously.
11680 var RecordDef = Roo.data.Record.create([
11681 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11682 {name: 'occupation'} // This field will use "occupation" as the mapping.
11684 var myReader = new Roo.data.JsonReader({
11685 totalProperty: "results", // The property which contains the total dataset size (optional)
11686 root: "rows", // The property which contains an Array of row objects
11687 id: "id" // The property within each row object that provides an ID for the record (optional)
11691 * This would consume a JSON file like this:
11693 { 'results': 2, 'rows': [
11694 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11695 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11698 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11699 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11700 * paged from the remote server.
11701 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11702 * @cfg {String} root name of the property which contains the Array of row objects.
11703 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11704 * @cfg {Array} fields Array of field definition objects
11706 * Create a new JsonReader
11707 * @param {Object} meta Metadata configuration options
11708 * @param {Object} recordType Either an Array of field definition objects,
11709 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11711 Roo.data.JsonReader = function(meta, recordType){
11714 // set some defaults:
11715 Roo.applyIf(meta, {
11716 totalProperty: 'total',
11717 successProperty : 'success',
11722 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11724 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11727 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11728 * Used by Store query builder to append _requestMeta to params.
11731 metaFromRemote : false,
11733 * This method is only used by a DataProxy which has retrieved data from a remote server.
11734 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11735 * @return {Object} data A data block which is used by an Roo.data.Store object as
11736 * a cache of Roo.data.Records.
11738 read : function(response){
11739 var json = response.responseText;
11741 var o = /* eval:var:o */ eval("("+json+")");
11743 throw {message: "JsonReader.read: Json object not found"};
11749 this.metaFromRemote = true;
11750 this.meta = o.metaData;
11751 this.recordType = Roo.data.Record.create(o.metaData.fields);
11752 this.onMetaChange(this.meta, this.recordType, o);
11754 return this.readRecords(o);
11757 // private function a store will implement
11758 onMetaChange : function(meta, recordType, o){
11765 simpleAccess: function(obj, subsc) {
11772 getJsonAccessor: function(){
11774 return function(expr) {
11776 return(re.test(expr))
11777 ? new Function("obj", "return obj." + expr)
11782 return Roo.emptyFn;
11787 * Create a data block containing Roo.data.Records from an XML document.
11788 * @param {Object} o An object which contains an Array of row objects in the property specified
11789 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11790 * which contains the total size of the dataset.
11791 * @return {Object} data A data block which is used by an Roo.data.Store object as
11792 * a cache of Roo.data.Records.
11794 readRecords : function(o){
11796 * After any data loads, the raw JSON data is available for further custom processing.
11800 var s = this.meta, Record = this.recordType,
11801 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11803 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11805 if(s.totalProperty) {
11806 this.getTotal = this.getJsonAccessor(s.totalProperty);
11808 if(s.successProperty) {
11809 this.getSuccess = this.getJsonAccessor(s.successProperty);
11811 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11813 var g = this.getJsonAccessor(s.id);
11814 this.getId = function(rec) {
11816 return (r === undefined || r === "") ? null : r;
11819 this.getId = function(){return null;};
11822 for(var jj = 0; jj < fl; jj++){
11824 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11825 this.ef[jj] = this.getJsonAccessor(map);
11829 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11830 if(s.totalProperty){
11831 var vt = parseInt(this.getTotal(o), 10);
11836 if(s.successProperty){
11837 var vs = this.getSuccess(o);
11838 if(vs === false || vs === 'false'){
11843 for(var i = 0; i < c; i++){
11846 var id = this.getId(n);
11847 for(var j = 0; j < fl; j++){
11849 var v = this.ef[j](n);
11851 Roo.log('missing convert for ' + f.name);
11855 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11857 var record = new Record(values, id);
11859 records[i] = record;
11865 totalRecords : totalRecords
11870 * Ext JS Library 1.1.1
11871 * Copyright(c) 2006-2007, Ext JS, LLC.
11873 * Originally Released Under LGPL - original licence link has changed is not relivant.
11876 * <script type="text/javascript">
11880 * @class Roo.data.ArrayReader
11881 * @extends Roo.data.DataReader
11882 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11883 * Each element of that Array represents a row of data fields. The
11884 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11885 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11889 var RecordDef = Roo.data.Record.create([
11890 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11891 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11893 var myReader = new Roo.data.ArrayReader({
11894 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11898 * This would consume an Array like this:
11900 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11902 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11904 * Create a new JsonReader
11905 * @param {Object} meta Metadata configuration options.
11906 * @param {Object} recordType Either an Array of field definition objects
11907 * as specified to {@link Roo.data.Record#create},
11908 * or an {@link Roo.data.Record} object
11909 * created using {@link Roo.data.Record#create}.
11911 Roo.data.ArrayReader = function(meta, recordType){
11912 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11915 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11917 * Create a data block containing Roo.data.Records from an XML document.
11918 * @param {Object} o An Array of row objects which represents the dataset.
11919 * @return {Object} data A data block which is used by an Roo.data.Store object as
11920 * a cache of Roo.data.Records.
11922 readRecords : function(o){
11923 var sid = this.meta ? this.meta.id : null;
11924 var recordType = this.recordType, fields = recordType.prototype.fields;
11927 for(var i = 0; i < root.length; i++){
11930 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11931 for(var j = 0, jlen = fields.length; j < jlen; j++){
11932 var f = fields.items[j];
11933 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11934 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11936 values[f.name] = v;
11938 var record = new recordType(values, id);
11940 records[records.length] = record;
11944 totalRecords : records.length
11953 * @class Roo.bootstrap.ComboBox
11954 * @extends Roo.bootstrap.TriggerField
11955 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11956 * @cfg {Boolean} append (true|false) default false
11957 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11958 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11959 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11960 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11961 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11962 * @cfg {Boolean} animate default true
11963 * @cfg {Boolean} emptyResultText only for touch device
11964 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11966 * Create a new ComboBox.
11967 * @param {Object} config Configuration options
11969 Roo.bootstrap.ComboBox = function(config){
11970 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11974 * Fires when the dropdown list is expanded
11975 * @param {Roo.bootstrap.ComboBox} combo This combo box
11980 * Fires when the dropdown list is collapsed
11981 * @param {Roo.bootstrap.ComboBox} combo This combo box
11985 * @event beforeselect
11986 * Fires before a list item is selected. Return false to cancel the selection.
11987 * @param {Roo.bootstrap.ComboBox} combo This combo box
11988 * @param {Roo.data.Record} record The data record returned from the underlying store
11989 * @param {Number} index The index of the selected item in the dropdown list
11991 'beforeselect' : true,
11994 * Fires when a list item is selected
11995 * @param {Roo.bootstrap.ComboBox} combo This combo box
11996 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11997 * @param {Number} index The index of the selected item in the dropdown list
12001 * @event beforequery
12002 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12003 * The event object passed has these properties:
12004 * @param {Roo.bootstrap.ComboBox} combo This combo box
12005 * @param {String} query The query
12006 * @param {Boolean} forceAll true to force "all" query
12007 * @param {Boolean} cancel true to cancel the query
12008 * @param {Object} e The query event object
12010 'beforequery': true,
12013 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12014 * @param {Roo.bootstrap.ComboBox} combo This combo box
12019 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12020 * @param {Roo.bootstrap.ComboBox} combo This combo box
12021 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12026 * Fires when the remove value from the combobox array
12027 * @param {Roo.bootstrap.ComboBox} combo This combo box
12031 * @event afterremove
12032 * Fires when the remove value from the combobox array
12033 * @param {Roo.bootstrap.ComboBox} combo This combo box
12035 'afterremove' : true,
12037 * @event specialfilter
12038 * Fires when specialfilter
12039 * @param {Roo.bootstrap.ComboBox} combo This combo box
12041 'specialfilter' : true,
12044 * Fires when tick the element
12045 * @param {Roo.bootstrap.ComboBox} combo This combo box
12049 * @event touchviewdisplay
12050 * Fires when touch view require special display (default is using displayField)
12051 * @param {Roo.bootstrap.ComboBox} combo This combo box
12052 * @param {Object} cfg set html .
12054 'touchviewdisplay' : true
12059 this.tickItems = [];
12061 this.selectedIndex = -1;
12062 if(this.mode == 'local'){
12063 if(config.queryDelay === undefined){
12064 this.queryDelay = 10;
12066 if(config.minChars === undefined){
12072 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12075 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12076 * rendering into an Roo.Editor, defaults to false)
12079 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12080 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12083 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12086 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12087 * the dropdown list (defaults to undefined, with no header element)
12091 * @cfg {String/Roo.Template} tpl The template to use to render the output
12095 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12097 listWidth: undefined,
12099 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12100 * mode = 'remote' or 'text' if mode = 'local')
12102 displayField: undefined,
12105 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12106 * mode = 'remote' or 'value' if mode = 'local').
12107 * Note: use of a valueField requires the user make a selection
12108 * in order for a value to be mapped.
12110 valueField: undefined,
12112 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12117 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12118 * field's data value (defaults to the underlying DOM element's name)
12120 hiddenName: undefined,
12122 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12126 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12128 selectedClass: 'active',
12131 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12135 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12136 * anchor positions (defaults to 'tl-bl')
12138 listAlign: 'tl-bl?',
12140 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12144 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12145 * query specified by the allQuery config option (defaults to 'query')
12147 triggerAction: 'query',
12149 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12150 * (defaults to 4, does not apply if editable = false)
12154 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12155 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12159 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12160 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12164 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12165 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12169 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12170 * when editable = true (defaults to false)
12172 selectOnFocus:false,
12174 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12176 queryParam: 'query',
12178 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12179 * when mode = 'remote' (defaults to 'Loading...')
12181 loadingText: 'Loading...',
12183 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12187 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12191 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12192 * traditional select (defaults to true)
12196 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12200 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12204 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12205 * listWidth has a higher value)
12209 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12210 * allow the user to set arbitrary text into the field (defaults to false)
12212 forceSelection:false,
12214 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12215 * if typeAhead = true (defaults to 250)
12217 typeAheadDelay : 250,
12219 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12220 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12222 valueNotFoundText : undefined,
12224 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12226 blockFocus : false,
12229 * @cfg {Boolean} disableClear Disable showing of clear button.
12231 disableClear : false,
12233 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12235 alwaysQuery : false,
12238 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12243 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12245 invalidClass : "has-warning",
12248 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12250 validClass : "has-success",
12253 * @cfg {Boolean} specialFilter (true|false) special filter default false
12255 specialFilter : false,
12258 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12260 mobileTouchView : true,
12263 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12265 useNativeIOS : false,
12267 ios_options : false,
12279 btnPosition : 'right',
12280 triggerList : true,
12281 showToggleBtn : true,
12283 emptyResultText: 'Empty',
12284 triggerText : 'Select',
12286 // element that contains real text value.. (when hidden is used..)
12288 getAutoCreate : function()
12293 * Render classic select for iso
12296 if(Roo.isIOS && this.useNativeIOS){
12297 cfg = this.getAutoCreateNativeIOS();
12305 if(Roo.isTouch && this.mobileTouchView){
12306 cfg = this.getAutoCreateTouchView();
12313 if(!this.tickable){
12314 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12319 * ComboBox with tickable selections
12322 var align = this.labelAlign || this.parentLabelAlign();
12325 cls : 'form-group roo-combobox-tickable' //input-group
12330 cls : 'tickable-buttons',
12335 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12336 html : this.triggerText
12342 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12349 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12356 buttons.cn.unshift({
12358 cls: 'roo-select2-search-field-input'
12364 Roo.each(buttons.cn, function(c){
12366 c.cls += ' btn-' + _this.size;
12369 if (_this.disabled) {
12380 cls: 'form-hidden-field'
12384 cls: 'roo-select2-choices',
12388 cls: 'roo-select2-search-field',
12400 cls: 'roo-select2-container input-group roo-select2-container-multi',
12405 // cls: 'typeahead typeahead-long dropdown-menu',
12406 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12411 if(this.hasFeedback && !this.allowBlank){
12415 cls: 'glyphicon form-control-feedback'
12418 combobox.cn.push(feedback);
12421 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12423 // Roo.log("left and has label");
12427 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12428 tooltip : 'This field is required'
12433 cls : 'control-label col-sm-' + this.labelWidth,
12434 html : this.fieldLabel
12438 cls : "col-sm-" + (12 - this.labelWidth),
12446 if(this.indicatorpos == 'right'){
12452 cls : 'control-label col-sm-' + this.labelWidth,
12453 html : this.fieldLabel
12458 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12459 tooltip : 'This field is required'
12462 cls : "col-sm-" + (12 - this.labelWidth),
12473 } else if ( this.fieldLabel.length) {
12474 // Roo.log(" label");
12478 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12479 tooltip : 'This field is required'
12483 //cls : 'input-group-addon',
12484 html : this.fieldLabel
12492 if(this.indicatorpos == 'right'){
12497 //cls : 'input-group-addon',
12498 html : this.fieldLabel
12504 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12505 tooltip : 'This field is required'
12516 // Roo.log(" no label && no align");
12523 ['xs','sm','md','lg'].map(function(size){
12524 if (settings[size]) {
12525 cfg.cls += ' col-' + size + '-' + settings[size];
12533 _initEventsCalled : false,
12536 initEvents: function()
12538 if (this._initEventsCalled) { // as we call render... prevent looping...
12541 this._initEventsCalled = true;
12544 throw "can not find store for combo";
12547 this.store = Roo.factory(this.store, Roo.data);
12549 // if we are building from html. then this element is so complex, that we can not really
12550 // use the rendered HTML.
12551 // so we have to trash and replace the previous code.
12552 if (Roo.XComponent.build_from_html) {
12554 // remove this element....
12555 var e = this.el.dom, k=0;
12556 while (e ) { e = e.previousSibling; ++k;}
12561 this.rendered = false;
12563 this.render(this.parent().getChildContainer(true), k);
12569 if(Roo.isIOS && this.useNativeIOS){
12570 this.initIOSView();
12578 if(Roo.isTouch && this.mobileTouchView){
12579 this.initTouchView();
12584 this.initTickableEvents();
12588 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12590 if(this.hiddenName){
12592 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12594 this.hiddenField.dom.value =
12595 this.hiddenValue !== undefined ? this.hiddenValue :
12596 this.value !== undefined ? this.value : '';
12598 // prevent input submission
12599 this.el.dom.removeAttribute('name');
12600 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12605 // this.el.dom.setAttribute('autocomplete', 'off');
12608 var cls = 'x-combo-list';
12610 //this.list = new Roo.Layer({
12611 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12617 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12618 _this.list.setWidth(lw);
12621 this.list.on('mouseover', this.onViewOver, this);
12622 this.list.on('mousemove', this.onViewMove, this);
12624 this.list.on('scroll', this.onViewScroll, this);
12627 this.list.swallowEvent('mousewheel');
12628 this.assetHeight = 0;
12631 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12632 this.assetHeight += this.header.getHeight();
12635 this.innerList = this.list.createChild({cls:cls+'-inner'});
12636 this.innerList.on('mouseover', this.onViewOver, this);
12637 this.innerList.on('mousemove', this.onViewMove, this);
12638 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12640 if(this.allowBlank && !this.pageSize && !this.disableClear){
12641 this.footer = this.list.createChild({cls:cls+'-ft'});
12642 this.pageTb = new Roo.Toolbar(this.footer);
12646 this.footer = this.list.createChild({cls:cls+'-ft'});
12647 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12648 {pageSize: this.pageSize});
12652 if (this.pageTb && this.allowBlank && !this.disableClear) {
12654 this.pageTb.add(new Roo.Toolbar.Fill(), {
12655 cls: 'x-btn-icon x-btn-clear',
12657 handler: function()
12660 _this.clearValue();
12661 _this.onSelect(false, -1);
12666 this.assetHeight += this.footer.getHeight();
12671 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12674 this.view = new Roo.View(this.list, this.tpl, {
12675 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12677 //this.view.wrapEl.setDisplayed(false);
12678 this.view.on('click', this.onViewClick, this);
12682 this.store.on('beforeload', this.onBeforeLoad, this);
12683 this.store.on('load', this.onLoad, this);
12684 this.store.on('loadexception', this.onLoadException, this);
12686 if(this.resizable){
12687 this.resizer = new Roo.Resizable(this.list, {
12688 pinned:true, handles:'se'
12690 this.resizer.on('resize', function(r, w, h){
12691 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12692 this.listWidth = w;
12693 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12694 this.restrictHeight();
12696 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12699 if(!this.editable){
12700 this.editable = true;
12701 this.setEditable(false);
12706 if (typeof(this.events.add.listeners) != 'undefined') {
12708 this.addicon = this.wrap.createChild(
12709 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12711 this.addicon.on('click', function(e) {
12712 this.fireEvent('add', this);
12715 if (typeof(this.events.edit.listeners) != 'undefined') {
12717 this.editicon = this.wrap.createChild(
12718 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12719 if (this.addicon) {
12720 this.editicon.setStyle('margin-left', '40px');
12722 this.editicon.on('click', function(e) {
12724 // we fire even if inothing is selected..
12725 this.fireEvent('edit', this, this.lastData );
12731 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12732 "up" : function(e){
12733 this.inKeyMode = true;
12737 "down" : function(e){
12738 if(!this.isExpanded()){
12739 this.onTriggerClick();
12741 this.inKeyMode = true;
12746 "enter" : function(e){
12747 // this.onViewClick();
12751 if(this.fireEvent("specialkey", this, e)){
12752 this.onViewClick(false);
12758 "esc" : function(e){
12762 "tab" : function(e){
12765 if(this.fireEvent("specialkey", this, e)){
12766 this.onViewClick(false);
12774 doRelay : function(foo, bar, hname){
12775 if(hname == 'down' || this.scope.isExpanded()){
12776 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12785 this.queryDelay = Math.max(this.queryDelay || 10,
12786 this.mode == 'local' ? 10 : 250);
12789 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12791 if(this.typeAhead){
12792 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12794 if(this.editable !== false){
12795 this.inputEl().on("keyup", this.onKeyUp, this);
12797 if(this.forceSelection){
12798 this.inputEl().on('blur', this.doForce, this);
12802 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12803 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12807 initTickableEvents: function()
12811 if(this.hiddenName){
12813 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12815 this.hiddenField.dom.value =
12816 this.hiddenValue !== undefined ? this.hiddenValue :
12817 this.value !== undefined ? this.value : '';
12819 // prevent input submission
12820 this.el.dom.removeAttribute('name');
12821 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12826 // this.list = this.el.select('ul.dropdown-menu',true).first();
12828 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12829 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12830 if(this.triggerList){
12831 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12834 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12835 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12837 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12838 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12840 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12841 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12843 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12844 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12845 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12848 this.cancelBtn.hide();
12853 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12854 _this.list.setWidth(lw);
12857 this.list.on('mouseover', this.onViewOver, this);
12858 this.list.on('mousemove', this.onViewMove, this);
12860 this.list.on('scroll', this.onViewScroll, this);
12863 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
12866 this.view = new Roo.View(this.list, this.tpl, {
12867 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12870 //this.view.wrapEl.setDisplayed(false);
12871 this.view.on('click', this.onViewClick, this);
12875 this.store.on('beforeload', this.onBeforeLoad, this);
12876 this.store.on('load', this.onLoad, this);
12877 this.store.on('loadexception', this.onLoadException, this);
12880 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12881 "up" : function(e){
12882 this.inKeyMode = true;
12886 "down" : function(e){
12887 this.inKeyMode = true;
12891 "enter" : function(e){
12892 if(this.fireEvent("specialkey", this, e)){
12893 this.onViewClick(false);
12899 "esc" : function(e){
12900 this.onTickableFooterButtonClick(e, false, false);
12903 "tab" : function(e){
12904 this.fireEvent("specialkey", this, e);
12906 this.onTickableFooterButtonClick(e, false, false);
12913 doRelay : function(e, fn, key){
12914 if(this.scope.isExpanded()){
12915 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12924 this.queryDelay = Math.max(this.queryDelay || 10,
12925 this.mode == 'local' ? 10 : 250);
12928 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12930 if(this.typeAhead){
12931 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12934 if(this.editable !== false){
12935 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12940 onDestroy : function(){
12942 this.view.setStore(null);
12943 this.view.el.removeAllListeners();
12944 this.view.el.remove();
12945 this.view.purgeListeners();
12948 this.list.dom.innerHTML = '';
12952 this.store.un('beforeload', this.onBeforeLoad, this);
12953 this.store.un('load', this.onLoad, this);
12954 this.store.un('loadexception', this.onLoadException, this);
12956 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12960 fireKey : function(e){
12961 if(e.isNavKeyPress() && !this.list.isVisible()){
12962 this.fireEvent("specialkey", this, e);
12967 onResize: function(w, h){
12968 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12970 // if(typeof w != 'number'){
12971 // // we do not handle it!?!?
12974 // var tw = this.trigger.getWidth();
12975 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12976 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12978 // this.inputEl().setWidth( this.adjustWidth('input', x));
12980 // //this.trigger.setStyle('left', x+'px');
12982 // if(this.list && this.listWidth === undefined){
12983 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12984 // this.list.setWidth(lw);
12985 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12993 * Allow or prevent the user from directly editing the field text. If false is passed,
12994 * the user will only be able to select from the items defined in the dropdown list. This method
12995 * is the runtime equivalent of setting the 'editable' config option at config time.
12996 * @param {Boolean} value True to allow the user to directly edit the field text
12998 setEditable : function(value){
12999 if(value == this.editable){
13002 this.editable = value;
13004 this.inputEl().dom.setAttribute('readOnly', true);
13005 this.inputEl().on('mousedown', this.onTriggerClick, this);
13006 this.inputEl().addClass('x-combo-noedit');
13008 this.inputEl().dom.setAttribute('readOnly', false);
13009 this.inputEl().un('mousedown', this.onTriggerClick, this);
13010 this.inputEl().removeClass('x-combo-noedit');
13016 onBeforeLoad : function(combo,opts){
13017 if(!this.hasFocus){
13021 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13023 this.restrictHeight();
13024 this.selectedIndex = -1;
13028 onLoad : function(){
13030 this.hasQuery = false;
13032 if(!this.hasFocus){
13036 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13037 this.loading.hide();
13040 if(this.store.getCount() > 0){
13042 this.restrictHeight();
13043 if(this.lastQuery == this.allQuery){
13044 if(this.editable && !this.tickable){
13045 this.inputEl().dom.select();
13049 !this.selectByValue(this.value, true) &&
13052 !this.store.lastOptions ||
13053 typeof(this.store.lastOptions.add) == 'undefined' ||
13054 this.store.lastOptions.add != true
13057 this.select(0, true);
13060 if(this.autoFocus){
13063 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13064 this.taTask.delay(this.typeAheadDelay);
13068 this.onEmptyResults();
13074 onLoadException : function()
13076 this.hasQuery = false;
13078 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13079 this.loading.hide();
13082 if(this.tickable && this.editable){
13087 // only causes errors at present
13088 //Roo.log(this.store.reader.jsonData);
13089 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13091 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13097 onTypeAhead : function(){
13098 if(this.store.getCount() > 0){
13099 var r = this.store.getAt(0);
13100 var newValue = r.data[this.displayField];
13101 var len = newValue.length;
13102 var selStart = this.getRawValue().length;
13104 if(selStart != len){
13105 this.setRawValue(newValue);
13106 this.selectText(selStart, newValue.length);
13112 onSelect : function(record, index){
13114 if(this.fireEvent('beforeselect', this, record, index) !== false){
13116 this.setFromData(index > -1 ? record.data : false);
13119 this.fireEvent('select', this, record, index);
13124 * Returns the currently selected field value or empty string if no value is set.
13125 * @return {String} value The selected value
13127 getValue : function()
13129 if(Roo.isIOS && this.useNativeIOS){
13130 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13134 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13137 if(this.valueField){
13138 return typeof this.value != 'undefined' ? this.value : '';
13140 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13144 getRawValue : function()
13146 if(Roo.isIOS && this.useNativeIOS){
13147 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13150 var v = this.inputEl().getValue();
13156 * Clears any text/value currently set in the field
13158 clearValue : function(){
13160 if(this.hiddenField){
13161 this.hiddenField.dom.value = '';
13164 this.setRawValue('');
13165 this.lastSelectionText = '';
13166 this.lastData = false;
13168 var close = this.closeTriggerEl();
13179 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13180 * will be displayed in the field. If the value does not match the data value of an existing item,
13181 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13182 * Otherwise the field will be blank (although the value will still be set).
13183 * @param {String} value The value to match
13185 setValue : function(v)
13187 if(Roo.isIOS && this.useNativeIOS){
13188 this.setIOSValue(v);
13198 if(this.valueField){
13199 var r = this.findRecord(this.valueField, v);
13201 text = r.data[this.displayField];
13202 }else if(this.valueNotFoundText !== undefined){
13203 text = this.valueNotFoundText;
13206 this.lastSelectionText = text;
13207 if(this.hiddenField){
13208 this.hiddenField.dom.value = v;
13210 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13213 var close = this.closeTriggerEl();
13216 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13222 * @property {Object} the last set data for the element
13227 * Sets the value of the field based on a object which is related to the record format for the store.
13228 * @param {Object} value the value to set as. or false on reset?
13230 setFromData : function(o){
13237 var dv = ''; // display value
13238 var vv = ''; // value value..
13240 if (this.displayField) {
13241 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13243 // this is an error condition!!!
13244 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13247 if(this.valueField){
13248 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13251 var close = this.closeTriggerEl();
13254 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13257 if(this.hiddenField){
13258 this.hiddenField.dom.value = vv;
13260 this.lastSelectionText = dv;
13261 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13265 // no hidden field.. - we store the value in 'value', but still display
13266 // display field!!!!
13267 this.lastSelectionText = dv;
13268 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13275 reset : function(){
13276 // overridden so that last data is reset..
13283 this.setValue(this.originalValue);
13284 //this.clearInvalid();
13285 this.lastData = false;
13287 this.view.clearSelections();
13293 findRecord : function(prop, value){
13295 if(this.store.getCount() > 0){
13296 this.store.each(function(r){
13297 if(r.data[prop] == value){
13307 getName: function()
13309 // returns hidden if it's set..
13310 if (!this.rendered) {return ''};
13311 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13315 onViewMove : function(e, t){
13316 this.inKeyMode = false;
13320 onViewOver : function(e, t){
13321 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13324 var item = this.view.findItemFromChild(t);
13327 var index = this.view.indexOf(item);
13328 this.select(index, false);
13333 onViewClick : function(view, doFocus, el, e)
13335 var index = this.view.getSelectedIndexes()[0];
13337 var r = this.store.getAt(index);
13341 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13348 Roo.each(this.tickItems, function(v,k){
13350 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13352 _this.tickItems.splice(k, 1);
13354 if(typeof(e) == 'undefined' && view == false){
13355 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13367 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13368 this.tickItems.push(r.data);
13371 if(typeof(e) == 'undefined' && view == false){
13372 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13379 this.onSelect(r, index);
13381 if(doFocus !== false && !this.blockFocus){
13382 this.inputEl().focus();
13387 restrictHeight : function(){
13388 //this.innerList.dom.style.height = '';
13389 //var inner = this.innerList.dom;
13390 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13391 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13392 //this.list.beginUpdate();
13393 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13394 this.list.alignTo(this.inputEl(), this.listAlign);
13395 this.list.alignTo(this.inputEl(), this.listAlign);
13396 //this.list.endUpdate();
13400 onEmptyResults : function(){
13402 if(this.tickable && this.editable){
13403 this.restrictHeight();
13411 * Returns true if the dropdown list is expanded, else false.
13413 isExpanded : function(){
13414 return this.list.isVisible();
13418 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13419 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13420 * @param {String} value The data value of the item to select
13421 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13422 * selected item if it is not currently in view (defaults to true)
13423 * @return {Boolean} True if the value matched an item in the list, else false
13425 selectByValue : function(v, scrollIntoView){
13426 if(v !== undefined && v !== null){
13427 var r = this.findRecord(this.valueField || this.displayField, v);
13429 this.select(this.store.indexOf(r), scrollIntoView);
13437 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13438 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13439 * @param {Number} index The zero-based index of the list item to select
13440 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13441 * selected item if it is not currently in view (defaults to true)
13443 select : function(index, scrollIntoView){
13444 this.selectedIndex = index;
13445 this.view.select(index);
13446 if(scrollIntoView !== false){
13447 var el = this.view.getNode(index);
13449 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13452 this.list.scrollChildIntoView(el, false);
13458 selectNext : function(){
13459 var ct = this.store.getCount();
13461 if(this.selectedIndex == -1){
13463 }else if(this.selectedIndex < ct-1){
13464 this.select(this.selectedIndex+1);
13470 selectPrev : function(){
13471 var ct = this.store.getCount();
13473 if(this.selectedIndex == -1){
13475 }else if(this.selectedIndex != 0){
13476 this.select(this.selectedIndex-1);
13482 onKeyUp : function(e){
13483 if(this.editable !== false && !e.isSpecialKey()){
13484 this.lastKey = e.getKey();
13485 this.dqTask.delay(this.queryDelay);
13490 validateBlur : function(){
13491 return !this.list || !this.list.isVisible();
13495 initQuery : function(){
13497 var v = this.getRawValue();
13499 if(this.tickable && this.editable){
13500 v = this.tickableInputEl().getValue();
13507 doForce : function(){
13508 if(this.inputEl().dom.value.length > 0){
13509 this.inputEl().dom.value =
13510 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13516 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13517 * query allowing the query action to be canceled if needed.
13518 * @param {String} query The SQL query to execute
13519 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13520 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13521 * saved in the current store (defaults to false)
13523 doQuery : function(q, forceAll){
13525 if(q === undefined || q === null){
13530 forceAll: forceAll,
13534 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13539 forceAll = qe.forceAll;
13540 if(forceAll === true || (q.length >= this.minChars)){
13542 this.hasQuery = true;
13544 if(this.lastQuery != q || this.alwaysQuery){
13545 this.lastQuery = q;
13546 if(this.mode == 'local'){
13547 this.selectedIndex = -1;
13549 this.store.clearFilter();
13552 if(this.specialFilter){
13553 this.fireEvent('specialfilter', this);
13558 this.store.filter(this.displayField, q);
13561 this.store.fireEvent("datachanged", this.store);
13568 this.store.baseParams[this.queryParam] = q;
13570 var options = {params : this.getParams(q)};
13573 options.add = true;
13574 options.params.start = this.page * this.pageSize;
13577 this.store.load(options);
13580 * this code will make the page width larger, at the beginning, the list not align correctly,
13581 * we should expand the list on onLoad
13582 * so command out it
13587 this.selectedIndex = -1;
13592 this.loadNext = false;
13596 getParams : function(q){
13598 //p[this.queryParam] = q;
13602 p.limit = this.pageSize;
13608 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13610 collapse : function(){
13611 if(!this.isExpanded()){
13618 this.hasFocus = false;
13620 this.cancelBtn.hide();
13621 this.trigger.show();
13624 this.tickableInputEl().dom.value = '';
13625 this.tickableInputEl().blur();
13630 Roo.get(document).un('mousedown', this.collapseIf, this);
13631 Roo.get(document).un('mousewheel', this.collapseIf, this);
13632 if (!this.editable) {
13633 Roo.get(document).un('keydown', this.listKeyPress, this);
13635 this.fireEvent('collapse', this);
13641 collapseIf : function(e){
13642 var in_combo = e.within(this.el);
13643 var in_list = e.within(this.list);
13644 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13646 if (in_combo || in_list || is_list) {
13647 //e.stopPropagation();
13652 this.onTickableFooterButtonClick(e, false, false);
13660 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13662 expand : function(){
13664 if(this.isExpanded() || !this.hasFocus){
13668 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13669 this.list.setWidth(lw);
13676 this.restrictHeight();
13680 this.tickItems = Roo.apply([], this.item);
13683 this.cancelBtn.show();
13684 this.trigger.hide();
13687 this.tickableInputEl().focus();
13692 Roo.get(document).on('mousedown', this.collapseIf, this);
13693 Roo.get(document).on('mousewheel', this.collapseIf, this);
13694 if (!this.editable) {
13695 Roo.get(document).on('keydown', this.listKeyPress, this);
13698 this.fireEvent('expand', this);
13702 // Implements the default empty TriggerField.onTriggerClick function
13703 onTriggerClick : function(e)
13705 Roo.log('trigger click');
13707 if(this.disabled || !this.triggerList){
13712 this.loadNext = false;
13714 if(this.isExpanded()){
13716 if (!this.blockFocus) {
13717 this.inputEl().focus();
13721 this.hasFocus = true;
13722 if(this.triggerAction == 'all') {
13723 this.doQuery(this.allQuery, true);
13725 this.doQuery(this.getRawValue());
13727 if (!this.blockFocus) {
13728 this.inputEl().focus();
13733 onTickableTriggerClick : function(e)
13740 this.loadNext = false;
13741 this.hasFocus = true;
13743 if(this.triggerAction == 'all') {
13744 this.doQuery(this.allQuery, true);
13746 this.doQuery(this.getRawValue());
13750 onSearchFieldClick : function(e)
13752 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13753 this.onTickableFooterButtonClick(e, false, false);
13757 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13762 this.loadNext = false;
13763 this.hasFocus = true;
13765 if(this.triggerAction == 'all') {
13766 this.doQuery(this.allQuery, true);
13768 this.doQuery(this.getRawValue());
13772 listKeyPress : function(e)
13774 //Roo.log('listkeypress');
13775 // scroll to first matching element based on key pres..
13776 if (e.isSpecialKey()) {
13779 var k = String.fromCharCode(e.getKey()).toUpperCase();
13782 var csel = this.view.getSelectedNodes();
13783 var cselitem = false;
13785 var ix = this.view.indexOf(csel[0]);
13786 cselitem = this.store.getAt(ix);
13787 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13793 this.store.each(function(v) {
13795 // start at existing selection.
13796 if (cselitem.id == v.id) {
13802 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13803 match = this.store.indexOf(v);
13809 if (match === false) {
13810 return true; // no more action?
13813 this.view.select(match);
13814 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13815 sn.scrollIntoView(sn.dom.parentNode, false);
13818 onViewScroll : function(e, t){
13820 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){
13824 this.hasQuery = true;
13826 this.loading = this.list.select('.loading', true).first();
13828 if(this.loading === null){
13829 this.list.createChild({
13831 cls: 'loading roo-select2-more-results roo-select2-active',
13832 html: 'Loading more results...'
13835 this.loading = this.list.select('.loading', true).first();
13837 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13839 this.loading.hide();
13842 this.loading.show();
13847 this.loadNext = true;
13849 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13854 addItem : function(o)
13856 var dv = ''; // display value
13858 if (this.displayField) {
13859 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13861 // this is an error condition!!!
13862 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13869 var choice = this.choices.createChild({
13871 cls: 'roo-select2-search-choice',
13880 cls: 'roo-select2-search-choice-close',
13885 }, this.searchField);
13887 var close = choice.select('a.roo-select2-search-choice-close', true).first();
13889 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13897 this.inputEl().dom.value = '';
13902 onRemoveItem : function(e, _self, o)
13904 e.preventDefault();
13906 this.lastItem = Roo.apply([], this.item);
13908 var index = this.item.indexOf(o.data) * 1;
13911 Roo.log('not this item?!');
13915 this.item.splice(index, 1);
13920 this.fireEvent('remove', this, e);
13926 syncValue : function()
13928 if(!this.item.length){
13935 Roo.each(this.item, function(i){
13936 if(_this.valueField){
13937 value.push(i[_this.valueField]);
13944 this.value = value.join(',');
13946 if(this.hiddenField){
13947 this.hiddenField.dom.value = this.value;
13950 this.store.fireEvent("datachanged", this.store);
13955 clearItem : function()
13957 if(!this.multiple){
13963 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13971 if(this.tickable && !Roo.isTouch){
13972 this.view.refresh();
13976 inputEl: function ()
13978 if(Roo.isIOS && this.useNativeIOS){
13979 return this.el.select('select.roo-ios-select', true).first();
13982 if(Roo.isTouch && this.mobileTouchView){
13983 return this.el.select('input.form-control',true).first();
13987 return this.searchField;
13990 return this.el.select('input.form-control',true).first();
13993 onTickableFooterButtonClick : function(e, btn, el)
13995 e.preventDefault();
13997 this.lastItem = Roo.apply([], this.item);
13999 if(btn && btn.name == 'cancel'){
14000 this.tickItems = Roo.apply([], this.item);
14009 Roo.each(this.tickItems, function(o){
14017 validate : function()
14019 var v = this.getRawValue();
14022 v = this.getValue();
14025 if(this.disabled || this.allowBlank || v.length){
14030 this.markInvalid();
14034 tickableInputEl : function()
14036 if(!this.tickable || !this.editable){
14037 return this.inputEl();
14040 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14044 getAutoCreateTouchView : function()
14049 cls: 'form-group' //input-group
14055 type : this.inputType,
14056 cls : 'form-control x-combo-noedit',
14057 autocomplete: 'new-password',
14058 placeholder : this.placeholder || '',
14063 input.name = this.name;
14067 input.cls += ' input-' + this.size;
14070 if (this.disabled) {
14071 input.disabled = true;
14082 inputblock.cls += ' input-group';
14084 inputblock.cn.unshift({
14086 cls : 'input-group-addon',
14091 if(this.removable && !this.multiple){
14092 inputblock.cls += ' roo-removable';
14094 inputblock.cn.push({
14097 cls : 'roo-combo-removable-btn close'
14101 if(this.hasFeedback && !this.allowBlank){
14103 inputblock.cls += ' has-feedback';
14105 inputblock.cn.push({
14107 cls: 'glyphicon form-control-feedback'
14114 inputblock.cls += (this.before) ? '' : ' input-group';
14116 inputblock.cn.push({
14118 cls : 'input-group-addon',
14129 cls: 'form-hidden-field'
14143 cls: 'form-hidden-field'
14147 cls: 'roo-select2-choices',
14151 cls: 'roo-select2-search-field',
14164 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14170 if(!this.multiple && this.showToggleBtn){
14177 if (this.caret != false) {
14180 cls: 'fa fa-' + this.caret
14187 cls : 'input-group-addon btn dropdown-toggle',
14192 cls: 'combobox-clear',
14206 combobox.cls += ' roo-select2-container-multi';
14209 var align = this.labelAlign || this.parentLabelAlign();
14213 if(this.fieldLabel.length && this.labelWidth){
14215 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14216 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14221 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14222 tooltip : 'This field is required'
14226 cls : 'control-label ' + lw,
14227 html : this.fieldLabel
14238 if(this.indicatorpos == 'right'){
14242 cls : 'control-label ' + lw,
14243 html : this.fieldLabel
14248 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14249 tooltip : 'This field is required'
14261 var settings = this;
14263 ['xs','sm','md','lg'].map(function(size){
14264 if (settings[size]) {
14265 cfg.cls += ' col-' + size + '-' + settings[size];
14272 initTouchView : function()
14274 this.renderTouchView();
14276 this.touchViewEl.on('scroll', function(){
14277 this.el.dom.scrollTop = 0;
14280 this.originalValue = this.getValue();
14282 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14284 this.inputEl().on("click", this.showTouchView, this);
14285 if (this.triggerEl) {
14286 this.triggerEl.on("click", this.showTouchView, this);
14290 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14291 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14293 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14295 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14296 this.store.on('load', this.onTouchViewLoad, this);
14297 this.store.on('loadexception', this.onTouchViewLoadException, this);
14299 if(this.hiddenName){
14301 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14303 this.hiddenField.dom.value =
14304 this.hiddenValue !== undefined ? this.hiddenValue :
14305 this.value !== undefined ? this.value : '';
14307 this.el.dom.removeAttribute('name');
14308 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14312 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14313 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14316 if(this.removable && !this.multiple){
14317 var close = this.closeTriggerEl();
14319 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14320 close.on('click', this.removeBtnClick, this, close);
14324 * fix the bug in Safari iOS8
14326 this.inputEl().on("focus", function(e){
14327 document.activeElement.blur();
14335 renderTouchView : function()
14337 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14338 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14340 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14341 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14343 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14344 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14345 this.touchViewBodyEl.setStyle('overflow', 'auto');
14347 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14348 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14350 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14351 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14355 showTouchView : function()
14361 this.touchViewHeaderEl.hide();
14363 if(this.modalTitle.length){
14364 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14365 this.touchViewHeaderEl.show();
14368 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14369 this.touchViewEl.show();
14371 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14372 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14373 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14375 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14377 if(this.modalTitle.length){
14378 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14381 this.touchViewBodyEl.setHeight(bodyHeight);
14385 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14387 this.touchViewEl.addClass('in');
14390 this.doTouchViewQuery();
14394 hideTouchView : function()
14396 this.touchViewEl.removeClass('in');
14400 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14402 this.touchViewEl.setStyle('display', 'none');
14407 setTouchViewValue : function()
14414 Roo.each(this.tickItems, function(o){
14419 this.hideTouchView();
14422 doTouchViewQuery : function()
14431 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14435 if(!this.alwaysQuery || this.mode == 'local'){
14436 this.onTouchViewLoad();
14443 onTouchViewBeforeLoad : function(combo,opts)
14449 onTouchViewLoad : function()
14451 if(this.store.getCount() < 1){
14452 this.onTouchViewEmptyResults();
14456 this.clearTouchView();
14458 var rawValue = this.getRawValue();
14460 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14462 this.tickItems = [];
14464 this.store.data.each(function(d, rowIndex){
14465 var row = this.touchViewListGroup.createChild(template);
14467 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14468 row.addClass(d.data.cls);
14471 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14474 html : d.data[this.displayField]
14477 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14478 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14481 row.removeClass('selected');
14482 if(!this.multiple && this.valueField &&
14483 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14486 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14487 row.addClass('selected');
14490 if(this.multiple && this.valueField &&
14491 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14495 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14496 this.tickItems.push(d.data);
14499 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14503 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14505 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14507 if(this.modalTitle.length){
14508 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14511 var listHeight = this.touchViewListGroup.getHeight();
14515 if(firstChecked && listHeight > bodyHeight){
14516 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14521 onTouchViewLoadException : function()
14523 this.hideTouchView();
14526 onTouchViewEmptyResults : function()
14528 this.clearTouchView();
14530 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14532 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14536 clearTouchView : function()
14538 this.touchViewListGroup.dom.innerHTML = '';
14541 onTouchViewClick : function(e, el, o)
14543 e.preventDefault();
14546 var rowIndex = o.rowIndex;
14548 var r = this.store.getAt(rowIndex);
14550 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14552 if(!this.multiple){
14553 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14554 c.dom.removeAttribute('checked');
14557 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14559 this.setFromData(r.data);
14561 var close = this.closeTriggerEl();
14567 this.hideTouchView();
14569 this.fireEvent('select', this, r, rowIndex);
14574 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14575 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14576 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14580 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14581 this.addItem(r.data);
14582 this.tickItems.push(r.data);
14586 getAutoCreateNativeIOS : function()
14589 cls: 'form-group' //input-group,
14594 cls : 'roo-ios-select'
14598 combobox.name = this.name;
14601 if (this.disabled) {
14602 combobox.disabled = true;
14605 var settings = this;
14607 ['xs','sm','md','lg'].map(function(size){
14608 if (settings[size]) {
14609 cfg.cls += ' col-' + size + '-' + settings[size];
14619 initIOSView : function()
14621 this.store.on('load', this.onIOSViewLoad, this);
14626 onIOSViewLoad : function()
14628 if(this.store.getCount() < 1){
14632 this.clearIOSView();
14634 if(this.allowBlank) {
14636 var default_text = '-- SELECT --';
14638 var opt = this.inputEl().createChild({
14641 html : default_text
14645 o[this.valueField] = 0;
14646 o[this.displayField] = default_text;
14648 this.ios_options.push({
14655 this.store.data.each(function(d, rowIndex){
14659 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14660 html = d.data[this.displayField];
14665 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
14666 value = d.data[this.valueField];
14675 if(this.value == d.data[this.valueField]){
14676 option['selected'] = true;
14679 var opt = this.inputEl().createChild(option);
14681 this.ios_options.push({
14688 this.inputEl().on('change', function(){
14689 this.fireEvent('select', this);
14694 clearIOSView: function()
14696 this.inputEl().dom.innerHTML = '';
14698 this.ios_options = [];
14701 setIOSValue: function(v)
14705 if(!this.ios_options){
14709 Roo.each(this.ios_options, function(opts){
14711 opts.el.dom.removeAttribute('selected');
14713 if(opts.data[this.valueField] != v){
14717 opts.el.dom.setAttribute('selected', true);
14723 * @cfg {Boolean} grow
14727 * @cfg {Number} growMin
14731 * @cfg {Number} growMax
14740 Roo.apply(Roo.bootstrap.ComboBox, {
14744 cls: 'modal-header',
14766 cls: 'list-group-item',
14770 cls: 'roo-combobox-list-group-item-value'
14774 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14788 listItemCheckbox : {
14790 cls: 'list-group-item',
14794 cls: 'roo-combobox-list-group-item-value'
14798 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14814 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14819 cls: 'modal-footer',
14827 cls: 'col-xs-6 text-left',
14830 cls: 'btn btn-danger roo-touch-view-cancel',
14836 cls: 'col-xs-6 text-right',
14839 cls: 'btn btn-success roo-touch-view-ok',
14850 Roo.apply(Roo.bootstrap.ComboBox, {
14852 touchViewTemplate : {
14854 cls: 'modal fade roo-combobox-touch-view',
14858 cls: 'modal-dialog',
14859 style : 'position:fixed', // we have to fix position....
14863 cls: 'modal-content',
14865 Roo.bootstrap.ComboBox.header,
14866 Roo.bootstrap.ComboBox.body,
14867 Roo.bootstrap.ComboBox.footer
14876 * Ext JS Library 1.1.1
14877 * Copyright(c) 2006-2007, Ext JS, LLC.
14879 * Originally Released Under LGPL - original licence link has changed is not relivant.
14882 * <script type="text/javascript">
14887 * @extends Roo.util.Observable
14888 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14889 * This class also supports single and multi selection modes. <br>
14890 * Create a data model bound view:
14892 var store = new Roo.data.Store(...);
14894 var view = new Roo.View({
14896 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14898 singleSelect: true,
14899 selectedClass: "ydataview-selected",
14903 // listen for node click?
14904 view.on("click", function(vw, index, node, e){
14905 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14909 dataModel.load("foobar.xml");
14911 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14913 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14914 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14916 * Note: old style constructor is still suported (container, template, config)
14919 * Create a new View
14920 * @param {Object} config The config object
14923 Roo.View = function(config, depreciated_tpl, depreciated_config){
14925 this.parent = false;
14927 if (typeof(depreciated_tpl) == 'undefined') {
14928 // new way.. - universal constructor.
14929 Roo.apply(this, config);
14930 this.el = Roo.get(this.el);
14933 this.el = Roo.get(config);
14934 this.tpl = depreciated_tpl;
14935 Roo.apply(this, depreciated_config);
14937 this.wrapEl = this.el.wrap().wrap();
14938 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14941 if(typeof(this.tpl) == "string"){
14942 this.tpl = new Roo.Template(this.tpl);
14944 // support xtype ctors..
14945 this.tpl = new Roo.factory(this.tpl, Roo);
14949 this.tpl.compile();
14954 * @event beforeclick
14955 * Fires before a click is processed. Returns false to cancel the default action.
14956 * @param {Roo.View} this
14957 * @param {Number} index The index of the target node
14958 * @param {HTMLElement} node The target node
14959 * @param {Roo.EventObject} e The raw event object
14961 "beforeclick" : true,
14964 * Fires when a template node is clicked.
14965 * @param {Roo.View} this
14966 * @param {Number} index The index of the target node
14967 * @param {HTMLElement} node The target node
14968 * @param {Roo.EventObject} e The raw event object
14973 * Fires when a template node is double clicked.
14974 * @param {Roo.View} this
14975 * @param {Number} index The index of the target node
14976 * @param {HTMLElement} node The target node
14977 * @param {Roo.EventObject} e The raw event object
14981 * @event contextmenu
14982 * Fires when a template node is right clicked.
14983 * @param {Roo.View} this
14984 * @param {Number} index The index of the target node
14985 * @param {HTMLElement} node The target node
14986 * @param {Roo.EventObject} e The raw event object
14988 "contextmenu" : true,
14990 * @event selectionchange
14991 * Fires when the selected nodes change.
14992 * @param {Roo.View} this
14993 * @param {Array} selections Array of the selected nodes
14995 "selectionchange" : true,
14998 * @event beforeselect
14999 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15000 * @param {Roo.View} this
15001 * @param {HTMLElement} node The node to be selected
15002 * @param {Array} selections Array of currently selected nodes
15004 "beforeselect" : true,
15006 * @event preparedata
15007 * Fires on every row to render, to allow you to change the data.
15008 * @param {Roo.View} this
15009 * @param {Object} data to be rendered (change this)
15011 "preparedata" : true
15019 "click": this.onClick,
15020 "dblclick": this.onDblClick,
15021 "contextmenu": this.onContextMenu,
15025 this.selections = [];
15027 this.cmp = new Roo.CompositeElementLite([]);
15029 this.store = Roo.factory(this.store, Roo.data);
15030 this.setStore(this.store, true);
15033 if ( this.footer && this.footer.xtype) {
15035 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15037 this.footer.dataSource = this.store;
15038 this.footer.container = fctr;
15039 this.footer = Roo.factory(this.footer, Roo);
15040 fctr.insertFirst(this.el);
15042 // this is a bit insane - as the paging toolbar seems to detach the el..
15043 // dom.parentNode.parentNode.parentNode
15044 // they get detached?
15048 Roo.View.superclass.constructor.call(this);
15053 Roo.extend(Roo.View, Roo.util.Observable, {
15056 * @cfg {Roo.data.Store} store Data store to load data from.
15061 * @cfg {String|Roo.Element} el The container element.
15066 * @cfg {String|Roo.Template} tpl The template used by this View
15070 * @cfg {String} dataName the named area of the template to use as the data area
15071 * Works with domtemplates roo-name="name"
15075 * @cfg {String} selectedClass The css class to add to selected nodes
15077 selectedClass : "x-view-selected",
15079 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15084 * @cfg {String} text to display on mask (default Loading)
15088 * @cfg {Boolean} multiSelect Allow multiple selection
15090 multiSelect : false,
15092 * @cfg {Boolean} singleSelect Allow single selection
15094 singleSelect: false,
15097 * @cfg {Boolean} toggleSelect - selecting
15099 toggleSelect : false,
15102 * @cfg {Boolean} tickable - selecting
15107 * Returns the element this view is bound to.
15108 * @return {Roo.Element}
15110 getEl : function(){
15111 return this.wrapEl;
15117 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15119 refresh : function(){
15120 //Roo.log('refresh');
15123 // if we are using something like 'domtemplate', then
15124 // the what gets used is:
15125 // t.applySubtemplate(NAME, data, wrapping data..)
15126 // the outer template then get' applied with
15127 // the store 'extra data'
15128 // and the body get's added to the
15129 // roo-name="data" node?
15130 // <span class='roo-tpl-{name}'></span> ?????
15134 this.clearSelections();
15135 this.el.update("");
15137 var records = this.store.getRange();
15138 if(records.length < 1) {
15140 // is this valid?? = should it render a template??
15142 this.el.update(this.emptyText);
15146 if (this.dataName) {
15147 this.el.update(t.apply(this.store.meta)); //????
15148 el = this.el.child('.roo-tpl-' + this.dataName);
15151 for(var i = 0, len = records.length; i < len; i++){
15152 var data = this.prepareData(records[i].data, i, records[i]);
15153 this.fireEvent("preparedata", this, data, i, records[i]);
15155 var d = Roo.apply({}, data);
15158 Roo.apply(d, {'roo-id' : Roo.id()});
15162 Roo.each(this.parent.item, function(item){
15163 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15166 Roo.apply(d, {'roo-data-checked' : 'checked'});
15170 html[html.length] = Roo.util.Format.trim(
15172 t.applySubtemplate(this.dataName, d, this.store.meta) :
15179 el.update(html.join(""));
15180 this.nodes = el.dom.childNodes;
15181 this.updateIndexes(0);
15186 * Function to override to reformat the data that is sent to
15187 * the template for each node.
15188 * DEPRICATED - use the preparedata event handler.
15189 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15190 * a JSON object for an UpdateManager bound view).
15192 prepareData : function(data, index, record)
15194 this.fireEvent("preparedata", this, data, index, record);
15198 onUpdate : function(ds, record){
15199 // Roo.log('on update');
15200 this.clearSelections();
15201 var index = this.store.indexOf(record);
15202 var n = this.nodes[index];
15203 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15204 n.parentNode.removeChild(n);
15205 this.updateIndexes(index, index);
15211 onAdd : function(ds, records, index)
15213 //Roo.log(['on Add', ds, records, index] );
15214 this.clearSelections();
15215 if(this.nodes.length == 0){
15219 var n = this.nodes[index];
15220 for(var i = 0, len = records.length; i < len; i++){
15221 var d = this.prepareData(records[i].data, i, records[i]);
15223 this.tpl.insertBefore(n, d);
15226 this.tpl.append(this.el, d);
15229 this.updateIndexes(index);
15232 onRemove : function(ds, record, index){
15233 // Roo.log('onRemove');
15234 this.clearSelections();
15235 var el = this.dataName ?
15236 this.el.child('.roo-tpl-' + this.dataName) :
15239 el.dom.removeChild(this.nodes[index]);
15240 this.updateIndexes(index);
15244 * Refresh an individual node.
15245 * @param {Number} index
15247 refreshNode : function(index){
15248 this.onUpdate(this.store, this.store.getAt(index));
15251 updateIndexes : function(startIndex, endIndex){
15252 var ns = this.nodes;
15253 startIndex = startIndex || 0;
15254 endIndex = endIndex || ns.length - 1;
15255 for(var i = startIndex; i <= endIndex; i++){
15256 ns[i].nodeIndex = i;
15261 * Changes the data store this view uses and refresh the view.
15262 * @param {Store} store
15264 setStore : function(store, initial){
15265 if(!initial && this.store){
15266 this.store.un("datachanged", this.refresh);
15267 this.store.un("add", this.onAdd);
15268 this.store.un("remove", this.onRemove);
15269 this.store.un("update", this.onUpdate);
15270 this.store.un("clear", this.refresh);
15271 this.store.un("beforeload", this.onBeforeLoad);
15272 this.store.un("load", this.onLoad);
15273 this.store.un("loadexception", this.onLoad);
15277 store.on("datachanged", this.refresh, this);
15278 store.on("add", this.onAdd, this);
15279 store.on("remove", this.onRemove, this);
15280 store.on("update", this.onUpdate, this);
15281 store.on("clear", this.refresh, this);
15282 store.on("beforeload", this.onBeforeLoad, this);
15283 store.on("load", this.onLoad, this);
15284 store.on("loadexception", this.onLoad, this);
15292 * onbeforeLoad - masks the loading area.
15295 onBeforeLoad : function(store,opts)
15297 //Roo.log('onBeforeLoad');
15299 this.el.update("");
15301 this.el.mask(this.mask ? this.mask : "Loading" );
15303 onLoad : function ()
15310 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15311 * @param {HTMLElement} node
15312 * @return {HTMLElement} The template node
15314 findItemFromChild : function(node){
15315 var el = this.dataName ?
15316 this.el.child('.roo-tpl-' + this.dataName,true) :
15319 if(!node || node.parentNode == el){
15322 var p = node.parentNode;
15323 while(p && p != el){
15324 if(p.parentNode == el){
15333 onClick : function(e){
15334 var item = this.findItemFromChild(e.getTarget());
15336 var index = this.indexOf(item);
15337 if(this.onItemClick(item, index, e) !== false){
15338 this.fireEvent("click", this, index, item, e);
15341 this.clearSelections();
15346 onContextMenu : function(e){
15347 var item = this.findItemFromChild(e.getTarget());
15349 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15354 onDblClick : function(e){
15355 var item = this.findItemFromChild(e.getTarget());
15357 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15361 onItemClick : function(item, index, e)
15363 if(this.fireEvent("beforeclick", this, index, item, e) === false){
15366 if (this.toggleSelect) {
15367 var m = this.isSelected(item) ? 'unselect' : 'select';
15370 _t[m](item, true, false);
15373 if(this.multiSelect || this.singleSelect){
15374 if(this.multiSelect && e.shiftKey && this.lastSelection){
15375 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15377 this.select(item, this.multiSelect && e.ctrlKey);
15378 this.lastSelection = item;
15381 if(!this.tickable){
15382 e.preventDefault();
15390 * Get the number of selected nodes.
15393 getSelectionCount : function(){
15394 return this.selections.length;
15398 * Get the currently selected nodes.
15399 * @return {Array} An array of HTMLElements
15401 getSelectedNodes : function(){
15402 return this.selections;
15406 * Get the indexes of the selected nodes.
15409 getSelectedIndexes : function(){
15410 var indexes = [], s = this.selections;
15411 for(var i = 0, len = s.length; i < len; i++){
15412 indexes.push(s[i].nodeIndex);
15418 * Clear all selections
15419 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15421 clearSelections : function(suppressEvent){
15422 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15423 this.cmp.elements = this.selections;
15424 this.cmp.removeClass(this.selectedClass);
15425 this.selections = [];
15426 if(!suppressEvent){
15427 this.fireEvent("selectionchange", this, this.selections);
15433 * Returns true if the passed node is selected
15434 * @param {HTMLElement/Number} node The node or node index
15435 * @return {Boolean}
15437 isSelected : function(node){
15438 var s = this.selections;
15442 node = this.getNode(node);
15443 return s.indexOf(node) !== -1;
15448 * @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
15449 * @param {Boolean} keepExisting (optional) true to keep existing selections
15450 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15452 select : function(nodeInfo, keepExisting, suppressEvent){
15453 if(nodeInfo instanceof Array){
15455 this.clearSelections(true);
15457 for(var i = 0, len = nodeInfo.length; i < len; i++){
15458 this.select(nodeInfo[i], true, true);
15462 var node = this.getNode(nodeInfo);
15463 if(!node || this.isSelected(node)){
15464 return; // already selected.
15467 this.clearSelections(true);
15470 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15471 Roo.fly(node).addClass(this.selectedClass);
15472 this.selections.push(node);
15473 if(!suppressEvent){
15474 this.fireEvent("selectionchange", this, this.selections);
15482 * @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
15483 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15484 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15486 unselect : function(nodeInfo, keepExisting, suppressEvent)
15488 if(nodeInfo instanceof Array){
15489 Roo.each(this.selections, function(s) {
15490 this.unselect(s, nodeInfo);
15494 var node = this.getNode(nodeInfo);
15495 if(!node || !this.isSelected(node)){
15496 //Roo.log("not selected");
15497 return; // not selected.
15501 Roo.each(this.selections, function(s) {
15503 Roo.fly(node).removeClass(this.selectedClass);
15510 this.selections= ns;
15511 this.fireEvent("selectionchange", this, this.selections);
15515 * Gets a template node.
15516 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15517 * @return {HTMLElement} The node or null if it wasn't found
15519 getNode : function(nodeInfo){
15520 if(typeof nodeInfo == "string"){
15521 return document.getElementById(nodeInfo);
15522 }else if(typeof nodeInfo == "number"){
15523 return this.nodes[nodeInfo];
15529 * Gets a range template nodes.
15530 * @param {Number} startIndex
15531 * @param {Number} endIndex
15532 * @return {Array} An array of nodes
15534 getNodes : function(start, end){
15535 var ns = this.nodes;
15536 start = start || 0;
15537 end = typeof end == "undefined" ? ns.length - 1 : end;
15540 for(var i = start; i <= end; i++){
15544 for(var i = start; i >= end; i--){
15552 * Finds the index of the passed node
15553 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15554 * @return {Number} The index of the node or -1
15556 indexOf : function(node){
15557 node = this.getNode(node);
15558 if(typeof node.nodeIndex == "number"){
15559 return node.nodeIndex;
15561 var ns = this.nodes;
15562 for(var i = 0, len = ns.length; i < len; i++){
15573 * based on jquery fullcalendar
15577 Roo.bootstrap = Roo.bootstrap || {};
15579 * @class Roo.bootstrap.Calendar
15580 * @extends Roo.bootstrap.Component
15581 * Bootstrap Calendar class
15582 * @cfg {Boolean} loadMask (true|false) default false
15583 * @cfg {Object} header generate the user specific header of the calendar, default false
15586 * Create a new Container
15587 * @param {Object} config The config object
15592 Roo.bootstrap.Calendar = function(config){
15593 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15597 * Fires when a date is selected
15598 * @param {DatePicker} this
15599 * @param {Date} date The selected date
15603 * @event monthchange
15604 * Fires when the displayed month changes
15605 * @param {DatePicker} this
15606 * @param {Date} date The selected month
15608 'monthchange': true,
15610 * @event evententer
15611 * Fires when mouse over an event
15612 * @param {Calendar} this
15613 * @param {event} Event
15615 'evententer': true,
15617 * @event eventleave
15618 * Fires when the mouse leaves an
15619 * @param {Calendar} this
15622 'eventleave': true,
15624 * @event eventclick
15625 * Fires when the mouse click an
15626 * @param {Calendar} this
15635 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
15638 * @cfg {Number} startDay
15639 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15647 getAutoCreate : function(){
15650 var fc_button = function(name, corner, style, content ) {
15651 return Roo.apply({},{
15653 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
15655 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15658 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15669 style : 'width:100%',
15676 cls : 'fc-header-left',
15678 fc_button('prev', 'left', 'arrow', '‹' ),
15679 fc_button('next', 'right', 'arrow', '›' ),
15680 { tag: 'span', cls: 'fc-header-space' },
15681 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
15689 cls : 'fc-header-center',
15693 cls: 'fc-header-title',
15696 html : 'month / year'
15704 cls : 'fc-header-right',
15706 /* fc_button('month', 'left', '', 'month' ),
15707 fc_button('week', '', '', 'week' ),
15708 fc_button('day', 'right', '', 'day' )
15720 header = this.header;
15723 var cal_heads = function() {
15725 // fixme - handle this.
15727 for (var i =0; i < Date.dayNames.length; i++) {
15728 var d = Date.dayNames[i];
15731 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15732 html : d.substring(0,3)
15736 ret[0].cls += ' fc-first';
15737 ret[6].cls += ' fc-last';
15740 var cal_cell = function(n) {
15743 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15748 cls: 'fc-day-number',
15752 cls: 'fc-day-content',
15756 style: 'position: relative;' // height: 17px;
15768 var cal_rows = function() {
15771 for (var r = 0; r < 6; r++) {
15778 for (var i =0; i < Date.dayNames.length; i++) {
15779 var d = Date.dayNames[i];
15780 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15783 row.cn[0].cls+=' fc-first';
15784 row.cn[0].cn[0].style = 'min-height:90px';
15785 row.cn[6].cls+=' fc-last';
15789 ret[0].cls += ' fc-first';
15790 ret[4].cls += ' fc-prev-last';
15791 ret[5].cls += ' fc-last';
15798 cls: 'fc-border-separate',
15799 style : 'width:100%',
15807 cls : 'fc-first fc-last',
15825 cls : 'fc-content',
15826 style : "position: relative;",
15829 cls : 'fc-view fc-view-month fc-grid',
15830 style : 'position: relative',
15831 unselectable : 'on',
15834 cls : 'fc-event-container',
15835 style : 'position:absolute;z-index:8;top:0;left:0;'
15853 initEvents : function()
15856 throw "can not find store for calendar";
15862 style: "text-align:center",
15866 style: "background-color:white;width:50%;margin:250 auto",
15870 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15881 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15883 var size = this.el.select('.fc-content', true).first().getSize();
15884 this.maskEl.setSize(size.width, size.height);
15885 this.maskEl.enableDisplayMode("block");
15886 if(!this.loadMask){
15887 this.maskEl.hide();
15890 this.store = Roo.factory(this.store, Roo.data);
15891 this.store.on('load', this.onLoad, this);
15892 this.store.on('beforeload', this.onBeforeLoad, this);
15896 this.cells = this.el.select('.fc-day',true);
15897 //Roo.log(this.cells);
15898 this.textNodes = this.el.query('.fc-day-number');
15899 this.cells.addClassOnOver('fc-state-hover');
15901 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15902 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15903 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15904 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15906 this.on('monthchange', this.onMonthChange, this);
15908 this.update(new Date().clearTime());
15911 resize : function() {
15912 var sz = this.el.getSize();
15914 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15915 this.el.select('.fc-day-content div',true).setHeight(34);
15920 showPrevMonth : function(e){
15921 this.update(this.activeDate.add("mo", -1));
15923 showToday : function(e){
15924 this.update(new Date().clearTime());
15927 showNextMonth : function(e){
15928 this.update(this.activeDate.add("mo", 1));
15932 showPrevYear : function(){
15933 this.update(this.activeDate.add("y", -1));
15937 showNextYear : function(){
15938 this.update(this.activeDate.add("y", 1));
15943 update : function(date)
15945 var vd = this.activeDate;
15946 this.activeDate = date;
15947 // if(vd && this.el){
15948 // var t = date.getTime();
15949 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15950 // Roo.log('using add remove');
15952 // this.fireEvent('monthchange', this, date);
15954 // this.cells.removeClass("fc-state-highlight");
15955 // this.cells.each(function(c){
15956 // if(c.dateValue == t){
15957 // c.addClass("fc-state-highlight");
15958 // setTimeout(function(){
15959 // try{c.dom.firstChild.focus();}catch(e){}
15969 var days = date.getDaysInMonth();
15971 var firstOfMonth = date.getFirstDateOfMonth();
15972 var startingPos = firstOfMonth.getDay()-this.startDay;
15974 if(startingPos < this.startDay){
15978 var pm = date.add(Date.MONTH, -1);
15979 var prevStart = pm.getDaysInMonth()-startingPos;
15981 this.cells = this.el.select('.fc-day',true);
15982 this.textNodes = this.el.query('.fc-day-number');
15983 this.cells.addClassOnOver('fc-state-hover');
15985 var cells = this.cells.elements;
15986 var textEls = this.textNodes;
15988 Roo.each(cells, function(cell){
15989 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15992 days += startingPos;
15994 // convert everything to numbers so it's fast
15995 var day = 86400000;
15996 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15999 //Roo.log(prevStart);
16001 var today = new Date().clearTime().getTime();
16002 var sel = date.clearTime().getTime();
16003 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16004 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16005 var ddMatch = this.disabledDatesRE;
16006 var ddText = this.disabledDatesText;
16007 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16008 var ddaysText = this.disabledDaysText;
16009 var format = this.format;
16011 var setCellClass = function(cal, cell){
16015 //Roo.log('set Cell Class');
16017 var t = d.getTime();
16021 cell.dateValue = t;
16023 cell.className += " fc-today";
16024 cell.className += " fc-state-highlight";
16025 cell.title = cal.todayText;
16028 // disable highlight in other month..
16029 //cell.className += " fc-state-highlight";
16034 cell.className = " fc-state-disabled";
16035 cell.title = cal.minText;
16039 cell.className = " fc-state-disabled";
16040 cell.title = cal.maxText;
16044 if(ddays.indexOf(d.getDay()) != -1){
16045 cell.title = ddaysText;
16046 cell.className = " fc-state-disabled";
16049 if(ddMatch && format){
16050 var fvalue = d.dateFormat(format);
16051 if(ddMatch.test(fvalue)){
16052 cell.title = ddText.replace("%0", fvalue);
16053 cell.className = " fc-state-disabled";
16057 if (!cell.initialClassName) {
16058 cell.initialClassName = cell.dom.className;
16061 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16066 for(; i < startingPos; i++) {
16067 textEls[i].innerHTML = (++prevStart);
16068 d.setDate(d.getDate()+1);
16070 cells[i].className = "fc-past fc-other-month";
16071 setCellClass(this, cells[i]);
16076 for(; i < days; i++){
16077 intDay = i - startingPos + 1;
16078 textEls[i].innerHTML = (intDay);
16079 d.setDate(d.getDate()+1);
16081 cells[i].className = ''; // "x-date-active";
16082 setCellClass(this, cells[i]);
16086 for(; i < 42; i++) {
16087 textEls[i].innerHTML = (++extraDays);
16088 d.setDate(d.getDate()+1);
16090 cells[i].className = "fc-future fc-other-month";
16091 setCellClass(this, cells[i]);
16094 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16096 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16098 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16099 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16101 if(totalRows != 6){
16102 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16103 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16106 this.fireEvent('monthchange', this, date);
16110 if(!this.internalRender){
16111 var main = this.el.dom.firstChild;
16112 var w = main.offsetWidth;
16113 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16114 Roo.fly(main).setWidth(w);
16115 this.internalRender = true;
16116 // opera does not respect the auto grow header center column
16117 // then, after it gets a width opera refuses to recalculate
16118 // without a second pass
16119 if(Roo.isOpera && !this.secondPass){
16120 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16121 this.secondPass = true;
16122 this.update.defer(10, this, [date]);
16129 findCell : function(dt) {
16130 dt = dt.clearTime().getTime();
16132 this.cells.each(function(c){
16133 //Roo.log("check " +c.dateValue + '?=' + dt);
16134 if(c.dateValue == dt){
16144 findCells : function(ev) {
16145 var s = ev.start.clone().clearTime().getTime();
16147 var e= ev.end.clone().clearTime().getTime();
16150 this.cells.each(function(c){
16151 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16153 if(c.dateValue > e){
16156 if(c.dateValue < s){
16165 // findBestRow: function(cells)
16169 // for (var i =0 ; i < cells.length;i++) {
16170 // ret = Math.max(cells[i].rows || 0,ret);
16177 addItem : function(ev)
16179 // look for vertical location slot in
16180 var cells = this.findCells(ev);
16182 // ev.row = this.findBestRow(cells);
16184 // work out the location.
16188 for(var i =0; i < cells.length; i++) {
16190 cells[i].row = cells[0].row;
16193 cells[i].row = cells[i].row + 1;
16203 if (crow.start.getY() == cells[i].getY()) {
16205 crow.end = cells[i];
16222 cells[0].events.push(ev);
16224 this.calevents.push(ev);
16227 clearEvents: function() {
16229 if(!this.calevents){
16233 Roo.each(this.cells.elements, function(c){
16239 Roo.each(this.calevents, function(e) {
16240 Roo.each(e.els, function(el) {
16241 el.un('mouseenter' ,this.onEventEnter, this);
16242 el.un('mouseleave' ,this.onEventLeave, this);
16247 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16253 renderEvents: function()
16257 this.cells.each(function(c) {
16266 if(c.row != c.events.length){
16267 r = 4 - (4 - (c.row - c.events.length));
16270 c.events = ev.slice(0, r);
16271 c.more = ev.slice(r);
16273 if(c.more.length && c.more.length == 1){
16274 c.events.push(c.more.pop());
16277 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16281 this.cells.each(function(c) {
16283 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16286 for (var e = 0; e < c.events.length; e++){
16287 var ev = c.events[e];
16288 var rows = ev.rows;
16290 for(var i = 0; i < rows.length; i++) {
16292 // how many rows should it span..
16295 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16296 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16298 unselectable : "on",
16301 cls: 'fc-event-inner',
16305 // cls: 'fc-event-time',
16306 // html : cells.length > 1 ? '' : ev.time
16310 cls: 'fc-event-title',
16311 html : String.format('{0}', ev.title)
16318 cls: 'ui-resizable-handle ui-resizable-e',
16319 html : '  '
16326 cfg.cls += ' fc-event-start';
16328 if ((i+1) == rows.length) {
16329 cfg.cls += ' fc-event-end';
16332 var ctr = _this.el.select('.fc-event-container',true).first();
16333 var cg = ctr.createChild(cfg);
16335 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16336 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16338 var r = (c.more.length) ? 1 : 0;
16339 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
16340 cg.setWidth(ebox.right - sbox.x -2);
16342 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16343 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16344 cg.on('click', _this.onEventClick, _this, ev);
16355 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16356 style : 'position: absolute',
16357 unselectable : "on",
16360 cls: 'fc-event-inner',
16364 cls: 'fc-event-title',
16372 cls: 'ui-resizable-handle ui-resizable-e',
16373 html : '  '
16379 var ctr = _this.el.select('.fc-event-container',true).first();
16380 var cg = ctr.createChild(cfg);
16382 var sbox = c.select('.fc-day-content',true).first().getBox();
16383 var ebox = c.select('.fc-day-content',true).first().getBox();
16385 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
16386 cg.setWidth(ebox.right - sbox.x -2);
16388 cg.on('click', _this.onMoreEventClick, _this, c.more);
16398 onEventEnter: function (e, el,event,d) {
16399 this.fireEvent('evententer', this, el, event);
16402 onEventLeave: function (e, el,event,d) {
16403 this.fireEvent('eventleave', this, el, event);
16406 onEventClick: function (e, el,event,d) {
16407 this.fireEvent('eventclick', this, el, event);
16410 onMonthChange: function () {
16414 onMoreEventClick: function(e, el, more)
16418 this.calpopover.placement = 'right';
16419 this.calpopover.setTitle('More');
16421 this.calpopover.setContent('');
16423 var ctr = this.calpopover.el.select('.popover-content', true).first();
16425 Roo.each(more, function(m){
16427 cls : 'fc-event-hori fc-event-draggable',
16430 var cg = ctr.createChild(cfg);
16432 cg.on('click', _this.onEventClick, _this, m);
16435 this.calpopover.show(el);
16440 onLoad: function ()
16442 this.calevents = [];
16445 if(this.store.getCount() > 0){
16446 this.store.data.each(function(d){
16449 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16450 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16451 time : d.data.start_time,
16452 title : d.data.title,
16453 description : d.data.description,
16454 venue : d.data.venue
16459 this.renderEvents();
16461 if(this.calevents.length && this.loadMask){
16462 this.maskEl.hide();
16466 onBeforeLoad: function()
16468 this.clearEvents();
16470 this.maskEl.show();
16484 * @class Roo.bootstrap.Popover
16485 * @extends Roo.bootstrap.Component
16486 * Bootstrap Popover class
16487 * @cfg {String} html contents of the popover (or false to use children..)
16488 * @cfg {String} title of popover (or false to hide)
16489 * @cfg {String} placement how it is placed
16490 * @cfg {String} trigger click || hover (or false to trigger manually)
16491 * @cfg {String} over what (parent or false to trigger manually.)
16492 * @cfg {Number} delay - delay before showing
16495 * Create a new Popover
16496 * @param {Object} config The config object
16499 Roo.bootstrap.Popover = function(config){
16500 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16506 * After the popover show
16508 * @param {Roo.bootstrap.Popover} this
16513 * After the popover hide
16515 * @param {Roo.bootstrap.Popover} this
16521 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
16523 title: 'Fill in a title',
16526 placement : 'right',
16527 trigger : 'hover', // hover
16533 can_build_overlaid : false,
16535 getChildContainer : function()
16537 return this.el.select('.popover-content',true).first();
16540 getAutoCreate : function(){
16543 cls : 'popover roo-dynamic',
16544 style: 'display:block',
16550 cls : 'popover-inner',
16554 cls: 'popover-title',
16558 cls : 'popover-content',
16569 setTitle: function(str)
16572 this.el.select('.popover-title',true).first().dom.innerHTML = str;
16574 setContent: function(str)
16577 this.el.select('.popover-content',true).first().dom.innerHTML = str;
16579 // as it get's added to the bottom of the page.
16580 onRender : function(ct, position)
16582 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16584 var cfg = Roo.apply({}, this.getAutoCreate());
16588 cfg.cls += ' ' + this.cls;
16591 cfg.style = this.style;
16593 //Roo.log("adding to ");
16594 this.el = Roo.get(document.body).createChild(cfg, position);
16595 // Roo.log(this.el);
16600 initEvents : function()
16602 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16603 this.el.enableDisplayMode('block');
16605 if (this.over === false) {
16608 if (this.triggers === false) {
16611 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16612 var triggers = this.trigger ? this.trigger.split(' ') : [];
16613 Roo.each(triggers, function(trigger) {
16615 if (trigger == 'click') {
16616 on_el.on('click', this.toggle, this);
16617 } else if (trigger != 'manual') {
16618 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
16619 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16621 on_el.on(eventIn ,this.enter, this);
16622 on_el.on(eventOut, this.leave, this);
16633 toggle : function () {
16634 this.hoverState == 'in' ? this.leave() : this.enter();
16637 enter : function () {
16639 clearTimeout(this.timeout);
16641 this.hoverState = 'in';
16643 if (!this.delay || !this.delay.show) {
16648 this.timeout = setTimeout(function () {
16649 if (_t.hoverState == 'in') {
16652 }, this.delay.show)
16655 leave : function() {
16656 clearTimeout(this.timeout);
16658 this.hoverState = 'out';
16660 if (!this.delay || !this.delay.hide) {
16665 this.timeout = setTimeout(function () {
16666 if (_t.hoverState == 'out') {
16669 }, this.delay.hide)
16672 show : function (on_el)
16675 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16679 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16680 if (this.html !== false) {
16681 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16683 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16684 if (!this.title.length) {
16685 this.el.select('.popover-title',true).hide();
16688 var placement = typeof this.placement == 'function' ?
16689 this.placement.call(this, this.el, on_el) :
16692 var autoToken = /\s?auto?\s?/i;
16693 var autoPlace = autoToken.test(placement);
16695 placement = placement.replace(autoToken, '') || 'top';
16699 //this.el.setXY([0,0]);
16701 this.el.dom.style.display='block';
16702 this.el.addClass(placement);
16704 //this.el.appendTo(on_el);
16706 var p = this.getPosition();
16707 var box = this.el.getBox();
16712 var align = Roo.bootstrap.Popover.alignment[placement];
16713 this.el.alignTo(on_el, align[0],align[1]);
16714 //var arrow = this.el.select('.arrow',true).first();
16715 //arrow.set(align[2],
16717 this.el.addClass('in');
16720 if (this.el.hasClass('fade')) {
16724 this.hoverState = 'in';
16726 this.fireEvent('show', this);
16731 this.el.setXY([0,0]);
16732 this.el.removeClass('in');
16734 this.hoverState = null;
16736 this.fireEvent('hide', this);
16741 Roo.bootstrap.Popover.alignment = {
16742 'left' : ['r-l', [-10,0], 'right'],
16743 'right' : ['l-r', [10,0], 'left'],
16744 'bottom' : ['t-b', [0,10], 'top'],
16745 'top' : [ 'b-t', [0,-10], 'bottom']
16756 * @class Roo.bootstrap.Progress
16757 * @extends Roo.bootstrap.Component
16758 * Bootstrap Progress class
16759 * @cfg {Boolean} striped striped of the progress bar
16760 * @cfg {Boolean} active animated of the progress bar
16764 * Create a new Progress
16765 * @param {Object} config The config object
16768 Roo.bootstrap.Progress = function(config){
16769 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16772 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
16777 getAutoCreate : function(){
16785 cfg.cls += ' progress-striped';
16789 cfg.cls += ' active';
16808 * @class Roo.bootstrap.ProgressBar
16809 * @extends Roo.bootstrap.Component
16810 * Bootstrap ProgressBar class
16811 * @cfg {Number} aria_valuenow aria-value now
16812 * @cfg {Number} aria_valuemin aria-value min
16813 * @cfg {Number} aria_valuemax aria-value max
16814 * @cfg {String} label label for the progress bar
16815 * @cfg {String} panel (success | info | warning | danger )
16816 * @cfg {String} role role of the progress bar
16817 * @cfg {String} sr_only text
16821 * Create a new ProgressBar
16822 * @param {Object} config The config object
16825 Roo.bootstrap.ProgressBar = function(config){
16826 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16829 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16833 aria_valuemax : 100,
16839 getAutoCreate : function()
16844 cls: 'progress-bar',
16845 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16857 cfg.role = this.role;
16860 if(this.aria_valuenow){
16861 cfg['aria-valuenow'] = this.aria_valuenow;
16864 if(this.aria_valuemin){
16865 cfg['aria-valuemin'] = this.aria_valuemin;
16868 if(this.aria_valuemax){
16869 cfg['aria-valuemax'] = this.aria_valuemax;
16872 if(this.label && !this.sr_only){
16873 cfg.html = this.label;
16877 cfg.cls += ' progress-bar-' + this.panel;
16883 update : function(aria_valuenow)
16885 this.aria_valuenow = aria_valuenow;
16887 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16902 * @class Roo.bootstrap.TabGroup
16903 * @extends Roo.bootstrap.Column
16904 * Bootstrap Column class
16905 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16906 * @cfg {Boolean} carousel true to make the group behave like a carousel
16907 * @cfg {Boolean} bullets show bullets for the panels
16908 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16909 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16910 * @cfg {Boolean} showarrow (true|false) show arrow default true
16913 * Create a new TabGroup
16914 * @param {Object} config The config object
16917 Roo.bootstrap.TabGroup = function(config){
16918 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16920 this.navId = Roo.id();
16923 Roo.bootstrap.TabGroup.register(this);
16927 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16930 transition : false,
16935 slideOnTouch : false,
16938 getAutoCreate : function()
16940 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16942 cfg.cls += ' tab-content';
16944 if (this.carousel) {
16945 cfg.cls += ' carousel slide';
16948 cls : 'carousel-inner',
16952 if(this.bullets && !Roo.isTouch){
16955 cls : 'carousel-bullets',
16959 if(this.bullets_cls){
16960 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16967 cfg.cn[0].cn.push(bullets);
16970 if(this.showarrow){
16971 cfg.cn[0].cn.push({
16973 class : 'carousel-arrow',
16977 class : 'carousel-prev',
16981 class : 'fa fa-chevron-left'
16987 class : 'carousel-next',
16991 class : 'fa fa-chevron-right'
17004 initEvents: function()
17006 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17007 // this.el.on("touchstart", this.onTouchStart, this);
17010 if(this.autoslide){
17013 this.slideFn = window.setInterval(function() {
17014 _this.showPanelNext();
17018 if(this.showarrow){
17019 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17020 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17026 // onTouchStart : function(e, el, o)
17028 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17032 // this.showPanelNext();
17036 getChildContainer : function()
17038 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17042 * register a Navigation item
17043 * @param {Roo.bootstrap.NavItem} the navitem to add
17045 register : function(item)
17047 this.tabs.push( item);
17048 item.navId = this.navId; // not really needed..
17053 getActivePanel : function()
17056 Roo.each(this.tabs, function(t) {
17066 getPanelByName : function(n)
17069 Roo.each(this.tabs, function(t) {
17070 if (t.tabId == n) {
17078 indexOfPanel : function(p)
17081 Roo.each(this.tabs, function(t,i) {
17082 if (t.tabId == p.tabId) {
17091 * show a specific panel
17092 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17093 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17095 showPanel : function (pan)
17097 if(this.transition || typeof(pan) == 'undefined'){
17098 Roo.log("waiting for the transitionend");
17102 if (typeof(pan) == 'number') {
17103 pan = this.tabs[pan];
17106 if (typeof(pan) == 'string') {
17107 pan = this.getPanelByName(pan);
17110 var cur = this.getActivePanel();
17113 Roo.log('pan or acitve pan is undefined');
17117 if (pan.tabId == this.getActivePanel().tabId) {
17121 if (false === cur.fireEvent('beforedeactivate')) {
17125 if(this.bullets > 0 && !Roo.isTouch){
17126 this.setActiveBullet(this.indexOfPanel(pan));
17129 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17131 this.transition = true;
17132 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
17133 var lr = dir == 'next' ? 'left' : 'right';
17134 pan.el.addClass(dir); // or prev
17135 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17136 cur.el.addClass(lr); // or right
17137 pan.el.addClass(lr);
17140 cur.el.on('transitionend', function() {
17141 Roo.log("trans end?");
17143 pan.el.removeClass([lr,dir]);
17144 pan.setActive(true);
17146 cur.el.removeClass([lr]);
17147 cur.setActive(false);
17149 _this.transition = false;
17151 }, this, { single: true } );
17156 cur.setActive(false);
17157 pan.setActive(true);
17162 showPanelNext : function()
17164 var i = this.indexOfPanel(this.getActivePanel());
17166 if (i >= this.tabs.length - 1 && !this.autoslide) {
17170 if (i >= this.tabs.length - 1 && this.autoslide) {
17174 this.showPanel(this.tabs[i+1]);
17177 showPanelPrev : function()
17179 var i = this.indexOfPanel(this.getActivePanel());
17181 if (i < 1 && !this.autoslide) {
17185 if (i < 1 && this.autoslide) {
17186 i = this.tabs.length;
17189 this.showPanel(this.tabs[i-1]);
17193 addBullet: function()
17195 if(!this.bullets || Roo.isTouch){
17198 var ctr = this.el.select('.carousel-bullets',true).first();
17199 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17200 var bullet = ctr.createChild({
17201 cls : 'bullet bullet-' + i
17202 },ctr.dom.lastChild);
17207 bullet.on('click', (function(e, el, o, ii, t){
17209 e.preventDefault();
17211 this.showPanel(ii);
17213 if(this.autoslide && this.slideFn){
17214 clearInterval(this.slideFn);
17215 this.slideFn = window.setInterval(function() {
17216 _this.showPanelNext();
17220 }).createDelegate(this, [i, bullet], true));
17225 setActiveBullet : function(i)
17231 Roo.each(this.el.select('.bullet', true).elements, function(el){
17232 el.removeClass('selected');
17235 var bullet = this.el.select('.bullet-' + i, true).first();
17241 bullet.addClass('selected');
17252 Roo.apply(Roo.bootstrap.TabGroup, {
17256 * register a Navigation Group
17257 * @param {Roo.bootstrap.NavGroup} the navgroup to add
17259 register : function(navgrp)
17261 this.groups[navgrp.navId] = navgrp;
17265 * fetch a Navigation Group based on the navigation ID
17266 * if one does not exist , it will get created.
17267 * @param {string} the navgroup to add
17268 * @returns {Roo.bootstrap.NavGroup} the navgroup
17270 get: function(navId) {
17271 if (typeof(this.groups[navId]) == 'undefined') {
17272 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17274 return this.groups[navId] ;
17289 * @class Roo.bootstrap.TabPanel
17290 * @extends Roo.bootstrap.Component
17291 * Bootstrap TabPanel class
17292 * @cfg {Boolean} active panel active
17293 * @cfg {String} html panel content
17294 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17295 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17296 * @cfg {String} href click to link..
17300 * Create a new TabPanel
17301 * @param {Object} config The config object
17304 Roo.bootstrap.TabPanel = function(config){
17305 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17309 * Fires when the active status changes
17310 * @param {Roo.bootstrap.TabPanel} this
17311 * @param {Boolean} state the new state
17316 * @event beforedeactivate
17317 * Fires before a tab is de-activated - can be used to do validation on a form.
17318 * @param {Roo.bootstrap.TabPanel} this
17319 * @return {Boolean} false if there is an error
17322 'beforedeactivate': true
17325 this.tabId = this.tabId || Roo.id();
17329 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
17337 getAutoCreate : function(){
17340 // item is needed for carousel - not sure if it has any effect otherwise
17341 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17342 html: this.html || ''
17346 cfg.cls += ' active';
17350 cfg.tabId = this.tabId;
17357 initEvents: function()
17359 var p = this.parent();
17361 this.navId = this.navId || p.navId;
17363 if (typeof(this.navId) != 'undefined') {
17364 // not really needed.. but just in case.. parent should be a NavGroup.
17365 var tg = Roo.bootstrap.TabGroup.get(this.navId);
17369 var i = tg.tabs.length - 1;
17371 if(this.active && tg.bullets > 0 && i < tg.bullets){
17372 tg.setActiveBullet(i);
17376 this.el.on('click', this.onClick, this);
17379 this.el.on("touchstart", this.onTouchStart, this);
17380 this.el.on("touchmove", this.onTouchMove, this);
17381 this.el.on("touchend", this.onTouchEnd, this);
17386 onRender : function(ct, position)
17388 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17391 setActive : function(state)
17393 Roo.log("panel - set active " + this.tabId + "=" + state);
17395 this.active = state;
17397 this.el.removeClass('active');
17399 } else if (!this.el.hasClass('active')) {
17400 this.el.addClass('active');
17403 this.fireEvent('changed', this, state);
17406 onClick : function(e)
17408 e.preventDefault();
17410 if(!this.href.length){
17414 window.location.href = this.href;
17423 onTouchStart : function(e)
17425 this.swiping = false;
17427 this.startX = e.browserEvent.touches[0].clientX;
17428 this.startY = e.browserEvent.touches[0].clientY;
17431 onTouchMove : function(e)
17433 this.swiping = true;
17435 this.endX = e.browserEvent.touches[0].clientX;
17436 this.endY = e.browserEvent.touches[0].clientY;
17439 onTouchEnd : function(e)
17446 var tabGroup = this.parent();
17448 if(this.endX > this.startX){ // swiping right
17449 tabGroup.showPanelPrev();
17453 if(this.startX > this.endX){ // swiping left
17454 tabGroup.showPanelNext();
17473 * @class Roo.bootstrap.DateField
17474 * @extends Roo.bootstrap.Input
17475 * Bootstrap DateField class
17476 * @cfg {Number} weekStart default 0
17477 * @cfg {String} viewMode default empty, (months|years)
17478 * @cfg {String} minViewMode default empty, (months|years)
17479 * @cfg {Number} startDate default -Infinity
17480 * @cfg {Number} endDate default Infinity
17481 * @cfg {Boolean} todayHighlight default false
17482 * @cfg {Boolean} todayBtn default false
17483 * @cfg {Boolean} calendarWeeks default false
17484 * @cfg {Object} daysOfWeekDisabled default empty
17485 * @cfg {Boolean} singleMode default false (true | false)
17487 * @cfg {Boolean} keyboardNavigation default true
17488 * @cfg {String} language default en
17491 * Create a new DateField
17492 * @param {Object} config The config object
17495 Roo.bootstrap.DateField = function(config){
17496 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17500 * Fires when this field show.
17501 * @param {Roo.bootstrap.DateField} this
17502 * @param {Mixed} date The date value
17507 * Fires when this field hide.
17508 * @param {Roo.bootstrap.DateField} this
17509 * @param {Mixed} date The date value
17514 * Fires when select a date.
17515 * @param {Roo.bootstrap.DateField} this
17516 * @param {Mixed} date The date value
17520 * @event beforeselect
17521 * Fires when before select a date.
17522 * @param {Roo.bootstrap.DateField} this
17523 * @param {Mixed} date The date value
17525 beforeselect : true
17529 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
17532 * @cfg {String} format
17533 * The default date format string which can be overriden for localization support. The format must be
17534 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17538 * @cfg {String} altFormats
17539 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17540 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17542 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17550 todayHighlight : false,
17556 keyboardNavigation: true,
17558 calendarWeeks: false,
17560 startDate: -Infinity,
17564 daysOfWeekDisabled: [],
17568 singleMode : false,
17570 UTCDate: function()
17572 return new Date(Date.UTC.apply(Date, arguments));
17575 UTCToday: function()
17577 var today = new Date();
17578 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17581 getDate: function() {
17582 var d = this.getUTCDate();
17583 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17586 getUTCDate: function() {
17590 setDate: function(d) {
17591 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17594 setUTCDate: function(d) {
17596 this.setValue(this.formatDate(this.date));
17599 onRender: function(ct, position)
17602 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17604 this.language = this.language || 'en';
17605 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17606 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17608 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17609 this.format = this.format || 'm/d/y';
17610 this.isInline = false;
17611 this.isInput = true;
17612 this.component = this.el.select('.add-on', true).first() || false;
17613 this.component = (this.component && this.component.length === 0) ? false : this.component;
17614 this.hasInput = this.component && this.inputEl().length;
17616 if (typeof(this.minViewMode === 'string')) {
17617 switch (this.minViewMode) {
17619 this.minViewMode = 1;
17622 this.minViewMode = 2;
17625 this.minViewMode = 0;
17630 if (typeof(this.viewMode === 'string')) {
17631 switch (this.viewMode) {
17644 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17646 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17648 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17650 this.picker().on('mousedown', this.onMousedown, this);
17651 this.picker().on('click', this.onClick, this);
17653 this.picker().addClass('datepicker-dropdown');
17655 this.startViewMode = this.viewMode;
17657 if(this.singleMode){
17658 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17659 v.setVisibilityMode(Roo.Element.DISPLAY);
17663 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17664 v.setStyle('width', '189px');
17668 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17669 if(!this.calendarWeeks){
17674 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17675 v.attr('colspan', function(i, val){
17676 return parseInt(val) + 1;
17681 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17683 this.setStartDate(this.startDate);
17684 this.setEndDate(this.endDate);
17686 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17693 if(this.isInline) {
17698 picker : function()
17700 return this.pickerEl;
17701 // return this.el.select('.datepicker', true).first();
17704 fillDow: function()
17706 var dowCnt = this.weekStart;
17715 if(this.calendarWeeks){
17723 while (dowCnt < this.weekStart + 7) {
17727 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17731 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17734 fillMonths: function()
17737 var months = this.picker().select('>.datepicker-months td', true).first();
17739 months.dom.innerHTML = '';
17745 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17748 months.createChild(month);
17755 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;
17757 if (this.date < this.startDate) {
17758 this.viewDate = new Date(this.startDate);
17759 } else if (this.date > this.endDate) {
17760 this.viewDate = new Date(this.endDate);
17762 this.viewDate = new Date(this.date);
17770 var d = new Date(this.viewDate),
17771 year = d.getUTCFullYear(),
17772 month = d.getUTCMonth(),
17773 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17774 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17775 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17776 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17777 currentDate = this.date && this.date.valueOf(),
17778 today = this.UTCToday();
17780 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17782 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17784 // this.picker.select('>tfoot th.today').
17785 // .text(dates[this.language].today)
17786 // .toggle(this.todayBtn !== false);
17788 this.updateNavArrows();
17791 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17793 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17795 prevMonth.setUTCDate(day);
17797 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17799 var nextMonth = new Date(prevMonth);
17801 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17803 nextMonth = nextMonth.valueOf();
17805 var fillMonths = false;
17807 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17809 while(prevMonth.valueOf() < nextMonth) {
17812 if (prevMonth.getUTCDay() === this.weekStart) {
17814 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17822 if(this.calendarWeeks){
17823 // ISO 8601: First week contains first thursday.
17824 // ISO also states week starts on Monday, but we can be more abstract here.
17826 // Start of current week: based on weekstart/current date
17827 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17828 // Thursday of this week
17829 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17830 // First Thursday of year, year from thursday
17831 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17832 // Calendar week: ms between thursdays, div ms per day, div 7 days
17833 calWeek = (th - yth) / 864e5 / 7 + 1;
17835 fillMonths.cn.push({
17843 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17845 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17848 if (this.todayHighlight &&
17849 prevMonth.getUTCFullYear() == today.getFullYear() &&
17850 prevMonth.getUTCMonth() == today.getMonth() &&
17851 prevMonth.getUTCDate() == today.getDate()) {
17852 clsName += ' today';
17855 if (currentDate && prevMonth.valueOf() === currentDate) {
17856 clsName += ' active';
17859 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17860 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17861 clsName += ' disabled';
17864 fillMonths.cn.push({
17866 cls: 'day ' + clsName,
17867 html: prevMonth.getDate()
17870 prevMonth.setDate(prevMonth.getDate()+1);
17873 var currentYear = this.date && this.date.getUTCFullYear();
17874 var currentMonth = this.date && this.date.getUTCMonth();
17876 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17878 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17879 v.removeClass('active');
17881 if(currentYear === year && k === currentMonth){
17882 v.addClass('active');
17885 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17886 v.addClass('disabled');
17892 year = parseInt(year/10, 10) * 10;
17894 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17896 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17899 for (var i = -1; i < 11; i++) {
17900 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17902 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17910 showMode: function(dir)
17913 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17916 Roo.each(this.picker().select('>div',true).elements, function(v){
17917 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17920 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17925 if(this.isInline) {
17929 this.picker().removeClass(['bottom', 'top']);
17931 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17933 * place to the top of element!
17937 this.picker().addClass('top');
17938 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17943 this.picker().addClass('bottom');
17945 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17948 parseDate : function(value)
17950 if(!value || value instanceof Date){
17953 var v = Date.parseDate(value, this.format);
17954 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17955 v = Date.parseDate(value, 'Y-m-d');
17957 if(!v && this.altFormats){
17958 if(!this.altFormatsArray){
17959 this.altFormatsArray = this.altFormats.split("|");
17961 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17962 v = Date.parseDate(value, this.altFormatsArray[i]);
17968 formatDate : function(date, fmt)
17970 return (!date || !(date instanceof Date)) ?
17971 date : date.dateFormat(fmt || this.format);
17974 onFocus : function()
17976 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17980 onBlur : function()
17982 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17984 var d = this.inputEl().getValue();
17993 this.picker().show();
17997 this.fireEvent('show', this, this.date);
18002 if(this.isInline) {
18005 this.picker().hide();
18006 this.viewMode = this.startViewMode;
18009 this.fireEvent('hide', this, this.date);
18013 onMousedown: function(e)
18015 e.stopPropagation();
18016 e.preventDefault();
18021 Roo.bootstrap.DateField.superclass.keyup.call(this);
18025 setValue: function(v)
18027 if(this.fireEvent('beforeselect', this, v) !== false){
18028 var d = new Date(this.parseDate(v) ).clearTime();
18030 if(isNaN(d.getTime())){
18031 this.date = this.viewDate = '';
18032 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18036 v = this.formatDate(d);
18038 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18040 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18044 this.fireEvent('select', this, this.date);
18048 getValue: function()
18050 return this.formatDate(this.date);
18053 fireKey: function(e)
18055 if (!this.picker().isVisible()){
18056 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18062 var dateChanged = false,
18064 newDate, newViewDate;
18069 e.preventDefault();
18073 if (!this.keyboardNavigation) {
18076 dir = e.keyCode == 37 ? -1 : 1;
18079 newDate = this.moveYear(this.date, dir);
18080 newViewDate = this.moveYear(this.viewDate, dir);
18081 } else if (e.shiftKey){
18082 newDate = this.moveMonth(this.date, dir);
18083 newViewDate = this.moveMonth(this.viewDate, dir);
18085 newDate = new Date(this.date);
18086 newDate.setUTCDate(this.date.getUTCDate() + dir);
18087 newViewDate = new Date(this.viewDate);
18088 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18090 if (this.dateWithinRange(newDate)){
18091 this.date = newDate;
18092 this.viewDate = newViewDate;
18093 this.setValue(this.formatDate(this.date));
18095 e.preventDefault();
18096 dateChanged = true;
18101 if (!this.keyboardNavigation) {
18104 dir = e.keyCode == 38 ? -1 : 1;
18106 newDate = this.moveYear(this.date, dir);
18107 newViewDate = this.moveYear(this.viewDate, dir);
18108 } else if (e.shiftKey){
18109 newDate = this.moveMonth(this.date, dir);
18110 newViewDate = this.moveMonth(this.viewDate, dir);
18112 newDate = new Date(this.date);
18113 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18114 newViewDate = new Date(this.viewDate);
18115 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18117 if (this.dateWithinRange(newDate)){
18118 this.date = newDate;
18119 this.viewDate = newViewDate;
18120 this.setValue(this.formatDate(this.date));
18122 e.preventDefault();
18123 dateChanged = true;
18127 this.setValue(this.formatDate(this.date));
18129 e.preventDefault();
18132 this.setValue(this.formatDate(this.date));
18146 onClick: function(e)
18148 e.stopPropagation();
18149 e.preventDefault();
18151 var target = e.getTarget();
18153 if(target.nodeName.toLowerCase() === 'i'){
18154 target = Roo.get(target).dom.parentNode;
18157 var nodeName = target.nodeName;
18158 var className = target.className;
18159 var html = target.innerHTML;
18160 //Roo.log(nodeName);
18162 switch(nodeName.toLowerCase()) {
18164 switch(className) {
18170 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18171 switch(this.viewMode){
18173 this.viewDate = this.moveMonth(this.viewDate, dir);
18177 this.viewDate = this.moveYear(this.viewDate, dir);
18183 var date = new Date();
18184 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18186 this.setValue(this.formatDate(this.date));
18193 if (className.indexOf('disabled') < 0) {
18194 this.viewDate.setUTCDate(1);
18195 if (className.indexOf('month') > -1) {
18196 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18198 var year = parseInt(html, 10) || 0;
18199 this.viewDate.setUTCFullYear(year);
18203 if(this.singleMode){
18204 this.setValue(this.formatDate(this.viewDate));
18215 //Roo.log(className);
18216 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18217 var day = parseInt(html, 10) || 1;
18218 var year = this.viewDate.getUTCFullYear(),
18219 month = this.viewDate.getUTCMonth();
18221 if (className.indexOf('old') > -1) {
18228 } else if (className.indexOf('new') > -1) {
18236 //Roo.log([year,month,day]);
18237 this.date = this.UTCDate(year, month, day,0,0,0,0);
18238 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18240 //Roo.log(this.formatDate(this.date));
18241 this.setValue(this.formatDate(this.date));
18248 setStartDate: function(startDate)
18250 this.startDate = startDate || -Infinity;
18251 if (this.startDate !== -Infinity) {
18252 this.startDate = this.parseDate(this.startDate);
18255 this.updateNavArrows();
18258 setEndDate: function(endDate)
18260 this.endDate = endDate || Infinity;
18261 if (this.endDate !== Infinity) {
18262 this.endDate = this.parseDate(this.endDate);
18265 this.updateNavArrows();
18268 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18270 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18271 if (typeof(this.daysOfWeekDisabled) !== 'object') {
18272 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18274 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18275 return parseInt(d, 10);
18278 this.updateNavArrows();
18281 updateNavArrows: function()
18283 if(this.singleMode){
18287 var d = new Date(this.viewDate),
18288 year = d.getUTCFullYear(),
18289 month = d.getUTCMonth();
18291 Roo.each(this.picker().select('.prev', true).elements, function(v){
18293 switch (this.viewMode) {
18296 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18302 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18309 Roo.each(this.picker().select('.next', true).elements, function(v){
18311 switch (this.viewMode) {
18314 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18320 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18328 moveMonth: function(date, dir)
18333 var new_date = new Date(date.valueOf()),
18334 day = new_date.getUTCDate(),
18335 month = new_date.getUTCMonth(),
18336 mag = Math.abs(dir),
18338 dir = dir > 0 ? 1 : -1;
18341 // If going back one month, make sure month is not current month
18342 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18344 return new_date.getUTCMonth() == month;
18346 // If going forward one month, make sure month is as expected
18347 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18349 return new_date.getUTCMonth() != new_month;
18351 new_month = month + dir;
18352 new_date.setUTCMonth(new_month);
18353 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18354 if (new_month < 0 || new_month > 11) {
18355 new_month = (new_month + 12) % 12;
18358 // For magnitudes >1, move one month at a time...
18359 for (var i=0; i<mag; i++) {
18360 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18361 new_date = this.moveMonth(new_date, dir);
18363 // ...then reset the day, keeping it in the new month
18364 new_month = new_date.getUTCMonth();
18365 new_date.setUTCDate(day);
18367 return new_month != new_date.getUTCMonth();
18370 // Common date-resetting loop -- if date is beyond end of month, make it
18373 new_date.setUTCDate(--day);
18374 new_date.setUTCMonth(new_month);
18379 moveYear: function(date, dir)
18381 return this.moveMonth(date, dir*12);
18384 dateWithinRange: function(date)
18386 return date >= this.startDate && date <= this.endDate;
18392 this.picker().remove();
18395 validateValue : function(value)
18397 if(value.length < 1) {
18398 if(this.allowBlank){
18404 if(value.length < this.minLength){
18407 if(value.length > this.maxLength){
18411 var vt = Roo.form.VTypes;
18412 if(!vt[this.vtype](value, this)){
18416 if(typeof this.validator == "function"){
18417 var msg = this.validator(value);
18423 if(this.regex && !this.regex.test(value)){
18427 if(typeof(this.parseDate(value)) == 'undefined'){
18431 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18435 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18445 Roo.apply(Roo.bootstrap.DateField, {
18456 html: '<i class="fa fa-arrow-left"/>'
18466 html: '<i class="fa fa-arrow-right"/>'
18508 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18509 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18510 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18511 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18512 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18525 navFnc: 'FullYear',
18530 navFnc: 'FullYear',
18535 Roo.apply(Roo.bootstrap.DateField, {
18539 cls: 'datepicker dropdown-menu roo-dynamic',
18543 cls: 'datepicker-days',
18547 cls: 'table-condensed',
18549 Roo.bootstrap.DateField.head,
18553 Roo.bootstrap.DateField.footer
18560 cls: 'datepicker-months',
18564 cls: 'table-condensed',
18566 Roo.bootstrap.DateField.head,
18567 Roo.bootstrap.DateField.content,
18568 Roo.bootstrap.DateField.footer
18575 cls: 'datepicker-years',
18579 cls: 'table-condensed',
18581 Roo.bootstrap.DateField.head,
18582 Roo.bootstrap.DateField.content,
18583 Roo.bootstrap.DateField.footer
18602 * @class Roo.bootstrap.TimeField
18603 * @extends Roo.bootstrap.Input
18604 * Bootstrap DateField class
18608 * Create a new TimeField
18609 * @param {Object} config The config object
18612 Roo.bootstrap.TimeField = function(config){
18613 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18617 * Fires when this field show.
18618 * @param {Roo.bootstrap.DateField} thisthis
18619 * @param {Mixed} date The date value
18624 * Fires when this field hide.
18625 * @param {Roo.bootstrap.DateField} this
18626 * @param {Mixed} date The date value
18631 * Fires when select a date.
18632 * @param {Roo.bootstrap.DateField} this
18633 * @param {Mixed} date The date value
18639 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
18642 * @cfg {String} format
18643 * The default time format string which can be overriden for localization support. The format must be
18644 * valid according to {@link Date#parseDate} (defaults to 'H:i').
18648 onRender: function(ct, position)
18651 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18653 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18655 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18657 this.pop = this.picker().select('>.datepicker-time',true).first();
18658 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18660 this.picker().on('mousedown', this.onMousedown, this);
18661 this.picker().on('click', this.onClick, this);
18663 this.picker().addClass('datepicker-dropdown');
18668 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18669 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18670 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18671 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18672 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18673 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18677 fireKey: function(e){
18678 if (!this.picker().isVisible()){
18679 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18685 e.preventDefault();
18693 this.onTogglePeriod();
18696 this.onIncrementMinutes();
18699 this.onDecrementMinutes();
18708 onClick: function(e) {
18709 e.stopPropagation();
18710 e.preventDefault();
18713 picker : function()
18715 return this.el.select('.datepicker', true).first();
18718 fillTime: function()
18720 var time = this.pop.select('tbody', true).first();
18722 time.dom.innerHTML = '';
18737 cls: 'hours-up glyphicon glyphicon-chevron-up'
18757 cls: 'minutes-up glyphicon glyphicon-chevron-up'
18778 cls: 'timepicker-hour',
18793 cls: 'timepicker-minute',
18808 cls: 'btn btn-primary period',
18830 cls: 'hours-down glyphicon glyphicon-chevron-down'
18850 cls: 'minutes-down glyphicon glyphicon-chevron-down'
18868 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18875 var hours = this.time.getHours();
18876 var minutes = this.time.getMinutes();
18889 hours = hours - 12;
18893 hours = '0' + hours;
18897 minutes = '0' + minutes;
18900 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18901 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18902 this.pop.select('button', true).first().dom.innerHTML = period;
18908 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18910 var cls = ['bottom'];
18912 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18919 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18924 this.picker().addClass(cls.join('-'));
18928 Roo.each(cls, function(c){
18930 _this.picker().setTop(_this.inputEl().getHeight());
18934 _this.picker().setTop(0 - _this.picker().getHeight());
18939 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18943 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18950 onFocus : function()
18952 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18956 onBlur : function()
18958 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18964 this.picker().show();
18969 this.fireEvent('show', this, this.date);
18974 this.picker().hide();
18977 this.fireEvent('hide', this, this.date);
18980 setTime : function()
18983 this.setValue(this.time.format(this.format));
18985 this.fireEvent('select', this, this.date);
18990 onMousedown: function(e){
18991 e.stopPropagation();
18992 e.preventDefault();
18995 onIncrementHours: function()
18997 Roo.log('onIncrementHours');
18998 this.time = this.time.add(Date.HOUR, 1);
19003 onDecrementHours: function()
19005 Roo.log('onDecrementHours');
19006 this.time = this.time.add(Date.HOUR, -1);
19010 onIncrementMinutes: function()
19012 Roo.log('onIncrementMinutes');
19013 this.time = this.time.add(Date.MINUTE, 1);
19017 onDecrementMinutes: function()
19019 Roo.log('onDecrementMinutes');
19020 this.time = this.time.add(Date.MINUTE, -1);
19024 onTogglePeriod: function()
19026 Roo.log('onTogglePeriod');
19027 this.time = this.time.add(Date.HOUR, 12);
19034 Roo.apply(Roo.bootstrap.TimeField, {
19064 cls: 'btn btn-info ok',
19076 Roo.apply(Roo.bootstrap.TimeField, {
19080 cls: 'datepicker dropdown-menu',
19084 cls: 'datepicker-time',
19088 cls: 'table-condensed',
19090 Roo.bootstrap.TimeField.content,
19091 Roo.bootstrap.TimeField.footer
19110 * @class Roo.bootstrap.MonthField
19111 * @extends Roo.bootstrap.Input
19112 * Bootstrap MonthField class
19114 * @cfg {String} language default en
19117 * Create a new MonthField
19118 * @param {Object} config The config object
19121 Roo.bootstrap.MonthField = function(config){
19122 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19127 * Fires when this field show.
19128 * @param {Roo.bootstrap.MonthField} this
19129 * @param {Mixed} date The date value
19134 * Fires when this field hide.
19135 * @param {Roo.bootstrap.MonthField} this
19136 * @param {Mixed} date The date value
19141 * Fires when select a date.
19142 * @param {Roo.bootstrap.MonthField} this
19143 * @param {String} oldvalue The old value
19144 * @param {String} newvalue The new value
19150 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
19152 onRender: function(ct, position)
19155 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19157 this.language = this.language || 'en';
19158 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19159 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19161 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19162 this.isInline = false;
19163 this.isInput = true;
19164 this.component = this.el.select('.add-on', true).first() || false;
19165 this.component = (this.component && this.component.length === 0) ? false : this.component;
19166 this.hasInput = this.component && this.inputEL().length;
19168 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19170 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19172 this.picker().on('mousedown', this.onMousedown, this);
19173 this.picker().on('click', this.onClick, this);
19175 this.picker().addClass('datepicker-dropdown');
19177 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19178 v.setStyle('width', '189px');
19185 if(this.isInline) {
19191 setValue: function(v, suppressEvent)
19193 var o = this.getValue();
19195 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19199 if(suppressEvent !== true){
19200 this.fireEvent('select', this, o, v);
19205 getValue: function()
19210 onClick: function(e)
19212 e.stopPropagation();
19213 e.preventDefault();
19215 var target = e.getTarget();
19217 if(target.nodeName.toLowerCase() === 'i'){
19218 target = Roo.get(target).dom.parentNode;
19221 var nodeName = target.nodeName;
19222 var className = target.className;
19223 var html = target.innerHTML;
19225 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19229 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19231 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19237 picker : function()
19239 return this.pickerEl;
19242 fillMonths: function()
19245 var months = this.picker().select('>.datepicker-months td', true).first();
19247 months.dom.innerHTML = '';
19253 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19256 months.createChild(month);
19265 if(typeof(this.vIndex) == 'undefined' && this.value.length){
19266 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19269 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19270 e.removeClass('active');
19272 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19273 e.addClass('active');
19280 if(this.isInline) {
19284 this.picker().removeClass(['bottom', 'top']);
19286 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19288 * place to the top of element!
19292 this.picker().addClass('top');
19293 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19298 this.picker().addClass('bottom');
19300 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19303 onFocus : function()
19305 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19309 onBlur : function()
19311 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19313 var d = this.inputEl().getValue();
19322 this.picker().show();
19323 this.picker().select('>.datepicker-months', true).first().show();
19327 this.fireEvent('show', this, this.date);
19332 if(this.isInline) {
19335 this.picker().hide();
19336 this.fireEvent('hide', this, this.date);
19340 onMousedown: function(e)
19342 e.stopPropagation();
19343 e.preventDefault();
19348 Roo.bootstrap.MonthField.superclass.keyup.call(this);
19352 fireKey: function(e)
19354 if (!this.picker().isVisible()){
19355 if (e.keyCode == 27) {// allow escape to hide and re-show picker
19366 e.preventDefault();
19370 dir = e.keyCode == 37 ? -1 : 1;
19372 this.vIndex = this.vIndex + dir;
19374 if(this.vIndex < 0){
19378 if(this.vIndex > 11){
19382 if(isNaN(this.vIndex)){
19386 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19392 dir = e.keyCode == 38 ? -1 : 1;
19394 this.vIndex = this.vIndex + dir * 4;
19396 if(this.vIndex < 0){
19400 if(this.vIndex > 11){
19404 if(isNaN(this.vIndex)){
19408 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19413 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19414 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19418 e.preventDefault();
19421 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19422 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19438 this.picker().remove();
19443 Roo.apply(Roo.bootstrap.MonthField, {
19462 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19463 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19468 Roo.apply(Roo.bootstrap.MonthField, {
19472 cls: 'datepicker dropdown-menu roo-dynamic',
19476 cls: 'datepicker-months',
19480 cls: 'table-condensed',
19482 Roo.bootstrap.DateField.content
19502 * @class Roo.bootstrap.CheckBox
19503 * @extends Roo.bootstrap.Input
19504 * Bootstrap CheckBox class
19506 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19507 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19508 * @cfg {String} boxLabel The text that appears beside the checkbox
19509 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19510 * @cfg {Boolean} checked initnal the element
19511 * @cfg {Boolean} inline inline the element (default false)
19512 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19515 * Create a new CheckBox
19516 * @param {Object} config The config object
19519 Roo.bootstrap.CheckBox = function(config){
19520 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19525 * Fires when the element is checked or unchecked.
19526 * @param {Roo.bootstrap.CheckBox} this This input
19527 * @param {Boolean} checked The new checked value
19534 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
19536 inputType: 'checkbox',
19544 getAutoCreate : function()
19546 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19552 cfg.cls = 'form-group ' + this.inputType; //input-group
19555 cfg.cls += ' ' + this.inputType + '-inline';
19561 type : this.inputType,
19562 value : this.inputValue,
19563 cls : 'roo-' + this.inputType, //'form-box',
19564 placeholder : this.placeholder || ''
19568 if(this.inputType != 'radio'){
19572 cls : 'roo-hidden-value',
19573 value : this.checked ? this.valueOff : this.inputValue
19578 if (this.weight) { // Validity check?
19579 cfg.cls += " " + this.inputType + "-" + this.weight;
19582 if (this.disabled) {
19583 input.disabled=true;
19587 input.checked = this.checked;
19594 input.name = this.name;
19596 if(this.inputType != 'radio'){
19597 hidden.name = this.name;
19598 input.name = '_hidden_' + this.name;
19603 input.cls += ' input-' + this.size;
19608 ['xs','sm','md','lg'].map(function(size){
19609 if (settings[size]) {
19610 cfg.cls += ' col-' + size + '-' + settings[size];
19614 var inputblock = input;
19616 if (this.before || this.after) {
19619 cls : 'input-group',
19624 inputblock.cn.push({
19626 cls : 'input-group-addon',
19631 inputblock.cn.push(input);
19633 if(this.inputType != 'radio'){
19634 inputblock.cn.push(hidden);
19638 inputblock.cn.push({
19640 cls : 'input-group-addon',
19647 if (align ==='left' && this.fieldLabel.length) {
19648 // Roo.log("left and has label");
19654 cls : 'control-label col-md-' + this.labelWidth,
19655 html : this.fieldLabel
19659 cls : "col-md-" + (12 - this.labelWidth),
19666 } else if ( this.fieldLabel.length) {
19667 // Roo.log(" label");
19671 tag: this.boxLabel ? 'span' : 'label',
19673 cls: 'control-label box-input-label',
19674 //cls : 'input-group-addon',
19675 html : this.fieldLabel
19685 // Roo.log(" no label && no align");
19686 cfg.cn = [ inputblock ] ;
19692 var boxLabelCfg = {
19694 //'for': id, // box label is handled by onclick - so no for...
19696 html: this.boxLabel
19700 boxLabelCfg.tooltip = this.tooltip;
19703 cfg.cn.push(boxLabelCfg);
19706 if(this.inputType != 'radio'){
19707 cfg.cn.push(hidden);
19715 * return the real input element.
19717 inputEl: function ()
19719 return this.el.select('input.roo-' + this.inputType,true).first();
19721 hiddenEl: function ()
19723 return this.el.select('input.roo-hidden-value',true).first();
19726 labelEl: function()
19728 return this.el.select('label.control-label',true).first();
19730 /* depricated... */
19734 return this.labelEl();
19737 boxLabelEl: function()
19739 return this.el.select('label.box-label',true).first();
19742 initEvents : function()
19744 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19746 this.inputEl().on('click', this.onClick, this);
19748 if (this.boxLabel) {
19749 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
19752 this.startValue = this.getValue();
19755 Roo.bootstrap.CheckBox.register(this);
19759 onClick : function()
19761 this.setChecked(!this.checked);
19764 setChecked : function(state,suppressEvent)
19766 this.startValue = this.getValue();
19768 if(this.inputType == 'radio'){
19770 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19771 e.dom.checked = false;
19774 this.inputEl().dom.checked = true;
19776 this.inputEl().dom.value = this.inputValue;
19778 if(suppressEvent !== true){
19779 this.fireEvent('check', this, true);
19787 this.checked = state;
19789 this.inputEl().dom.checked = state;
19792 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19794 if(suppressEvent !== true){
19795 this.fireEvent('check', this, state);
19801 getValue : function()
19803 if(this.inputType == 'radio'){
19804 return this.getGroupValue();
19807 return this.hiddenEl().dom.value;
19811 getGroupValue : function()
19813 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19817 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19820 setValue : function(v,suppressEvent)
19822 if(this.inputType == 'radio'){
19823 this.setGroupValue(v, suppressEvent);
19827 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19832 setGroupValue : function(v, suppressEvent)
19834 this.startValue = this.getValue();
19836 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19837 e.dom.checked = false;
19839 if(e.dom.value == v){
19840 e.dom.checked = true;
19844 if(suppressEvent !== true){
19845 this.fireEvent('check', this, true);
19853 validate : function()
19857 (this.inputType == 'radio' && this.validateRadio()) ||
19858 (this.inputType == 'checkbox' && this.validateCheckbox())
19864 this.markInvalid();
19868 validateRadio : function()
19870 if(this.allowBlank){
19876 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19877 if(!e.dom.checked){
19889 validateCheckbox : function()
19892 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19895 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19903 for(var i in group){
19908 r = (group[i].getValue() == group[i].inputValue) ? true : false;
19915 * Mark this field as valid
19917 markValid : function()
19919 if(this.allowBlank){
19925 this.fireEvent('valid', this);
19927 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19930 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19937 if(this.inputType == 'radio'){
19938 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19939 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19940 e.findParent('.form-group', false, true).addClass(_this.validClass);
19947 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19948 this.el.findParent('.form-group', false, true).addClass(this.validClass);
19952 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19958 for(var i in group){
19959 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19960 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19965 * Mark this field as invalid
19966 * @param {String} msg The validation message
19968 markInvalid : function(msg)
19970 if(this.allowBlank){
19976 this.fireEvent('invalid', this, msg);
19978 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19981 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19985 label.markInvalid();
19988 if(this.inputType == 'radio'){
19989 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19990 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19991 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19998 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19999 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20003 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20009 for(var i in group){
20010 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20011 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20016 disable : function()
20018 if(this.inputType != 'radio'){
20019 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20026 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20027 _this.getActionEl().addClass(this.disabledClass);
20028 e.dom.disabled = true;
20032 this.disabled = true;
20033 this.fireEvent("disable", this);
20037 enable : function()
20039 if(this.inputType != 'radio'){
20040 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20047 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20048 _this.getActionEl().removeClass(this.disabledClass);
20049 e.dom.disabled = false;
20053 this.disabled = false;
20054 this.fireEvent("enable", this);
20060 Roo.apply(Roo.bootstrap.CheckBox, {
20065 * register a CheckBox Group
20066 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20068 register : function(checkbox)
20070 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20071 this.groups[checkbox.groupId] = {};
20074 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20078 this.groups[checkbox.groupId][checkbox.name] = checkbox;
20082 * fetch a CheckBox Group based on the group ID
20083 * @param {string} the group ID
20084 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20086 get: function(groupId) {
20087 if (typeof(this.groups[groupId]) == 'undefined') {
20091 return this.groups[groupId] ;
20103 *<div class="radio">
20105 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
20106 Option one is this and that—be sure to include why it's great
20113 *<label class="radio-inline">fieldLabel</label>
20114 *<label class="radio-inline">
20115 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
20123 * @class Roo.bootstrap.Radio
20124 * @extends Roo.bootstrap.CheckBox
20125 * Bootstrap Radio class
20128 * Create a new Radio
20129 * @param {Object} config The config object
20132 Roo.bootstrap.Radio = function(config){
20133 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20137 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
20139 inputType: 'radio',
20143 getAutoCreate : function()
20145 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20146 align = align || 'left'; // default...
20153 tag : this.inline ? 'span' : 'div',
20154 cls : 'form-group',
20158 var inline = this.inline ? ' radio-inline' : '';
20162 // does not need for, as we wrap the input with it..
20164 cls : 'control-label box-label' + inline,
20167 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
20171 //cls : 'control-label' + inline,
20172 html : this.fieldLabel,
20173 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
20179 type : this.inputType,
20180 //value : (!this.checked) ? this.valueOff : this.inputValue,
20181 value : this.inputValue,
20183 placeholder : this.placeholder || '' // ?? needed????
20186 if (this.weight) { // Validity check?
20187 input.cls += " radio-" + this.weight;
20189 if (this.disabled) {
20190 input.disabled=true;
20194 input.checked = this.checked;
20198 input.name = this.name;
20202 input.cls += ' input-' + this.size;
20205 //?? can span's inline have a width??
20208 ['xs','sm','md','lg'].map(function(size){
20209 if (settings[size]) {
20210 cfg.cls += ' col-' + size + '-' + settings[size];
20214 var inputblock = input;
20216 if (this.before || this.after) {
20219 cls : 'input-group',
20224 inputblock.cn.push({
20226 cls : 'input-group-addon',
20230 inputblock.cn.push(input);
20232 inputblock.cn.push({
20234 cls : 'input-group-addon',
20242 if (this.fieldLabel && this.fieldLabel.length) {
20243 cfg.cn.push(fieldLabel);
20246 // normal bootstrap puts the input inside the label.
20247 // however with our styled version - it has to go after the input.
20249 //lbl.cn.push(inputblock);
20253 cls: 'radio' + inline,
20260 cfg.cn.push( lblwrap);
20265 html: this.boxLabel
20274 initEvents : function()
20276 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20278 this.inputEl().on('click', this.onClick, this);
20279 if (this.boxLabel) {
20280 //Roo.log('find label');
20281 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
20286 inputEl: function ()
20288 return this.el.select('input.roo-radio',true).first();
20290 onClick : function()
20293 this.setChecked(true);
20296 setChecked : function(state,suppressEvent)
20299 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20300 v.dom.checked = false;
20303 Roo.log(this.inputEl().dom);
20304 this.checked = state;
20305 this.inputEl().dom.checked = state;
20307 if(suppressEvent !== true){
20308 this.fireEvent('check', this, state);
20311 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
20315 getGroupValue : function()
20318 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20319 if(v.dom.checked == true){
20320 value = v.dom.value;
20328 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
20329 * @return {Mixed} value The field value
20331 getValue : function(){
20332 return this.getGroupValue();
20336 //<script type="text/javascript">
20339 * Based Ext JS Library 1.1.1
20340 * Copyright(c) 2006-2007, Ext JS, LLC.
20346 * @class Roo.HtmlEditorCore
20347 * @extends Roo.Component
20348 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20350 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20353 Roo.HtmlEditorCore = function(config){
20356 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20361 * @event initialize
20362 * Fires when the editor is fully initialized (including the iframe)
20363 * @param {Roo.HtmlEditorCore} this
20368 * Fires when the editor is first receives the focus. Any insertion must wait
20369 * until after this event.
20370 * @param {Roo.HtmlEditorCore} this
20374 * @event beforesync
20375 * Fires before the textarea is updated with content from the editor iframe. Return false
20376 * to cancel the sync.
20377 * @param {Roo.HtmlEditorCore} this
20378 * @param {String} html
20382 * @event beforepush
20383 * Fires before the iframe editor is updated with content from the textarea. Return false
20384 * to cancel the push.
20385 * @param {Roo.HtmlEditorCore} this
20386 * @param {String} html
20391 * Fires when the textarea is updated with content from the editor iframe.
20392 * @param {Roo.HtmlEditorCore} this
20393 * @param {String} html
20398 * Fires when the iframe editor is updated with content from the textarea.
20399 * @param {Roo.HtmlEditorCore} this
20400 * @param {String} html
20405 * @event editorevent
20406 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20407 * @param {Roo.HtmlEditorCore} this
20413 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20415 // defaults : white / black...
20416 this.applyBlacklists();
20423 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20427 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20433 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20438 * @cfg {Number} height (in pixels)
20442 * @cfg {Number} width (in pixels)
20447 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20450 stylesheets: false,
20455 // private properties
20456 validationEvent : false,
20458 initialized : false,
20460 sourceEditMode : false,
20461 onFocus : Roo.emptyFn,
20463 hideMode:'offsets',
20467 // blacklist + whitelisted elements..
20474 * Protected method that will not generally be called directly. It
20475 * is called when the editor initializes the iframe with HTML contents. Override this method if you
20476 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20478 getDocMarkup : function(){
20482 // inherit styels from page...??
20483 if (this.stylesheets === false) {
20485 Roo.get(document.head).select('style').each(function(node) {
20486 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20489 Roo.get(document.head).select('link').each(function(node) {
20490 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20493 } else if (!this.stylesheets.length) {
20495 st = '<style type="text/css">' +
20496 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20502 st += '<style type="text/css">' +
20503 'IMG { cursor: pointer } ' +
20507 return '<html><head>' + st +
20508 //<style type="text/css">' +
20509 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20511 ' </head><body class="roo-htmleditor-body"></body></html>';
20515 onRender : function(ct, position)
20518 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20519 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20522 this.el.dom.style.border = '0 none';
20523 this.el.dom.setAttribute('tabIndex', -1);
20524 this.el.addClass('x-hidden hide');
20528 if(Roo.isIE){ // fix IE 1px bogus margin
20529 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20533 this.frameId = Roo.id();
20537 var iframe = this.owner.wrap.createChild({
20539 cls: 'form-control', // bootstrap..
20541 name: this.frameId,
20542 frameBorder : 'no',
20543 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
20548 this.iframe = iframe.dom;
20550 this.assignDocWin();
20552 this.doc.designMode = 'on';
20555 this.doc.write(this.getDocMarkup());
20559 var task = { // must defer to wait for browser to be ready
20561 //console.log("run task?" + this.doc.readyState);
20562 this.assignDocWin();
20563 if(this.doc.body || this.doc.readyState == 'complete'){
20565 this.doc.designMode="on";
20569 Roo.TaskMgr.stop(task);
20570 this.initEditor.defer(10, this);
20577 Roo.TaskMgr.start(task);
20582 onResize : function(w, h)
20584 Roo.log('resize: ' +w + ',' + h );
20585 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20589 if(typeof w == 'number'){
20591 this.iframe.style.width = w + 'px';
20593 if(typeof h == 'number'){
20595 this.iframe.style.height = h + 'px';
20597 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20604 * Toggles the editor between standard and source edit mode.
20605 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20607 toggleSourceEdit : function(sourceEditMode){
20609 this.sourceEditMode = sourceEditMode === true;
20611 if(this.sourceEditMode){
20613 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
20616 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20617 //this.iframe.className = '';
20620 //this.setSize(this.owner.wrap.getSize());
20621 //this.fireEvent('editmodechange', this, this.sourceEditMode);
20628 * Protected method that will not generally be called directly. If you need/want
20629 * custom HTML cleanup, this is the method you should override.
20630 * @param {String} html The HTML to be cleaned
20631 * return {String} The cleaned HTML
20633 cleanHtml : function(html){
20634 html = String(html);
20635 if(html.length > 5){
20636 if(Roo.isSafari){ // strip safari nonsense
20637 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20640 if(html == ' '){
20647 * HTML Editor -> Textarea
20648 * Protected method that will not generally be called directly. Syncs the contents
20649 * of the editor iframe with the textarea.
20651 syncValue : function(){
20652 if(this.initialized){
20653 var bd = (this.doc.body || this.doc.documentElement);
20654 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20655 var html = bd.innerHTML;
20657 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20658 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20660 html = '<div style="'+m[0]+'">' + html + '</div>';
20663 html = this.cleanHtml(html);
20664 // fix up the special chars.. normaly like back quotes in word...
20665 // however we do not want to do this with chinese..
20666 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20667 var cc = b.charCodeAt();
20669 (cc >= 0x4E00 && cc < 0xA000 ) ||
20670 (cc >= 0x3400 && cc < 0x4E00 ) ||
20671 (cc >= 0xf900 && cc < 0xfb00 )
20677 if(this.owner.fireEvent('beforesync', this, html) !== false){
20678 this.el.dom.value = html;
20679 this.owner.fireEvent('sync', this, html);
20685 * Protected method that will not generally be called directly. Pushes the value of the textarea
20686 * into the iframe editor.
20688 pushValue : function(){
20689 if(this.initialized){
20690 var v = this.el.dom.value.trim();
20692 // if(v.length < 1){
20696 if(this.owner.fireEvent('beforepush', this, v) !== false){
20697 var d = (this.doc.body || this.doc.documentElement);
20699 this.cleanUpPaste();
20700 this.el.dom.value = d.innerHTML;
20701 this.owner.fireEvent('push', this, v);
20707 deferFocus : function(){
20708 this.focus.defer(10, this);
20712 focus : function(){
20713 if(this.win && !this.sourceEditMode){
20720 assignDocWin: function()
20722 var iframe = this.iframe;
20725 this.doc = iframe.contentWindow.document;
20726 this.win = iframe.contentWindow;
20728 // if (!Roo.get(this.frameId)) {
20731 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20732 // this.win = Roo.get(this.frameId).dom.contentWindow;
20734 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20738 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20739 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20744 initEditor : function(){
20745 //console.log("INIT EDITOR");
20746 this.assignDocWin();
20750 this.doc.designMode="on";
20752 this.doc.write(this.getDocMarkup());
20755 var dbody = (this.doc.body || this.doc.documentElement);
20756 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20757 // this copies styles from the containing element into thsi one..
20758 // not sure why we need all of this..
20759 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20761 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20762 //ss['background-attachment'] = 'fixed'; // w3c
20763 dbody.bgProperties = 'fixed'; // ie
20764 //Roo.DomHelper.applyStyles(dbody, ss);
20765 Roo.EventManager.on(this.doc, {
20766 //'mousedown': this.onEditorEvent,
20767 'mouseup': this.onEditorEvent,
20768 'dblclick': this.onEditorEvent,
20769 'click': this.onEditorEvent,
20770 'keyup': this.onEditorEvent,
20775 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20777 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20778 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20780 this.initialized = true;
20782 this.owner.fireEvent('initialize', this);
20787 onDestroy : function(){
20793 //for (var i =0; i < this.toolbars.length;i++) {
20794 // // fixme - ask toolbars for heights?
20795 // this.toolbars[i].onDestroy();
20798 //this.wrap.dom.innerHTML = '';
20799 //this.wrap.remove();
20804 onFirstFocus : function(){
20806 this.assignDocWin();
20809 this.activated = true;
20812 if(Roo.isGecko){ // prevent silly gecko errors
20814 var s = this.win.getSelection();
20815 if(!s.focusNode || s.focusNode.nodeType != 3){
20816 var r = s.getRangeAt(0);
20817 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20822 this.execCmd('useCSS', true);
20823 this.execCmd('styleWithCSS', false);
20826 this.owner.fireEvent('activate', this);
20830 adjustFont: function(btn){
20831 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20832 //if(Roo.isSafari){ // safari
20835 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20836 if(Roo.isSafari){ // safari
20837 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20838 v = (v < 10) ? 10 : v;
20839 v = (v > 48) ? 48 : v;
20840 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20845 v = Math.max(1, v+adjust);
20847 this.execCmd('FontSize', v );
20850 onEditorEvent : function(e)
20852 this.owner.fireEvent('editorevent', this, e);
20853 // this.updateToolbar();
20854 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20857 insertTag : function(tg)
20859 // could be a bit smarter... -> wrap the current selected tRoo..
20860 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20862 range = this.createRange(this.getSelection());
20863 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20864 wrappingNode.appendChild(range.extractContents());
20865 range.insertNode(wrappingNode);
20872 this.execCmd("formatblock", tg);
20876 insertText : function(txt)
20880 var range = this.createRange();
20881 range.deleteContents();
20882 //alert(Sender.getAttribute('label'));
20884 range.insertNode(this.doc.createTextNode(txt));
20890 * Executes a Midas editor command on the editor document and performs necessary focus and
20891 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20892 * @param {String} cmd The Midas command
20893 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20895 relayCmd : function(cmd, value){
20897 this.execCmd(cmd, value);
20898 this.owner.fireEvent('editorevent', this);
20899 //this.updateToolbar();
20900 this.owner.deferFocus();
20904 * Executes a Midas editor command directly on the editor document.
20905 * For visual commands, you should use {@link #relayCmd} instead.
20906 * <b>This should only be called after the editor is initialized.</b>
20907 * @param {String} cmd The Midas command
20908 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20910 execCmd : function(cmd, value){
20911 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20918 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20920 * @param {String} text | dom node..
20922 insertAtCursor : function(text)
20927 if(!this.activated){
20933 var r = this.doc.selection.createRange();
20944 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20948 // from jquery ui (MIT licenced)
20950 var win = this.win;
20952 if (win.getSelection && win.getSelection().getRangeAt) {
20953 range = win.getSelection().getRangeAt(0);
20954 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20955 range.insertNode(node);
20956 } else if (win.document.selection && win.document.selection.createRange) {
20957 // no firefox support
20958 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20959 win.document.selection.createRange().pasteHTML(txt);
20961 // no firefox support
20962 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20963 this.execCmd('InsertHTML', txt);
20972 mozKeyPress : function(e){
20974 var c = e.getCharCode(), cmd;
20977 c = String.fromCharCode(c).toLowerCase();
20991 this.cleanUpPaste.defer(100, this);
20999 e.preventDefault();
21007 fixKeys : function(){ // load time branching for fastest keydown performance
21009 return function(e){
21010 var k = e.getKey(), r;
21013 r = this.doc.selection.createRange();
21016 r.pasteHTML('    ');
21023 r = this.doc.selection.createRange();
21025 var target = r.parentElement();
21026 if(!target || target.tagName.toLowerCase() != 'li'){
21028 r.pasteHTML('<br />');
21034 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21035 this.cleanUpPaste.defer(100, this);
21041 }else if(Roo.isOpera){
21042 return function(e){
21043 var k = e.getKey();
21047 this.execCmd('InsertHTML','    ');
21050 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21051 this.cleanUpPaste.defer(100, this);
21056 }else if(Roo.isSafari){
21057 return function(e){
21058 var k = e.getKey();
21062 this.execCmd('InsertText','\t');
21066 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21067 this.cleanUpPaste.defer(100, this);
21075 getAllAncestors: function()
21077 var p = this.getSelectedNode();
21080 a.push(p); // push blank onto stack..
21081 p = this.getParentElement();
21085 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21089 a.push(this.doc.body);
21093 lastSelNode : false,
21096 getSelection : function()
21098 this.assignDocWin();
21099 return Roo.isIE ? this.doc.selection : this.win.getSelection();
21102 getSelectedNode: function()
21104 // this may only work on Gecko!!!
21106 // should we cache this!!!!
21111 var range = this.createRange(this.getSelection()).cloneRange();
21114 var parent = range.parentElement();
21116 var testRange = range.duplicate();
21117 testRange.moveToElementText(parent);
21118 if (testRange.inRange(range)) {
21121 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21124 parent = parent.parentElement;
21129 // is ancestor a text element.
21130 var ac = range.commonAncestorContainer;
21131 if (ac.nodeType == 3) {
21132 ac = ac.parentNode;
21135 var ar = ac.childNodes;
21138 var other_nodes = [];
21139 var has_other_nodes = false;
21140 for (var i=0;i<ar.length;i++) {
21141 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
21144 // fullly contained node.
21146 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21151 // probably selected..
21152 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21153 other_nodes.push(ar[i]);
21157 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
21162 has_other_nodes = true;
21164 if (!nodes.length && other_nodes.length) {
21165 nodes= other_nodes;
21167 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21173 createRange: function(sel)
21175 // this has strange effects when using with
21176 // top toolbar - not sure if it's a great idea.
21177 //this.editor.contentWindow.focus();
21178 if (typeof sel != "undefined") {
21180 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21182 return this.doc.createRange();
21185 return this.doc.createRange();
21188 getParentElement: function()
21191 this.assignDocWin();
21192 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21194 var range = this.createRange(sel);
21197 var p = range.commonAncestorContainer;
21198 while (p.nodeType == 3) { // text node
21209 * Range intersection.. the hard stuff...
21213 * [ -- selected range --- ]
21217 * if end is before start or hits it. fail.
21218 * if start is after end or hits it fail.
21220 * if either hits (but other is outside. - then it's not
21226 // @see http://www.thismuchiknow.co.uk/?p=64.
21227 rangeIntersectsNode : function(range, node)
21229 var nodeRange = node.ownerDocument.createRange();
21231 nodeRange.selectNode(node);
21233 nodeRange.selectNodeContents(node);
21236 var rangeStartRange = range.cloneRange();
21237 rangeStartRange.collapse(true);
21239 var rangeEndRange = range.cloneRange();
21240 rangeEndRange.collapse(false);
21242 var nodeStartRange = nodeRange.cloneRange();
21243 nodeStartRange.collapse(true);
21245 var nodeEndRange = nodeRange.cloneRange();
21246 nodeEndRange.collapse(false);
21248 return rangeStartRange.compareBoundaryPoints(
21249 Range.START_TO_START, nodeEndRange) == -1 &&
21250 rangeEndRange.compareBoundaryPoints(
21251 Range.START_TO_START, nodeStartRange) == 1;
21255 rangeCompareNode : function(range, node)
21257 var nodeRange = node.ownerDocument.createRange();
21259 nodeRange.selectNode(node);
21261 nodeRange.selectNodeContents(node);
21265 range.collapse(true);
21267 nodeRange.collapse(true);
21269 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21270 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
21272 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21274 var nodeIsBefore = ss == 1;
21275 var nodeIsAfter = ee == -1;
21277 if (nodeIsBefore && nodeIsAfter) {
21280 if (!nodeIsBefore && nodeIsAfter) {
21281 return 1; //right trailed.
21284 if (nodeIsBefore && !nodeIsAfter) {
21285 return 2; // left trailed.
21291 // private? - in a new class?
21292 cleanUpPaste : function()
21294 // cleans up the whole document..
21295 Roo.log('cleanuppaste');
21297 this.cleanUpChildren(this.doc.body);
21298 var clean = this.cleanWordChars(this.doc.body.innerHTML);
21299 if (clean != this.doc.body.innerHTML) {
21300 this.doc.body.innerHTML = clean;
21305 cleanWordChars : function(input) {// change the chars to hex code
21306 var he = Roo.HtmlEditorCore;
21308 var output = input;
21309 Roo.each(he.swapCodes, function(sw) {
21310 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21312 output = output.replace(swapper, sw[1]);
21319 cleanUpChildren : function (n)
21321 if (!n.childNodes.length) {
21324 for (var i = n.childNodes.length-1; i > -1 ; i--) {
21325 this.cleanUpChild(n.childNodes[i]);
21332 cleanUpChild : function (node)
21335 //console.log(node);
21336 if (node.nodeName == "#text") {
21337 // clean up silly Windows -- stuff?
21340 if (node.nodeName == "#comment") {
21341 node.parentNode.removeChild(node);
21342 // clean up silly Windows -- stuff?
21345 var lcname = node.tagName.toLowerCase();
21346 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21347 // whitelist of tags..
21349 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21351 node.parentNode.removeChild(node);
21356 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21358 // remove <a name=....> as rendering on yahoo mailer is borked with this.
21359 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21361 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21362 // remove_keep_children = true;
21365 if (remove_keep_children) {
21366 this.cleanUpChildren(node);
21367 // inserts everything just before this node...
21368 while (node.childNodes.length) {
21369 var cn = node.childNodes[0];
21370 node.removeChild(cn);
21371 node.parentNode.insertBefore(cn, node);
21373 node.parentNode.removeChild(node);
21377 if (!node.attributes || !node.attributes.length) {
21378 this.cleanUpChildren(node);
21382 function cleanAttr(n,v)
21385 if (v.match(/^\./) || v.match(/^\//)) {
21388 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21391 if (v.match(/^#/)) {
21394 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21395 node.removeAttribute(n);
21399 var cwhite = this.cwhite;
21400 var cblack = this.cblack;
21402 function cleanStyle(n,v)
21404 if (v.match(/expression/)) { //XSS?? should we even bother..
21405 node.removeAttribute(n);
21409 var parts = v.split(/;/);
21412 Roo.each(parts, function(p) {
21413 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21417 var l = p.split(':').shift().replace(/\s+/g,'');
21418 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21420 if ( cwhite.length && cblack.indexOf(l) > -1) {
21421 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21422 //node.removeAttribute(n);
21426 // only allow 'c whitelisted system attributes'
21427 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21428 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21429 //node.removeAttribute(n);
21439 if (clean.length) {
21440 node.setAttribute(n, clean.join(';'));
21442 node.removeAttribute(n);
21448 for (var i = node.attributes.length-1; i > -1 ; i--) {
21449 var a = node.attributes[i];
21452 if (a.name.toLowerCase().substr(0,2)=='on') {
21453 node.removeAttribute(a.name);
21456 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21457 node.removeAttribute(a.name);
21460 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21461 cleanAttr(a.name,a.value); // fixme..
21464 if (a.name == 'style') {
21465 cleanStyle(a.name,a.value);
21468 /// clean up MS crap..
21469 // tecnically this should be a list of valid class'es..
21472 if (a.name == 'class') {
21473 if (a.value.match(/^Mso/)) {
21474 node.className = '';
21477 if (a.value.match(/body/)) {
21478 node.className = '';
21489 this.cleanUpChildren(node);
21495 * Clean up MS wordisms...
21497 cleanWord : function(node)
21502 this.cleanWord(this.doc.body);
21505 if (node.nodeName == "#text") {
21506 // clean up silly Windows -- stuff?
21509 if (node.nodeName == "#comment") {
21510 node.parentNode.removeChild(node);
21511 // clean up silly Windows -- stuff?
21515 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21516 node.parentNode.removeChild(node);
21520 // remove - but keep children..
21521 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21522 while (node.childNodes.length) {
21523 var cn = node.childNodes[0];
21524 node.removeChild(cn);
21525 node.parentNode.insertBefore(cn, node);
21527 node.parentNode.removeChild(node);
21528 this.iterateChildren(node, this.cleanWord);
21532 if (node.className.length) {
21534 var cn = node.className.split(/\W+/);
21536 Roo.each(cn, function(cls) {
21537 if (cls.match(/Mso[a-zA-Z]+/)) {
21542 node.className = cna.length ? cna.join(' ') : '';
21544 node.removeAttribute("class");
21548 if (node.hasAttribute("lang")) {
21549 node.removeAttribute("lang");
21552 if (node.hasAttribute("style")) {
21554 var styles = node.getAttribute("style").split(";");
21556 Roo.each(styles, function(s) {
21557 if (!s.match(/:/)) {
21560 var kv = s.split(":");
21561 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21564 // what ever is left... we allow.
21567 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21568 if (!nstyle.length) {
21569 node.removeAttribute('style');
21572 this.iterateChildren(node, this.cleanWord);
21578 * iterateChildren of a Node, calling fn each time, using this as the scole..
21579 * @param {DomNode} node node to iterate children of.
21580 * @param {Function} fn method of this class to call on each item.
21582 iterateChildren : function(node, fn)
21584 if (!node.childNodes.length) {
21587 for (var i = node.childNodes.length-1; i > -1 ; i--) {
21588 fn.call(this, node.childNodes[i])
21594 * cleanTableWidths.
21596 * Quite often pasting from word etc.. results in tables with column and widths.
21597 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21600 cleanTableWidths : function(node)
21605 this.cleanTableWidths(this.doc.body);
21610 if (node.nodeName == "#text" || node.nodeName == "#comment") {
21613 Roo.log(node.tagName);
21614 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21615 this.iterateChildren(node, this.cleanTableWidths);
21618 if (node.hasAttribute('width')) {
21619 node.removeAttribute('width');
21623 if (node.hasAttribute("style")) {
21626 var styles = node.getAttribute("style").split(";");
21628 Roo.each(styles, function(s) {
21629 if (!s.match(/:/)) {
21632 var kv = s.split(":");
21633 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21636 // what ever is left... we allow.
21639 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21640 if (!nstyle.length) {
21641 node.removeAttribute('style');
21645 this.iterateChildren(node, this.cleanTableWidths);
21653 domToHTML : function(currentElement, depth, nopadtext) {
21655 depth = depth || 0;
21656 nopadtext = nopadtext || false;
21658 if (!currentElement) {
21659 return this.domToHTML(this.doc.body);
21662 //Roo.log(currentElement);
21664 var allText = false;
21665 var nodeName = currentElement.nodeName;
21666 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21668 if (nodeName == '#text') {
21670 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21675 if (nodeName != 'BODY') {
21678 // Prints the node tagName, such as <A>, <IMG>, etc
21681 for(i = 0; i < currentElement.attributes.length;i++) {
21683 var aname = currentElement.attributes.item(i).name;
21684 if (!currentElement.attributes.item(i).value.length) {
21687 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21690 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21699 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21702 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21707 // Traverse the tree
21709 var currentElementChild = currentElement.childNodes.item(i);
21710 var allText = true;
21711 var innerHTML = '';
21713 while (currentElementChild) {
21714 // Formatting code (indent the tree so it looks nice on the screen)
21715 var nopad = nopadtext;
21716 if (lastnode == 'SPAN') {
21720 if (currentElementChild.nodeName == '#text') {
21721 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21722 toadd = nopadtext ? toadd : toadd.trim();
21723 if (!nopad && toadd.length > 80) {
21724 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21726 innerHTML += toadd;
21729 currentElementChild = currentElement.childNodes.item(i);
21735 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21737 // Recursively traverse the tree structure of the child node
21738 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21739 lastnode = currentElementChild.nodeName;
21741 currentElementChild=currentElement.childNodes.item(i);
21747 // The remaining code is mostly for formatting the tree
21748 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
21753 ret+= "</"+tagName+">";
21759 applyBlacklists : function()
21761 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
21762 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
21766 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21767 if (b.indexOf(tag) > -1) {
21770 this.white.push(tag);
21774 Roo.each(w, function(tag) {
21775 if (b.indexOf(tag) > -1) {
21778 if (this.white.indexOf(tag) > -1) {
21781 this.white.push(tag);
21786 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21787 if (w.indexOf(tag) > -1) {
21790 this.black.push(tag);
21794 Roo.each(b, function(tag) {
21795 if (w.indexOf(tag) > -1) {
21798 if (this.black.indexOf(tag) > -1) {
21801 this.black.push(tag);
21806 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21807 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21811 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21812 if (b.indexOf(tag) > -1) {
21815 this.cwhite.push(tag);
21819 Roo.each(w, function(tag) {
21820 if (b.indexOf(tag) > -1) {
21823 if (this.cwhite.indexOf(tag) > -1) {
21826 this.cwhite.push(tag);
21831 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21832 if (w.indexOf(tag) > -1) {
21835 this.cblack.push(tag);
21839 Roo.each(b, function(tag) {
21840 if (w.indexOf(tag) > -1) {
21843 if (this.cblack.indexOf(tag) > -1) {
21846 this.cblack.push(tag);
21851 setStylesheets : function(stylesheets)
21853 if(typeof(stylesheets) == 'string'){
21854 Roo.get(this.iframe.contentDocument.head).createChild({
21856 rel : 'stylesheet',
21865 Roo.each(stylesheets, function(s) {
21870 Roo.get(_this.iframe.contentDocument.head).createChild({
21872 rel : 'stylesheet',
21881 removeStylesheets : function()
21885 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21890 // hide stuff that is not compatible
21904 * @event specialkey
21908 * @cfg {String} fieldClass @hide
21911 * @cfg {String} focusClass @hide
21914 * @cfg {String} autoCreate @hide
21917 * @cfg {String} inputType @hide
21920 * @cfg {String} invalidClass @hide
21923 * @cfg {String} invalidText @hide
21926 * @cfg {String} msgFx @hide
21929 * @cfg {String} validateOnBlur @hide
21933 Roo.HtmlEditorCore.white = [
21934 'area', 'br', 'img', 'input', 'hr', 'wbr',
21936 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21937 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21938 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21939 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21940 'table', 'ul', 'xmp',
21942 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
21945 'dir', 'menu', 'ol', 'ul', 'dl',
21951 Roo.HtmlEditorCore.black = [
21952 // 'embed', 'object', // enable - backend responsiblity to clean thiese
21954 'base', 'basefont', 'bgsound', 'blink', 'body',
21955 'frame', 'frameset', 'head', 'html', 'ilayer',
21956 'iframe', 'layer', 'link', 'meta', 'object',
21957 'script', 'style' ,'title', 'xml' // clean later..
21959 Roo.HtmlEditorCore.clean = [
21960 'script', 'style', 'title', 'xml'
21962 Roo.HtmlEditorCore.remove = [
21967 Roo.HtmlEditorCore.ablack = [
21971 Roo.HtmlEditorCore.aclean = [
21972 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
21976 Roo.HtmlEditorCore.pwhite= [
21977 'http', 'https', 'mailto'
21980 // white listed style attributes.
21981 Roo.HtmlEditorCore.cwhite= [
21982 // 'text-align', /// default is to allow most things..
21988 // black listed style attributes.
21989 Roo.HtmlEditorCore.cblack= [
21990 // 'font-size' -- this can be set by the project
21994 Roo.HtmlEditorCore.swapCodes =[
22013 * @class Roo.bootstrap.HtmlEditor
22014 * @extends Roo.bootstrap.TextArea
22015 * Bootstrap HtmlEditor class
22018 * Create a new HtmlEditor
22019 * @param {Object} config The config object
22022 Roo.bootstrap.HtmlEditor = function(config){
22023 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22024 if (!this.toolbars) {
22025 this.toolbars = [];
22027 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22030 * @event initialize
22031 * Fires when the editor is fully initialized (including the iframe)
22032 * @param {HtmlEditor} this
22037 * Fires when the editor is first receives the focus. Any insertion must wait
22038 * until after this event.
22039 * @param {HtmlEditor} this
22043 * @event beforesync
22044 * Fires before the textarea is updated with content from the editor iframe. Return false
22045 * to cancel the sync.
22046 * @param {HtmlEditor} this
22047 * @param {String} html
22051 * @event beforepush
22052 * Fires before the iframe editor is updated with content from the textarea. Return false
22053 * to cancel the push.
22054 * @param {HtmlEditor} this
22055 * @param {String} html
22060 * Fires when the textarea is updated with content from the editor iframe.
22061 * @param {HtmlEditor} this
22062 * @param {String} html
22067 * Fires when the iframe editor is updated with content from the textarea.
22068 * @param {HtmlEditor} this
22069 * @param {String} html
22073 * @event editmodechange
22074 * Fires when the editor switches edit modes
22075 * @param {HtmlEditor} this
22076 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22078 editmodechange: true,
22080 * @event editorevent
22081 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22082 * @param {HtmlEditor} this
22086 * @event firstfocus
22087 * Fires when on first focus - needed by toolbars..
22088 * @param {HtmlEditor} this
22093 * Auto save the htmlEditor value as a file into Events
22094 * @param {HtmlEditor} this
22098 * @event savedpreview
22099 * preview the saved version of htmlEditor
22100 * @param {HtmlEditor} this
22107 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
22111 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22116 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22121 * @cfg {Number} height (in pixels)
22125 * @cfg {Number} width (in pixels)
22130 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22133 stylesheets: false,
22138 // private properties
22139 validationEvent : false,
22141 initialized : false,
22144 onFocus : Roo.emptyFn,
22146 hideMode:'offsets',
22149 tbContainer : false,
22151 toolbarContainer :function() {
22152 return this.wrap.select('.x-html-editor-tb',true).first();
22156 * Protected method that will not generally be called directly. It
22157 * is called when the editor creates its toolbar. Override this method if you need to
22158 * add custom toolbar buttons.
22159 * @param {HtmlEditor} editor
22161 createToolbar : function(){
22163 Roo.log("create toolbars");
22165 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22166 this.toolbars[0].render(this.toolbarContainer());
22170 // if (!editor.toolbars || !editor.toolbars.length) {
22171 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22174 // for (var i =0 ; i < editor.toolbars.length;i++) {
22175 // editor.toolbars[i] = Roo.factory(
22176 // typeof(editor.toolbars[i]) == 'string' ?
22177 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
22178 // Roo.bootstrap.HtmlEditor);
22179 // editor.toolbars[i].init(editor);
22185 onRender : function(ct, position)
22187 // Roo.log("Call onRender: " + this.xtype);
22189 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22191 this.wrap = this.inputEl().wrap({
22192 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22195 this.editorcore.onRender(ct, position);
22197 if (this.resizable) {
22198 this.resizeEl = new Roo.Resizable(this.wrap, {
22202 minHeight : this.height,
22203 height: this.height,
22204 handles : this.resizable,
22207 resize : function(r, w, h) {
22208 _t.onResize(w,h); // -something
22214 this.createToolbar(this);
22217 if(!this.width && this.resizable){
22218 this.setSize(this.wrap.getSize());
22220 if (this.resizeEl) {
22221 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22222 // should trigger onReize..
22228 onResize : function(w, h)
22230 Roo.log('resize: ' +w + ',' + h );
22231 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22235 if(this.inputEl() ){
22236 if(typeof w == 'number'){
22237 var aw = w - this.wrap.getFrameWidth('lr');
22238 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22241 if(typeof h == 'number'){
22242 var tbh = -11; // fixme it needs to tool bar size!
22243 for (var i =0; i < this.toolbars.length;i++) {
22244 // fixme - ask toolbars for heights?
22245 tbh += this.toolbars[i].el.getHeight();
22246 //if (this.toolbars[i].footer) {
22247 // tbh += this.toolbars[i].footer.el.getHeight();
22255 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22256 ah -= 5; // knock a few pixes off for look..
22257 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22261 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22262 this.editorcore.onResize(ew,eh);
22267 * Toggles the editor between standard and source edit mode.
22268 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22270 toggleSourceEdit : function(sourceEditMode)
22272 this.editorcore.toggleSourceEdit(sourceEditMode);
22274 if(this.editorcore.sourceEditMode){
22275 Roo.log('editor - showing textarea');
22278 // Roo.log(this.syncValue());
22280 this.inputEl().removeClass(['hide', 'x-hidden']);
22281 this.inputEl().dom.removeAttribute('tabIndex');
22282 this.inputEl().focus();
22284 Roo.log('editor - hiding textarea');
22286 // Roo.log(this.pushValue());
22289 this.inputEl().addClass(['hide', 'x-hidden']);
22290 this.inputEl().dom.setAttribute('tabIndex', -1);
22291 //this.deferFocus();
22294 if(this.resizable){
22295 this.setSize(this.wrap.getSize());
22298 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22301 // private (for BoxComponent)
22302 adjustSize : Roo.BoxComponent.prototype.adjustSize,
22304 // private (for BoxComponent)
22305 getResizeEl : function(){
22309 // private (for BoxComponent)
22310 getPositionEl : function(){
22315 initEvents : function(){
22316 this.originalValue = this.getValue();
22320 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22323 // markInvalid : Roo.emptyFn,
22325 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22328 // clearInvalid : Roo.emptyFn,
22330 setValue : function(v){
22331 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22332 this.editorcore.pushValue();
22337 deferFocus : function(){
22338 this.focus.defer(10, this);
22342 focus : function(){
22343 this.editorcore.focus();
22349 onDestroy : function(){
22355 for (var i =0; i < this.toolbars.length;i++) {
22356 // fixme - ask toolbars for heights?
22357 this.toolbars[i].onDestroy();
22360 this.wrap.dom.innerHTML = '';
22361 this.wrap.remove();
22366 onFirstFocus : function(){
22367 //Roo.log("onFirstFocus");
22368 this.editorcore.onFirstFocus();
22369 for (var i =0; i < this.toolbars.length;i++) {
22370 this.toolbars[i].onFirstFocus();
22376 syncValue : function()
22378 this.editorcore.syncValue();
22381 pushValue : function()
22383 this.editorcore.pushValue();
22387 // hide stuff that is not compatible
22401 * @event specialkey
22405 * @cfg {String} fieldClass @hide
22408 * @cfg {String} focusClass @hide
22411 * @cfg {String} autoCreate @hide
22414 * @cfg {String} inputType @hide
22417 * @cfg {String} invalidClass @hide
22420 * @cfg {String} invalidText @hide
22423 * @cfg {String} msgFx @hide
22426 * @cfg {String} validateOnBlur @hide
22435 Roo.namespace('Roo.bootstrap.htmleditor');
22437 * @class Roo.bootstrap.HtmlEditorToolbar1
22442 new Roo.bootstrap.HtmlEditor({
22445 new Roo.bootstrap.HtmlEditorToolbar1({
22446 disable : { fonts: 1 , format: 1, ..., ... , ...],
22452 * @cfg {Object} disable List of elements to disable..
22453 * @cfg {Array} btns List of additional buttons.
22457 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22460 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22463 Roo.apply(this, config);
22465 // default disabled, based on 'good practice'..
22466 this.disable = this.disable || {};
22467 Roo.applyIf(this.disable, {
22470 specialElements : true
22472 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22474 this.editor = config.editor;
22475 this.editorcore = config.editor.editorcore;
22477 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22479 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22480 // dont call parent... till later.
22482 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
22487 editorcore : false,
22492 "h1","h2","h3","h4","h5","h6",
22494 "abbr", "acronym", "address", "cite", "samp", "var",
22498 onRender : function(ct, position)
22500 // Roo.log("Call onRender: " + this.xtype);
22502 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22504 this.el.dom.style.marginBottom = '0';
22506 var editorcore = this.editorcore;
22507 var editor= this.editor;
22510 var btn = function(id,cmd , toggle, handler){
22512 var event = toggle ? 'toggle' : 'click';
22517 xns: Roo.bootstrap,
22520 enableToggle:toggle !== false,
22522 pressed : toggle ? false : null,
22525 a.listeners[toggle ? 'toggle' : 'click'] = function() {
22526 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
22535 xns: Roo.bootstrap,
22536 glyphicon : 'font',
22540 xns: Roo.bootstrap,
22544 Roo.each(this.formats, function(f) {
22545 style.menu.items.push({
22547 xns: Roo.bootstrap,
22548 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22553 editorcore.insertTag(this.tagname);
22560 children.push(style);
22563 btn('bold',false,true);
22564 btn('italic',false,true);
22565 btn('align-left', 'justifyleft',true);
22566 btn('align-center', 'justifycenter',true);
22567 btn('align-right' , 'justifyright',true);
22568 btn('link', false, false, function(btn) {
22569 //Roo.log("create link?");
22570 var url = prompt(this.createLinkText, this.defaultLinkValue);
22571 if(url && url != 'http:/'+'/'){
22572 this.editorcore.relayCmd('createlink', url);
22575 btn('list','insertunorderedlist',true);
22576 btn('pencil', false,true, function(btn){
22579 this.toggleSourceEdit(btn.pressed);
22585 xns: Roo.bootstrap,
22590 xns: Roo.bootstrap,
22595 cog.menu.items.push({
22597 xns: Roo.bootstrap,
22598 html : Clean styles,
22603 editorcore.insertTag(this.tagname);
22612 this.xtype = 'NavSimplebar';
22614 for(var i=0;i< children.length;i++) {
22616 this.buttons.add(this.addxtypeChild(children[i]));
22620 editor.on('editorevent', this.updateToolbar, this);
22622 onBtnClick : function(id)
22624 this.editorcore.relayCmd(id);
22625 this.editorcore.focus();
22629 * Protected method that will not generally be called directly. It triggers
22630 * a toolbar update by reading the markup state of the current selection in the editor.
22632 updateToolbar: function(){
22634 if(!this.editorcore.activated){
22635 this.editor.onFirstFocus(); // is this neeed?
22639 var btns = this.buttons;
22640 var doc = this.editorcore.doc;
22641 btns.get('bold').setActive(doc.queryCommandState('bold'));
22642 btns.get('italic').setActive(doc.queryCommandState('italic'));
22643 //btns.get('underline').setActive(doc.queryCommandState('underline'));
22645 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22646 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22647 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22649 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22650 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22653 var ans = this.editorcore.getAllAncestors();
22654 if (this.formatCombo) {
22657 var store = this.formatCombo.store;
22658 this.formatCombo.setValue("");
22659 for (var i =0; i < ans.length;i++) {
22660 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22662 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22670 // hides menus... - so this cant be on a menu...
22671 Roo.bootstrap.MenuMgr.hideAll();
22673 Roo.bootstrap.MenuMgr.hideAll();
22674 //this.editorsyncValue();
22676 onFirstFocus: function() {
22677 this.buttons.each(function(item){
22681 toggleSourceEdit : function(sourceEditMode){
22684 if(sourceEditMode){
22685 Roo.log("disabling buttons");
22686 this.buttons.each( function(item){
22687 if(item.cmd != 'pencil'){
22693 Roo.log("enabling buttons");
22694 if(this.editorcore.initialized){
22695 this.buttons.each( function(item){
22701 Roo.log("calling toggole on editor");
22702 // tell the editor that it's been pressed..
22703 this.editor.toggleSourceEdit(sourceEditMode);
22713 * @class Roo.bootstrap.Table.AbstractSelectionModel
22714 * @extends Roo.util.Observable
22715 * Abstract base class for grid SelectionModels. It provides the interface that should be
22716 * implemented by descendant classes. This class should not be directly instantiated.
22719 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22720 this.locked = false;
22721 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22725 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
22726 /** @ignore Called by the grid automatically. Do not call directly. */
22727 init : function(grid){
22733 * Locks the selections.
22736 this.locked = true;
22740 * Unlocks the selections.
22742 unlock : function(){
22743 this.locked = false;
22747 * Returns true if the selections are locked.
22748 * @return {Boolean}
22750 isLocked : function(){
22751 return this.locked;
22755 * @extends Roo.bootstrap.Table.AbstractSelectionModel
22756 * @class Roo.bootstrap.Table.RowSelectionModel
22757 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22758 * It supports multiple selections and keyboard selection/navigation.
22760 * @param {Object} config
22763 Roo.bootstrap.Table.RowSelectionModel = function(config){
22764 Roo.apply(this, config);
22765 this.selections = new Roo.util.MixedCollection(false, function(o){
22770 this.lastActive = false;
22774 * @event selectionchange
22775 * Fires when the selection changes
22776 * @param {SelectionModel} this
22778 "selectionchange" : true,
22780 * @event afterselectionchange
22781 * Fires after the selection changes (eg. by key press or clicking)
22782 * @param {SelectionModel} this
22784 "afterselectionchange" : true,
22786 * @event beforerowselect
22787 * Fires when a row is selected being selected, return false to cancel.
22788 * @param {SelectionModel} this
22789 * @param {Number} rowIndex The selected index
22790 * @param {Boolean} keepExisting False if other selections will be cleared
22792 "beforerowselect" : true,
22795 * Fires when a row is selected.
22796 * @param {SelectionModel} this
22797 * @param {Number} rowIndex The selected index
22798 * @param {Roo.data.Record} r The record
22800 "rowselect" : true,
22802 * @event rowdeselect
22803 * Fires when a row is deselected.
22804 * @param {SelectionModel} this
22805 * @param {Number} rowIndex The selected index
22807 "rowdeselect" : true
22809 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22810 this.locked = false;
22813 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
22815 * @cfg {Boolean} singleSelect
22816 * True to allow selection of only one row at a time (defaults to false)
22818 singleSelect : false,
22821 initEvents : function()
22824 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22825 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
22826 //}else{ // allow click to work like normal
22827 // this.grid.on("rowclick", this.handleDragableRowClick, this);
22829 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22830 this.grid.on("rowclick", this.handleMouseDown, this);
22832 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22833 "up" : function(e){
22835 this.selectPrevious(e.shiftKey);
22836 }else if(this.last !== false && this.lastActive !== false){
22837 var last = this.last;
22838 this.selectRange(this.last, this.lastActive-1);
22839 this.grid.getView().focusRow(this.lastActive);
22840 if(last !== false){
22844 this.selectFirstRow();
22846 this.fireEvent("afterselectionchange", this);
22848 "down" : function(e){
22850 this.selectNext(e.shiftKey);
22851 }else if(this.last !== false && this.lastActive !== false){
22852 var last = this.last;
22853 this.selectRange(this.last, this.lastActive+1);
22854 this.grid.getView().focusRow(this.lastActive);
22855 if(last !== false){
22859 this.selectFirstRow();
22861 this.fireEvent("afterselectionchange", this);
22865 this.grid.store.on('load', function(){
22866 this.selections.clear();
22869 var view = this.grid.view;
22870 view.on("refresh", this.onRefresh, this);
22871 view.on("rowupdated", this.onRowUpdated, this);
22872 view.on("rowremoved", this.onRemove, this);
22877 onRefresh : function()
22879 var ds = this.grid.store, i, v = this.grid.view;
22880 var s = this.selections;
22881 s.each(function(r){
22882 if((i = ds.indexOfId(r.id)) != -1){
22891 onRemove : function(v, index, r){
22892 this.selections.remove(r);
22896 onRowUpdated : function(v, index, r){
22897 if(this.isSelected(r)){
22898 v.onRowSelect(index);
22904 * @param {Array} records The records to select
22905 * @param {Boolean} keepExisting (optional) True to keep existing selections
22907 selectRecords : function(records, keepExisting)
22910 this.clearSelections();
22912 var ds = this.grid.store;
22913 for(var i = 0, len = records.length; i < len; i++){
22914 this.selectRow(ds.indexOf(records[i]), true);
22919 * Gets the number of selected rows.
22922 getCount : function(){
22923 return this.selections.length;
22927 * Selects the first row in the grid.
22929 selectFirstRow : function(){
22934 * Select the last row.
22935 * @param {Boolean} keepExisting (optional) True to keep existing selections
22937 selectLastRow : function(keepExisting){
22938 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22939 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22943 * Selects the row immediately following the last selected row.
22944 * @param {Boolean} keepExisting (optional) True to keep existing selections
22946 selectNext : function(keepExisting)
22948 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22949 this.selectRow(this.last+1, keepExisting);
22950 this.grid.getView().focusRow(this.last);
22955 * Selects the row that precedes the last selected row.
22956 * @param {Boolean} keepExisting (optional) True to keep existing selections
22958 selectPrevious : function(keepExisting){
22960 this.selectRow(this.last-1, keepExisting);
22961 this.grid.getView().focusRow(this.last);
22966 * Returns the selected records
22967 * @return {Array} Array of selected records
22969 getSelections : function(){
22970 return [].concat(this.selections.items);
22974 * Returns the first selected record.
22977 getSelected : function(){
22978 return this.selections.itemAt(0);
22983 * Clears all selections.
22985 clearSelections : function(fast)
22991 var ds = this.grid.store;
22992 var s = this.selections;
22993 s.each(function(r){
22994 this.deselectRow(ds.indexOfId(r.id));
22998 this.selections.clear();
23005 * Selects all rows.
23007 selectAll : function(){
23011 this.selections.clear();
23012 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23013 this.selectRow(i, true);
23018 * Returns True if there is a selection.
23019 * @return {Boolean}
23021 hasSelection : function(){
23022 return this.selections.length > 0;
23026 * Returns True if the specified row is selected.
23027 * @param {Number/Record} record The record or index of the record to check
23028 * @return {Boolean}
23030 isSelected : function(index){
23031 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23032 return (r && this.selections.key(r.id) ? true : false);
23036 * Returns True if the specified record id is selected.
23037 * @param {String} id The id of record to check
23038 * @return {Boolean}
23040 isIdSelected : function(id){
23041 return (this.selections.key(id) ? true : false);
23046 handleMouseDBClick : function(e, t){
23050 handleMouseDown : function(e, t)
23052 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23053 if(this.isLocked() || rowIndex < 0 ){
23056 if(e.shiftKey && this.last !== false){
23057 var last = this.last;
23058 this.selectRange(last, rowIndex, e.ctrlKey);
23059 this.last = last; // reset the last
23063 var isSelected = this.isSelected(rowIndex);
23064 //Roo.log("select row:" + rowIndex);
23066 this.deselectRow(rowIndex);
23068 this.selectRow(rowIndex, true);
23072 if(e.button !== 0 && isSelected){
23073 alert('rowIndex 2: ' + rowIndex);
23074 view.focusRow(rowIndex);
23075 }else if(e.ctrlKey && isSelected){
23076 this.deselectRow(rowIndex);
23077 }else if(!isSelected){
23078 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23079 view.focusRow(rowIndex);
23083 this.fireEvent("afterselectionchange", this);
23086 handleDragableRowClick : function(grid, rowIndex, e)
23088 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23089 this.selectRow(rowIndex, false);
23090 grid.view.focusRow(rowIndex);
23091 this.fireEvent("afterselectionchange", this);
23096 * Selects multiple rows.
23097 * @param {Array} rows Array of the indexes of the row to select
23098 * @param {Boolean} keepExisting (optional) True to keep existing selections
23100 selectRows : function(rows, keepExisting){
23102 this.clearSelections();
23104 for(var i = 0, len = rows.length; i < len; i++){
23105 this.selectRow(rows[i], true);
23110 * Selects a range of rows. All rows in between startRow and endRow are also selected.
23111 * @param {Number} startRow The index of the first row in the range
23112 * @param {Number} endRow The index of the last row in the range
23113 * @param {Boolean} keepExisting (optional) True to retain existing selections
23115 selectRange : function(startRow, endRow, keepExisting){
23120 this.clearSelections();
23122 if(startRow <= endRow){
23123 for(var i = startRow; i <= endRow; i++){
23124 this.selectRow(i, true);
23127 for(var i = startRow; i >= endRow; i--){
23128 this.selectRow(i, true);
23134 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23135 * @param {Number} startRow The index of the first row in the range
23136 * @param {Number} endRow The index of the last row in the range
23138 deselectRange : function(startRow, endRow, preventViewNotify){
23142 for(var i = startRow; i <= endRow; i++){
23143 this.deselectRow(i, preventViewNotify);
23149 * @param {Number} row The index of the row to select
23150 * @param {Boolean} keepExisting (optional) True to keep existing selections
23152 selectRow : function(index, keepExisting, preventViewNotify)
23154 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23157 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23158 if(!keepExisting || this.singleSelect){
23159 this.clearSelections();
23162 var r = this.grid.store.getAt(index);
23163 //console.log('selectRow - record id :' + r.id);
23165 this.selections.add(r);
23166 this.last = this.lastActive = index;
23167 if(!preventViewNotify){
23168 var proxy = new Roo.Element(
23169 this.grid.getRowDom(index)
23171 proxy.addClass('bg-info info');
23173 this.fireEvent("rowselect", this, index, r);
23174 this.fireEvent("selectionchange", this);
23180 * @param {Number} row The index of the row to deselect
23182 deselectRow : function(index, preventViewNotify)
23187 if(this.last == index){
23190 if(this.lastActive == index){
23191 this.lastActive = false;
23194 var r = this.grid.store.getAt(index);
23199 this.selections.remove(r);
23200 //.console.log('deselectRow - record id :' + r.id);
23201 if(!preventViewNotify){
23203 var proxy = new Roo.Element(
23204 this.grid.getRowDom(index)
23206 proxy.removeClass('bg-info info');
23208 this.fireEvent("rowdeselect", this, index);
23209 this.fireEvent("selectionchange", this);
23213 restoreLast : function(){
23215 this.last = this._last;
23220 acceptsNav : function(row, col, cm){
23221 return !cm.isHidden(col) && cm.isCellEditable(col, row);
23225 onEditorKey : function(field, e){
23226 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23231 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23233 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23235 }else if(k == e.ENTER && !e.ctrlKey){
23239 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23241 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23243 }else if(k == e.ESC){
23247 g.startEditing(newCell[0], newCell[1]);
23253 * Ext JS Library 1.1.1
23254 * Copyright(c) 2006-2007, Ext JS, LLC.
23256 * Originally Released Under LGPL - original licence link has changed is not relivant.
23259 * <script type="text/javascript">
23263 * @class Roo.bootstrap.PagingToolbar
23264 * @extends Roo.bootstrap.NavSimplebar
23265 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23267 * Create a new PagingToolbar
23268 * @param {Object} config The config object
23269 * @param {Roo.data.Store} store
23271 Roo.bootstrap.PagingToolbar = function(config)
23273 // old args format still supported... - xtype is prefered..
23274 // created from xtype...
23276 this.ds = config.dataSource;
23278 if (config.store && !this.ds) {
23279 this.store= Roo.factory(config.store, Roo.data);
23280 this.ds = this.store;
23281 this.ds.xmodule = this.xmodule || false;
23284 this.toolbarItems = [];
23285 if (config.items) {
23286 this.toolbarItems = config.items;
23289 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23294 this.bind(this.ds);
23297 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23301 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23303 * @cfg {Roo.data.Store} dataSource
23304 * The underlying data store providing the paged data
23307 * @cfg {String/HTMLElement/Element} container
23308 * container The id or element that will contain the toolbar
23311 * @cfg {Boolean} displayInfo
23312 * True to display the displayMsg (defaults to false)
23315 * @cfg {Number} pageSize
23316 * The number of records to display per page (defaults to 20)
23320 * @cfg {String} displayMsg
23321 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23323 displayMsg : 'Displaying {0} - {1} of {2}',
23325 * @cfg {String} emptyMsg
23326 * The message to display when no records are found (defaults to "No data to display")
23328 emptyMsg : 'No data to display',
23330 * Customizable piece of the default paging text (defaults to "Page")
23333 beforePageText : "Page",
23335 * Customizable piece of the default paging text (defaults to "of %0")
23338 afterPageText : "of {0}",
23340 * Customizable piece of the default paging text (defaults to "First Page")
23343 firstText : "First Page",
23345 * Customizable piece of the default paging text (defaults to "Previous Page")
23348 prevText : "Previous Page",
23350 * Customizable piece of the default paging text (defaults to "Next Page")
23353 nextText : "Next Page",
23355 * Customizable piece of the default paging text (defaults to "Last Page")
23358 lastText : "Last Page",
23360 * Customizable piece of the default paging text (defaults to "Refresh")
23363 refreshText : "Refresh",
23367 onRender : function(ct, position)
23369 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23370 this.navgroup.parentId = this.id;
23371 this.navgroup.onRender(this.el, null);
23372 // add the buttons to the navgroup
23374 if(this.displayInfo){
23375 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23376 this.displayEl = this.el.select('.x-paging-info', true).first();
23377 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23378 // this.displayEl = navel.el.select('span',true).first();
23384 Roo.each(_this.buttons, function(e){ // this might need to use render????
23385 Roo.factory(e).onRender(_this.el, null);
23389 Roo.each(_this.toolbarItems, function(e) {
23390 _this.navgroup.addItem(e);
23394 this.first = this.navgroup.addItem({
23395 tooltip: this.firstText,
23397 icon : 'fa fa-backward',
23399 preventDefault: true,
23400 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23403 this.prev = this.navgroup.addItem({
23404 tooltip: this.prevText,
23406 icon : 'fa fa-step-backward',
23408 preventDefault: true,
23409 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
23411 //this.addSeparator();
23414 var field = this.navgroup.addItem( {
23416 cls : 'x-paging-position',
23418 html : this.beforePageText +
23419 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23420 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
23423 this.field = field.el.select('input', true).first();
23424 this.field.on("keydown", this.onPagingKeydown, this);
23425 this.field.on("focus", function(){this.dom.select();});
23428 this.afterTextEl = field.el.select('.x-paging-after',true).first();
23429 //this.field.setHeight(18);
23430 //this.addSeparator();
23431 this.next = this.navgroup.addItem({
23432 tooltip: this.nextText,
23434 html : ' <i class="fa fa-step-forward">',
23436 preventDefault: true,
23437 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
23439 this.last = this.navgroup.addItem({
23440 tooltip: this.lastText,
23441 icon : 'fa fa-forward',
23444 preventDefault: true,
23445 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
23447 //this.addSeparator();
23448 this.loading = this.navgroup.addItem({
23449 tooltip: this.refreshText,
23450 icon: 'fa fa-refresh',
23451 preventDefault: true,
23452 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23458 updateInfo : function(){
23459 if(this.displayEl){
23460 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23461 var msg = count == 0 ?
23465 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
23467 this.displayEl.update(msg);
23472 onLoad : function(ds, r, o){
23473 this.cursor = o.params ? o.params.start : 0;
23474 var d = this.getPageData(),
23478 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23479 this.field.dom.value = ap;
23480 this.first.setDisabled(ap == 1);
23481 this.prev.setDisabled(ap == 1);
23482 this.next.setDisabled(ap == ps);
23483 this.last.setDisabled(ap == ps);
23484 this.loading.enable();
23489 getPageData : function(){
23490 var total = this.ds.getTotalCount();
23493 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23494 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23499 onLoadError : function(){
23500 this.loading.enable();
23504 onPagingKeydown : function(e){
23505 var k = e.getKey();
23506 var d = this.getPageData();
23508 var v = this.field.dom.value, pageNum;
23509 if(!v || isNaN(pageNum = parseInt(v, 10))){
23510 this.field.dom.value = d.activePage;
23513 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23514 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23517 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))
23519 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23520 this.field.dom.value = pageNum;
23521 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23524 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23526 var v = this.field.dom.value, pageNum;
23527 var increment = (e.shiftKey) ? 10 : 1;
23528 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23531 if(!v || isNaN(pageNum = parseInt(v, 10))) {
23532 this.field.dom.value = d.activePage;
23535 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23537 this.field.dom.value = parseInt(v, 10) + increment;
23538 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23539 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23546 beforeLoad : function(){
23548 this.loading.disable();
23553 onClick : function(which){
23562 ds.load({params:{start: 0, limit: this.pageSize}});
23565 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23568 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23571 var total = ds.getTotalCount();
23572 var extra = total % this.pageSize;
23573 var lastStart = extra ? (total - extra) : total-this.pageSize;
23574 ds.load({params:{start: lastStart, limit: this.pageSize}});
23577 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23583 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23584 * @param {Roo.data.Store} store The data store to unbind
23586 unbind : function(ds){
23587 ds.un("beforeload", this.beforeLoad, this);
23588 ds.un("load", this.onLoad, this);
23589 ds.un("loadexception", this.onLoadError, this);
23590 ds.un("remove", this.updateInfo, this);
23591 ds.un("add", this.updateInfo, this);
23592 this.ds = undefined;
23596 * Binds the paging toolbar to the specified {@link Roo.data.Store}
23597 * @param {Roo.data.Store} store The data store to bind
23599 bind : function(ds){
23600 ds.on("beforeload", this.beforeLoad, this);
23601 ds.on("load", this.onLoad, this);
23602 ds.on("loadexception", this.onLoadError, this);
23603 ds.on("remove", this.updateInfo, this);
23604 ds.on("add", this.updateInfo, this);
23615 * @class Roo.bootstrap.MessageBar
23616 * @extends Roo.bootstrap.Component
23617 * Bootstrap MessageBar class
23618 * @cfg {String} html contents of the MessageBar
23619 * @cfg {String} weight (info | success | warning | danger) default info
23620 * @cfg {String} beforeClass insert the bar before the given class
23621 * @cfg {Boolean} closable (true | false) default false
23622 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23625 * Create a new Element
23626 * @param {Object} config The config object
23629 Roo.bootstrap.MessageBar = function(config){
23630 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23633 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
23639 beforeClass: 'bootstrap-sticky-wrap',
23641 getAutoCreate : function(){
23645 cls: 'alert alert-dismissable alert-' + this.weight,
23650 html: this.html || ''
23656 cfg.cls += ' alert-messages-fixed';
23670 onRender : function(ct, position)
23672 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23675 var cfg = Roo.apply({}, this.getAutoCreate());
23679 cfg.cls += ' ' + this.cls;
23682 cfg.style = this.style;
23684 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23686 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23689 this.el.select('>button.close').on('click', this.hide, this);
23695 if (!this.rendered) {
23701 this.fireEvent('show', this);
23707 if (!this.rendered) {
23713 this.fireEvent('hide', this);
23716 update : function()
23718 // var e = this.el.dom.firstChild;
23720 // if(this.closable){
23721 // e = e.nextSibling;
23724 // e.data = this.html || '';
23726 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23742 * @class Roo.bootstrap.Graph
23743 * @extends Roo.bootstrap.Component
23744 * Bootstrap Graph class
23748 @cfg {String} graphtype bar | vbar | pie
23749 @cfg {number} g_x coodinator | centre x (pie)
23750 @cfg {number} g_y coodinator | centre y (pie)
23751 @cfg {number} g_r radius (pie)
23752 @cfg {number} g_height height of the chart (respected by all elements in the set)
23753 @cfg {number} g_width width of the chart (respected by all elements in the set)
23754 @cfg {Object} title The title of the chart
23757 -opts (object) options for the chart
23759 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23760 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23762 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.
23763 o stacked (boolean) whether or not to tread values as in a stacked bar chart
23765 o stretch (boolean)
23767 -opts (object) options for the pie
23770 o startAngle (number)
23771 o endAngle (number)
23775 * Create a new Input
23776 * @param {Object} config The config object
23779 Roo.bootstrap.Graph = function(config){
23780 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23786 * The img click event for the img.
23787 * @param {Roo.EventObject} e
23793 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
23804 //g_colors: this.colors,
23811 getAutoCreate : function(){
23822 onRender : function(ct,position){
23825 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23827 if (typeof(Raphael) == 'undefined') {
23828 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23832 this.raphael = Raphael(this.el.dom);
23834 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23835 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23836 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23837 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23839 r.text(160, 10, "Single Series Chart").attr(txtattr);
23840 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23841 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23842 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23844 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23845 r.barchart(330, 10, 300, 220, data1);
23846 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23847 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23850 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23851 // r.barchart(30, 30, 560, 250, xdata, {
23852 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23853 // axis : "0 0 1 1",
23854 // axisxlabels : xdata
23855 // //yvalues : cols,
23858 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23860 // this.load(null,xdata,{
23861 // axis : "0 0 1 1",
23862 // axisxlabels : xdata
23867 load : function(graphtype,xdata,opts)
23869 this.raphael.clear();
23871 graphtype = this.graphtype;
23876 var r = this.raphael,
23877 fin = function () {
23878 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23880 fout = function () {
23881 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23883 pfin = function() {
23884 this.sector.stop();
23885 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23888 this.label[0].stop();
23889 this.label[0].attr({ r: 7.5 });
23890 this.label[1].attr({ "font-weight": 800 });
23893 pfout = function() {
23894 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23897 this.label[0].animate({ r: 5 }, 500, "bounce");
23898 this.label[1].attr({ "font-weight": 400 });
23904 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23907 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23910 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
23911 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23913 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23920 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23925 setTitle: function(o)
23930 initEvents: function() {
23933 this.el.on('click', this.onClick, this);
23937 onClick : function(e)
23939 Roo.log('img onclick');
23940 this.fireEvent('click', this, e);
23952 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23955 * @class Roo.bootstrap.dash.NumberBox
23956 * @extends Roo.bootstrap.Component
23957 * Bootstrap NumberBox class
23958 * @cfg {String} headline Box headline
23959 * @cfg {String} content Box content
23960 * @cfg {String} icon Box icon
23961 * @cfg {String} footer Footer text
23962 * @cfg {String} fhref Footer href
23965 * Create a new NumberBox
23966 * @param {Object} config The config object
23970 Roo.bootstrap.dash.NumberBox = function(config){
23971 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23975 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
23984 getAutoCreate : function(){
23988 cls : 'small-box ',
23996 cls : 'roo-headline',
23997 html : this.headline
24001 cls : 'roo-content',
24002 html : this.content
24016 cls : 'ion ' + this.icon
24025 cls : 'small-box-footer',
24026 href : this.fhref || '#',
24030 cfg.cn.push(footer);
24037 onRender : function(ct,position){
24038 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24045 setHeadline: function (value)
24047 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24050 setFooter: function (value, href)
24052 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24055 this.el.select('a.small-box-footer',true).first().attr('href', href);
24060 setContent: function (value)
24062 this.el.select('.roo-content',true).first().dom.innerHTML = value;
24065 initEvents: function()
24079 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24082 * @class Roo.bootstrap.dash.TabBox
24083 * @extends Roo.bootstrap.Component
24084 * Bootstrap TabBox class
24085 * @cfg {String} title Title of the TabBox
24086 * @cfg {String} icon Icon of the TabBox
24087 * @cfg {Boolean} showtabs (true|false) show the tabs default true
24088 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24091 * Create a new TabBox
24092 * @param {Object} config The config object
24096 Roo.bootstrap.dash.TabBox = function(config){
24097 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24102 * When a pane is added
24103 * @param {Roo.bootstrap.dash.TabPane} pane
24107 * @event activatepane
24108 * When a pane is activated
24109 * @param {Roo.bootstrap.dash.TabPane} pane
24111 "activatepane" : true
24119 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
24124 tabScrollable : false,
24126 getChildContainer : function()
24128 return this.el.select('.tab-content', true).first();
24131 getAutoCreate : function(){
24135 cls: 'pull-left header',
24143 cls: 'fa ' + this.icon
24149 cls: 'nav nav-tabs pull-right',
24155 if(this.tabScrollable){
24162 cls: 'nav nav-tabs pull-right',
24173 cls: 'nav-tabs-custom',
24178 cls: 'tab-content no-padding',
24186 initEvents : function()
24188 //Roo.log('add add pane handler');
24189 this.on('addpane', this.onAddPane, this);
24192 * Updates the box title
24193 * @param {String} html to set the title to.
24195 setTitle : function(value)
24197 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24199 onAddPane : function(pane)
24201 this.panes.push(pane);
24202 //Roo.log('addpane');
24204 // tabs are rendere left to right..
24205 if(!this.showtabs){
24209 var ctr = this.el.select('.nav-tabs', true).first();
24212 var existing = ctr.select('.nav-tab',true);
24213 var qty = existing.getCount();;
24216 var tab = ctr.createChild({
24218 cls : 'nav-tab' + (qty ? '' : ' active'),
24226 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24229 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24231 pane.el.addClass('active');
24236 onTabClick : function(ev,un,ob,pane)
24238 //Roo.log('tab - prev default');
24239 ev.preventDefault();
24242 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24243 pane.tab.addClass('active');
24244 //Roo.log(pane.title);
24245 this.getChildContainer().select('.tab-pane',true).removeClass('active');
24246 // technically we should have a deactivate event.. but maybe add later.
24247 // and it should not de-activate the selected tab...
24248 this.fireEvent('activatepane', pane);
24249 pane.el.addClass('active');
24250 pane.fireEvent('activate');
24255 getActivePane : function()
24258 Roo.each(this.panes, function(p) {
24259 if(p.el.hasClass('active')){
24280 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24282 * @class Roo.bootstrap.TabPane
24283 * @extends Roo.bootstrap.Component
24284 * Bootstrap TabPane class
24285 * @cfg {Boolean} active (false | true) Default false
24286 * @cfg {String} title title of panel
24290 * Create a new TabPane
24291 * @param {Object} config The config object
24294 Roo.bootstrap.dash.TabPane = function(config){
24295 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24301 * When a pane is activated
24302 * @param {Roo.bootstrap.dash.TabPane} pane
24309 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
24314 // the tabBox that this is attached to.
24317 getAutoCreate : function()
24325 cfg.cls += ' active';
24330 initEvents : function()
24332 //Roo.log('trigger add pane handler');
24333 this.parent().fireEvent('addpane', this)
24337 * Updates the tab title
24338 * @param {String} html to set the title to.
24340 setTitle: function(str)
24346 this.tab.select('a', true).first().dom.innerHTML = str;
24363 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24366 * @class Roo.bootstrap.menu.Menu
24367 * @extends Roo.bootstrap.Component
24368 * Bootstrap Menu class - container for Menu
24369 * @cfg {String} html Text of the menu
24370 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24371 * @cfg {String} icon Font awesome icon
24372 * @cfg {String} pos Menu align to (top | bottom) default bottom
24376 * Create a new Menu
24377 * @param {Object} config The config object
24381 Roo.bootstrap.menu.Menu = function(config){
24382 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24386 * @event beforeshow
24387 * Fires before this menu is displayed
24388 * @param {Roo.bootstrap.menu.Menu} this
24392 * @event beforehide
24393 * Fires before this menu is hidden
24394 * @param {Roo.bootstrap.menu.Menu} this
24399 * Fires after this menu is displayed
24400 * @param {Roo.bootstrap.menu.Menu} this
24405 * Fires after this menu is hidden
24406 * @param {Roo.bootstrap.menu.Menu} this
24411 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24412 * @param {Roo.bootstrap.menu.Menu} this
24413 * @param {Roo.EventObject} e
24420 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
24424 weight : 'default',
24429 getChildContainer : function() {
24430 if(this.isSubMenu){
24434 return this.el.select('ul.dropdown-menu', true).first();
24437 getAutoCreate : function()
24442 cls : 'roo-menu-text',
24450 cls : 'fa ' + this.icon
24461 cls : 'dropdown-button btn btn-' + this.weight,
24466 cls : 'dropdown-toggle btn btn-' + this.weight,
24476 cls : 'dropdown-menu'
24482 if(this.pos == 'top'){
24483 cfg.cls += ' dropup';
24486 if(this.isSubMenu){
24489 cls : 'dropdown-menu'
24496 onRender : function(ct, position)
24498 this.isSubMenu = ct.hasClass('dropdown-submenu');
24500 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24503 initEvents : function()
24505 if(this.isSubMenu){
24509 this.hidden = true;
24511 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24512 this.triggerEl.on('click', this.onTriggerPress, this);
24514 this.buttonEl = this.el.select('button.dropdown-button', true).first();
24515 this.buttonEl.on('click', this.onClick, this);
24521 if(this.isSubMenu){
24525 return this.el.select('ul.dropdown-menu', true).first();
24528 onClick : function(e)
24530 this.fireEvent("click", this, e);
24533 onTriggerPress : function(e)
24535 if (this.isVisible()) {
24542 isVisible : function(){
24543 return !this.hidden;
24548 this.fireEvent("beforeshow", this);
24550 this.hidden = false;
24551 this.el.addClass('open');
24553 Roo.get(document).on("mouseup", this.onMouseUp, this);
24555 this.fireEvent("show", this);
24562 this.fireEvent("beforehide", this);
24564 this.hidden = true;
24565 this.el.removeClass('open');
24567 Roo.get(document).un("mouseup", this.onMouseUp);
24569 this.fireEvent("hide", this);
24572 onMouseUp : function()
24586 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24589 * @class Roo.bootstrap.menu.Item
24590 * @extends Roo.bootstrap.Component
24591 * Bootstrap MenuItem class
24592 * @cfg {Boolean} submenu (true | false) default false
24593 * @cfg {String} html text of the item
24594 * @cfg {String} href the link
24595 * @cfg {Boolean} disable (true | false) default false
24596 * @cfg {Boolean} preventDefault (true | false) default true
24597 * @cfg {String} icon Font awesome icon
24598 * @cfg {String} pos Submenu align to (left | right) default right
24602 * Create a new Item
24603 * @param {Object} config The config object
24607 Roo.bootstrap.menu.Item = function(config){
24608 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24612 * Fires when the mouse is hovering over this menu
24613 * @param {Roo.bootstrap.menu.Item} this
24614 * @param {Roo.EventObject} e
24619 * Fires when the mouse exits this menu
24620 * @param {Roo.bootstrap.menu.Item} this
24621 * @param {Roo.EventObject} e
24627 * The raw click event for the entire grid.
24628 * @param {Roo.EventObject} e
24634 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
24639 preventDefault: true,
24644 getAutoCreate : function()
24649 cls : 'roo-menu-item-text',
24657 cls : 'fa ' + this.icon
24666 href : this.href || '#',
24673 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24677 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24679 if(this.pos == 'left'){
24680 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24687 initEvents : function()
24689 this.el.on('mouseover', this.onMouseOver, this);
24690 this.el.on('mouseout', this.onMouseOut, this);
24692 this.el.select('a', true).first().on('click', this.onClick, this);
24696 onClick : function(e)
24698 if(this.preventDefault){
24699 e.preventDefault();
24702 this.fireEvent("click", this, e);
24705 onMouseOver : function(e)
24707 if(this.submenu && this.pos == 'left'){
24708 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24711 this.fireEvent("mouseover", this, e);
24714 onMouseOut : function(e)
24716 this.fireEvent("mouseout", this, e);
24728 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24731 * @class Roo.bootstrap.menu.Separator
24732 * @extends Roo.bootstrap.Component
24733 * Bootstrap Separator class
24736 * Create a new Separator
24737 * @param {Object} config The config object
24741 Roo.bootstrap.menu.Separator = function(config){
24742 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24745 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
24747 getAutoCreate : function(){
24768 * @class Roo.bootstrap.Tooltip
24769 * Bootstrap Tooltip class
24770 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24771 * to determine which dom element triggers the tooltip.
24773 * It needs to add support for additional attributes like tooltip-position
24776 * Create a new Toolti
24777 * @param {Object} config The config object
24780 Roo.bootstrap.Tooltip = function(config){
24781 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24784 Roo.apply(Roo.bootstrap.Tooltip, {
24786 * @function init initialize tooltip monitoring.
24790 currentTip : false,
24791 currentRegion : false,
24797 Roo.get(document).on('mouseover', this.enter ,this);
24798 Roo.get(document).on('mouseout', this.leave, this);
24801 this.currentTip = new Roo.bootstrap.Tooltip();
24804 enter : function(ev)
24806 var dom = ev.getTarget();
24808 //Roo.log(['enter',dom]);
24809 var el = Roo.fly(dom);
24810 if (this.currentEl) {
24812 //Roo.log(this.currentEl);
24813 //Roo.log(this.currentEl.contains(dom));
24814 if (this.currentEl == el) {
24817 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24823 if (this.currentTip.el) {
24824 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24828 if(!el || el.dom == document){
24834 // you can not look for children, as if el is the body.. then everythign is the child..
24835 if (!el.attr('tooltip')) { //
24836 if (!el.select("[tooltip]").elements.length) {
24839 // is the mouse over this child...?
24840 bindEl = el.select("[tooltip]").first();
24841 var xy = ev.getXY();
24842 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24843 //Roo.log("not in region.");
24846 //Roo.log("child element over..");
24849 this.currentEl = bindEl;
24850 this.currentTip.bind(bindEl);
24851 this.currentRegion = Roo.lib.Region.getRegion(dom);
24852 this.currentTip.enter();
24855 leave : function(ev)
24857 var dom = ev.getTarget();
24858 //Roo.log(['leave',dom]);
24859 if (!this.currentEl) {
24864 if (dom != this.currentEl.dom) {
24867 var xy = ev.getXY();
24868 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
24871 // only activate leave if mouse cursor is outside... bounding box..
24876 if (this.currentTip) {
24877 this.currentTip.leave();
24879 //Roo.log('clear currentEl');
24880 this.currentEl = false;
24885 'left' : ['r-l', [-2,0], 'right'],
24886 'right' : ['l-r', [2,0], 'left'],
24887 'bottom' : ['t-b', [0,2], 'top'],
24888 'top' : [ 'b-t', [0,-2], 'bottom']
24894 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
24899 delay : null, // can be { show : 300 , hide: 500}
24903 hoverState : null, //???
24905 placement : 'bottom',
24907 getAutoCreate : function(){
24914 cls : 'tooltip-arrow'
24917 cls : 'tooltip-inner'
24924 bind : function(el)
24930 enter : function () {
24932 if (this.timeout != null) {
24933 clearTimeout(this.timeout);
24936 this.hoverState = 'in';
24937 //Roo.log("enter - show");
24938 if (!this.delay || !this.delay.show) {
24943 this.timeout = setTimeout(function () {
24944 if (_t.hoverState == 'in') {
24947 }, this.delay.show);
24951 clearTimeout(this.timeout);
24953 this.hoverState = 'out';
24954 if (!this.delay || !this.delay.hide) {
24960 this.timeout = setTimeout(function () {
24961 //Roo.log("leave - timeout");
24963 if (_t.hoverState == 'out') {
24965 Roo.bootstrap.Tooltip.currentEl = false;
24973 this.render(document.body);
24976 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24978 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24980 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24982 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24984 var placement = typeof this.placement == 'function' ?
24985 this.placement.call(this, this.el, on_el) :
24988 var autoToken = /\s?auto?\s?/i;
24989 var autoPlace = autoToken.test(placement);
24991 placement = placement.replace(autoToken, '') || 'top';
24995 //this.el.setXY([0,0]);
24997 //this.el.dom.style.display='block';
24999 //this.el.appendTo(on_el);
25001 var p = this.getPosition();
25002 var box = this.el.getBox();
25008 var align = Roo.bootstrap.Tooltip.alignment[placement];
25010 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25012 if(placement == 'top' || placement == 'bottom'){
25014 placement = 'right';
25017 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25018 placement = 'left';
25021 var scroll = Roo.select('body', true).first().getScroll();
25023 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25029 align = Roo.bootstrap.Tooltip.alignment[placement];
25031 this.el.alignTo(this.bindEl, align[0],align[1]);
25032 //var arrow = this.el.select('.arrow',true).first();
25033 //arrow.set(align[2],
25035 this.el.addClass(placement);
25037 this.el.addClass('in fade');
25039 this.hoverState = null;
25041 if (this.el.hasClass('fade')) {
25052 //this.el.setXY([0,0]);
25053 this.el.removeClass('in');
25069 * @class Roo.bootstrap.LocationPicker
25070 * @extends Roo.bootstrap.Component
25071 * Bootstrap LocationPicker class
25072 * @cfg {Number} latitude Position when init default 0
25073 * @cfg {Number} longitude Position when init default 0
25074 * @cfg {Number} zoom default 15
25075 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25076 * @cfg {Boolean} mapTypeControl default false
25077 * @cfg {Boolean} disableDoubleClickZoom default false
25078 * @cfg {Boolean} scrollwheel default true
25079 * @cfg {Boolean} streetViewControl default false
25080 * @cfg {Number} radius default 0
25081 * @cfg {String} locationName
25082 * @cfg {Boolean} draggable default true
25083 * @cfg {Boolean} enableAutocomplete default false
25084 * @cfg {Boolean} enableReverseGeocode default true
25085 * @cfg {String} markerTitle
25088 * Create a new LocationPicker
25089 * @param {Object} config The config object
25093 Roo.bootstrap.LocationPicker = function(config){
25095 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25100 * Fires when the picker initialized.
25101 * @param {Roo.bootstrap.LocationPicker} this
25102 * @param {Google Location} location
25106 * @event positionchanged
25107 * Fires when the picker position changed.
25108 * @param {Roo.bootstrap.LocationPicker} this
25109 * @param {Google Location} location
25111 positionchanged : true,
25114 * Fires when the map resize.
25115 * @param {Roo.bootstrap.LocationPicker} this
25120 * Fires when the map show.
25121 * @param {Roo.bootstrap.LocationPicker} this
25126 * Fires when the map hide.
25127 * @param {Roo.bootstrap.LocationPicker} this
25132 * Fires when click the map.
25133 * @param {Roo.bootstrap.LocationPicker} this
25134 * @param {Map event} e
25138 * @event mapRightClick
25139 * Fires when right click the map.
25140 * @param {Roo.bootstrap.LocationPicker} this
25141 * @param {Map event} e
25143 mapRightClick : true,
25145 * @event markerClick
25146 * Fires when click the marker.
25147 * @param {Roo.bootstrap.LocationPicker} this
25148 * @param {Map event} e
25150 markerClick : true,
25152 * @event markerRightClick
25153 * Fires when right click the marker.
25154 * @param {Roo.bootstrap.LocationPicker} this
25155 * @param {Map event} e
25157 markerRightClick : true,
25159 * @event OverlayViewDraw
25160 * Fires when OverlayView Draw
25161 * @param {Roo.bootstrap.LocationPicker} this
25163 OverlayViewDraw : true,
25165 * @event OverlayViewOnAdd
25166 * Fires when OverlayView Draw
25167 * @param {Roo.bootstrap.LocationPicker} this
25169 OverlayViewOnAdd : true,
25171 * @event OverlayViewOnRemove
25172 * Fires when OverlayView Draw
25173 * @param {Roo.bootstrap.LocationPicker} this
25175 OverlayViewOnRemove : true,
25177 * @event OverlayViewShow
25178 * Fires when OverlayView Draw
25179 * @param {Roo.bootstrap.LocationPicker} this
25180 * @param {Pixel} cpx
25182 OverlayViewShow : true,
25184 * @event OverlayViewHide
25185 * Fires when OverlayView Draw
25186 * @param {Roo.bootstrap.LocationPicker} this
25188 OverlayViewHide : true,
25190 * @event loadexception
25191 * Fires when load google lib failed.
25192 * @param {Roo.bootstrap.LocationPicker} this
25194 loadexception : true
25199 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
25201 gMapContext: false,
25207 mapTypeControl: false,
25208 disableDoubleClickZoom: false,
25210 streetViewControl: false,
25214 enableAutocomplete: false,
25215 enableReverseGeocode: true,
25218 getAutoCreate: function()
25223 cls: 'roo-location-picker'
25229 initEvents: function(ct, position)
25231 if(!this.el.getWidth() || this.isApplied()){
25235 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25240 initial: function()
25242 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25243 this.fireEvent('loadexception', this);
25247 if(!this.mapTypeId){
25248 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25251 this.gMapContext = this.GMapContext();
25253 this.initOverlayView();
25255 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25259 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25260 _this.setPosition(_this.gMapContext.marker.position);
25263 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25264 _this.fireEvent('mapClick', this, event);
25268 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25269 _this.fireEvent('mapRightClick', this, event);
25273 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25274 _this.fireEvent('markerClick', this, event);
25278 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25279 _this.fireEvent('markerRightClick', this, event);
25283 this.setPosition(this.gMapContext.location);
25285 this.fireEvent('initial', this, this.gMapContext.location);
25288 initOverlayView: function()
25292 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25296 _this.fireEvent('OverlayViewDraw', _this);
25301 _this.fireEvent('OverlayViewOnAdd', _this);
25304 onRemove: function()
25306 _this.fireEvent('OverlayViewOnRemove', _this);
25309 show: function(cpx)
25311 _this.fireEvent('OverlayViewShow', _this, cpx);
25316 _this.fireEvent('OverlayViewHide', _this);
25322 fromLatLngToContainerPixel: function(event)
25324 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25327 isApplied: function()
25329 return this.getGmapContext() == false ? false : true;
25332 getGmapContext: function()
25334 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25337 GMapContext: function()
25339 var position = new google.maps.LatLng(this.latitude, this.longitude);
25341 var _map = new google.maps.Map(this.el.dom, {
25344 mapTypeId: this.mapTypeId,
25345 mapTypeControl: this.mapTypeControl,
25346 disableDoubleClickZoom: this.disableDoubleClickZoom,
25347 scrollwheel: this.scrollwheel,
25348 streetViewControl: this.streetViewControl,
25349 locationName: this.locationName,
25350 draggable: this.draggable,
25351 enableAutocomplete: this.enableAutocomplete,
25352 enableReverseGeocode: this.enableReverseGeocode
25355 var _marker = new google.maps.Marker({
25356 position: position,
25358 title: this.markerTitle,
25359 draggable: this.draggable
25366 location: position,
25367 radius: this.radius,
25368 locationName: this.locationName,
25369 addressComponents: {
25370 formatted_address: null,
25371 addressLine1: null,
25372 addressLine2: null,
25374 streetNumber: null,
25378 stateOrProvince: null
25381 domContainer: this.el.dom,
25382 geodecoder: new google.maps.Geocoder()
25386 drawCircle: function(center, radius, options)
25388 if (this.gMapContext.circle != null) {
25389 this.gMapContext.circle.setMap(null);
25393 options = Roo.apply({}, options, {
25394 strokeColor: "#0000FF",
25395 strokeOpacity: .35,
25397 fillColor: "#0000FF",
25401 options.map = this.gMapContext.map;
25402 options.radius = radius;
25403 options.center = center;
25404 this.gMapContext.circle = new google.maps.Circle(options);
25405 return this.gMapContext.circle;
25411 setPosition: function(location)
25413 this.gMapContext.location = location;
25414 this.gMapContext.marker.setPosition(location);
25415 this.gMapContext.map.panTo(location);
25416 this.drawCircle(location, this.gMapContext.radius, {});
25420 if (this.gMapContext.settings.enableReverseGeocode) {
25421 this.gMapContext.geodecoder.geocode({
25422 latLng: this.gMapContext.location
25423 }, function(results, status) {
25425 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25426 _this.gMapContext.locationName = results[0].formatted_address;
25427 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25429 _this.fireEvent('positionchanged', this, location);
25436 this.fireEvent('positionchanged', this, location);
25441 google.maps.event.trigger(this.gMapContext.map, "resize");
25443 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25445 this.fireEvent('resize', this);
25448 setPositionByLatLng: function(latitude, longitude)
25450 this.setPosition(new google.maps.LatLng(latitude, longitude));
25453 getCurrentPosition: function()
25456 latitude: this.gMapContext.location.lat(),
25457 longitude: this.gMapContext.location.lng()
25461 getAddressName: function()
25463 return this.gMapContext.locationName;
25466 getAddressComponents: function()
25468 return this.gMapContext.addressComponents;
25471 address_component_from_google_geocode: function(address_components)
25475 for (var i = 0; i < address_components.length; i++) {
25476 var component = address_components[i];
25477 if (component.types.indexOf("postal_code") >= 0) {
25478 result.postalCode = component.short_name;
25479 } else if (component.types.indexOf("street_number") >= 0) {
25480 result.streetNumber = component.short_name;
25481 } else if (component.types.indexOf("route") >= 0) {
25482 result.streetName = component.short_name;
25483 } else if (component.types.indexOf("neighborhood") >= 0) {
25484 result.city = component.short_name;
25485 } else if (component.types.indexOf("locality") >= 0) {
25486 result.city = component.short_name;
25487 } else if (component.types.indexOf("sublocality") >= 0) {
25488 result.district = component.short_name;
25489 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25490 result.stateOrProvince = component.short_name;
25491 } else if (component.types.indexOf("country") >= 0) {
25492 result.country = component.short_name;
25496 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25497 result.addressLine2 = "";
25501 setZoomLevel: function(zoom)
25503 this.gMapContext.map.setZoom(zoom);
25516 this.fireEvent('show', this);
25527 this.fireEvent('hide', this);
25532 Roo.apply(Roo.bootstrap.LocationPicker, {
25534 OverlayView : function(map, options)
25536 options = options || {};
25550 * @class Roo.bootstrap.Alert
25551 * @extends Roo.bootstrap.Component
25552 * Bootstrap Alert class
25553 * @cfg {String} title The title of alert
25554 * @cfg {String} html The content of alert
25555 * @cfg {String} weight ( success | info | warning | danger )
25556 * @cfg {String} faicon font-awesomeicon
25559 * Create a new alert
25560 * @param {Object} config The config object
25564 Roo.bootstrap.Alert = function(config){
25565 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25569 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
25576 getAutoCreate : function()
25585 cls : 'roo-alert-icon'
25590 cls : 'roo-alert-title',
25595 cls : 'roo-alert-text',
25602 cfg.cn[0].cls += ' fa ' + this.faicon;
25606 cfg.cls += ' alert-' + this.weight;
25612 initEvents: function()
25614 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25617 setTitle : function(str)
25619 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25622 setText : function(str)
25624 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25627 setWeight : function(weight)
25630 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25633 this.weight = weight;
25635 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25638 setIcon : function(icon)
25641 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25644 this.faicon = icon;
25646 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25667 * @class Roo.bootstrap.UploadCropbox
25668 * @extends Roo.bootstrap.Component
25669 * Bootstrap UploadCropbox class
25670 * @cfg {String} emptyText show when image has been loaded
25671 * @cfg {String} rotateNotify show when image too small to rotate
25672 * @cfg {Number} errorTimeout default 3000
25673 * @cfg {Number} minWidth default 300
25674 * @cfg {Number} minHeight default 300
25675 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25676 * @cfg {Boolean} isDocument (true|false) default false
25677 * @cfg {String} url action url
25678 * @cfg {String} paramName default 'imageUpload'
25679 * @cfg {String} method default POST
25680 * @cfg {Boolean} loadMask (true|false) default true
25681 * @cfg {Boolean} loadingText default 'Loading...'
25684 * Create a new UploadCropbox
25685 * @param {Object} config The config object
25688 Roo.bootstrap.UploadCropbox = function(config){
25689 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25693 * @event beforeselectfile
25694 * Fire before select file
25695 * @param {Roo.bootstrap.UploadCropbox} this
25697 "beforeselectfile" : true,
25700 * Fire after initEvent
25701 * @param {Roo.bootstrap.UploadCropbox} this
25706 * Fire after initEvent
25707 * @param {Roo.bootstrap.UploadCropbox} this
25708 * @param {String} data
25713 * Fire when preparing the file data
25714 * @param {Roo.bootstrap.UploadCropbox} this
25715 * @param {Object} file
25720 * Fire when get exception
25721 * @param {Roo.bootstrap.UploadCropbox} this
25722 * @param {XMLHttpRequest} xhr
25724 "exception" : true,
25726 * @event beforeloadcanvas
25727 * Fire before load the canvas
25728 * @param {Roo.bootstrap.UploadCropbox} this
25729 * @param {String} src
25731 "beforeloadcanvas" : true,
25734 * Fire when trash image
25735 * @param {Roo.bootstrap.UploadCropbox} this
25740 * Fire when download the image
25741 * @param {Roo.bootstrap.UploadCropbox} this
25745 * @event footerbuttonclick
25746 * Fire when footerbuttonclick
25747 * @param {Roo.bootstrap.UploadCropbox} this
25748 * @param {String} type
25750 "footerbuttonclick" : true,
25754 * @param {Roo.bootstrap.UploadCropbox} this
25759 * Fire when rotate the image
25760 * @param {Roo.bootstrap.UploadCropbox} this
25761 * @param {String} pos
25766 * Fire when inspect the file
25767 * @param {Roo.bootstrap.UploadCropbox} this
25768 * @param {Object} file
25773 * Fire when xhr upload the file
25774 * @param {Roo.bootstrap.UploadCropbox} this
25775 * @param {Object} data
25780 * Fire when arrange the file data
25781 * @param {Roo.bootstrap.UploadCropbox} this
25782 * @param {Object} formData
25787 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25790 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
25792 emptyText : 'Click to upload image',
25793 rotateNotify : 'Image is too small to rotate',
25794 errorTimeout : 3000,
25808 cropType : 'image/jpeg',
25810 canvasLoaded : false,
25811 isDocument : false,
25813 paramName : 'imageUpload',
25815 loadingText : 'Loading...',
25818 getAutoCreate : function()
25822 cls : 'roo-upload-cropbox',
25826 cls : 'roo-upload-cropbox-selector',
25831 cls : 'roo-upload-cropbox-body',
25832 style : 'cursor:pointer',
25836 cls : 'roo-upload-cropbox-preview'
25840 cls : 'roo-upload-cropbox-thumb'
25844 cls : 'roo-upload-cropbox-empty-notify',
25845 html : this.emptyText
25849 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25850 html : this.rotateNotify
25856 cls : 'roo-upload-cropbox-footer',
25859 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25869 onRender : function(ct, position)
25871 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25873 if (this.buttons.length) {
25875 Roo.each(this.buttons, function(bb) {
25877 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25879 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25885 this.maskEl = this.el;
25889 initEvents : function()
25891 this.urlAPI = (window.createObjectURL && window) ||
25892 (window.URL && URL.revokeObjectURL && URL) ||
25893 (window.webkitURL && webkitURL);
25895 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25896 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25898 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25899 this.selectorEl.hide();
25901 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25902 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25904 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25905 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25906 this.thumbEl.hide();
25908 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25909 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25911 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25912 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25913 this.errorEl.hide();
25915 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25916 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25917 this.footerEl.hide();
25919 this.setThumbBoxSize();
25925 this.fireEvent('initial', this);
25932 window.addEventListener("resize", function() { _this.resize(); } );
25934 this.bodyEl.on('click', this.beforeSelectFile, this);
25937 this.bodyEl.on('touchstart', this.onTouchStart, this);
25938 this.bodyEl.on('touchmove', this.onTouchMove, this);
25939 this.bodyEl.on('touchend', this.onTouchEnd, this);
25943 this.bodyEl.on('mousedown', this.onMouseDown, this);
25944 this.bodyEl.on('mousemove', this.onMouseMove, this);
25945 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25946 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25947 Roo.get(document).on('mouseup', this.onMouseUp, this);
25950 this.selectorEl.on('change', this.onFileSelected, this);
25956 this.baseScale = 1;
25958 this.baseRotate = 1;
25959 this.dragable = false;
25960 this.pinching = false;
25963 this.cropData = false;
25964 this.notifyEl.dom.innerHTML = this.emptyText;
25966 this.selectorEl.dom.value = '';
25970 resize : function()
25972 if(this.fireEvent('resize', this) != false){
25973 this.setThumbBoxPosition();
25974 this.setCanvasPosition();
25978 onFooterButtonClick : function(e, el, o, type)
25981 case 'rotate-left' :
25982 this.onRotateLeft(e);
25984 case 'rotate-right' :
25985 this.onRotateRight(e);
25988 this.beforeSelectFile(e);
26003 this.fireEvent('footerbuttonclick', this, type);
26006 beforeSelectFile : function(e)
26008 e.preventDefault();
26010 if(this.fireEvent('beforeselectfile', this) != false){
26011 this.selectorEl.dom.click();
26015 onFileSelected : function(e)
26017 e.preventDefault();
26019 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26023 var file = this.selectorEl.dom.files[0];
26025 if(this.fireEvent('inspect', this, file) != false){
26026 this.prepare(file);
26031 trash : function(e)
26033 this.fireEvent('trash', this);
26036 download : function(e)
26038 this.fireEvent('download', this);
26041 loadCanvas : function(src)
26043 if(this.fireEvent('beforeloadcanvas', this, src) != false){
26047 this.imageEl = document.createElement('img');
26051 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26053 this.imageEl.src = src;
26057 onLoadCanvas : function()
26059 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26060 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26062 this.bodyEl.un('click', this.beforeSelectFile, this);
26064 this.notifyEl.hide();
26065 this.thumbEl.show();
26066 this.footerEl.show();
26068 this.baseRotateLevel();
26070 if(this.isDocument){
26071 this.setThumbBoxSize();
26074 this.setThumbBoxPosition();
26076 this.baseScaleLevel();
26082 this.canvasLoaded = true;
26085 this.maskEl.unmask();
26090 setCanvasPosition : function()
26092 if(!this.canvasEl){
26096 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26097 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26099 this.previewEl.setLeft(pw);
26100 this.previewEl.setTop(ph);
26104 onMouseDown : function(e)
26108 this.dragable = true;
26109 this.pinching = false;
26111 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26112 this.dragable = false;
26116 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26117 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26121 onMouseMove : function(e)
26125 if(!this.canvasLoaded){
26129 if (!this.dragable){
26133 var minX = Math.ceil(this.thumbEl.getLeft(true));
26134 var minY = Math.ceil(this.thumbEl.getTop(true));
26136 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26137 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26139 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26140 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26142 x = x - this.mouseX;
26143 y = y - this.mouseY;
26145 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26146 var bgY = Math.ceil(y + this.previewEl.getTop(true));
26148 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26149 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26151 this.previewEl.setLeft(bgX);
26152 this.previewEl.setTop(bgY);
26154 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26155 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26158 onMouseUp : function(e)
26162 this.dragable = false;
26165 onMouseWheel : function(e)
26169 this.startScale = this.scale;
26171 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26173 if(!this.zoomable()){
26174 this.scale = this.startScale;
26183 zoomable : function()
26185 var minScale = this.thumbEl.getWidth() / this.minWidth;
26187 if(this.minWidth < this.minHeight){
26188 minScale = this.thumbEl.getHeight() / this.minHeight;
26191 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26192 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26196 (this.rotate == 0 || this.rotate == 180) &&
26198 width > this.imageEl.OriginWidth ||
26199 height > this.imageEl.OriginHeight ||
26200 (width < this.minWidth && height < this.minHeight)
26208 (this.rotate == 90 || this.rotate == 270) &&
26210 width > this.imageEl.OriginWidth ||
26211 height > this.imageEl.OriginHeight ||
26212 (width < this.minHeight && height < this.minWidth)
26219 !this.isDocument &&
26220 (this.rotate == 0 || this.rotate == 180) &&
26222 width < this.minWidth ||
26223 width > this.imageEl.OriginWidth ||
26224 height < this.minHeight ||
26225 height > this.imageEl.OriginHeight
26232 !this.isDocument &&
26233 (this.rotate == 90 || this.rotate == 270) &&
26235 width < this.minHeight ||
26236 width > this.imageEl.OriginWidth ||
26237 height < this.minWidth ||
26238 height > this.imageEl.OriginHeight
26248 onRotateLeft : function(e)
26250 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26252 var minScale = this.thumbEl.getWidth() / this.minWidth;
26254 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26255 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26257 this.startScale = this.scale;
26259 while (this.getScaleLevel() < minScale){
26261 this.scale = this.scale + 1;
26263 if(!this.zoomable()){
26268 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26269 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26274 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26281 this.scale = this.startScale;
26283 this.onRotateFail();
26288 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26290 if(this.isDocument){
26291 this.setThumbBoxSize();
26292 this.setThumbBoxPosition();
26293 this.setCanvasPosition();
26298 this.fireEvent('rotate', this, 'left');
26302 onRotateRight : function(e)
26304 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26306 var minScale = this.thumbEl.getWidth() / this.minWidth;
26308 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26309 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26311 this.startScale = this.scale;
26313 while (this.getScaleLevel() < minScale){
26315 this.scale = this.scale + 1;
26317 if(!this.zoomable()){
26322 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26323 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26328 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26335 this.scale = this.startScale;
26337 this.onRotateFail();
26342 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26344 if(this.isDocument){
26345 this.setThumbBoxSize();
26346 this.setThumbBoxPosition();
26347 this.setCanvasPosition();
26352 this.fireEvent('rotate', this, 'right');
26355 onRotateFail : function()
26357 this.errorEl.show(true);
26361 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26366 this.previewEl.dom.innerHTML = '';
26368 var canvasEl = document.createElement("canvas");
26370 var contextEl = canvasEl.getContext("2d");
26372 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26373 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26374 var center = this.imageEl.OriginWidth / 2;
26376 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26377 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26378 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26379 center = this.imageEl.OriginHeight / 2;
26382 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26384 contextEl.translate(center, center);
26385 contextEl.rotate(this.rotate * Math.PI / 180);
26387 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26389 this.canvasEl = document.createElement("canvas");
26391 this.contextEl = this.canvasEl.getContext("2d");
26393 switch (this.rotate) {
26396 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26397 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26399 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26404 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26405 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26407 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26408 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);
26412 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26417 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26418 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26420 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26421 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);
26425 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);
26430 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26431 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26433 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26434 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26438 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);
26445 this.previewEl.appendChild(this.canvasEl);
26447 this.setCanvasPosition();
26452 if(!this.canvasLoaded){
26456 var imageCanvas = document.createElement("canvas");
26458 var imageContext = imageCanvas.getContext("2d");
26460 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26461 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26463 var center = imageCanvas.width / 2;
26465 imageContext.translate(center, center);
26467 imageContext.rotate(this.rotate * Math.PI / 180);
26469 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26471 var canvas = document.createElement("canvas");
26473 var context = canvas.getContext("2d");
26475 canvas.width = this.minWidth;
26476 canvas.height = this.minHeight;
26478 switch (this.rotate) {
26481 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26482 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26484 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26485 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26487 var targetWidth = this.minWidth - 2 * x;
26488 var targetHeight = this.minHeight - 2 * y;
26492 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26493 scale = targetWidth / width;
26496 if(x > 0 && y == 0){
26497 scale = targetHeight / height;
26500 if(x > 0 && y > 0){
26501 scale = targetWidth / width;
26503 if(width < height){
26504 scale = targetHeight / height;
26508 context.scale(scale, scale);
26510 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26511 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26513 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26514 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26516 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26521 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26522 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26524 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26525 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26527 var targetWidth = this.minWidth - 2 * x;
26528 var targetHeight = this.minHeight - 2 * y;
26532 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26533 scale = targetWidth / width;
26536 if(x > 0 && y == 0){
26537 scale = targetHeight / height;
26540 if(x > 0 && y > 0){
26541 scale = targetWidth / width;
26543 if(width < height){
26544 scale = targetHeight / height;
26548 context.scale(scale, scale);
26550 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26551 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26553 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26554 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26556 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26558 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26563 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26564 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26566 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26567 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26569 var targetWidth = this.minWidth - 2 * x;
26570 var targetHeight = this.minHeight - 2 * y;
26574 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26575 scale = targetWidth / width;
26578 if(x > 0 && y == 0){
26579 scale = targetHeight / height;
26582 if(x > 0 && y > 0){
26583 scale = targetWidth / width;
26585 if(width < height){
26586 scale = targetHeight / height;
26590 context.scale(scale, scale);
26592 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26593 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26595 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26596 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26598 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26599 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26601 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26606 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26607 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26609 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26610 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26612 var targetWidth = this.minWidth - 2 * x;
26613 var targetHeight = this.minHeight - 2 * y;
26617 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26618 scale = targetWidth / width;
26621 if(x > 0 && y == 0){
26622 scale = targetHeight / height;
26625 if(x > 0 && y > 0){
26626 scale = targetWidth / width;
26628 if(width < height){
26629 scale = targetHeight / height;
26633 context.scale(scale, scale);
26635 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26636 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26638 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26639 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26641 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26643 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26650 this.cropData = canvas.toDataURL(this.cropType);
26652 if(this.fireEvent('crop', this, this.cropData) !== false){
26653 this.process(this.file, this.cropData);
26660 setThumbBoxSize : function()
26664 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26665 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26666 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26668 this.minWidth = width;
26669 this.minHeight = height;
26671 if(this.rotate == 90 || this.rotate == 270){
26672 this.minWidth = height;
26673 this.minHeight = width;
26678 width = Math.ceil(this.minWidth * height / this.minHeight);
26680 if(this.minWidth > this.minHeight){
26682 height = Math.ceil(this.minHeight * width / this.minWidth);
26685 this.thumbEl.setStyle({
26686 width : width + 'px',
26687 height : height + 'px'
26694 setThumbBoxPosition : function()
26696 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26697 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26699 this.thumbEl.setLeft(x);
26700 this.thumbEl.setTop(y);
26704 baseRotateLevel : function()
26706 this.baseRotate = 1;
26709 typeof(this.exif) != 'undefined' &&
26710 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26711 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26713 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26716 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26720 baseScaleLevel : function()
26724 if(this.isDocument){
26726 if(this.baseRotate == 6 || this.baseRotate == 8){
26728 height = this.thumbEl.getHeight();
26729 this.baseScale = height / this.imageEl.OriginWidth;
26731 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26732 width = this.thumbEl.getWidth();
26733 this.baseScale = width / this.imageEl.OriginHeight;
26739 height = this.thumbEl.getHeight();
26740 this.baseScale = height / this.imageEl.OriginHeight;
26742 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26743 width = this.thumbEl.getWidth();
26744 this.baseScale = width / this.imageEl.OriginWidth;
26750 if(this.baseRotate == 6 || this.baseRotate == 8){
26752 width = this.thumbEl.getHeight();
26753 this.baseScale = width / this.imageEl.OriginHeight;
26755 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26756 height = this.thumbEl.getWidth();
26757 this.baseScale = height / this.imageEl.OriginHeight;
26760 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26761 height = this.thumbEl.getWidth();
26762 this.baseScale = height / this.imageEl.OriginHeight;
26764 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26765 width = this.thumbEl.getHeight();
26766 this.baseScale = width / this.imageEl.OriginWidth;
26773 width = this.thumbEl.getWidth();
26774 this.baseScale = width / this.imageEl.OriginWidth;
26776 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26777 height = this.thumbEl.getHeight();
26778 this.baseScale = height / this.imageEl.OriginHeight;
26781 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26783 height = this.thumbEl.getHeight();
26784 this.baseScale = height / this.imageEl.OriginHeight;
26786 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26787 width = this.thumbEl.getWidth();
26788 this.baseScale = width / this.imageEl.OriginWidth;
26796 getScaleLevel : function()
26798 return this.baseScale * Math.pow(1.1, this.scale);
26801 onTouchStart : function(e)
26803 if(!this.canvasLoaded){
26804 this.beforeSelectFile(e);
26808 var touches = e.browserEvent.touches;
26814 if(touches.length == 1){
26815 this.onMouseDown(e);
26819 if(touches.length != 2){
26825 for(var i = 0, finger; finger = touches[i]; i++){
26826 coords.push(finger.pageX, finger.pageY);
26829 var x = Math.pow(coords[0] - coords[2], 2);
26830 var y = Math.pow(coords[1] - coords[3], 2);
26832 this.startDistance = Math.sqrt(x + y);
26834 this.startScale = this.scale;
26836 this.pinching = true;
26837 this.dragable = false;
26841 onTouchMove : function(e)
26843 if(!this.pinching && !this.dragable){
26847 var touches = e.browserEvent.touches;
26854 this.onMouseMove(e);
26860 for(var i = 0, finger; finger = touches[i]; i++){
26861 coords.push(finger.pageX, finger.pageY);
26864 var x = Math.pow(coords[0] - coords[2], 2);
26865 var y = Math.pow(coords[1] - coords[3], 2);
26867 this.endDistance = Math.sqrt(x + y);
26869 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26871 if(!this.zoomable()){
26872 this.scale = this.startScale;
26880 onTouchEnd : function(e)
26882 this.pinching = false;
26883 this.dragable = false;
26887 process : function(file, crop)
26890 this.maskEl.mask(this.loadingText);
26893 this.xhr = new XMLHttpRequest();
26895 file.xhr = this.xhr;
26897 this.xhr.open(this.method, this.url, true);
26900 "Accept": "application/json",
26901 "Cache-Control": "no-cache",
26902 "X-Requested-With": "XMLHttpRequest"
26905 for (var headerName in headers) {
26906 var headerValue = headers[headerName];
26908 this.xhr.setRequestHeader(headerName, headerValue);
26914 this.xhr.onload = function()
26916 _this.xhrOnLoad(_this.xhr);
26919 this.xhr.onerror = function()
26921 _this.xhrOnError(_this.xhr);
26924 var formData = new FormData();
26926 formData.append('returnHTML', 'NO');
26929 formData.append('crop', crop);
26932 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26933 formData.append(this.paramName, file, file.name);
26936 if(typeof(file.filename) != 'undefined'){
26937 formData.append('filename', file.filename);
26940 if(typeof(file.mimetype) != 'undefined'){
26941 formData.append('mimetype', file.mimetype);
26944 if(this.fireEvent('arrange', this, formData) != false){
26945 this.xhr.send(formData);
26949 xhrOnLoad : function(xhr)
26952 this.maskEl.unmask();
26955 if (xhr.readyState !== 4) {
26956 this.fireEvent('exception', this, xhr);
26960 var response = Roo.decode(xhr.responseText);
26962 if(!response.success){
26963 this.fireEvent('exception', this, xhr);
26967 var response = Roo.decode(xhr.responseText);
26969 this.fireEvent('upload', this, response);
26973 xhrOnError : function()
26976 this.maskEl.unmask();
26979 Roo.log('xhr on error');
26981 var response = Roo.decode(xhr.responseText);
26987 prepare : function(file)
26990 this.maskEl.mask(this.loadingText);
26996 if(typeof(file) === 'string'){
26997 this.loadCanvas(file);
27001 if(!file || !this.urlAPI){
27006 this.cropType = file.type;
27010 if(this.fireEvent('prepare', this, this.file) != false){
27012 var reader = new FileReader();
27014 reader.onload = function (e) {
27015 if (e.target.error) {
27016 Roo.log(e.target.error);
27020 var buffer = e.target.result,
27021 dataView = new DataView(buffer),
27023 maxOffset = dataView.byteLength - 4,
27027 if (dataView.getUint16(0) === 0xffd8) {
27028 while (offset < maxOffset) {
27029 markerBytes = dataView.getUint16(offset);
27031 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27032 markerLength = dataView.getUint16(offset + 2) + 2;
27033 if (offset + markerLength > dataView.byteLength) {
27034 Roo.log('Invalid meta data: Invalid segment size.');
27038 if(markerBytes == 0xffe1){
27039 _this.parseExifData(
27046 offset += markerLength;
27056 var url = _this.urlAPI.createObjectURL(_this.file);
27058 _this.loadCanvas(url);
27063 reader.readAsArrayBuffer(this.file);
27069 parseExifData : function(dataView, offset, length)
27071 var tiffOffset = offset + 10,
27075 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27076 // No Exif data, might be XMP data instead
27080 // Check for the ASCII code for "Exif" (0x45786966):
27081 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27082 // No Exif data, might be XMP data instead
27085 if (tiffOffset + 8 > dataView.byteLength) {
27086 Roo.log('Invalid Exif data: Invalid segment size.');
27089 // Check for the two null bytes:
27090 if (dataView.getUint16(offset + 8) !== 0x0000) {
27091 Roo.log('Invalid Exif data: Missing byte alignment offset.');
27094 // Check the byte alignment:
27095 switch (dataView.getUint16(tiffOffset)) {
27097 littleEndian = true;
27100 littleEndian = false;
27103 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27106 // Check for the TIFF tag marker (0x002A):
27107 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27108 Roo.log('Invalid Exif data: Missing TIFF marker.');
27111 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27112 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27114 this.parseExifTags(
27117 tiffOffset + dirOffset,
27122 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27127 if (dirOffset + 6 > dataView.byteLength) {
27128 Roo.log('Invalid Exif data: Invalid directory offset.');
27131 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27132 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27133 if (dirEndOffset + 4 > dataView.byteLength) {
27134 Roo.log('Invalid Exif data: Invalid directory size.');
27137 for (i = 0; i < tagsNumber; i += 1) {
27141 dirOffset + 2 + 12 * i, // tag offset
27145 // Return the offset to the next directory:
27146 return dataView.getUint32(dirEndOffset, littleEndian);
27149 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
27151 var tag = dataView.getUint16(offset, littleEndian);
27153 this.exif[tag] = this.getExifValue(
27157 dataView.getUint16(offset + 2, littleEndian), // tag type
27158 dataView.getUint32(offset + 4, littleEndian), // tag length
27163 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27165 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27174 Roo.log('Invalid Exif data: Invalid tag type.');
27178 tagSize = tagType.size * length;
27179 // Determine if the value is contained in the dataOffset bytes,
27180 // or if the value at the dataOffset is a pointer to the actual data:
27181 dataOffset = tagSize > 4 ?
27182 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27183 if (dataOffset + tagSize > dataView.byteLength) {
27184 Roo.log('Invalid Exif data: Invalid data offset.');
27187 if (length === 1) {
27188 return tagType.getValue(dataView, dataOffset, littleEndian);
27191 for (i = 0; i < length; i += 1) {
27192 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27195 if (tagType.ascii) {
27197 // Concatenate the chars:
27198 for (i = 0; i < values.length; i += 1) {
27200 // Ignore the terminating NULL byte(s):
27201 if (c === '\u0000') {
27213 Roo.apply(Roo.bootstrap.UploadCropbox, {
27215 'Orientation': 0x0112
27219 1: 0, //'top-left',
27221 3: 180, //'bottom-right',
27222 // 4: 'bottom-left',
27224 6: 90, //'right-top',
27225 // 7: 'right-bottom',
27226 8: 270 //'left-bottom'
27230 // byte, 8-bit unsigned int:
27232 getValue: function (dataView, dataOffset) {
27233 return dataView.getUint8(dataOffset);
27237 // ascii, 8-bit byte:
27239 getValue: function (dataView, dataOffset) {
27240 return String.fromCharCode(dataView.getUint8(dataOffset));
27245 // short, 16 bit int:
27247 getValue: function (dataView, dataOffset, littleEndian) {
27248 return dataView.getUint16(dataOffset, littleEndian);
27252 // long, 32 bit int:
27254 getValue: function (dataView, dataOffset, littleEndian) {
27255 return dataView.getUint32(dataOffset, littleEndian);
27259 // rational = two long values, first is numerator, second is denominator:
27261 getValue: function (dataView, dataOffset, littleEndian) {
27262 return dataView.getUint32(dataOffset, littleEndian) /
27263 dataView.getUint32(dataOffset + 4, littleEndian);
27267 // slong, 32 bit signed int:
27269 getValue: function (dataView, dataOffset, littleEndian) {
27270 return dataView.getInt32(dataOffset, littleEndian);
27274 // srational, two slongs, first is numerator, second is denominator:
27276 getValue: function (dataView, dataOffset, littleEndian) {
27277 return dataView.getInt32(dataOffset, littleEndian) /
27278 dataView.getInt32(dataOffset + 4, littleEndian);
27288 cls : 'btn-group roo-upload-cropbox-rotate-left',
27289 action : 'rotate-left',
27293 cls : 'btn btn-default',
27294 html : '<i class="fa fa-undo"></i>'
27300 cls : 'btn-group roo-upload-cropbox-picture',
27301 action : 'picture',
27305 cls : 'btn btn-default',
27306 html : '<i class="fa fa-picture-o"></i>'
27312 cls : 'btn-group roo-upload-cropbox-rotate-right',
27313 action : 'rotate-right',
27317 cls : 'btn btn-default',
27318 html : '<i class="fa fa-repeat"></i>'
27326 cls : 'btn-group roo-upload-cropbox-rotate-left',
27327 action : 'rotate-left',
27331 cls : 'btn btn-default',
27332 html : '<i class="fa fa-undo"></i>'
27338 cls : 'btn-group roo-upload-cropbox-download',
27339 action : 'download',
27343 cls : 'btn btn-default',
27344 html : '<i class="fa fa-download"></i>'
27350 cls : 'btn-group roo-upload-cropbox-crop',
27355 cls : 'btn btn-default',
27356 html : '<i class="fa fa-crop"></i>'
27362 cls : 'btn-group roo-upload-cropbox-trash',
27367 cls : 'btn btn-default',
27368 html : '<i class="fa fa-trash"></i>'
27374 cls : 'btn-group roo-upload-cropbox-rotate-right',
27375 action : 'rotate-right',
27379 cls : 'btn btn-default',
27380 html : '<i class="fa fa-repeat"></i>'
27388 cls : 'btn-group roo-upload-cropbox-rotate-left',
27389 action : 'rotate-left',
27393 cls : 'btn btn-default',
27394 html : '<i class="fa fa-undo"></i>'
27400 cls : 'btn-group roo-upload-cropbox-rotate-right',
27401 action : 'rotate-right',
27405 cls : 'btn btn-default',
27406 html : '<i class="fa fa-repeat"></i>'
27419 * @class Roo.bootstrap.DocumentManager
27420 * @extends Roo.bootstrap.Component
27421 * Bootstrap DocumentManager class
27422 * @cfg {String} paramName default 'imageUpload'
27423 * @cfg {String} toolTipName default 'filename'
27424 * @cfg {String} method default POST
27425 * @cfg {String} url action url
27426 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27427 * @cfg {Boolean} multiple multiple upload default true
27428 * @cfg {Number} thumbSize default 300
27429 * @cfg {String} fieldLabel
27430 * @cfg {Number} labelWidth default 4
27431 * @cfg {String} labelAlign (left|top) default left
27432 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27435 * Create a new DocumentManager
27436 * @param {Object} config The config object
27439 Roo.bootstrap.DocumentManager = function(config){
27440 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27443 this.delegates = [];
27448 * Fire when initial the DocumentManager
27449 * @param {Roo.bootstrap.DocumentManager} this
27454 * inspect selected file
27455 * @param {Roo.bootstrap.DocumentManager} this
27456 * @param {File} file
27461 * Fire when xhr load exception
27462 * @param {Roo.bootstrap.DocumentManager} this
27463 * @param {XMLHttpRequest} xhr
27465 "exception" : true,
27467 * @event afterupload
27468 * Fire when xhr load exception
27469 * @param {Roo.bootstrap.DocumentManager} this
27470 * @param {XMLHttpRequest} xhr
27472 "afterupload" : true,
27475 * prepare the form data
27476 * @param {Roo.bootstrap.DocumentManager} this
27477 * @param {Object} formData
27482 * Fire when remove the file
27483 * @param {Roo.bootstrap.DocumentManager} this
27484 * @param {Object} file
27489 * Fire after refresh the file
27490 * @param {Roo.bootstrap.DocumentManager} this
27495 * Fire after click the image
27496 * @param {Roo.bootstrap.DocumentManager} this
27497 * @param {Object} file
27502 * Fire when upload a image and editable set to true
27503 * @param {Roo.bootstrap.DocumentManager} this
27504 * @param {Object} file
27508 * @event beforeselectfile
27509 * Fire before select file
27510 * @param {Roo.bootstrap.DocumentManager} this
27512 "beforeselectfile" : true,
27515 * Fire before process file
27516 * @param {Roo.bootstrap.DocumentManager} this
27517 * @param {Object} file
27524 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
27533 paramName : 'imageUpload',
27534 toolTipName : 'filename',
27537 labelAlign : 'left',
27542 getAutoCreate : function()
27544 var managerWidget = {
27546 cls : 'roo-document-manager',
27550 cls : 'roo-document-manager-selector',
27555 cls : 'roo-document-manager-uploader',
27559 cls : 'roo-document-manager-upload-btn',
27560 html : '<i class="fa fa-plus"></i>'
27571 cls : 'column col-md-12',
27576 if(this.fieldLabel.length){
27581 cls : 'column col-md-12',
27582 html : this.fieldLabel
27586 cls : 'column col-md-12',
27591 if(this.labelAlign == 'left'){
27595 cls : 'column col-md-' + this.labelWidth,
27596 html : this.fieldLabel
27600 cls : 'column col-md-' + (12 - this.labelWidth),
27610 cls : 'row clearfix',
27618 initEvents : function()
27620 this.managerEl = this.el.select('.roo-document-manager', true).first();
27621 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27623 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27624 this.selectorEl.hide();
27627 this.selectorEl.attr('multiple', 'multiple');
27630 this.selectorEl.on('change', this.onFileSelected, this);
27632 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27633 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27635 this.uploader.on('click', this.onUploaderClick, this);
27637 this.renderProgressDialog();
27641 window.addEventListener("resize", function() { _this.refresh(); } );
27643 this.fireEvent('initial', this);
27646 renderProgressDialog : function()
27650 this.progressDialog = new Roo.bootstrap.Modal({
27651 cls : 'roo-document-manager-progress-dialog',
27652 allow_close : false,
27662 btnclick : function() {
27663 _this.uploadCancel();
27669 this.progressDialog.render(Roo.get(document.body));
27671 this.progress = new Roo.bootstrap.Progress({
27672 cls : 'roo-document-manager-progress',
27677 this.progress.render(this.progressDialog.getChildContainer());
27679 this.progressBar = new Roo.bootstrap.ProgressBar({
27680 cls : 'roo-document-manager-progress-bar',
27683 aria_valuemax : 12,
27687 this.progressBar.render(this.progress.getChildContainer());
27690 onUploaderClick : function(e)
27692 e.preventDefault();
27694 if(this.fireEvent('beforeselectfile', this) != false){
27695 this.selectorEl.dom.click();
27700 onFileSelected : function(e)
27702 e.preventDefault();
27704 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27708 Roo.each(this.selectorEl.dom.files, function(file){
27709 if(this.fireEvent('inspect', this, file) != false){
27710 this.files.push(file);
27720 this.selectorEl.dom.value = '';
27722 if(!this.files.length){
27726 if(this.boxes > 0 && this.files.length > this.boxes){
27727 this.files = this.files.slice(0, this.boxes);
27730 this.uploader.show();
27732 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27733 this.uploader.hide();
27742 Roo.each(this.files, function(file){
27744 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27745 var f = this.renderPreview(file);
27750 if(file.type.indexOf('image') != -1){
27751 this.delegates.push(
27753 _this.process(file);
27754 }).createDelegate(this)
27762 _this.process(file);
27763 }).createDelegate(this)
27768 this.files = files;
27770 this.delegates = this.delegates.concat(docs);
27772 if(!this.delegates.length){
27777 this.progressBar.aria_valuemax = this.delegates.length;
27784 arrange : function()
27786 if(!this.delegates.length){
27787 this.progressDialog.hide();
27792 var delegate = this.delegates.shift();
27794 this.progressDialog.show();
27796 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27798 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27803 refresh : function()
27805 this.uploader.show();
27807 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27808 this.uploader.hide();
27811 Roo.isTouch ? this.closable(false) : this.closable(true);
27813 this.fireEvent('refresh', this);
27816 onRemove : function(e, el, o)
27818 e.preventDefault();
27820 this.fireEvent('remove', this, o);
27824 remove : function(o)
27828 Roo.each(this.files, function(file){
27829 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27838 this.files = files;
27845 Roo.each(this.files, function(file){
27850 file.target.remove();
27859 onClick : function(e, el, o)
27861 e.preventDefault();
27863 this.fireEvent('click', this, o);
27867 closable : function(closable)
27869 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27871 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27883 xhrOnLoad : function(xhr)
27885 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27889 if (xhr.readyState !== 4) {
27891 this.fireEvent('exception', this, xhr);
27895 var response = Roo.decode(xhr.responseText);
27897 if(!response.success){
27899 this.fireEvent('exception', this, xhr);
27903 var file = this.renderPreview(response.data);
27905 this.files.push(file);
27909 this.fireEvent('afterupload', this, xhr);
27913 xhrOnError : function(xhr)
27915 Roo.log('xhr on error');
27917 var response = Roo.decode(xhr.responseText);
27924 process : function(file)
27926 if(this.fireEvent('process', this, file) !== false){
27927 if(this.editable && file.type.indexOf('image') != -1){
27928 this.fireEvent('edit', this, file);
27932 this.uploadStart(file, false);
27939 uploadStart : function(file, crop)
27941 this.xhr = new XMLHttpRequest();
27943 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27948 file.xhr = this.xhr;
27950 this.managerEl.createChild({
27952 cls : 'roo-document-manager-loading',
27956 tooltip : file.name,
27957 cls : 'roo-document-manager-thumb',
27958 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27964 this.xhr.open(this.method, this.url, true);
27967 "Accept": "application/json",
27968 "Cache-Control": "no-cache",
27969 "X-Requested-With": "XMLHttpRequest"
27972 for (var headerName in headers) {
27973 var headerValue = headers[headerName];
27975 this.xhr.setRequestHeader(headerName, headerValue);
27981 this.xhr.onload = function()
27983 _this.xhrOnLoad(_this.xhr);
27986 this.xhr.onerror = function()
27988 _this.xhrOnError(_this.xhr);
27991 var formData = new FormData();
27993 formData.append('returnHTML', 'NO');
27996 formData.append('crop', crop);
27999 formData.append(this.paramName, file, file.name);
28006 if(this.fireEvent('prepare', this, formData, options) != false){
28008 if(options.manually){
28012 this.xhr.send(formData);
28016 this.uploadCancel();
28019 uploadCancel : function()
28025 this.delegates = [];
28027 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28034 renderPreview : function(file)
28036 if(typeof(file.target) != 'undefined' && file.target){
28040 var previewEl = this.managerEl.createChild({
28042 cls : 'roo-document-manager-preview',
28046 tooltip : file[this.toolTipName],
28047 cls : 'roo-document-manager-thumb',
28048 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28053 html : '<i class="fa fa-times-circle"></i>'
28058 var close = previewEl.select('button.close', true).first();
28060 close.on('click', this.onRemove, this, file);
28062 file.target = previewEl;
28064 var image = previewEl.select('img', true).first();
28068 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28070 image.on('click', this.onClick, this, file);
28076 onPreviewLoad : function(file, image)
28078 if(typeof(file.target) == 'undefined' || !file.target){
28082 var width = image.dom.naturalWidth || image.dom.width;
28083 var height = image.dom.naturalHeight || image.dom.height;
28085 if(width > height){
28086 file.target.addClass('wide');
28090 file.target.addClass('tall');
28095 uploadFromSource : function(file, crop)
28097 this.xhr = new XMLHttpRequest();
28099 this.managerEl.createChild({
28101 cls : 'roo-document-manager-loading',
28105 tooltip : file.name,
28106 cls : 'roo-document-manager-thumb',
28107 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28113 this.xhr.open(this.method, this.url, true);
28116 "Accept": "application/json",
28117 "Cache-Control": "no-cache",
28118 "X-Requested-With": "XMLHttpRequest"
28121 for (var headerName in headers) {
28122 var headerValue = headers[headerName];
28124 this.xhr.setRequestHeader(headerName, headerValue);
28130 this.xhr.onload = function()
28132 _this.xhrOnLoad(_this.xhr);
28135 this.xhr.onerror = function()
28137 _this.xhrOnError(_this.xhr);
28140 var formData = new FormData();
28142 formData.append('returnHTML', 'NO');
28144 formData.append('crop', crop);
28146 if(typeof(file.filename) != 'undefined'){
28147 formData.append('filename', file.filename);
28150 if(typeof(file.mimetype) != 'undefined'){
28151 formData.append('mimetype', file.mimetype);
28154 if(this.fireEvent('prepare', this, formData) != false){
28155 this.xhr.send(formData);
28165 * @class Roo.bootstrap.DocumentViewer
28166 * @extends Roo.bootstrap.Component
28167 * Bootstrap DocumentViewer class
28168 * @cfg {Boolean} showDownload (true|false) show download button (default true)
28169 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28172 * Create a new DocumentViewer
28173 * @param {Object} config The config object
28176 Roo.bootstrap.DocumentViewer = function(config){
28177 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28182 * Fire after initEvent
28183 * @param {Roo.bootstrap.DocumentViewer} this
28189 * @param {Roo.bootstrap.DocumentViewer} this
28194 * Fire after download button
28195 * @param {Roo.bootstrap.DocumentViewer} this
28200 * Fire after trash button
28201 * @param {Roo.bootstrap.DocumentViewer} this
28208 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
28210 showDownload : true,
28214 getAutoCreate : function()
28218 cls : 'roo-document-viewer',
28222 cls : 'roo-document-viewer-body',
28226 cls : 'roo-document-viewer-thumb',
28230 cls : 'roo-document-viewer-image'
28238 cls : 'roo-document-viewer-footer',
28241 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28245 cls : 'btn-group roo-document-viewer-download',
28249 cls : 'btn btn-default',
28250 html : '<i class="fa fa-download"></i>'
28256 cls : 'btn-group roo-document-viewer-trash',
28260 cls : 'btn btn-default',
28261 html : '<i class="fa fa-trash"></i>'
28274 initEvents : function()
28276 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28277 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28279 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28280 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28282 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28283 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28285 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28286 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28288 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28289 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28291 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28292 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28294 this.bodyEl.on('click', this.onClick, this);
28295 this.downloadBtn.on('click', this.onDownload, this);
28296 this.trashBtn.on('click', this.onTrash, this);
28298 this.downloadBtn.hide();
28299 this.trashBtn.hide();
28301 if(this.showDownload){
28302 this.downloadBtn.show();
28305 if(this.showTrash){
28306 this.trashBtn.show();
28309 if(!this.showDownload && !this.showTrash) {
28310 this.footerEl.hide();
28315 initial : function()
28317 this.fireEvent('initial', this);
28321 onClick : function(e)
28323 e.preventDefault();
28325 this.fireEvent('click', this);
28328 onDownload : function(e)
28330 e.preventDefault();
28332 this.fireEvent('download', this);
28335 onTrash : function(e)
28337 e.preventDefault();
28339 this.fireEvent('trash', this);
28351 * @class Roo.bootstrap.NavProgressBar
28352 * @extends Roo.bootstrap.Component
28353 * Bootstrap NavProgressBar class
28356 * Create a new nav progress bar
28357 * @param {Object} config The config object
28360 Roo.bootstrap.NavProgressBar = function(config){
28361 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28363 this.bullets = this.bullets || [];
28365 // Roo.bootstrap.NavProgressBar.register(this);
28369 * Fires when the active item changes
28370 * @param {Roo.bootstrap.NavProgressBar} this
28371 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28372 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
28379 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
28384 getAutoCreate : function()
28386 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28390 cls : 'roo-navigation-bar-group',
28394 cls : 'roo-navigation-top-bar'
28398 cls : 'roo-navigation-bullets-bar',
28402 cls : 'roo-navigation-bar'
28409 cls : 'roo-navigation-bottom-bar'
28419 initEvents: function()
28424 onRender : function(ct, position)
28426 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28428 if(this.bullets.length){
28429 Roo.each(this.bullets, function(b){
28438 addItem : function(cfg)
28440 var item = new Roo.bootstrap.NavProgressItem(cfg);
28442 item.parentId = this.id;
28443 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28446 var top = new Roo.bootstrap.Element({
28448 cls : 'roo-navigation-bar-text'
28451 var bottom = new Roo.bootstrap.Element({
28453 cls : 'roo-navigation-bar-text'
28456 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28457 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28459 var topText = new Roo.bootstrap.Element({
28461 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28464 var bottomText = new Roo.bootstrap.Element({
28466 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28469 topText.onRender(top.el, null);
28470 bottomText.onRender(bottom.el, null);
28473 item.bottomEl = bottom;
28476 this.barItems.push(item);
28481 getActive : function()
28483 var active = false;
28485 Roo.each(this.barItems, function(v){
28487 if (!v.isActive()) {
28499 setActiveItem : function(item)
28503 Roo.each(this.barItems, function(v){
28504 if (v.rid == item.rid) {
28508 if (v.isActive()) {
28509 v.setActive(false);
28514 item.setActive(true);
28516 this.fireEvent('changed', this, item, prev);
28519 getBarItem: function(rid)
28523 Roo.each(this.barItems, function(e) {
28524 if (e.rid != rid) {
28535 indexOfItem : function(item)
28539 Roo.each(this.barItems, function(v, i){
28541 if (v.rid != item.rid) {
28552 setActiveNext : function()
28554 var i = this.indexOfItem(this.getActive());
28556 if (i > this.barItems.length) {
28560 this.setActiveItem(this.barItems[i+1]);
28563 setActivePrev : function()
28565 var i = this.indexOfItem(this.getActive());
28571 this.setActiveItem(this.barItems[i-1]);
28574 format : function()
28576 if(!this.barItems.length){
28580 var width = 100 / this.barItems.length;
28582 Roo.each(this.barItems, function(i){
28583 i.el.setStyle('width', width + '%');
28584 i.topEl.el.setStyle('width', width + '%');
28585 i.bottomEl.el.setStyle('width', width + '%');
28594 * Nav Progress Item
28599 * @class Roo.bootstrap.NavProgressItem
28600 * @extends Roo.bootstrap.Component
28601 * Bootstrap NavProgressItem class
28602 * @cfg {String} rid the reference id
28603 * @cfg {Boolean} active (true|false) Is item active default false
28604 * @cfg {Boolean} disabled (true|false) Is item active default false
28605 * @cfg {String} html
28606 * @cfg {String} position (top|bottom) text position default bottom
28607 * @cfg {String} icon show icon instead of number
28610 * Create a new NavProgressItem
28611 * @param {Object} config The config object
28613 Roo.bootstrap.NavProgressItem = function(config){
28614 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28619 * The raw click event for the entire grid.
28620 * @param {Roo.bootstrap.NavProgressItem} this
28621 * @param {Roo.EventObject} e
28628 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
28634 position : 'bottom',
28637 getAutoCreate : function()
28639 var iconCls = 'roo-navigation-bar-item-icon';
28641 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28645 cls: 'roo-navigation-bar-item',
28655 cfg.cls += ' active';
28658 cfg.cls += ' disabled';
28664 disable : function()
28666 this.setDisabled(true);
28669 enable : function()
28671 this.setDisabled(false);
28674 initEvents: function()
28676 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28678 this.iconEl.on('click', this.onClick, this);
28681 onClick : function(e)
28683 e.preventDefault();
28689 if(this.fireEvent('click', this, e) === false){
28693 this.parent().setActiveItem(this);
28696 isActive: function ()
28698 return this.active;
28701 setActive : function(state)
28703 if(this.active == state){
28707 this.active = state;
28710 this.el.addClass('active');
28714 this.el.removeClass('active');
28719 setDisabled : function(state)
28721 if(this.disabled == state){
28725 this.disabled = state;
28728 this.el.addClass('disabled');
28732 this.el.removeClass('disabled');
28735 tooltipEl : function()
28737 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28750 * @class Roo.bootstrap.FieldLabel
28751 * @extends Roo.bootstrap.Component
28752 * Bootstrap FieldLabel class
28753 * @cfg {String} html contents of the element
28754 * @cfg {String} tag tag of the element default label
28755 * @cfg {String} cls class of the element
28756 * @cfg {String} target label target
28757 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28758 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28759 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28760 * @cfg {String} iconTooltip default "This field is required"
28763 * Create a new FieldLabel
28764 * @param {Object} config The config object
28767 Roo.bootstrap.FieldLabel = function(config){
28768 Roo.bootstrap.Element.superclass.constructor.call(this, config);
28773 * Fires after the field has been marked as invalid.
28774 * @param {Roo.form.FieldLabel} this
28775 * @param {String} msg The validation message
28780 * Fires after the field has been validated with no errors.
28781 * @param {Roo.form.FieldLabel} this
28787 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
28794 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28795 validClass : 'text-success fa fa-lg fa-check',
28796 iconTooltip : 'This field is required',
28798 getAutoCreate : function(){
28802 cls : 'roo-bootstrap-field-label ' + this.cls,
28808 tooltip : this.iconTooltip
28820 initEvents: function()
28822 Roo.bootstrap.Element.superclass.initEvents.call(this);
28824 this.iconEl = this.el.select('i', true).first();
28826 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28828 Roo.bootstrap.FieldLabel.register(this);
28832 * Mark this field as valid
28834 markValid : function()
28836 this.iconEl.show();
28838 this.iconEl.removeClass(this.invalidClass);
28840 this.iconEl.addClass(this.validClass);
28842 this.fireEvent('valid', this);
28846 * Mark this field as invalid
28847 * @param {String} msg The validation message
28849 markInvalid : function(msg)
28851 this.iconEl.show();
28853 this.iconEl.removeClass(this.validClass);
28855 this.iconEl.addClass(this.invalidClass);
28857 this.fireEvent('invalid', this, msg);
28863 Roo.apply(Roo.bootstrap.FieldLabel, {
28868 * register a FieldLabel Group
28869 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28871 register : function(label)
28873 if(this.groups.hasOwnProperty(label.target)){
28877 this.groups[label.target] = label;
28881 * fetch a FieldLabel Group based on the target
28882 * @param {string} target
28883 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28885 get: function(target) {
28886 if (typeof(this.groups[target]) == 'undefined') {
28890 return this.groups[target] ;
28899 * page DateSplitField.
28905 * @class Roo.bootstrap.DateSplitField
28906 * @extends Roo.bootstrap.Component
28907 * Bootstrap DateSplitField class
28908 * @cfg {string} fieldLabel - the label associated
28909 * @cfg {Number} labelWidth set the width of label (0-12)
28910 * @cfg {String} labelAlign (top|left)
28911 * @cfg {Boolean} dayAllowBlank (true|false) default false
28912 * @cfg {Boolean} monthAllowBlank (true|false) default false
28913 * @cfg {Boolean} yearAllowBlank (true|false) default false
28914 * @cfg {string} dayPlaceholder
28915 * @cfg {string} monthPlaceholder
28916 * @cfg {string} yearPlaceholder
28917 * @cfg {string} dayFormat default 'd'
28918 * @cfg {string} monthFormat default 'm'
28919 * @cfg {string} yearFormat default 'Y'
28923 * Create a new DateSplitField
28924 * @param {Object} config The config object
28927 Roo.bootstrap.DateSplitField = function(config){
28928 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28934 * getting the data of years
28935 * @param {Roo.bootstrap.DateSplitField} this
28936 * @param {Object} years
28941 * getting the data of days
28942 * @param {Roo.bootstrap.DateSplitField} this
28943 * @param {Object} days
28948 * Fires after the field has been marked as invalid.
28949 * @param {Roo.form.Field} this
28950 * @param {String} msg The validation message
28955 * Fires after the field has been validated with no errors.
28956 * @param {Roo.form.Field} this
28962 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
28965 labelAlign : 'top',
28967 dayAllowBlank : false,
28968 monthAllowBlank : false,
28969 yearAllowBlank : false,
28970 dayPlaceholder : '',
28971 monthPlaceholder : '',
28972 yearPlaceholder : '',
28976 isFormField : true,
28978 getAutoCreate : function()
28982 cls : 'row roo-date-split-field-group',
28987 cls : 'form-hidden-field roo-date-split-field-group-value',
28993 if(this.fieldLabel){
28996 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29000 html : this.fieldLabel
29006 Roo.each(['day', 'month', 'year'], function(t){
29009 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
29016 inputEl: function ()
29018 return this.el.select('.roo-date-split-field-group-value', true).first();
29021 onRender : function(ct, position)
29025 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29027 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29029 this.dayField = new Roo.bootstrap.ComboBox({
29030 allowBlank : this.dayAllowBlank,
29031 alwaysQuery : true,
29032 displayField : 'value',
29035 forceSelection : true,
29037 placeholder : this.dayPlaceholder,
29038 selectOnFocus : true,
29039 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29040 triggerAction : 'all',
29042 valueField : 'value',
29043 store : new Roo.data.SimpleStore({
29044 data : (function() {
29046 _this.fireEvent('days', _this, days);
29049 fields : [ 'value' ]
29052 select : function (_self, record, index)
29054 _this.setValue(_this.getValue());
29059 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29061 this.monthField = new Roo.bootstrap.MonthField({
29062 after : '<i class=\"fa fa-calendar\"></i>',
29063 allowBlank : this.monthAllowBlank,
29064 placeholder : this.monthPlaceholder,
29067 render : function (_self)
29069 this.el.select('span.input-group-addon', true).first().on('click', function(e){
29070 e.preventDefault();
29074 select : function (_self, oldvalue, newvalue)
29076 _this.setValue(_this.getValue());
29081 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29083 this.yearField = new Roo.bootstrap.ComboBox({
29084 allowBlank : this.yearAllowBlank,
29085 alwaysQuery : true,
29086 displayField : 'value',
29089 forceSelection : true,
29091 placeholder : this.yearPlaceholder,
29092 selectOnFocus : true,
29093 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29094 triggerAction : 'all',
29096 valueField : 'value',
29097 store : new Roo.data.SimpleStore({
29098 data : (function() {
29100 _this.fireEvent('years', _this, years);
29103 fields : [ 'value' ]
29106 select : function (_self, record, index)
29108 _this.setValue(_this.getValue());
29113 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29116 setValue : function(v, format)
29118 this.inputEl.dom.value = v;
29120 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29122 var d = Date.parseDate(v, f);
29129 this.setDay(d.format(this.dayFormat));
29130 this.setMonth(d.format(this.monthFormat));
29131 this.setYear(d.format(this.yearFormat));
29138 setDay : function(v)
29140 this.dayField.setValue(v);
29141 this.inputEl.dom.value = this.getValue();
29146 setMonth : function(v)
29148 this.monthField.setValue(v, true);
29149 this.inputEl.dom.value = this.getValue();
29154 setYear : function(v)
29156 this.yearField.setValue(v);
29157 this.inputEl.dom.value = this.getValue();
29162 getDay : function()
29164 return this.dayField.getValue();
29167 getMonth : function()
29169 return this.monthField.getValue();
29172 getYear : function()
29174 return this.yearField.getValue();
29177 getValue : function()
29179 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29181 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29191 this.inputEl.dom.value = '';
29196 validate : function()
29198 var d = this.dayField.validate();
29199 var m = this.monthField.validate();
29200 var y = this.yearField.validate();
29205 (!this.dayAllowBlank && !d) ||
29206 (!this.monthAllowBlank && !m) ||
29207 (!this.yearAllowBlank && !y)
29212 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29221 this.markInvalid();
29226 markValid : function()
29229 var label = this.el.select('label', true).first();
29230 var icon = this.el.select('i.fa-star', true).first();
29236 this.fireEvent('valid', this);
29240 * Mark this field as invalid
29241 * @param {String} msg The validation message
29243 markInvalid : function(msg)
29246 var label = this.el.select('label', true).first();
29247 var icon = this.el.select('i.fa-star', true).first();
29249 if(label && !icon){
29250 this.el.select('.roo-date-split-field-label', true).createChild({
29252 cls : 'text-danger fa fa-lg fa-star',
29253 tooltip : 'This field is required',
29254 style : 'margin-right:5px;'
29258 this.fireEvent('invalid', this, msg);
29261 clearInvalid : function()
29263 var label = this.el.select('label', true).first();
29264 var icon = this.el.select('i.fa-star', true).first();
29270 this.fireEvent('valid', this);
29273 getName: function()
29283 * http://masonry.desandro.com
29285 * The idea is to render all the bricks based on vertical width...
29287 * The original code extends 'outlayer' - we might need to use that....
29293 * @class Roo.bootstrap.LayoutMasonry
29294 * @extends Roo.bootstrap.Component
29295 * Bootstrap Layout Masonry class
29298 * Create a new Element
29299 * @param {Object} config The config object
29302 Roo.bootstrap.LayoutMasonry = function(config){
29303 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29309 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
29312 * @cfg {Boolean} isLayoutInstant = no animation?
29314 isLayoutInstant : false, // needed?
29317 * @cfg {Number} boxWidth width of the columns
29322 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
29327 * @cfg {Number} padWidth padding below box..
29332 * @cfg {Number} gutter gutter width..
29337 * @cfg {Number} maxCols maximum number of columns
29343 * @cfg {Boolean} isAutoInitial defalut true
29345 isAutoInitial : true,
29350 * @cfg {Boolean} isHorizontal defalut false
29352 isHorizontal : false,
29354 currentSize : null,
29360 bricks: null, //CompositeElement
29364 _isLayoutInited : false,
29366 // isAlternative : false, // only use for vertical layout...
29369 * @cfg {Number} alternativePadWidth padding below box..
29371 alternativePadWidth : 50,
29373 getAutoCreate : function(){
29377 cls: 'blog-masonary-wrapper ' + this.cls,
29379 cls : 'mas-boxes masonary'
29386 getChildContainer: function( )
29388 if (this.boxesEl) {
29389 return this.boxesEl;
29392 this.boxesEl = this.el.select('.mas-boxes').first();
29394 return this.boxesEl;
29398 initEvents : function()
29402 if(this.isAutoInitial){
29403 Roo.log('hook children rendered');
29404 this.on('childrenrendered', function() {
29405 Roo.log('children rendered');
29411 initial : function()
29413 this.currentSize = this.el.getBox(true);
29415 Roo.EventManager.onWindowResize(this.resize, this);
29417 if(!this.isAutoInitial){
29425 //this.layout.defer(500,this);
29429 resize : function()
29433 var cs = this.el.getBox(true);
29435 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29436 Roo.log("no change in with or X");
29440 this.currentSize = cs;
29446 layout : function()
29448 this._resetLayout();
29450 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29452 this.layoutItems( isInstant );
29454 this._isLayoutInited = true;
29458 _resetLayout : function()
29460 if(this.isHorizontal){
29461 this.horizontalMeasureColumns();
29465 this.verticalMeasureColumns();
29469 verticalMeasureColumns : function()
29471 this.getContainerWidth();
29473 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29474 // this.colWidth = Math.floor(this.containerWidth * 0.8);
29478 var boxWidth = this.boxWidth + this.padWidth;
29480 if(this.containerWidth < this.boxWidth){
29481 boxWidth = this.containerWidth
29484 var containerWidth = this.containerWidth;
29486 var cols = Math.floor(containerWidth / boxWidth);
29488 this.cols = Math.max( cols, 1 );
29490 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29492 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29494 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29496 this.colWidth = boxWidth + avail - this.padWidth;
29498 this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29499 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
29502 horizontalMeasureColumns : function()
29504 this.getContainerWidth();
29506 var boxWidth = this.boxWidth;
29508 if(this.containerWidth < boxWidth){
29509 boxWidth = this.containerWidth;
29512 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29514 this.el.setHeight(boxWidth);
29518 getContainerWidth : function()
29520 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
29523 layoutItems : function( isInstant )
29525 var items = Roo.apply([], this.bricks);
29527 if(this.isHorizontal){
29528 this._horizontalLayoutItems( items , isInstant );
29532 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29533 // this._verticalAlternativeLayoutItems( items , isInstant );
29537 this._verticalLayoutItems( items , isInstant );
29541 _verticalLayoutItems : function ( items , isInstant)
29543 if ( !items || !items.length ) {
29548 ['xs', 'xs', 'xs', 'tall'],
29549 ['xs', 'xs', 'tall'],
29550 ['xs', 'xs', 'sm'],
29551 ['xs', 'xs', 'xs'],
29557 ['sm', 'xs', 'xs'],
29561 ['tall', 'xs', 'xs', 'xs'],
29562 ['tall', 'xs', 'xs'],
29574 Roo.each(items, function(item, k){
29576 switch (item.size) {
29577 // these layouts take up a full box,
29588 boxes.push([item]);
29611 var filterPattern = function(box, length)
29619 var pattern = box.slice(0, length);
29623 Roo.each(pattern, function(i){
29624 format.push(i.size);
29627 Roo.each(standard, function(s){
29629 if(String(s) != String(format)){
29638 if(!match && length == 1){
29643 filterPattern(box, length - 1);
29647 queue.push(pattern);
29649 box = box.slice(length, box.length);
29651 filterPattern(box, 4);
29657 Roo.each(boxes, function(box, k){
29663 if(box.length == 1){
29668 filterPattern(box, 4);
29672 this._processVerticalLayoutQueue( queue, isInstant );
29676 // _verticalAlternativeLayoutItems : function( items , isInstant )
29678 // if ( !items || !items.length ) {
29682 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
29686 _horizontalLayoutItems : function ( items , isInstant)
29688 if ( !items || !items.length || items.length < 3) {
29694 var eItems = items.slice(0, 3);
29696 items = items.slice(3, items.length);
29699 ['xs', 'xs', 'xs', 'wide'],
29700 ['xs', 'xs', 'wide'],
29701 ['xs', 'xs', 'sm'],
29702 ['xs', 'xs', 'xs'],
29708 ['sm', 'xs', 'xs'],
29712 ['wide', 'xs', 'xs', 'xs'],
29713 ['wide', 'xs', 'xs'],
29726 Roo.each(items, function(item, k){
29728 switch (item.size) {
29739 boxes.push([item]);
29763 var filterPattern = function(box, length)
29771 var pattern = box.slice(0, length);
29775 Roo.each(pattern, function(i){
29776 format.push(i.size);
29779 Roo.each(standard, function(s){
29781 if(String(s) != String(format)){
29790 if(!match && length == 1){
29795 filterPattern(box, length - 1);
29799 queue.push(pattern);
29801 box = box.slice(length, box.length);
29803 filterPattern(box, 4);
29809 Roo.each(boxes, function(box, k){
29815 if(box.length == 1){
29820 filterPattern(box, 4);
29827 var pos = this.el.getBox(true);
29831 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29833 var hit_end = false;
29835 Roo.each(queue, function(box){
29839 Roo.each(box, function(b){
29841 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29851 Roo.each(box, function(b){
29853 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29856 mx = Math.max(mx, b.x);
29860 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29864 Roo.each(box, function(b){
29866 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29880 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29883 /** Sets position of item in DOM
29884 * @param {Element} item
29885 * @param {Number} x - horizontal position
29886 * @param {Number} y - vertical position
29887 * @param {Boolean} isInstant - disables transitions
29889 _processVerticalLayoutQueue : function( queue, isInstant )
29891 var pos = this.el.getBox(true);
29896 for (var i = 0; i < this.cols; i++){
29900 Roo.each(queue, function(box, k){
29902 var col = k % this.cols;
29904 Roo.each(box, function(b,kk){
29906 b.el.position('absolute');
29908 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29909 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29911 if(b.size == 'md-left' || b.size == 'md-right'){
29912 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29913 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29916 b.el.setWidth(width);
29917 b.el.setHeight(height);
29919 b.el.select('iframe',true).setSize(width,height);
29923 for (var i = 0; i < this.cols; i++){
29925 if(maxY[i] < maxY[col]){
29930 col = Math.min(col, i);
29934 x = pos.x + col * (this.colWidth + this.padWidth);
29938 var positions = [];
29940 switch (box.length){
29942 positions = this.getVerticalOneBoxColPositions(x, y, box);
29945 positions = this.getVerticalTwoBoxColPositions(x, y, box);
29948 positions = this.getVerticalThreeBoxColPositions(x, y, box);
29951 positions = this.getVerticalFourBoxColPositions(x, y, box);
29957 Roo.each(box, function(b,kk){
29959 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29961 var sz = b.el.getSize();
29963 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29971 for (var i = 0; i < this.cols; i++){
29972 mY = Math.max(mY, maxY[i]);
29975 this.el.setHeight(mY - pos.y);
29979 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29981 // var pos = this.el.getBox(true);
29984 // var maxX = pos.right;
29986 // var maxHeight = 0;
29988 // Roo.each(items, function(item, k){
29992 // item.el.position('absolute');
29994 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29996 // item.el.setWidth(width);
29998 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30000 // item.el.setHeight(height);
30003 // item.el.setXY([x, y], isInstant ? false : true);
30005 // item.el.setXY([maxX - width, y], isInstant ? false : true);
30008 // y = y + height + this.alternativePadWidth;
30010 // maxHeight = maxHeight + height + this.alternativePadWidth;
30014 // this.el.setHeight(maxHeight);
30018 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30020 var pos = this.el.getBox(true);
30025 var maxX = pos.right;
30027 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30029 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30031 Roo.each(queue, function(box, k){
30033 Roo.each(box, function(b, kk){
30035 b.el.position('absolute');
30037 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30038 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30040 if(b.size == 'md-left' || b.size == 'md-right'){
30041 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30042 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30045 b.el.setWidth(width);
30046 b.el.setHeight(height);
30054 var positions = [];
30056 switch (box.length){
30058 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30061 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30064 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30067 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30073 Roo.each(box, function(b,kk){
30075 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30077 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30085 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30087 Roo.each(eItems, function(b,k){
30089 b.size = (k == 0) ? 'sm' : 'xs';
30090 b.x = (k == 0) ? 2 : 1;
30091 b.y = (k == 0) ? 2 : 1;
30093 b.el.position('absolute');
30095 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30097 b.el.setWidth(width);
30099 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30101 b.el.setHeight(height);
30105 var positions = [];
30108 x : maxX - this.unitWidth * 2 - this.gutter,
30113 x : maxX - this.unitWidth,
30114 y : minY + (this.unitWidth + this.gutter) * 2
30118 x : maxX - this.unitWidth * 3 - this.gutter * 2,
30122 Roo.each(eItems, function(b,k){
30124 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30130 getVerticalOneBoxColPositions : function(x, y, box)
30134 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30136 if(box[0].size == 'md-left'){
30140 if(box[0].size == 'md-right'){
30145 x : x + (this.unitWidth + this.gutter) * rand,
30152 getVerticalTwoBoxColPositions : function(x, y, box)
30156 if(box[0].size == 'xs'){
30160 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30164 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30178 x : x + (this.unitWidth + this.gutter) * 2,
30179 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30186 getVerticalThreeBoxColPositions : function(x, y, box)
30190 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30198 x : x + (this.unitWidth + this.gutter) * 1,
30203 x : x + (this.unitWidth + this.gutter) * 2,
30211 if(box[0].size == 'xs' && box[1].size == 'xs'){
30220 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30224 x : x + (this.unitWidth + this.gutter) * 1,
30238 x : x + (this.unitWidth + this.gutter) * 2,
30243 x : x + (this.unitWidth + this.gutter) * 2,
30244 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30251 getVerticalFourBoxColPositions : function(x, y, box)
30255 if(box[0].size == 'xs'){
30264 y : y + (this.unitHeight + this.gutter) * 1
30269 y : y + (this.unitHeight + this.gutter) * 2
30273 x : x + (this.unitWidth + this.gutter) * 1,
30287 x : x + (this.unitWidth + this.gutter) * 2,
30292 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30293 y : y + (this.unitHeight + this.gutter) * 1
30297 x : x + (this.unitWidth + this.gutter) * 2,
30298 y : y + (this.unitWidth + this.gutter) * 2
30305 getHorizontalOneBoxColPositions : function(maxX, minY, box)
30309 if(box[0].size == 'md-left'){
30311 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30318 if(box[0].size == 'md-right'){
30320 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30321 y : minY + (this.unitWidth + this.gutter) * 1
30327 var rand = Math.floor(Math.random() * (4 - box[0].y));
30330 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30331 y : minY + (this.unitWidth + this.gutter) * rand
30338 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30342 if(box[0].size == 'xs'){
30345 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30350 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30351 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30359 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30364 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30365 y : minY + (this.unitWidth + this.gutter) * 2
30372 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30376 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30379 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30384 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30385 y : minY + (this.unitWidth + this.gutter) * 1
30389 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30390 y : minY + (this.unitWidth + this.gutter) * 2
30397 if(box[0].size == 'xs' && box[1].size == 'xs'){
30400 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30405 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30410 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30411 y : minY + (this.unitWidth + this.gutter) * 1
30419 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30424 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30425 y : minY + (this.unitWidth + this.gutter) * 2
30429 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30430 y : minY + (this.unitWidth + this.gutter) * 2
30437 getHorizontalFourBoxColPositions : function(maxX, minY, box)
30441 if(box[0].size == 'xs'){
30444 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30449 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30454 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),
30459 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30460 y : minY + (this.unitWidth + this.gutter) * 1
30468 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30473 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30474 y : minY + (this.unitWidth + this.gutter) * 2
30478 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30479 y : minY + (this.unitWidth + this.gutter) * 2
30483 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),
30484 y : minY + (this.unitWidth + this.gutter) * 2
30498 * http://masonry.desandro.com
30500 * The idea is to render all the bricks based on vertical width...
30502 * The original code extends 'outlayer' - we might need to use that....
30508 * @class Roo.bootstrap.LayoutMasonryAuto
30509 * @extends Roo.bootstrap.Component
30510 * Bootstrap Layout Masonry class
30513 * Create a new Element
30514 * @param {Object} config The config object
30517 Roo.bootstrap.LayoutMasonryAuto = function(config){
30518 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30521 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
30524 * @cfg {Boolean} isFitWidth - resize the width..
30526 isFitWidth : false, // options..
30528 * @cfg {Boolean} isOriginLeft = left align?
30530 isOriginLeft : true,
30532 * @cfg {Boolean} isOriginTop = top align?
30534 isOriginTop : false,
30536 * @cfg {Boolean} isLayoutInstant = no animation?
30538 isLayoutInstant : false, // needed?
30540 * @cfg {Boolean} isResizingContainer = not sure if this is used..
30542 isResizingContainer : true,
30544 * @cfg {Number} columnWidth width of the columns
30550 * @cfg {Number} maxCols maximum number of columns
30555 * @cfg {Number} padHeight padding below box..
30561 * @cfg {Boolean} isAutoInitial defalut true
30564 isAutoInitial : true,
30570 initialColumnWidth : 0,
30571 currentSize : null,
30573 colYs : null, // array.
30580 bricks: null, //CompositeElement
30581 cols : 0, // array?
30582 // element : null, // wrapped now this.el
30583 _isLayoutInited : null,
30586 getAutoCreate : function(){
30590 cls: 'blog-masonary-wrapper ' + this.cls,
30592 cls : 'mas-boxes masonary'
30599 getChildContainer: function( )
30601 if (this.boxesEl) {
30602 return this.boxesEl;
30605 this.boxesEl = this.el.select('.mas-boxes').first();
30607 return this.boxesEl;
30611 initEvents : function()
30615 if(this.isAutoInitial){
30616 Roo.log('hook children rendered');
30617 this.on('childrenrendered', function() {
30618 Roo.log('children rendered');
30625 initial : function()
30627 this.reloadItems();
30629 this.currentSize = this.el.getBox(true);
30631 /// was window resize... - let's see if this works..
30632 Roo.EventManager.onWindowResize(this.resize, this);
30634 if(!this.isAutoInitial){
30639 this.layout.defer(500,this);
30642 reloadItems: function()
30644 this.bricks = this.el.select('.masonry-brick', true);
30646 this.bricks.each(function(b) {
30647 //Roo.log(b.getSize());
30648 if (!b.attr('originalwidth')) {
30649 b.attr('originalwidth', b.getSize().width);
30654 Roo.log(this.bricks.elements.length);
30657 resize : function()
30660 var cs = this.el.getBox(true);
30662 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30663 Roo.log("no change in with or X");
30666 this.currentSize = cs;
30670 layout : function()
30673 this._resetLayout();
30674 //this._manageStamps();
30676 // don't animate first layout
30677 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30678 this.layoutItems( isInstant );
30680 // flag for initalized
30681 this._isLayoutInited = true;
30684 layoutItems : function( isInstant )
30686 //var items = this._getItemsForLayout( this.items );
30687 // original code supports filtering layout items.. we just ignore it..
30689 this._layoutItems( this.bricks , isInstant );
30691 this._postLayout();
30693 _layoutItems : function ( items , isInstant)
30695 //this.fireEvent( 'layout', this, items );
30698 if ( !items || !items.elements.length ) {
30699 // no items, emit event with empty array
30704 items.each(function(item) {
30705 Roo.log("layout item");
30707 // get x/y object from method
30708 var position = this._getItemLayoutPosition( item );
30710 position.item = item;
30711 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30712 queue.push( position );
30715 this._processLayoutQueue( queue );
30717 /** Sets position of item in DOM
30718 * @param {Element} item
30719 * @param {Number} x - horizontal position
30720 * @param {Number} y - vertical position
30721 * @param {Boolean} isInstant - disables transitions
30723 _processLayoutQueue : function( queue )
30725 for ( var i=0, len = queue.length; i < len; i++ ) {
30726 var obj = queue[i];
30727 obj.item.position('absolute');
30728 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30734 * Any logic you want to do after each layout,
30735 * i.e. size the container
30737 _postLayout : function()
30739 this.resizeContainer();
30742 resizeContainer : function()
30744 if ( !this.isResizingContainer ) {
30747 var size = this._getContainerSize();
30749 this.el.setSize(size.width,size.height);
30750 this.boxesEl.setSize(size.width,size.height);
30756 _resetLayout : function()
30758 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30759 this.colWidth = this.el.getWidth();
30760 //this.gutter = this.el.getWidth();
30762 this.measureColumns();
30768 this.colYs.push( 0 );
30774 measureColumns : function()
30776 this.getContainerWidth();
30777 // if columnWidth is 0, default to outerWidth of first item
30778 if ( !this.columnWidth ) {
30779 var firstItem = this.bricks.first();
30780 Roo.log(firstItem);
30781 this.columnWidth = this.containerWidth;
30782 if (firstItem && firstItem.attr('originalwidth') ) {
30783 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30785 // columnWidth fall back to item of first element
30786 Roo.log("set column width?");
30787 this.initialColumnWidth = this.columnWidth ;
30789 // if first elem has no width, default to size of container
30794 if (this.initialColumnWidth) {
30795 this.columnWidth = this.initialColumnWidth;
30800 // column width is fixed at the top - however if container width get's smaller we should
30803 // this bit calcs how man columns..
30805 var columnWidth = this.columnWidth += this.gutter;
30807 // calculate columns
30808 var containerWidth = this.containerWidth + this.gutter;
30810 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30811 // fix rounding errors, typically with gutters
30812 var excess = columnWidth - containerWidth % columnWidth;
30815 // if overshoot is less than a pixel, round up, otherwise floor it
30816 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30817 cols = Math[ mathMethod ]( cols );
30818 this.cols = Math.max( cols, 1 );
30819 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30821 // padding positioning..
30822 var totalColWidth = this.cols * this.columnWidth;
30823 var padavail = this.containerWidth - totalColWidth;
30824 // so for 2 columns - we need 3 'pads'
30826 var padNeeded = (1+this.cols) * this.padWidth;
30828 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30830 this.columnWidth += padExtra
30831 //this.padWidth = Math.floor(padavail / ( this.cols));
30833 // adjust colum width so that padding is fixed??
30835 // we have 3 columns ... total = width * 3
30836 // we have X left over... that should be used by
30838 //if (this.expandC) {
30846 getContainerWidth : function()
30848 /* // container is parent if fit width
30849 var container = this.isFitWidth ? this.element.parentNode : this.element;
30850 // check that this.size and size are there
30851 // IE8 triggers resize on body size change, so they might not be
30853 var size = getSize( container ); //FIXME
30854 this.containerWidth = size && size.innerWidth; //FIXME
30857 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30861 _getItemLayoutPosition : function( item ) // what is item?
30863 // we resize the item to our columnWidth..
30865 item.setWidth(this.columnWidth);
30866 item.autoBoxAdjust = false;
30868 var sz = item.getSize();
30870 // how many columns does this brick span
30871 var remainder = this.containerWidth % this.columnWidth;
30873 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30874 // round if off by 1 pixel, otherwise use ceil
30875 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
30876 colSpan = Math.min( colSpan, this.cols );
30878 // normally this should be '1' as we dont' currently allow multi width columns..
30880 var colGroup = this._getColGroup( colSpan );
30881 // get the minimum Y value from the columns
30882 var minimumY = Math.min.apply( Math, colGroup );
30883 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30885 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
30887 // position the brick
30889 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30890 y: this.currentSize.y + minimumY + this.padHeight
30894 // apply setHeight to necessary columns
30895 var setHeight = minimumY + sz.height + this.padHeight;
30896 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30898 var setSpan = this.cols + 1 - colGroup.length;
30899 for ( var i = 0; i < setSpan; i++ ) {
30900 this.colYs[ shortColIndex + i ] = setHeight ;
30907 * @param {Number} colSpan - number of columns the element spans
30908 * @returns {Array} colGroup
30910 _getColGroup : function( colSpan )
30912 if ( colSpan < 2 ) {
30913 // if brick spans only one column, use all the column Ys
30918 // how many different places could this brick fit horizontally
30919 var groupCount = this.cols + 1 - colSpan;
30920 // for each group potential horizontal position
30921 for ( var i = 0; i < groupCount; i++ ) {
30922 // make an array of colY values for that one group
30923 var groupColYs = this.colYs.slice( i, i + colSpan );
30924 // and get the max value of the array
30925 colGroup[i] = Math.max.apply( Math, groupColYs );
30930 _manageStamp : function( stamp )
30932 var stampSize = stamp.getSize();
30933 var offset = stamp.getBox();
30934 // get the columns that this stamp affects
30935 var firstX = this.isOriginLeft ? offset.x : offset.right;
30936 var lastX = firstX + stampSize.width;
30937 var firstCol = Math.floor( firstX / this.columnWidth );
30938 firstCol = Math.max( 0, firstCol );
30940 var lastCol = Math.floor( lastX / this.columnWidth );
30941 // lastCol should not go over if multiple of columnWidth #425
30942 lastCol -= lastX % this.columnWidth ? 0 : 1;
30943 lastCol = Math.min( this.cols - 1, lastCol );
30945 // set colYs to bottom of the stamp
30946 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30949 for ( var i = firstCol; i <= lastCol; i++ ) {
30950 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30955 _getContainerSize : function()
30957 this.maxY = Math.max.apply( Math, this.colYs );
30962 if ( this.isFitWidth ) {
30963 size.width = this._getContainerFitWidth();
30969 _getContainerFitWidth : function()
30971 var unusedCols = 0;
30972 // count unused columns
30975 if ( this.colYs[i] !== 0 ) {
30980 // fit container to columns that have been used
30981 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30984 needsResizeLayout : function()
30986 var previousWidth = this.containerWidth;
30987 this.getContainerWidth();
30988 return previousWidth !== this.containerWidth;
31003 * @class Roo.bootstrap.MasonryBrick
31004 * @extends Roo.bootstrap.Component
31005 * Bootstrap MasonryBrick class
31008 * Create a new MasonryBrick
31009 * @param {Object} config The config object
31012 Roo.bootstrap.MasonryBrick = function(config){
31013 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31019 * When a MasonryBrick is clcik
31020 * @param {Roo.bootstrap.MasonryBrick} this
31021 * @param {Roo.EventObject} e
31027 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
31030 * @cfg {String} title
31034 * @cfg {String} html
31038 * @cfg {String} bgimage
31042 * @cfg {String} videourl
31046 * @cfg {String} cls
31050 * @cfg {String} href
31054 * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
31059 * @cfg {String} (center|bottom) placetitle
31064 * @cfg {Boolean} isFitContainer defalut true
31066 isFitContainer : true,
31069 * @cfg {Boolean} preventDefault defalut false
31071 preventDefault : false,
31073 getAutoCreate : function()
31075 if(!this.isFitContainer){
31076 return this.getSplitAutoCreate();
31079 var cls = 'masonry-brick masonry-brick-full';
31081 if(this.href.length){
31082 cls += ' masonry-brick-link';
31085 if(this.bgimage.length){
31086 cls += ' masonry-brick-image';
31089 if(!this.html.length){
31090 cls += ' enable-mask';
31094 cls += ' masonry-' + this.size + '-brick';
31097 if(this.placetitle.length){
31099 switch (this.placetitle) {
31101 cls += ' masonry-center-title';
31104 cls += ' masonry-bottom-title';
31111 if(!this.html.length && !this.bgimage.length){
31112 cls += ' masonry-center-title';
31115 if(!this.html.length && this.bgimage.length){
31116 cls += ' masonry-bottom-title';
31121 cls += ' ' + this.cls;
31125 tag: (this.href.length) ? 'a' : 'div',
31130 cls: 'masonry-brick-paragraph',
31136 if(this.href.length){
31137 cfg.href = this.href;
31140 var cn = cfg.cn[0].cn;
31142 if(this.title.length){
31145 cls: 'masonry-brick-title',
31150 if(this.html.length){
31153 cls: 'masonry-brick-text',
31157 if (!this.title.length && !this.html.length) {
31158 cfg.cn[0].cls += ' hide';
31161 if(this.bgimage.length){
31164 cls: 'masonry-brick-image-view',
31169 if(this.videourl.length){
31170 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31171 // youtube support only?
31174 cls: 'masonry-brick-image-view',
31177 allowfullscreen : true
31185 cls: 'masonry-brick-mask'
31192 getSplitAutoCreate : function()
31194 var cls = 'masonry-brick masonry-brick-split';
31196 if(this.href.length){
31197 cls += ' masonry-brick-link';
31200 if(this.bgimage.length){
31201 cls += ' masonry-brick-image';
31205 cls += ' masonry-' + this.size + '-brick';
31208 switch (this.placetitle) {
31210 cls += ' masonry-center-title';
31213 cls += ' masonry-bottom-title';
31216 if(!this.bgimage.length){
31217 cls += ' masonry-center-title';
31220 if(this.bgimage.length){
31221 cls += ' masonry-bottom-title';
31227 cls += ' ' + this.cls;
31231 tag: (this.href.length) ? 'a' : 'div',
31236 cls: 'masonry-brick-split-head',
31240 cls: 'masonry-brick-paragraph',
31247 cls: 'masonry-brick-split-body',
31253 if(this.href.length){
31254 cfg.href = this.href;
31257 if(this.title.length){
31258 cfg.cn[0].cn[0].cn.push({
31260 cls: 'masonry-brick-title',
31265 if(this.html.length){
31266 cfg.cn[1].cn.push({
31268 cls: 'masonry-brick-text',
31273 if(this.bgimage.length){
31274 cfg.cn[0].cn.push({
31276 cls: 'masonry-brick-image-view',
31281 if(this.videourl.length){
31282 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31283 // youtube support only?
31284 cfg.cn[0].cn.cn.push({
31286 cls: 'masonry-brick-image-view',
31289 allowfullscreen : true
31296 initEvents: function()
31298 switch (this.size) {
31331 this.el.on('touchstart', this.onTouchStart, this);
31332 this.el.on('touchmove', this.onTouchMove, this);
31333 this.el.on('touchend', this.onTouchEnd, this);
31334 this.el.on('contextmenu', this.onContextMenu, this);
31336 this.el.on('mouseenter' ,this.enter, this);
31337 this.el.on('mouseleave', this.leave, this);
31338 this.el.on('click', this.onClick, this);
31341 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31342 this.parent().bricks.push(this);
31347 onClick: function(e, el)
31349 var time = this.endTimer - this.startTimer;
31353 e.preventDefault();
31358 if(!this.preventDefault){
31362 e.preventDefault();
31363 this.fireEvent('click', this);
31366 enter: function(e, el)
31368 e.preventDefault();
31370 if(!this.isFitContainer){
31374 if(this.bgimage.length && this.html.length){
31375 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31379 leave: function(e, el)
31381 e.preventDefault();
31383 if(!this.isFitContainer){
31387 if(this.bgimage.length && this.html.length){
31388 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31392 onTouchStart: function(e, el)
31394 // e.preventDefault();
31396 this.touchmoved = false;
31398 if(!this.isFitContainer){
31402 if(!this.bgimage.length || !this.html.length){
31406 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31408 this.timer = new Date().getTime();
31412 onTouchMove: function(e, el)
31414 this.touchmoved = true;
31417 onContextMenu : function(e,el)
31419 e.preventDefault();
31420 e.stopPropagation();
31424 onTouchEnd: function(e, el)
31426 // e.preventDefault();
31428 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31435 if(!this.bgimage.length || !this.html.length){
31437 if(this.href.length){
31438 window.location.href = this.href;
31444 if(!this.isFitContainer){
31448 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31450 window.location.href = this.href;
31465 * @class Roo.bootstrap.Brick
31466 * @extends Roo.bootstrap.Component
31467 * Bootstrap Brick class
31470 * Create a new Brick
31471 * @param {Object} config The config object
31474 Roo.bootstrap.Brick = function(config){
31475 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31481 * When a Brick is click
31482 * @param {Roo.bootstrap.Brick} this
31483 * @param {Roo.EventObject} e
31489 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
31492 * @cfg {String} title
31496 * @cfg {String} html
31500 * @cfg {String} bgimage
31504 * @cfg {String} cls
31508 * @cfg {String} href
31512 * @cfg {String} video
31516 * @cfg {Boolean} square
31520 getAutoCreate : function()
31522 var cls = 'roo-brick';
31524 if(this.href.length){
31525 cls += ' roo-brick-link';
31528 if(this.bgimage.length){
31529 cls += ' roo-brick-image';
31532 if(!this.html.length && !this.bgimage.length){
31533 cls += ' roo-brick-center-title';
31536 if(!this.html.length && this.bgimage.length){
31537 cls += ' roo-brick-bottom-title';
31541 cls += ' ' + this.cls;
31545 tag: (this.href.length) ? 'a' : 'div',
31550 cls: 'roo-brick-paragraph',
31556 if(this.href.length){
31557 cfg.href = this.href;
31560 var cn = cfg.cn[0].cn;
31562 if(this.title.length){
31565 cls: 'roo-brick-title',
31570 if(this.html.length){
31573 cls: 'roo-brick-text',
31580 if(this.bgimage.length){
31583 cls: 'roo-brick-image-view',
31591 initEvents: function()
31593 if(this.title.length || this.html.length){
31594 this.el.on('mouseenter' ,this.enter, this);
31595 this.el.on('mouseleave', this.leave, this);
31599 Roo.EventManager.onWindowResize(this.resize, this);
31604 resize : function()
31606 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31608 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31610 if(this.bgimage.length){
31611 var image = this.el.select('.roo-brick-image-view', true).first();
31612 image.setWidth(paragraph.getWidth());
31613 image.setHeight(paragraph.getWidth());
31615 this.el.setHeight(paragraph.getWidth());
31621 enter: function(e, el)
31623 e.preventDefault();
31625 if(this.bgimage.length){
31626 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31627 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31631 leave: function(e, el)
31633 e.preventDefault();
31635 if(this.bgimage.length){
31636 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31637 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31653 * @class Roo.bootstrap.NumberField
31654 * @extends Roo.bootstrap.Input
31655 * Bootstrap NumberField class
31661 * Create a new NumberField
31662 * @param {Object} config The config object
31665 Roo.bootstrap.NumberField = function(config){
31666 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
31669 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
31672 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
31674 allowDecimals : true,
31676 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
31678 decimalSeparator : ".",
31680 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
31682 decimalPrecision : 2,
31684 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
31686 allowNegative : true,
31688 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
31690 minValue : Number.NEGATIVE_INFINITY,
31692 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
31694 maxValue : Number.MAX_VALUE,
31696 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
31698 minText : "The minimum value for this field is {0}",
31700 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
31702 maxText : "The maximum value for this field is {0}",
31704 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
31705 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
31707 nanText : "{0} is not a valid number",
31709 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
31714 initEvents : function()
31716 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
31718 var allowed = "0123456789";
31720 if(this.allowDecimals){
31721 allowed += this.decimalSeparator;
31724 if(this.allowNegative){
31728 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
31730 var keyPress = function(e){
31732 var k = e.getKey();
31734 var c = e.getCharCode();
31737 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
31738 allowed.indexOf(String.fromCharCode(c)) === -1
31744 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
31748 if(allowed.indexOf(String.fromCharCode(c)) === -1){
31753 this.el.on("keypress", keyPress, this);
31756 validateValue : function(value)
31759 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
31763 var num = this.parseValue(value);
31766 this.markInvalid(String.format(this.nanText, value));
31770 if(num < this.minValue){
31771 this.markInvalid(String.format(this.minText, this.minValue));
31775 if(num > this.maxValue){
31776 this.markInvalid(String.format(this.maxText, this.maxValue));
31783 getValue : function()
31785 return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
31788 parseValue : function(value)
31790 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
31791 return isNaN(value) ? '' : value;
31794 fixPrecision : function(value)
31796 var nan = isNaN(value);
31798 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
31799 return nan ? '' : value;
31801 return parseFloat(value).toFixed(this.decimalPrecision);
31804 setValue : function(v)
31806 v = this.fixPrecision(v);
31807 Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
31810 decimalPrecisionFcn : function(v)
31812 return Math.floor(v);
31815 beforeBlur : function()
31821 var v = this.parseValue(this.getRawValue());
31836 * @class Roo.bootstrap.DocumentSlider
31837 * @extends Roo.bootstrap.Component
31838 * Bootstrap DocumentSlider class
31841 * Create a new DocumentViewer
31842 * @param {Object} config The config object
31845 Roo.bootstrap.DocumentSlider = function(config){
31846 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
31853 * Fire after initEvent
31854 * @param {Roo.bootstrap.DocumentSlider} this
31859 * Fire after update
31860 * @param {Roo.bootstrap.DocumentSlider} this
31866 * @param {Roo.bootstrap.DocumentSlider} this
31872 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
31878 getAutoCreate : function()
31882 cls : 'roo-document-slider',
31886 cls : 'roo-document-slider-header',
31890 cls : 'roo-document-slider-header-title'
31896 cls : 'roo-document-slider-body',
31900 cls : 'roo-document-slider-prev',
31904 cls : 'fa fa-chevron-left'
31910 cls : 'roo-document-slider-thumb',
31914 cls : 'roo-document-slider-image'
31920 cls : 'roo-document-slider-next',
31924 cls : 'fa fa-chevron-right'
31936 initEvents : function()
31938 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
31939 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
31941 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
31942 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
31944 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
31945 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31947 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
31948 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31950 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
31951 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31953 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
31954 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
31956 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
31957 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
31959 this.thumbEl.on('click', this.onClick, this);
31961 this.prevIndicator.on('click', this.prev, this);
31963 this.nextIndicator.on('click', this.next, this);
31967 initial : function()
31969 if(this.files.length){
31970 this.indicator = 1;
31974 this.fireEvent('initial', this);
31977 update : function()
31979 this.imageEl.attr('src', this.files[this.indicator - 1]);
31981 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
31983 this.prevIndicator.show();
31985 if(this.indicator == 1){
31986 this.prevIndicator.hide();
31989 this.nextIndicator.show();
31991 if(this.indicator == this.files.length){
31992 this.nextIndicator.hide();
31995 this.thumbEl.scrollTo('top');
31997 this.fireEvent('update', this);
32000 onClick : function(e)
32002 e.preventDefault();
32004 this.fireEvent('click', this);
32009 e.preventDefault();
32011 this.indicator = Math.max(1, this.indicator - 1);
32018 e.preventDefault();
32020 this.indicator = Math.min(this.files.length, this.indicator + 1);
32027 * Ext JS Library 1.1.1
32028 * Copyright(c) 2006-2007, Ext JS, LLC.
32030 * Originally Released Under LGPL - original licence link has changed is not relivant.
32033 * <script type="text/javascript">
32038 * @class Roo.bootstrap.SplitBar
32039 * @extends Roo.util.Observable
32040 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
32044 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
32045 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
32046 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
32047 split.minSize = 100;
32048 split.maxSize = 600;
32049 split.animate = true;
32050 split.on('moved', splitterMoved);
32053 * Create a new SplitBar
32054 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
32055 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
32056 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32057 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
32058 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
32059 position of the SplitBar).
32061 Roo.bootstrap.SplitBar = function(cfg){
32066 // dragElement : elm
32067 // resizingElement: el,
32069 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
32070 // placement : Roo.bootstrap.SplitBar.LEFT ,
32071 // existingProxy ???
32074 this.el = Roo.get(cfg.dragElement, true);
32075 this.el.dom.unselectable = "on";
32077 this.resizingEl = Roo.get(cfg.resizingElement, true);
32081 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32082 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
32085 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
32088 * The minimum size of the resizing element. (Defaults to 0)
32094 * The maximum size of the resizing element. (Defaults to 2000)
32097 this.maxSize = 2000;
32100 * Whether to animate the transition to the new size
32103 this.animate = false;
32106 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
32109 this.useShim = false;
32114 if(!cfg.existingProxy){
32116 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
32118 this.proxy = Roo.get(cfg.existingProxy).dom;
32121 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
32124 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
32127 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
32130 this.dragSpecs = {};
32133 * @private The adapter to use to positon and resize elements
32135 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32136 this.adapter.init(this);
32138 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32140 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
32141 this.el.addClass("roo-splitbar-h");
32144 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
32145 this.el.addClass("roo-splitbar-v");
32151 * Fires when the splitter is moved (alias for {@link #event-moved})
32152 * @param {Roo.bootstrap.SplitBar} this
32153 * @param {Number} newSize the new width or height
32158 * Fires when the splitter is moved
32159 * @param {Roo.bootstrap.SplitBar} this
32160 * @param {Number} newSize the new width or height
32164 * @event beforeresize
32165 * Fires before the splitter is dragged
32166 * @param {Roo.bootstrap.SplitBar} this
32168 "beforeresize" : true,
32170 "beforeapply" : true
32173 Roo.util.Observable.call(this);
32176 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
32177 onStartProxyDrag : function(x, y){
32178 this.fireEvent("beforeresize", this);
32180 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
32182 o.enableDisplayMode("block");
32183 // all splitbars share the same overlay
32184 Roo.bootstrap.SplitBar.prototype.overlay = o;
32186 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32187 this.overlay.show();
32188 Roo.get(this.proxy).setDisplayed("block");
32189 var size = this.adapter.getElementSize(this);
32190 this.activeMinSize = this.getMinimumSize();;
32191 this.activeMaxSize = this.getMaximumSize();;
32192 var c1 = size - this.activeMinSize;
32193 var c2 = Math.max(this.activeMaxSize - size, 0);
32194 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32195 this.dd.resetConstraints();
32196 this.dd.setXConstraint(
32197 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
32198 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
32200 this.dd.setYConstraint(0, 0);
32202 this.dd.resetConstraints();
32203 this.dd.setXConstraint(0, 0);
32204 this.dd.setYConstraint(
32205 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
32206 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
32209 this.dragSpecs.startSize = size;
32210 this.dragSpecs.startPoint = [x, y];
32211 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
32215 * @private Called after the drag operation by the DDProxy
32217 onEndProxyDrag : function(e){
32218 Roo.get(this.proxy).setDisplayed(false);
32219 var endPoint = Roo.lib.Event.getXY(e);
32221 this.overlay.hide();
32224 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32225 newSize = this.dragSpecs.startSize +
32226 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
32227 endPoint[0] - this.dragSpecs.startPoint[0] :
32228 this.dragSpecs.startPoint[0] - endPoint[0]
32231 newSize = this.dragSpecs.startSize +
32232 (this.placement == Roo.bootstrap.SplitBar.TOP ?
32233 endPoint[1] - this.dragSpecs.startPoint[1] :
32234 this.dragSpecs.startPoint[1] - endPoint[1]
32237 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
32238 if(newSize != this.dragSpecs.startSize){
32239 if(this.fireEvent('beforeapply', this, newSize) !== false){
32240 this.adapter.setElementSize(this, newSize);
32241 this.fireEvent("moved", this, newSize);
32242 this.fireEvent("resize", this, newSize);
32248 * Get the adapter this SplitBar uses
32249 * @return The adapter object
32251 getAdapter : function(){
32252 return this.adapter;
32256 * Set the adapter this SplitBar uses
32257 * @param {Object} adapter A SplitBar adapter object
32259 setAdapter : function(adapter){
32260 this.adapter = adapter;
32261 this.adapter.init(this);
32265 * Gets the minimum size for the resizing element
32266 * @return {Number} The minimum size
32268 getMinimumSize : function(){
32269 return this.minSize;
32273 * Sets the minimum size for the resizing element
32274 * @param {Number} minSize The minimum size
32276 setMinimumSize : function(minSize){
32277 this.minSize = minSize;
32281 * Gets the maximum size for the resizing element
32282 * @return {Number} The maximum size
32284 getMaximumSize : function(){
32285 return this.maxSize;
32289 * Sets the maximum size for the resizing element
32290 * @param {Number} maxSize The maximum size
32292 setMaximumSize : function(maxSize){
32293 this.maxSize = maxSize;
32297 * Sets the initialize size for the resizing element
32298 * @param {Number} size The initial size
32300 setCurrentSize : function(size){
32301 var oldAnimate = this.animate;
32302 this.animate = false;
32303 this.adapter.setElementSize(this, size);
32304 this.animate = oldAnimate;
32308 * Destroy this splitbar.
32309 * @param {Boolean} removeEl True to remove the element
32311 destroy : function(removeEl){
32313 this.shim.remove();
32316 this.proxy.parentNode.removeChild(this.proxy);
32324 * @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.
32326 Roo.bootstrap.SplitBar.createProxy = function(dir){
32327 var proxy = new Roo.Element(document.createElement("div"));
32328 proxy.unselectable();
32329 var cls = 'roo-splitbar-proxy';
32330 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
32331 document.body.appendChild(proxy.dom);
32336 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
32337 * Default Adapter. It assumes the splitter and resizing element are not positioned
32338 * elements and only gets/sets the width of the element. Generally used for table based layouts.
32340 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
32343 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
32344 // do nothing for now
32345 init : function(s){
32349 * Called before drag operations to get the current size of the resizing element.
32350 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32352 getElementSize : function(s){
32353 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32354 return s.resizingEl.getWidth();
32356 return s.resizingEl.getHeight();
32361 * Called after drag operations to set the size of the resizing element.
32362 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32363 * @param {Number} newSize The new size to set
32364 * @param {Function} onComplete A function to be invoked when resizing is complete
32366 setElementSize : function(s, newSize, onComplete){
32367 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32369 s.resizingEl.setWidth(newSize);
32371 onComplete(s, newSize);
32374 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
32379 s.resizingEl.setHeight(newSize);
32381 onComplete(s, newSize);
32384 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
32391 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
32392 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
32393 * Adapter that moves the splitter element to align with the resized sizing element.
32394 * Used with an absolute positioned SplitBar.
32395 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
32396 * document.body, make sure you assign an id to the body element.
32398 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
32399 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32400 this.container = Roo.get(container);
32403 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
32404 init : function(s){
32405 this.basic.init(s);
32408 getElementSize : function(s){
32409 return this.basic.getElementSize(s);
32412 setElementSize : function(s, newSize, onComplete){
32413 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
32416 moveSplitter : function(s){
32417 var yes = Roo.bootstrap.SplitBar;
32418 switch(s.placement){
32420 s.el.setX(s.resizingEl.getRight());
32423 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
32426 s.el.setY(s.resizingEl.getBottom());
32429 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
32436 * Orientation constant - Create a vertical SplitBar
32440 Roo.bootstrap.SplitBar.VERTICAL = 1;
32443 * Orientation constant - Create a horizontal SplitBar
32447 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
32450 * Placement constant - The resizing element is to the left of the splitter element
32454 Roo.bootstrap.SplitBar.LEFT = 1;
32457 * Placement constant - The resizing element is to the right of the splitter element
32461 Roo.bootstrap.SplitBar.RIGHT = 2;
32464 * Placement constant - The resizing element is positioned above the splitter element
32468 Roo.bootstrap.SplitBar.TOP = 3;
32471 * Placement constant - The resizing element is positioned under splitter element
32475 Roo.bootstrap.SplitBar.BOTTOM = 4;
32476 Roo.namespace("Roo.bootstrap.layout");/*
32478 * Ext JS Library 1.1.1
32479 * Copyright(c) 2006-2007, Ext JS, LLC.
32481 * Originally Released Under LGPL - original licence link has changed is not relivant.
32484 * <script type="text/javascript">
32488 * @class Roo.bootstrap.layout.Manager
32489 * @extends Roo.bootstrap.Component
32490 * Base class for layout managers.
32492 Roo.bootstrap.layout.Manager = function(config)
32494 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
32500 /** false to disable window resize monitoring @type Boolean */
32501 this.monitorWindowResize = true;
32506 * Fires when a layout is performed.
32507 * @param {Roo.LayoutManager} this
32511 * @event regionresized
32512 * Fires when the user resizes a region.
32513 * @param {Roo.LayoutRegion} region The resized region
32514 * @param {Number} newSize The new size (width for east/west, height for north/south)
32516 "regionresized" : true,
32518 * @event regioncollapsed
32519 * Fires when a region is collapsed.
32520 * @param {Roo.LayoutRegion} region The collapsed region
32522 "regioncollapsed" : true,
32524 * @event regionexpanded
32525 * Fires when a region is expanded.
32526 * @param {Roo.LayoutRegion} region The expanded region
32528 "regionexpanded" : true
32530 this.updating = false;
32533 this.el = Roo.get(config.el);
32539 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
32544 monitorWindowResize : true,
32550 onRender : function(ct, position)
32553 this.el = Roo.get(ct);
32556 //this.fireEvent('render',this);
32560 initEvents: function()
32564 // ie scrollbar fix
32565 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32566 document.body.scroll = "no";
32567 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32568 this.el.position('relative');
32570 this.id = this.el.id;
32571 this.el.addClass("roo-layout-container");
32572 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32573 if(this.el.dom != document.body ) {
32574 this.el.on('resize', this.layout,this);
32575 this.el.on('show', this.layout,this);
32581 * Returns true if this layout is currently being updated
32582 * @return {Boolean}
32584 isUpdating : function(){
32585 return this.updating;
32589 * Suspend the LayoutManager from doing auto-layouts while
32590 * making multiple add or remove calls
32592 beginUpdate : function(){
32593 this.updating = true;
32597 * Restore auto-layouts and optionally disable the manager from performing a layout
32598 * @param {Boolean} noLayout true to disable a layout update
32600 endUpdate : function(noLayout){
32601 this.updating = false;
32607 layout: function(){
32611 onRegionResized : function(region, newSize){
32612 this.fireEvent("regionresized", region, newSize);
32616 onRegionCollapsed : function(region){
32617 this.fireEvent("regioncollapsed", region);
32620 onRegionExpanded : function(region){
32621 this.fireEvent("regionexpanded", region);
32625 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32626 * performs box-model adjustments.
32627 * @return {Object} The size as an object {width: (the width), height: (the height)}
32629 getViewSize : function()
32632 if(this.el.dom != document.body){
32633 size = this.el.getSize();
32635 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32637 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32638 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32643 * Returns the Element this layout is bound to.
32644 * @return {Roo.Element}
32646 getEl : function(){
32651 * Returns the specified region.
32652 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32653 * @return {Roo.LayoutRegion}
32655 getRegion : function(target){
32656 return this.regions[target.toLowerCase()];
32659 onWindowResize : function(){
32660 if(this.monitorWindowResize){
32667 * Ext JS Library 1.1.1
32668 * Copyright(c) 2006-2007, Ext JS, LLC.
32670 * Originally Released Under LGPL - original licence link has changed is not relivant.
32673 * <script type="text/javascript">
32676 * @class Roo.bootstrap.layout.Border
32677 * @extends Roo.bootstrap.layout.Manager
32678 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32679 * please see: examples/bootstrap/nested.html<br><br>
32681 <b>The container the layout is rendered into can be either the body element or any other element.
32682 If it is not the body element, the container needs to either be an absolute positioned element,
32683 or you will need to add "position:relative" to the css of the container. You will also need to specify
32684 the container size if it is not the body element.</b>
32687 * Create a new Border
32688 * @param {Object} config Configuration options
32690 Roo.bootstrap.layout.Border = function(config){
32691 config = config || {};
32692 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
32696 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32697 if(config[region]){
32698 config[region].region = region;
32699 this.addRegion(config[region]);
32705 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
32707 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
32709 * Creates and adds a new region if it doesn't already exist.
32710 * @param {String} target The target region key (north, south, east, west or center).
32711 * @param {Object} config The regions config object
32712 * @return {BorderLayoutRegion} The new region
32714 addRegion : function(config)
32716 if(!this.regions[config.region]){
32717 var r = this.factory(config);
32718 this.bindRegion(r);
32720 return this.regions[config.region];
32724 bindRegion : function(r){
32725 this.regions[r.config.region] = r;
32727 r.on("visibilitychange", this.layout, this);
32728 r.on("paneladded", this.layout, this);
32729 r.on("panelremoved", this.layout, this);
32730 r.on("invalidated", this.layout, this);
32731 r.on("resized", this.onRegionResized, this);
32732 r.on("collapsed", this.onRegionCollapsed, this);
32733 r.on("expanded", this.onRegionExpanded, this);
32737 * Performs a layout update.
32739 layout : function()
32741 if(this.updating) {
32745 // render all the rebions if they have not been done alreayd?
32746 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32747 if(this.regions[region] && !this.regions[region].bodyEl){
32748 this.regions[region].onRender(this.el)
32752 var size = this.getViewSize();
32753 var w = size.width;
32754 var h = size.height;
32759 //var x = 0, y = 0;
32761 var rs = this.regions;
32762 var north = rs["north"];
32763 var south = rs["south"];
32764 var west = rs["west"];
32765 var east = rs["east"];
32766 var center = rs["center"];
32767 //if(this.hideOnLayout){ // not supported anymore
32768 //c.el.setStyle("display", "none");
32770 if(north && north.isVisible()){
32771 var b = north.getBox();
32772 var m = north.getMargins();
32773 b.width = w - (m.left+m.right);
32776 centerY = b.height + b.y + m.bottom;
32777 centerH -= centerY;
32778 north.updateBox(this.safeBox(b));
32780 if(south && south.isVisible()){
32781 var b = south.getBox();
32782 var m = south.getMargins();
32783 b.width = w - (m.left+m.right);
32785 var totalHeight = (b.height + m.top + m.bottom);
32786 b.y = h - totalHeight + m.top;
32787 centerH -= totalHeight;
32788 south.updateBox(this.safeBox(b));
32790 if(west && west.isVisible()){
32791 var b = west.getBox();
32792 var m = west.getMargins();
32793 b.height = centerH - (m.top+m.bottom);
32795 b.y = centerY + m.top;
32796 var totalWidth = (b.width + m.left + m.right);
32797 centerX += totalWidth;
32798 centerW -= totalWidth;
32799 west.updateBox(this.safeBox(b));
32801 if(east && east.isVisible()){
32802 var b = east.getBox();
32803 var m = east.getMargins();
32804 b.height = centerH - (m.top+m.bottom);
32805 var totalWidth = (b.width + m.left + m.right);
32806 b.x = w - totalWidth + m.left;
32807 b.y = centerY + m.top;
32808 centerW -= totalWidth;
32809 east.updateBox(this.safeBox(b));
32812 var m = center.getMargins();
32814 x: centerX + m.left,
32815 y: centerY + m.top,
32816 width: centerW - (m.left+m.right),
32817 height: centerH - (m.top+m.bottom)
32819 //if(this.hideOnLayout){
32820 //center.el.setStyle("display", "block");
32822 center.updateBox(this.safeBox(centerBox));
32825 this.fireEvent("layout", this);
32829 safeBox : function(box){
32830 box.width = Math.max(0, box.width);
32831 box.height = Math.max(0, box.height);
32836 * Adds a ContentPanel (or subclass) to this layout.
32837 * @param {String} target The target region key (north, south, east, west or center).
32838 * @param {Roo.ContentPanel} panel The panel to add
32839 * @return {Roo.ContentPanel} The added panel
32841 add : function(target, panel){
32843 target = target.toLowerCase();
32844 return this.regions[target].add(panel);
32848 * Remove a ContentPanel (or subclass) to this layout.
32849 * @param {String} target The target region key (north, south, east, west or center).
32850 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32851 * @return {Roo.ContentPanel} The removed panel
32853 remove : function(target, panel){
32854 target = target.toLowerCase();
32855 return this.regions[target].remove(panel);
32859 * Searches all regions for a panel with the specified id
32860 * @param {String} panelId
32861 * @return {Roo.ContentPanel} The panel or null if it wasn't found
32863 findPanel : function(panelId){
32864 var rs = this.regions;
32865 for(var target in rs){
32866 if(typeof rs[target] != "function"){
32867 var p = rs[target].getPanel(panelId);
32877 * Searches all regions for a panel with the specified id and activates (shows) it.
32878 * @param {String/ContentPanel} panelId The panels id or the panel itself
32879 * @return {Roo.ContentPanel} The shown panel or null
32881 showPanel : function(panelId) {
32882 var rs = this.regions;
32883 for(var target in rs){
32884 var r = rs[target];
32885 if(typeof r != "function"){
32886 if(r.hasPanel(panelId)){
32887 return r.showPanel(panelId);
32895 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32896 * @param {Roo.state.Provider} provider (optional) An alternate state provider
32899 restoreState : function(provider){
32901 provider = Roo.state.Manager;
32903 var sm = new Roo.LayoutStateManager();
32904 sm.init(this, provider);
32910 * Adds a xtype elements to the layout.
32914 xtype : 'ContentPanel',
32921 xtype : 'NestedLayoutPanel',
32927 items : [ ... list of content panels or nested layout panels.. ]
32931 * @param {Object} cfg Xtype definition of item to add.
32933 addxtype : function(cfg)
32935 // basically accepts a pannel...
32936 // can accept a layout region..!?!?
32937 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32940 // theory? children can only be panels??
32942 //if (!cfg.xtype.match(/Panel$/)) {
32947 if (typeof(cfg.region) == 'undefined') {
32948 Roo.log("Failed to add Panel, region was not set");
32952 var region = cfg.region;
32958 xitems = cfg.items;
32965 case 'Content': // ContentPanel (el, cfg)
32966 case 'Scroll': // ContentPanel (el, cfg)
32968 cfg.autoCreate = true;
32969 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32971 // var el = this.el.createChild();
32972 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32975 this.add(region, ret);
32979 case 'TreePanel': // our new panel!
32980 cfg.el = this.el.createChild();
32981 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32982 this.add(region, ret);
32987 // create a new Layout (which is a Border Layout...
32989 var clayout = cfg.layout;
32990 clayout.el = this.el.createChild();
32991 clayout.items = clayout.items || [];
32995 // replace this exitems with the clayout ones..
32996 xitems = clayout.items;
32998 // force background off if it's in center...
32999 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33000 cfg.background = false;
33002 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
33005 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33006 //console.log('adding nested layout panel ' + cfg.toSource());
33007 this.add(region, ret);
33008 nb = {}; /// find first...
33013 // needs grid and region
33015 //var el = this.getRegion(region).el.createChild();
33017 *var el = this.el.createChild();
33018 // create the grid first...
33019 cfg.grid.container = el;
33020 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
33023 if (region == 'center' && this.active ) {
33024 cfg.background = false;
33027 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33029 this.add(region, ret);
33031 if (cfg.background) {
33032 // render grid on panel activation (if panel background)
33033 ret.on('activate', function(gp) {
33034 if (!gp.grid.rendered) {
33035 // gp.grid.render(el);
33039 // cfg.grid.render(el);
33045 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
33046 // it was the old xcomponent building that caused this before.
33047 // espeically if border is the top element in the tree.
33057 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33059 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33060 this.add(region, ret);
33064 throw "Can not add '" + cfg.xtype + "' to Border";
33070 this.beginUpdate();
33074 Roo.each(xitems, function(i) {
33075 region = nb && i.region ? i.region : false;
33077 var add = ret.addxtype(i);
33080 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33081 if (!i.background) {
33082 abn[region] = nb[region] ;
33089 // make the last non-background panel active..
33090 //if (nb) { Roo.log(abn); }
33093 for(var r in abn) {
33094 region = this.getRegion(r);
33096 // tried using nb[r], but it does not work..
33098 region.showPanel(abn[r]);
33109 factory : function(cfg)
33112 var validRegions = Roo.bootstrap.layout.Border.regions;
33114 var target = cfg.region;
33117 var r = Roo.bootstrap.layout;
33121 return new r.North(cfg);
33123 return new r.South(cfg);
33125 return new r.East(cfg);
33127 return new r.West(cfg);
33129 return new r.Center(cfg);
33131 throw 'Layout region "'+target+'" not supported.';
33138 * Ext JS Library 1.1.1
33139 * Copyright(c) 2006-2007, Ext JS, LLC.
33141 * Originally Released Under LGPL - original licence link has changed is not relivant.
33144 * <script type="text/javascript">
33148 * @class Roo.bootstrap.layout.Basic
33149 * @extends Roo.util.Observable
33150 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33151 * and does not have a titlebar, tabs or any other features. All it does is size and position
33152 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33153 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
33154 * @cfg {string} region the region that it inhabits..
33155 * @cfg {bool} skipConfig skip config?
33159 Roo.bootstrap.layout.Basic = function(config){
33161 this.mgr = config.mgr;
33163 this.position = config.region;
33165 var skipConfig = config.skipConfig;
33169 * @scope Roo.BasicLayoutRegion
33173 * @event beforeremove
33174 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33175 * @param {Roo.LayoutRegion} this
33176 * @param {Roo.ContentPanel} panel The panel
33177 * @param {Object} e The cancel event object
33179 "beforeremove" : true,
33181 * @event invalidated
33182 * Fires when the layout for this region is changed.
33183 * @param {Roo.LayoutRegion} this
33185 "invalidated" : true,
33187 * @event visibilitychange
33188 * Fires when this region is shown or hidden
33189 * @param {Roo.LayoutRegion} this
33190 * @param {Boolean} visibility true or false
33192 "visibilitychange" : true,
33194 * @event paneladded
33195 * Fires when a panel is added.
33196 * @param {Roo.LayoutRegion} this
33197 * @param {Roo.ContentPanel} panel The panel
33199 "paneladded" : true,
33201 * @event panelremoved
33202 * Fires when a panel is removed.
33203 * @param {Roo.LayoutRegion} this
33204 * @param {Roo.ContentPanel} panel The panel
33206 "panelremoved" : true,
33208 * @event beforecollapse
33209 * Fires when this region before collapse.
33210 * @param {Roo.LayoutRegion} this
33212 "beforecollapse" : true,
33215 * Fires when this region is collapsed.
33216 * @param {Roo.LayoutRegion} this
33218 "collapsed" : true,
33221 * Fires when this region is expanded.
33222 * @param {Roo.LayoutRegion} this
33227 * Fires when this region is slid into view.
33228 * @param {Roo.LayoutRegion} this
33230 "slideshow" : true,
33233 * Fires when this region slides out of view.
33234 * @param {Roo.LayoutRegion} this
33236 "slidehide" : true,
33238 * @event panelactivated
33239 * Fires when a panel is activated.
33240 * @param {Roo.LayoutRegion} this
33241 * @param {Roo.ContentPanel} panel The activated panel
33243 "panelactivated" : true,
33246 * Fires when the user resizes this region.
33247 * @param {Roo.LayoutRegion} this
33248 * @param {Number} newSize The new size (width for east/west, height for north/south)
33252 /** A collection of panels in this region. @type Roo.util.MixedCollection */
33253 this.panels = new Roo.util.MixedCollection();
33254 this.panels.getKey = this.getPanelId.createDelegate(this);
33256 this.activePanel = null;
33257 // ensure listeners are added...
33259 if (config.listeners || config.events) {
33260 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
33261 listeners : config.listeners || {},
33262 events : config.events || {}
33266 if(skipConfig !== true){
33267 this.applyConfig(config);
33271 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
33273 getPanelId : function(p){
33277 applyConfig : function(config){
33278 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33279 this.config = config;
33284 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
33285 * the width, for horizontal (north, south) the height.
33286 * @param {Number} newSize The new width or height
33288 resizeTo : function(newSize){
33289 var el = this.el ? this.el :
33290 (this.activePanel ? this.activePanel.getEl() : null);
33292 switch(this.position){
33295 el.setWidth(newSize);
33296 this.fireEvent("resized", this, newSize);
33300 el.setHeight(newSize);
33301 this.fireEvent("resized", this, newSize);
33307 getBox : function(){
33308 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33311 getMargins : function(){
33312 return this.margins;
33315 updateBox : function(box){
33317 var el = this.activePanel.getEl();
33318 el.dom.style.left = box.x + "px";
33319 el.dom.style.top = box.y + "px";
33320 this.activePanel.setSize(box.width, box.height);
33324 * Returns the container element for this region.
33325 * @return {Roo.Element}
33327 getEl : function(){
33328 return this.activePanel;
33332 * Returns true if this region is currently visible.
33333 * @return {Boolean}
33335 isVisible : function(){
33336 return this.activePanel ? true : false;
33339 setActivePanel : function(panel){
33340 panel = this.getPanel(panel);
33341 if(this.activePanel && this.activePanel != panel){
33342 this.activePanel.setActiveState(false);
33343 this.activePanel.getEl().setLeftTop(-10000,-10000);
33345 this.activePanel = panel;
33346 panel.setActiveState(true);
33348 panel.setSize(this.box.width, this.box.height);
33350 this.fireEvent("panelactivated", this, panel);
33351 this.fireEvent("invalidated");
33355 * Show the specified panel.
33356 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33357 * @return {Roo.ContentPanel} The shown panel or null
33359 showPanel : function(panel){
33360 panel = this.getPanel(panel);
33362 this.setActivePanel(panel);
33368 * Get the active panel for this region.
33369 * @return {Roo.ContentPanel} The active panel or null
33371 getActivePanel : function(){
33372 return this.activePanel;
33376 * Add the passed ContentPanel(s)
33377 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33378 * @return {Roo.ContentPanel} The panel added (if only one was added)
33380 add : function(panel){
33381 if(arguments.length > 1){
33382 for(var i = 0, len = arguments.length; i < len; i++) {
33383 this.add(arguments[i]);
33387 if(this.hasPanel(panel)){
33388 this.showPanel(panel);
33391 var el = panel.getEl();
33392 if(el.dom.parentNode != this.mgr.el.dom){
33393 this.mgr.el.dom.appendChild(el.dom);
33395 if(panel.setRegion){
33396 panel.setRegion(this);
33398 this.panels.add(panel);
33399 el.setStyle("position", "absolute");
33400 if(!panel.background){
33401 this.setActivePanel(panel);
33402 if(this.config.initialSize && this.panels.getCount()==1){
33403 this.resizeTo(this.config.initialSize);
33406 this.fireEvent("paneladded", this, panel);
33411 * Returns true if the panel is in this region.
33412 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33413 * @return {Boolean}
33415 hasPanel : function(panel){
33416 if(typeof panel == "object"){ // must be panel obj
33417 panel = panel.getId();
33419 return this.getPanel(panel) ? true : false;
33423 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33424 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33425 * @param {Boolean} preservePanel Overrides the config preservePanel option
33426 * @return {Roo.ContentPanel} The panel that was removed
33428 remove : function(panel, preservePanel){
33429 panel = this.getPanel(panel);
33434 this.fireEvent("beforeremove", this, panel, e);
33435 if(e.cancel === true){
33438 var panelId = panel.getId();
33439 this.panels.removeKey(panelId);
33444 * Returns the panel specified or null if it's not in this region.
33445 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33446 * @return {Roo.ContentPanel}
33448 getPanel : function(id){
33449 if(typeof id == "object"){ // must be panel obj
33452 return this.panels.get(id);
33456 * Returns this regions position (north/south/east/west/center).
33459 getPosition: function(){
33460 return this.position;
33464 * Ext JS Library 1.1.1
33465 * Copyright(c) 2006-2007, Ext JS, LLC.
33467 * Originally Released Under LGPL - original licence link has changed is not relivant.
33470 * <script type="text/javascript">
33474 * @class Roo.bootstrap.layout.Region
33475 * @extends Roo.bootstrap.layout.Basic
33476 * This class represents a region in a layout manager.
33478 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33479 * @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})
33480 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
33481 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
33482 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
33483 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
33484 * @cfg {String} title The title for the region (overrides panel titles)
33485 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
33486 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33487 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
33488 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33489 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
33490 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33491 * the space available, similar to FireFox 1.5 tabs (defaults to false)
33492 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
33493 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
33494 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
33496 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
33497 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
33498 * @cfg {Boolean} disableTabTips True to disable tab tooltips
33499 * @cfg {Number} width For East/West panels
33500 * @cfg {Number} height For North/South panels
33501 * @cfg {Boolean} split To show the splitter
33502 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
33504 * @cfg {string} cls Extra CSS classes to add to region
33506 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
33507 * @cfg {string} region the region that it inhabits..
33510 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
33511 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
33513 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
33514 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
33515 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
33517 Roo.bootstrap.layout.Region = function(config)
33519 this.applyConfig(config);
33521 var mgr = config.mgr;
33522 var pos = config.region;
33523 config.skipConfig = true;
33524 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
33527 this.onRender(mgr.el);
33530 this.visible = true;
33531 this.collapsed = false;
33532 this.unrendered_panels = [];
33535 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
33537 position: '', // set by wrapper (eg. north/south etc..)
33538 unrendered_panels : null, // unrendered panels.
33539 createBody : function(){
33540 /** This region's body element
33541 * @type Roo.Element */
33542 this.bodyEl = this.el.createChild({
33544 cls: "roo-layout-panel-body tab-content" // bootstrap added...
33548 onRender: function(ctr, pos)
33550 var dh = Roo.DomHelper;
33551 /** This region's container element
33552 * @type Roo.Element */
33553 this.el = dh.append(ctr.dom, {
33555 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
33557 /** This region's title element
33558 * @type Roo.Element */
33560 this.titleEl = dh.append(this.el.dom,
33563 unselectable: "on",
33564 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
33566 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
33567 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
33570 this.titleEl.enableDisplayMode();
33571 /** This region's title text element
33572 * @type HTMLElement */
33573 this.titleTextEl = this.titleEl.dom.firstChild;
33574 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33576 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
33577 this.closeBtn.enableDisplayMode();
33578 this.closeBtn.on("click", this.closeClicked, this);
33579 this.closeBtn.hide();
33581 this.createBody(this.config);
33582 if(this.config.hideWhenEmpty){
33584 this.on("paneladded", this.validateVisibility, this);
33585 this.on("panelremoved", this.validateVisibility, this);
33587 if(this.autoScroll){
33588 this.bodyEl.setStyle("overflow", "auto");
33590 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
33592 //if(c.titlebar !== false){
33593 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
33594 this.titleEl.hide();
33596 this.titleEl.show();
33597 if(this.config.title){
33598 this.titleTextEl.innerHTML = this.config.title;
33602 if(this.config.collapsed){
33603 this.collapse(true);
33605 if(this.config.hidden){
33609 if (this.unrendered_panels && this.unrendered_panels.length) {
33610 for (var i =0;i< this.unrendered_panels.length; i++) {
33611 this.add(this.unrendered_panels[i]);
33613 this.unrendered_panels = null;
33619 applyConfig : function(c)
33622 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
33623 var dh = Roo.DomHelper;
33624 if(c.titlebar !== false){
33625 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
33626 this.collapseBtn.on("click", this.collapse, this);
33627 this.collapseBtn.enableDisplayMode();
33629 if(c.showPin === true || this.showPin){
33630 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
33631 this.stickBtn.enableDisplayMode();
33632 this.stickBtn.on("click", this.expand, this);
33633 this.stickBtn.hide();
33638 /** This region's collapsed element
33639 * @type Roo.Element */
33642 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33643 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33646 if(c.floatable !== false){
33647 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33648 this.collapsedEl.on("click", this.collapseClick, this);
33651 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33652 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33653 id: "message", unselectable: "on", style:{"float":"left"}});
33654 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33656 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33657 this.expandBtn.on("click", this.expand, this);
33661 if(this.collapseBtn){
33662 this.collapseBtn.setVisible(c.collapsible == true);
33665 this.cmargins = c.cmargins || this.cmargins ||
33666 (this.position == "west" || this.position == "east" ?
33667 {top: 0, left: 2, right:2, bottom: 0} :
33668 {top: 2, left: 0, right:0, bottom: 2});
33670 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33673 this.bottomTabs = c.tabPosition != "top";
33675 this.autoScroll = c.autoScroll || false;
33680 this.duration = c.duration || .30;
33681 this.slideDuration = c.slideDuration || .45;
33686 * Returns true if this region is currently visible.
33687 * @return {Boolean}
33689 isVisible : function(){
33690 return this.visible;
33694 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33695 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
33697 //setCollapsedTitle : function(title){
33698 // title = title || " ";
33699 // if(this.collapsedTitleTextEl){
33700 // this.collapsedTitleTextEl.innerHTML = title;
33704 getBox : function(){
33706 // if(!this.collapsed){
33707 b = this.el.getBox(false, true);
33709 // b = this.collapsedEl.getBox(false, true);
33714 getMargins : function(){
33715 return this.margins;
33716 //return this.collapsed ? this.cmargins : this.margins;
33719 highlight : function(){
33720 this.el.addClass("x-layout-panel-dragover");
33723 unhighlight : function(){
33724 this.el.removeClass("x-layout-panel-dragover");
33727 updateBox : function(box)
33729 if (!this.bodyEl) {
33730 return; // not rendered yet..
33734 if(!this.collapsed){
33735 this.el.dom.style.left = box.x + "px";
33736 this.el.dom.style.top = box.y + "px";
33737 this.updateBody(box.width, box.height);
33739 this.collapsedEl.dom.style.left = box.x + "px";
33740 this.collapsedEl.dom.style.top = box.y + "px";
33741 this.collapsedEl.setSize(box.width, box.height);
33744 this.tabs.autoSizeTabs();
33748 updateBody : function(w, h)
33751 this.el.setWidth(w);
33752 w -= this.el.getBorderWidth("rl");
33753 if(this.config.adjustments){
33754 w += this.config.adjustments[0];
33757 if(h !== null && h > 0){
33758 this.el.setHeight(h);
33759 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33760 h -= this.el.getBorderWidth("tb");
33761 if(this.config.adjustments){
33762 h += this.config.adjustments[1];
33764 this.bodyEl.setHeight(h);
33766 h = this.tabs.syncHeight(h);
33769 if(this.panelSize){
33770 w = w !== null ? w : this.panelSize.width;
33771 h = h !== null ? h : this.panelSize.height;
33773 if(this.activePanel){
33774 var el = this.activePanel.getEl();
33775 w = w !== null ? w : el.getWidth();
33776 h = h !== null ? h : el.getHeight();
33777 this.panelSize = {width: w, height: h};
33778 this.activePanel.setSize(w, h);
33780 if(Roo.isIE && this.tabs){
33781 this.tabs.el.repaint();
33786 * Returns the container element for this region.
33787 * @return {Roo.Element}
33789 getEl : function(){
33794 * Hides this region.
33797 //if(!this.collapsed){
33798 this.el.dom.style.left = "-2000px";
33801 // this.collapsedEl.dom.style.left = "-2000px";
33802 // this.collapsedEl.hide();
33804 this.visible = false;
33805 this.fireEvent("visibilitychange", this, false);
33809 * Shows this region if it was previously hidden.
33812 //if(!this.collapsed){
33815 // this.collapsedEl.show();
33817 this.visible = true;
33818 this.fireEvent("visibilitychange", this, true);
33821 closeClicked : function(){
33822 if(this.activePanel){
33823 this.remove(this.activePanel);
33827 collapseClick : function(e){
33829 e.stopPropagation();
33832 e.stopPropagation();
33838 * Collapses this region.
33839 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33842 collapse : function(skipAnim, skipCheck = false){
33843 if(this.collapsed) {
33847 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
33849 this.collapsed = true;
33851 this.split.el.hide();
33853 if(this.config.animate && skipAnim !== true){
33854 this.fireEvent("invalidated", this);
33855 this.animateCollapse();
33857 this.el.setLocation(-20000,-20000);
33859 this.collapsedEl.show();
33860 this.fireEvent("collapsed", this);
33861 this.fireEvent("invalidated", this);
33867 animateCollapse : function(){
33872 * Expands this region if it was previously collapsed.
33873 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33874 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33877 expand : function(e, skipAnim){
33879 e.stopPropagation();
33881 if(!this.collapsed || this.el.hasActiveFx()) {
33885 this.afterSlideIn();
33888 this.collapsed = false;
33889 if(this.config.animate && skipAnim !== true){
33890 this.animateExpand();
33894 this.split.el.show();
33896 this.collapsedEl.setLocation(-2000,-2000);
33897 this.collapsedEl.hide();
33898 this.fireEvent("invalidated", this);
33899 this.fireEvent("expanded", this);
33903 animateExpand : function(){
33907 initTabs : function()
33909 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33911 var ts = new Roo.bootstrap.panel.Tabs({
33912 el: this.bodyEl.dom,
33913 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33914 disableTooltips: this.config.disableTabTips,
33915 toolbar : this.config.toolbar
33918 if(this.config.hideTabs){
33919 ts.stripWrap.setDisplayed(false);
33922 ts.resizeTabs = this.config.resizeTabs === true;
33923 ts.minTabWidth = this.config.minTabWidth || 40;
33924 ts.maxTabWidth = this.config.maxTabWidth || 250;
33925 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33926 ts.monitorResize = false;
33927 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33928 ts.bodyEl.addClass('roo-layout-tabs-body');
33929 this.panels.each(this.initPanelAsTab, this);
33932 initPanelAsTab : function(panel){
33933 var ti = this.tabs.addTab(
33937 this.config.closeOnTab && panel.isClosable(),
33940 if(panel.tabTip !== undefined){
33941 ti.setTooltip(panel.tabTip);
33943 ti.on("activate", function(){
33944 this.setActivePanel(panel);
33947 if(this.config.closeOnTab){
33948 ti.on("beforeclose", function(t, e){
33950 this.remove(panel);
33954 panel.tabItem = ti;
33959 updatePanelTitle : function(panel, title)
33961 if(this.activePanel == panel){
33962 this.updateTitle(title);
33965 var ti = this.tabs.getTab(panel.getEl().id);
33967 if(panel.tabTip !== undefined){
33968 ti.setTooltip(panel.tabTip);
33973 updateTitle : function(title){
33974 if(this.titleTextEl && !this.config.title){
33975 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
33979 setActivePanel : function(panel)
33981 panel = this.getPanel(panel);
33982 if(this.activePanel && this.activePanel != panel){
33983 this.activePanel.setActiveState(false);
33985 this.activePanel = panel;
33986 panel.setActiveState(true);
33987 if(this.panelSize){
33988 panel.setSize(this.panelSize.width, this.panelSize.height);
33991 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33993 this.updateTitle(panel.getTitle());
33995 this.fireEvent("invalidated", this);
33997 this.fireEvent("panelactivated", this, panel);
34001 * Shows the specified panel.
34002 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34003 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34005 showPanel : function(panel)
34007 panel = this.getPanel(panel);
34010 var tab = this.tabs.getTab(panel.getEl().id);
34011 if(tab.isHidden()){
34012 this.tabs.unhideTab(tab.id);
34016 this.setActivePanel(panel);
34023 * Get the active panel for this region.
34024 * @return {Roo.ContentPanel} The active panel or null
34026 getActivePanel : function(){
34027 return this.activePanel;
34030 validateVisibility : function(){
34031 if(this.panels.getCount() < 1){
34032 this.updateTitle(" ");
34033 this.closeBtn.hide();
34036 if(!this.isVisible()){
34043 * Adds the passed ContentPanel(s) to this region.
34044 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34045 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34047 add : function(panel)
34049 if(arguments.length > 1){
34050 for(var i = 0, len = arguments.length; i < len; i++) {
34051 this.add(arguments[i]);
34056 // if we have not been rendered yet, then we can not really do much of this..
34057 if (!this.bodyEl) {
34058 this.unrendered_panels.push(panel);
34065 if(this.hasPanel(panel)){
34066 this.showPanel(panel);
34069 panel.setRegion(this);
34070 this.panels.add(panel);
34071 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34072 // sinle panel - no tab...?? would it not be better to render it with the tabs,
34073 // and hide them... ???
34074 this.bodyEl.dom.appendChild(panel.getEl().dom);
34075 if(panel.background !== true){
34076 this.setActivePanel(panel);
34078 this.fireEvent("paneladded", this, panel);
34085 this.initPanelAsTab(panel);
34089 if(panel.background !== true){
34090 this.tabs.activate(panel.getEl().id);
34092 this.fireEvent("paneladded", this, panel);
34097 * Hides the tab for the specified panel.
34098 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34100 hidePanel : function(panel){
34101 if(this.tabs && (panel = this.getPanel(panel))){
34102 this.tabs.hideTab(panel.getEl().id);
34107 * Unhides the tab for a previously hidden panel.
34108 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34110 unhidePanel : function(panel){
34111 if(this.tabs && (panel = this.getPanel(panel))){
34112 this.tabs.unhideTab(panel.getEl().id);
34116 clearPanels : function(){
34117 while(this.panels.getCount() > 0){
34118 this.remove(this.panels.first());
34123 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34124 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34125 * @param {Boolean} preservePanel Overrides the config preservePanel option
34126 * @return {Roo.ContentPanel} The panel that was removed
34128 remove : function(panel, preservePanel)
34130 panel = this.getPanel(panel);
34135 this.fireEvent("beforeremove", this, panel, e);
34136 if(e.cancel === true){
34139 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34140 var panelId = panel.getId();
34141 this.panels.removeKey(panelId);
34143 document.body.appendChild(panel.getEl().dom);
34146 this.tabs.removeTab(panel.getEl().id);
34147 }else if (!preservePanel){
34148 this.bodyEl.dom.removeChild(panel.getEl().dom);
34150 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34151 var p = this.panels.first();
34152 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34153 tempEl.appendChild(p.getEl().dom);
34154 this.bodyEl.update("");
34155 this.bodyEl.dom.appendChild(p.getEl().dom);
34157 this.updateTitle(p.getTitle());
34159 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34160 this.setActivePanel(p);
34162 panel.setRegion(null);
34163 if(this.activePanel == panel){
34164 this.activePanel = null;
34166 if(this.config.autoDestroy !== false && preservePanel !== true){
34167 try{panel.destroy();}catch(e){}
34169 this.fireEvent("panelremoved", this, panel);
34174 * Returns the TabPanel component used by this region
34175 * @return {Roo.TabPanel}
34177 getTabs : function(){
34181 createTool : function(parentEl, className){
34182 var btn = Roo.DomHelper.append(parentEl, {
34184 cls: "x-layout-tools-button",
34187 cls: "roo-layout-tools-button-inner " + className,
34191 btn.addClassOnOver("roo-layout-tools-button-over");
34196 * Ext JS Library 1.1.1
34197 * Copyright(c) 2006-2007, Ext JS, LLC.
34199 * Originally Released Under LGPL - original licence link has changed is not relivant.
34202 * <script type="text/javascript">
34208 * @class Roo.SplitLayoutRegion
34209 * @extends Roo.LayoutRegion
34210 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34212 Roo.bootstrap.layout.Split = function(config){
34213 this.cursor = config.cursor;
34214 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
34217 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
34219 splitTip : "Drag to resize.",
34220 collapsibleSplitTip : "Drag to resize. Double click to hide.",
34221 useSplitTips : false,
34223 applyConfig : function(config){
34224 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
34227 onRender : function(ctr,pos) {
34229 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
34230 if(!this.config.split){
34235 var splitEl = Roo.DomHelper.append(ctr.dom, {
34237 id: this.el.id + "-split",
34238 cls: "roo-layout-split roo-layout-split-"+this.position,
34241 /** The SplitBar for this region
34242 * @type Roo.SplitBar */
34243 // does not exist yet...
34244 Roo.log([this.position, this.orientation]);
34246 this.split = new Roo.bootstrap.SplitBar({
34247 dragElement : splitEl,
34248 resizingElement: this.el,
34249 orientation : this.orientation
34252 this.split.on("moved", this.onSplitMove, this);
34253 this.split.useShim = this.config.useShim === true;
34254 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34255 if(this.useSplitTips){
34256 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34258 //if(config.collapsible){
34259 // this.split.el.on("dblclick", this.collapse, this);
34262 if(typeof this.config.minSize != "undefined"){
34263 this.split.minSize = this.config.minSize;
34265 if(typeof this.config.maxSize != "undefined"){
34266 this.split.maxSize = this.config.maxSize;
34268 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
34269 this.hideSplitter();
34274 getHMaxSize : function(){
34275 var cmax = this.config.maxSize || 10000;
34276 var center = this.mgr.getRegion("center");
34277 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34280 getVMaxSize : function(){
34281 var cmax = this.config.maxSize || 10000;
34282 var center = this.mgr.getRegion("center");
34283 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34286 onSplitMove : function(split, newSize){
34287 this.fireEvent("resized", this, newSize);
34291 * Returns the {@link Roo.SplitBar} for this region.
34292 * @return {Roo.SplitBar}
34294 getSplitBar : function(){
34299 this.hideSplitter();
34300 Roo.bootstrap.layout.Split.superclass.hide.call(this);
34303 hideSplitter : function(){
34305 this.split.el.setLocation(-2000,-2000);
34306 this.split.el.hide();
34312 this.split.el.show();
34314 Roo.bootstrap.layout.Split.superclass.show.call(this);
34317 beforeSlide: function(){
34318 if(Roo.isGecko){// firefox overflow auto bug workaround
34319 this.bodyEl.clip();
34321 this.tabs.bodyEl.clip();
34323 if(this.activePanel){
34324 this.activePanel.getEl().clip();
34326 if(this.activePanel.beforeSlide){
34327 this.activePanel.beforeSlide();
34333 afterSlide : function(){
34334 if(Roo.isGecko){// firefox overflow auto bug workaround
34335 this.bodyEl.unclip();
34337 this.tabs.bodyEl.unclip();
34339 if(this.activePanel){
34340 this.activePanel.getEl().unclip();
34341 if(this.activePanel.afterSlide){
34342 this.activePanel.afterSlide();
34348 initAutoHide : function(){
34349 if(this.autoHide !== false){
34350 if(!this.autoHideHd){
34351 var st = new Roo.util.DelayedTask(this.slideIn, this);
34352 this.autoHideHd = {
34353 "mouseout": function(e){
34354 if(!e.within(this.el, true)){
34358 "mouseover" : function(e){
34364 this.el.on(this.autoHideHd);
34368 clearAutoHide : function(){
34369 if(this.autoHide !== false){
34370 this.el.un("mouseout", this.autoHideHd.mouseout);
34371 this.el.un("mouseover", this.autoHideHd.mouseover);
34375 clearMonitor : function(){
34376 Roo.get(document).un("click", this.slideInIf, this);
34379 // these names are backwards but not changed for compat
34380 slideOut : function(){
34381 if(this.isSlid || this.el.hasActiveFx()){
34384 this.isSlid = true;
34385 if(this.collapseBtn){
34386 this.collapseBtn.hide();
34388 this.closeBtnState = this.closeBtn.getStyle('display');
34389 this.closeBtn.hide();
34391 this.stickBtn.show();
34394 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34395 this.beforeSlide();
34396 this.el.setStyle("z-index", 10001);
34397 this.el.slideIn(this.getSlideAnchor(), {
34398 callback: function(){
34400 this.initAutoHide();
34401 Roo.get(document).on("click", this.slideInIf, this);
34402 this.fireEvent("slideshow", this);
34409 afterSlideIn : function(){
34410 this.clearAutoHide();
34411 this.isSlid = false;
34412 this.clearMonitor();
34413 this.el.setStyle("z-index", "");
34414 if(this.collapseBtn){
34415 this.collapseBtn.show();
34417 this.closeBtn.setStyle('display', this.closeBtnState);
34419 this.stickBtn.hide();
34421 this.fireEvent("slidehide", this);
34424 slideIn : function(cb){
34425 if(!this.isSlid || this.el.hasActiveFx()){
34429 this.isSlid = false;
34430 this.beforeSlide();
34431 this.el.slideOut(this.getSlideAnchor(), {
34432 callback: function(){
34433 this.el.setLeftTop(-10000, -10000);
34435 this.afterSlideIn();
34443 slideInIf : function(e){
34444 if(!e.within(this.el)){
34449 animateCollapse : function(){
34450 this.beforeSlide();
34451 this.el.setStyle("z-index", 20000);
34452 var anchor = this.getSlideAnchor();
34453 this.el.slideOut(anchor, {
34454 callback : function(){
34455 this.el.setStyle("z-index", "");
34456 this.collapsedEl.slideIn(anchor, {duration:.3});
34458 this.el.setLocation(-10000,-10000);
34460 this.fireEvent("collapsed", this);
34467 animateExpand : function(){
34468 this.beforeSlide();
34469 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34470 this.el.setStyle("z-index", 20000);
34471 this.collapsedEl.hide({
34474 this.el.slideIn(this.getSlideAnchor(), {
34475 callback : function(){
34476 this.el.setStyle("z-index", "");
34479 this.split.el.show();
34481 this.fireEvent("invalidated", this);
34482 this.fireEvent("expanded", this);
34510 getAnchor : function(){
34511 return this.anchors[this.position];
34514 getCollapseAnchor : function(){
34515 return this.canchors[this.position];
34518 getSlideAnchor : function(){
34519 return this.sanchors[this.position];
34522 getAlignAdj : function(){
34523 var cm = this.cmargins;
34524 switch(this.position){
34540 getExpandAdj : function(){
34541 var c = this.collapsedEl, cm = this.cmargins;
34542 switch(this.position){
34544 return [-(cm.right+c.getWidth()+cm.left), 0];
34547 return [cm.right+c.getWidth()+cm.left, 0];
34550 return [0, -(cm.top+cm.bottom+c.getHeight())];
34553 return [0, cm.top+cm.bottom+c.getHeight()];
34559 * Ext JS Library 1.1.1
34560 * Copyright(c) 2006-2007, Ext JS, LLC.
34562 * Originally Released Under LGPL - original licence link has changed is not relivant.
34565 * <script type="text/javascript">
34568 * These classes are private internal classes
34570 Roo.bootstrap.layout.Center = function(config){
34571 config.region = "center";
34572 Roo.bootstrap.layout.Region.call(this, config);
34573 this.visible = true;
34574 this.minWidth = config.minWidth || 20;
34575 this.minHeight = config.minHeight || 20;
34578 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
34580 // center panel can't be hidden
34584 // center panel can't be hidden
34587 getMinWidth: function(){
34588 return this.minWidth;
34591 getMinHeight: function(){
34592 return this.minHeight;
34605 Roo.bootstrap.layout.North = function(config)
34607 config.region = 'north';
34608 config.cursor = 'n-resize';
34610 Roo.bootstrap.layout.Split.call(this, config);
34614 this.split.placement = Roo.bootstrap.SplitBar.TOP;
34615 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34616 this.split.el.addClass("roo-layout-split-v");
34618 var size = config.initialSize || config.height;
34619 if(typeof size != "undefined"){
34620 this.el.setHeight(size);
34623 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
34625 orientation: Roo.bootstrap.SplitBar.VERTICAL,
34629 getBox : function(){
34630 if(this.collapsed){
34631 return this.collapsedEl.getBox();
34633 var box = this.el.getBox();
34635 box.height += this.split.el.getHeight();
34640 updateBox : function(box){
34641 if(this.split && !this.collapsed){
34642 box.height -= this.split.el.getHeight();
34643 this.split.el.setLeft(box.x);
34644 this.split.el.setTop(box.y+box.height);
34645 this.split.el.setWidth(box.width);
34647 if(this.collapsed){
34648 this.updateBody(box.width, null);
34650 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34658 Roo.bootstrap.layout.South = function(config){
34659 config.region = 'south';
34660 config.cursor = 's-resize';
34661 Roo.bootstrap.layout.Split.call(this, config);
34663 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
34664 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34665 this.split.el.addClass("roo-layout-split-v");
34667 var size = config.initialSize || config.height;
34668 if(typeof size != "undefined"){
34669 this.el.setHeight(size);
34673 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
34674 orientation: Roo.bootstrap.SplitBar.VERTICAL,
34675 getBox : function(){
34676 if(this.collapsed){
34677 return this.collapsedEl.getBox();
34679 var box = this.el.getBox();
34681 var sh = this.split.el.getHeight();
34688 updateBox : function(box){
34689 if(this.split && !this.collapsed){
34690 var sh = this.split.el.getHeight();
34693 this.split.el.setLeft(box.x);
34694 this.split.el.setTop(box.y-sh);
34695 this.split.el.setWidth(box.width);
34697 if(this.collapsed){
34698 this.updateBody(box.width, null);
34700 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34704 Roo.bootstrap.layout.East = function(config){
34705 config.region = "east";
34706 config.cursor = "e-resize";
34707 Roo.bootstrap.layout.Split.call(this, config);
34709 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
34710 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34711 this.split.el.addClass("roo-layout-split-h");
34713 var size = config.initialSize || config.width;
34714 if(typeof size != "undefined"){
34715 this.el.setWidth(size);
34718 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
34719 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34720 getBox : function(){
34721 if(this.collapsed){
34722 return this.collapsedEl.getBox();
34724 var box = this.el.getBox();
34726 var sw = this.split.el.getWidth();
34733 updateBox : function(box){
34734 if(this.split && !this.collapsed){
34735 var sw = this.split.el.getWidth();
34737 this.split.el.setLeft(box.x);
34738 this.split.el.setTop(box.y);
34739 this.split.el.setHeight(box.height);
34742 if(this.collapsed){
34743 this.updateBody(null, box.height);
34745 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34749 Roo.bootstrap.layout.West = function(config){
34750 config.region = "west";
34751 config.cursor = "w-resize";
34753 Roo.bootstrap.layout.Split.call(this, config);
34755 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
34756 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34757 this.split.el.addClass("roo-layout-split-h");
34761 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
34762 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34764 onRender: function(ctr, pos)
34766 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
34767 var size = this.config.initialSize || this.config.width;
34768 if(typeof size != "undefined"){
34769 this.el.setWidth(size);
34773 getBox : function(){
34774 if(this.collapsed){
34775 return this.collapsedEl.getBox();
34777 var box = this.el.getBox();
34779 box.width += this.split.el.getWidth();
34784 updateBox : function(box){
34785 if(this.split && !this.collapsed){
34786 var sw = this.split.el.getWidth();
34788 this.split.el.setLeft(box.x+box.width);
34789 this.split.el.setTop(box.y);
34790 this.split.el.setHeight(box.height);
34792 if(this.collapsed){
34793 this.updateBody(null, box.height);
34795 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34798 Roo.namespace("Roo.bootstrap.panel");/*
34800 * Ext JS Library 1.1.1
34801 * Copyright(c) 2006-2007, Ext JS, LLC.
34803 * Originally Released Under LGPL - original licence link has changed is not relivant.
34806 * <script type="text/javascript">
34809 * @class Roo.ContentPanel
34810 * @extends Roo.util.Observable
34811 * A basic ContentPanel element.
34812 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
34813 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
34814 * @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
34815 * @cfg {Boolean} closable True if the panel can be closed/removed
34816 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
34817 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34818 * @cfg {Toolbar} toolbar A toolbar for this panel
34819 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
34820 * @cfg {String} title The title for this panel
34821 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34822 * @cfg {String} url Calls {@link #setUrl} with this value
34823 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34824 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
34825 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
34826 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
34827 * @cfg {Boolean} badges render the badges
34830 * Create a new ContentPanel.
34831 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34832 * @param {String/Object} config A string to set only the title or a config object
34833 * @param {String} content (optional) Set the HTML content for this panel
34834 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34836 Roo.bootstrap.panel.Content = function( config){
34838 this.tpl = config.tpl || false;
34840 var el = config.el;
34841 var content = config.content;
34843 if(config.autoCreate){ // xtype is available if this is called from factory
34846 this.el = Roo.get(el);
34847 if(!this.el && config && config.autoCreate){
34848 if(typeof config.autoCreate == "object"){
34849 if(!config.autoCreate.id){
34850 config.autoCreate.id = config.id||el;
34852 this.el = Roo.DomHelper.append(document.body,
34853 config.autoCreate, true);
34855 var elcfg = { tag: "div",
34856 cls: "roo-layout-inactive-content",
34860 elcfg.html = config.html;
34864 this.el = Roo.DomHelper.append(document.body, elcfg , true);
34867 this.closable = false;
34868 this.loaded = false;
34869 this.active = false;
34872 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
34874 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
34876 this.wrapEl = this.el; //this.el.wrap();
34878 if (config.toolbar.items) {
34879 ti = config.toolbar.items ;
34880 delete config.toolbar.items ;
34884 this.toolbar.render(this.wrapEl, 'before');
34885 for(var i =0;i < ti.length;i++) {
34886 // Roo.log(['add child', items[i]]);
34887 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34889 this.toolbar.items = nitems;
34890 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
34891 delete config.toolbar;
34895 // xtype created footer. - not sure if will work as we normally have to render first..
34896 if (this.footer && !this.footer.el && this.footer.xtype) {
34897 if (!this.wrapEl) {
34898 this.wrapEl = this.el.wrap();
34901 this.footer.container = this.wrapEl.createChild();
34903 this.footer = Roo.factory(this.footer, Roo);
34908 if(typeof config == "string"){
34909 this.title = config;
34911 Roo.apply(this, config);
34915 this.resizeEl = Roo.get(this.resizeEl, true);
34917 this.resizeEl = this.el;
34919 // handle view.xtype
34927 * Fires when this panel is activated.
34928 * @param {Roo.ContentPanel} this
34932 * @event deactivate
34933 * Fires when this panel is activated.
34934 * @param {Roo.ContentPanel} this
34936 "deactivate" : true,
34940 * Fires when this panel is resized if fitToFrame is true.
34941 * @param {Roo.ContentPanel} this
34942 * @param {Number} width The width after any component adjustments
34943 * @param {Number} height The height after any component adjustments
34949 * Fires when this tab is created
34950 * @param {Roo.ContentPanel} this
34961 if(this.autoScroll){
34962 this.resizeEl.setStyle("overflow", "auto");
34964 // fix randome scrolling
34965 //this.el.on('scroll', function() {
34966 // Roo.log('fix random scolling');
34967 // this.scrollTo('top',0);
34970 content = content || this.content;
34972 this.setContent(content);
34974 if(config && config.url){
34975 this.setUrl(this.url, this.params, this.loadOnce);
34980 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
34982 if (this.view && typeof(this.view.xtype) != 'undefined') {
34983 this.view.el = this.el.appendChild(document.createElement("div"));
34984 this.view = Roo.factory(this.view);
34985 this.view.render && this.view.render(false, '');
34989 this.fireEvent('render', this);
34992 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
34996 setRegion : function(region){
34997 this.region = region;
34998 this.setActiveClass(region && !this.background);
35002 setActiveClass: function(state)
35005 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
35006 this.el.setStyle('position','relative');
35008 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
35009 this.el.setStyle('position', 'absolute');
35014 * Returns the toolbar for this Panel if one was configured.
35015 * @return {Roo.Toolbar}
35017 getToolbar : function(){
35018 return this.toolbar;
35021 setActiveState : function(active)
35023 this.active = active;
35024 this.setActiveClass(active);
35026 this.fireEvent("deactivate", this);
35028 this.fireEvent("activate", this);
35032 * Updates this panel's element
35033 * @param {String} content The new content
35034 * @param {Boolean} loadScripts (optional) true to look for and process scripts
35036 setContent : function(content, loadScripts){
35037 this.el.update(content, loadScripts);
35040 ignoreResize : function(w, h){
35041 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35044 this.lastSize = {width: w, height: h};
35049 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35050 * @return {Roo.UpdateManager} The UpdateManager
35052 getUpdateManager : function(){
35053 return this.el.getUpdateManager();
35056 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35057 * @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:
35060 url: "your-url.php",
35061 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35062 callback: yourFunction,
35063 scope: yourObject, //(optional scope)
35066 text: "Loading...",
35071 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35072 * 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.
35073 * @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}
35074 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35075 * @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.
35076 * @return {Roo.ContentPanel} this
35079 var um = this.el.getUpdateManager();
35080 um.update.apply(um, arguments);
35086 * 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.
35087 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35088 * @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)
35089 * @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)
35090 * @return {Roo.UpdateManager} The UpdateManager
35092 setUrl : function(url, params, loadOnce){
35093 if(this.refreshDelegate){
35094 this.removeListener("activate", this.refreshDelegate);
35096 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35097 this.on("activate", this.refreshDelegate);
35098 return this.el.getUpdateManager();
35101 _handleRefresh : function(url, params, loadOnce){
35102 if(!loadOnce || !this.loaded){
35103 var updater = this.el.getUpdateManager();
35104 updater.update(url, params, this._setLoaded.createDelegate(this));
35108 _setLoaded : function(){
35109 this.loaded = true;
35113 * Returns this panel's id
35116 getId : function(){
35121 * Returns this panel's element - used by regiosn to add.
35122 * @return {Roo.Element}
35124 getEl : function(){
35125 return this.wrapEl || this.el;
35130 adjustForComponents : function(width, height)
35132 //Roo.log('adjustForComponents ');
35133 if(this.resizeEl != this.el){
35134 width -= this.el.getFrameWidth('lr');
35135 height -= this.el.getFrameWidth('tb');
35138 var te = this.toolbar.getEl();
35139 height -= te.getHeight();
35140 te.setWidth(width);
35143 var te = this.footer.getEl();
35144 Roo.log("footer:" + te.getHeight());
35146 height -= te.getHeight();
35147 te.setWidth(width);
35151 if(this.adjustments){
35152 width += this.adjustments[0];
35153 height += this.adjustments[1];
35155 return {"width": width, "height": height};
35158 setSize : function(width, height){
35159 if(this.fitToFrame && !this.ignoreResize(width, height)){
35160 if(this.fitContainer && this.resizeEl != this.el){
35161 this.el.setSize(width, height);
35163 var size = this.adjustForComponents(width, height);
35164 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35165 this.fireEvent('resize', this, size.width, size.height);
35170 * Returns this panel's title
35173 getTitle : function(){
35178 * Set this panel's title
35179 * @param {String} title
35181 setTitle : function(title){
35182 this.title = title;
35184 this.region.updatePanelTitle(this, title);
35189 * Returns true is this panel was configured to be closable
35190 * @return {Boolean}
35192 isClosable : function(){
35193 return this.closable;
35196 beforeSlide : function(){
35198 this.resizeEl.clip();
35201 afterSlide : function(){
35203 this.resizeEl.unclip();
35207 * Force a content refresh from the URL specified in the {@link #setUrl} method.
35208 * Will fail silently if the {@link #setUrl} method has not been called.
35209 * This does not activate the panel, just updates its content.
35211 refresh : function(){
35212 if(this.refreshDelegate){
35213 this.loaded = false;
35214 this.refreshDelegate();
35219 * Destroys this panel
35221 destroy : function(){
35222 this.el.removeAllListeners();
35223 var tempEl = document.createElement("span");
35224 tempEl.appendChild(this.el.dom);
35225 tempEl.innerHTML = "";
35231 * form - if the content panel contains a form - this is a reference to it.
35232 * @type {Roo.form.Form}
35236 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35237 * This contains a reference to it.
35243 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35253 * @param {Object} cfg Xtype definition of item to add.
35257 getChildContainer: function () {
35258 return this.getEl();
35263 var ret = new Roo.factory(cfg);
35268 if (cfg.xtype.match(/^Form$/)) {
35271 //if (this.footer) {
35272 // el = this.footer.container.insertSibling(false, 'before');
35274 el = this.el.createChild();
35277 this.form = new Roo.form.Form(cfg);
35280 if ( this.form.allItems.length) {
35281 this.form.render(el.dom);
35285 // should only have one of theses..
35286 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35287 // views.. should not be just added - used named prop 'view''
35289 cfg.el = this.el.appendChild(document.createElement("div"));
35292 var ret = new Roo.factory(cfg);
35294 ret.render && ret.render(false, ''); // render blank..
35304 * @class Roo.bootstrap.panel.Grid
35305 * @extends Roo.bootstrap.panel.Content
35307 * Create a new GridPanel.
35308 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
35309 * @param {Object} config A the config object
35315 Roo.bootstrap.panel.Grid = function(config)
35319 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35320 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
35322 config.el = this.wrapper;
35323 //this.el = this.wrapper;
35325 if (config.container) {
35326 // ctor'ed from a Border/panel.grid
35329 this.wrapper.setStyle("overflow", "hidden");
35330 this.wrapper.addClass('roo-grid-container');
35335 if(config.toolbar){
35336 var tool_el = this.wrapper.createChild();
35337 this.toolbar = Roo.factory(config.toolbar);
35339 if (config.toolbar.items) {
35340 ti = config.toolbar.items ;
35341 delete config.toolbar.items ;
35345 this.toolbar.render(tool_el);
35346 for(var i =0;i < ti.length;i++) {
35347 // Roo.log(['add child', items[i]]);
35348 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35350 this.toolbar.items = nitems;
35352 delete config.toolbar;
35355 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
35356 config.grid.scrollBody = true;;
35357 config.grid.monitorWindowResize = false; // turn off autosizing
35358 config.grid.autoHeight = false;
35359 config.grid.autoWidth = false;
35361 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
35363 if (config.background) {
35364 // render grid on panel activation (if panel background)
35365 this.on('activate', function(gp) {
35366 if (!gp.grid.rendered) {
35367 gp.grid.render(this.wrapper);
35368 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
35373 this.grid.render(this.wrapper);
35374 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
35377 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
35378 // ??? needed ??? config.el = this.wrapper;
35383 // xtype created footer. - not sure if will work as we normally have to render first..
35384 if (this.footer && !this.footer.el && this.footer.xtype) {
35386 var ctr = this.grid.getView().getFooterPanel(true);
35387 this.footer.dataSource = this.grid.dataSource;
35388 this.footer = Roo.factory(this.footer, Roo);
35389 this.footer.render(ctr);
35399 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
35400 getId : function(){
35401 return this.grid.id;
35405 * Returns the grid for this panel
35406 * @return {Roo.bootstrap.Table}
35408 getGrid : function(){
35412 setSize : function(width, height){
35413 if(!this.ignoreResize(width, height)){
35414 var grid = this.grid;
35415 var size = this.adjustForComponents(width, height);
35416 var gridel = grid.getGridEl();
35417 gridel.setSize(size.width, size.height);
35419 var thd = grid.getGridEl().select('thead',true).first();
35420 var tbd = grid.getGridEl().select('tbody', true).first();
35422 tbd.setSize(width, height - thd.getHeight());
35431 beforeSlide : function(){
35432 this.grid.getView().scroller.clip();
35435 afterSlide : function(){
35436 this.grid.getView().scroller.unclip();
35439 destroy : function(){
35440 this.grid.destroy();
35442 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
35447 * @class Roo.bootstrap.panel.Nest
35448 * @extends Roo.bootstrap.panel.Content
35450 * Create a new Panel, that can contain a layout.Border.
35453 * @param {Roo.BorderLayout} layout The layout for this panel
35454 * @param {String/Object} config A string to set only the title or a config object
35456 Roo.bootstrap.panel.Nest = function(config)
35458 // construct with only one argument..
35459 /* FIXME - implement nicer consturctors
35460 if (layout.layout) {
35462 layout = config.layout;
35463 delete config.layout;
35465 if (layout.xtype && !layout.getEl) {
35466 // then layout needs constructing..
35467 layout = Roo.factory(layout, Roo);
35471 config.el = config.layout.getEl();
35473 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
35475 config.layout.monitorWindowResize = false; // turn off autosizing
35476 this.layout = config.layout;
35477 this.layout.getEl().addClass("roo-layout-nested-layout");
35484 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
35486 setSize : function(width, height){
35487 if(!this.ignoreResize(width, height)){
35488 var size = this.adjustForComponents(width, height);
35489 var el = this.layout.getEl();
35490 if (size.height < 1) {
35491 el.setWidth(size.width);
35493 el.setSize(size.width, size.height);
35495 var touch = el.dom.offsetWidth;
35496 this.layout.layout();
35497 // ie requires a double layout on the first pass
35498 if(Roo.isIE && !this.initialized){
35499 this.initialized = true;
35500 this.layout.layout();
35505 // activate all subpanels if not currently active..
35507 setActiveState : function(active){
35508 this.active = active;
35509 this.setActiveClass(active);
35512 this.fireEvent("deactivate", this);
35516 this.fireEvent("activate", this);
35517 // not sure if this should happen before or after..
35518 if (!this.layout) {
35519 return; // should not happen..
35522 for (var r in this.layout.regions) {
35523 reg = this.layout.getRegion(r);
35524 if (reg.getActivePanel()) {
35525 //reg.showPanel(reg.getActivePanel()); // force it to activate..
35526 reg.setActivePanel(reg.getActivePanel());
35529 if (!reg.panels.length) {
35532 reg.showPanel(reg.getPanel(0));
35541 * Returns the nested BorderLayout for this panel
35542 * @return {Roo.BorderLayout}
35544 getLayout : function(){
35545 return this.layout;
35549 * Adds a xtype elements to the layout of the nested panel
35553 xtype : 'ContentPanel',
35560 xtype : 'NestedLayoutPanel',
35566 items : [ ... list of content panels or nested layout panels.. ]
35570 * @param {Object} cfg Xtype definition of item to add.
35572 addxtype : function(cfg) {
35573 return this.layout.addxtype(cfg);
35578 * Ext JS Library 1.1.1
35579 * Copyright(c) 2006-2007, Ext JS, LLC.
35581 * Originally Released Under LGPL - original licence link has changed is not relivant.
35584 * <script type="text/javascript">
35587 * @class Roo.TabPanel
35588 * @extends Roo.util.Observable
35589 * A lightweight tab container.
35593 // basic tabs 1, built from existing content
35594 var tabs = new Roo.TabPanel("tabs1");
35595 tabs.addTab("script", "View Script");
35596 tabs.addTab("markup", "View Markup");
35597 tabs.activate("script");
35599 // more advanced tabs, built from javascript
35600 var jtabs = new Roo.TabPanel("jtabs");
35601 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
35603 // set up the UpdateManager
35604 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
35605 var updater = tab2.getUpdateManager();
35606 updater.setDefaultUrl("ajax1.htm");
35607 tab2.on('activate', updater.refresh, updater, true);
35609 // Use setUrl for Ajax loading
35610 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
35611 tab3.setUrl("ajax2.htm", null, true);
35614 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
35617 jtabs.activate("jtabs-1");
35620 * Create a new TabPanel.
35621 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
35622 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
35624 Roo.bootstrap.panel.Tabs = function(config){
35626 * The container element for this TabPanel.
35627 * @type Roo.Element
35629 this.el = Roo.get(config.el);
35632 if(typeof config == "boolean"){
35633 this.tabPosition = config ? "bottom" : "top";
35635 Roo.apply(this, config);
35639 if(this.tabPosition == "bottom"){
35640 this.bodyEl = Roo.get(this.createBody(this.el.dom));
35641 this.el.addClass("roo-tabs-bottom");
35643 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
35644 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
35645 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
35647 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
35649 if(this.tabPosition != "bottom"){
35650 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
35651 * @type Roo.Element
35653 this.bodyEl = Roo.get(this.createBody(this.el.dom));
35654 this.el.addClass("roo-tabs-top");
35658 this.bodyEl.setStyle("position", "relative");
35660 this.active = null;
35661 this.activateDelegate = this.activate.createDelegate(this);
35666 * Fires when the active tab changes
35667 * @param {Roo.TabPanel} this
35668 * @param {Roo.TabPanelItem} activePanel The new active tab
35672 * @event beforetabchange
35673 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
35674 * @param {Roo.TabPanel} this
35675 * @param {Object} e Set cancel to true on this object to cancel the tab change
35676 * @param {Roo.TabPanelItem} tab The tab being changed to
35678 "beforetabchange" : true
35681 Roo.EventManager.onWindowResize(this.onResize, this);
35682 this.cpad = this.el.getPadding("lr");
35683 this.hiddenCount = 0;
35686 // toolbar on the tabbar support...
35687 if (this.toolbar) {
35688 alert("no toolbar support yet");
35689 this.toolbar = false;
35691 var tcfg = this.toolbar;
35692 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
35693 this.toolbar = new Roo.Toolbar(tcfg);
35694 if (Roo.isSafari) {
35695 var tbl = tcfg.container.child('table', true);
35696 tbl.setAttribute('width', '100%');
35704 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
35707 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
35709 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
35711 tabPosition : "top",
35713 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
35715 currentTabWidth : 0,
35717 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
35721 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
35725 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
35727 preferredTabWidth : 175,
35729 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
35731 resizeTabs : false,
35733 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
35735 monitorResize : true,
35737 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
35742 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
35743 * @param {String} id The id of the div to use <b>or create</b>
35744 * @param {String} text The text for the tab
35745 * @param {String} content (optional) Content to put in the TabPanelItem body
35746 * @param {Boolean} closable (optional) True to create a close icon on the tab
35747 * @return {Roo.TabPanelItem} The created TabPanelItem
35749 addTab : function(id, text, content, closable, tpl)
35751 var item = new Roo.bootstrap.panel.TabItem({
35755 closable : closable,
35758 this.addTabItem(item);
35760 item.setContent(content);
35766 * Returns the {@link Roo.TabPanelItem} with the specified id/index
35767 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
35768 * @return {Roo.TabPanelItem}
35770 getTab : function(id){
35771 return this.items[id];
35775 * Hides the {@link Roo.TabPanelItem} with the specified id/index
35776 * @param {String/Number} id The id or index of the TabPanelItem to hide.
35778 hideTab : function(id){
35779 var t = this.items[id];
35782 this.hiddenCount++;
35783 this.autoSizeTabs();
35788 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
35789 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
35791 unhideTab : function(id){
35792 var t = this.items[id];
35794 t.setHidden(false);
35795 this.hiddenCount--;
35796 this.autoSizeTabs();
35801 * Adds an existing {@link Roo.TabPanelItem}.
35802 * @param {Roo.TabPanelItem} item The TabPanelItem to add
35804 addTabItem : function(item){
35805 this.items[item.id] = item;
35806 this.items.push(item);
35807 // if(this.resizeTabs){
35808 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
35809 // this.autoSizeTabs();
35811 // item.autoSize();
35816 * Removes a {@link Roo.TabPanelItem}.
35817 * @param {String/Number} id The id or index of the TabPanelItem to remove.
35819 removeTab : function(id){
35820 var items = this.items;
35821 var tab = items[id];
35822 if(!tab) { return; }
35823 var index = items.indexOf(tab);
35824 if(this.active == tab && items.length > 1){
35825 var newTab = this.getNextAvailable(index);
35830 this.stripEl.dom.removeChild(tab.pnode.dom);
35831 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
35832 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
35834 items.splice(index, 1);
35835 delete this.items[tab.id];
35836 tab.fireEvent("close", tab);
35837 tab.purgeListeners();
35838 this.autoSizeTabs();
35841 getNextAvailable : function(start){
35842 var items = this.items;
35844 // look for a next tab that will slide over to
35845 // replace the one being removed
35846 while(index < items.length){
35847 var item = items[++index];
35848 if(item && !item.isHidden()){
35852 // if one isn't found select the previous tab (on the left)
35855 var item = items[--index];
35856 if(item && !item.isHidden()){
35864 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
35865 * @param {String/Number} id The id or index of the TabPanelItem to disable.
35867 disableTab : function(id){
35868 var tab = this.items[id];
35869 if(tab && this.active != tab){
35875 * Enables a {@link Roo.TabPanelItem} that is disabled.
35876 * @param {String/Number} id The id or index of the TabPanelItem to enable.
35878 enableTab : function(id){
35879 var tab = this.items[id];
35884 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
35885 * @param {String/Number} id The id or index of the TabPanelItem to activate.
35886 * @return {Roo.TabPanelItem} The TabPanelItem.
35888 activate : function(id){
35889 var tab = this.items[id];
35893 if(tab == this.active || tab.disabled){
35897 this.fireEvent("beforetabchange", this, e, tab);
35898 if(e.cancel !== true && !tab.disabled){
35900 this.active.hide();
35902 this.active = this.items[id];
35903 this.active.show();
35904 this.fireEvent("tabchange", this, this.active);
35910 * Gets the active {@link Roo.TabPanelItem}.
35911 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
35913 getActiveTab : function(){
35914 return this.active;
35918 * Updates the tab body element to fit the height of the container element
35919 * for overflow scrolling
35920 * @param {Number} targetHeight (optional) Override the starting height from the elements height
35922 syncHeight : function(targetHeight){
35923 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35924 var bm = this.bodyEl.getMargins();
35925 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35926 this.bodyEl.setHeight(newHeight);
35930 onResize : function(){
35931 if(this.monitorResize){
35932 this.autoSizeTabs();
35937 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35939 beginUpdate : function(){
35940 this.updating = true;
35944 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
35946 endUpdate : function(){
35947 this.updating = false;
35948 this.autoSizeTabs();
35952 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
35954 autoSizeTabs : function(){
35955 var count = this.items.length;
35956 var vcount = count - this.hiddenCount;
35957 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
35960 var w = Math.max(this.el.getWidth() - this.cpad, 10);
35961 var availWidth = Math.floor(w / vcount);
35962 var b = this.stripBody;
35963 if(b.getWidth() > w){
35964 var tabs = this.items;
35965 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
35966 if(availWidth < this.minTabWidth){
35967 /*if(!this.sleft){ // incomplete scrolling code
35968 this.createScrollButtons();
35971 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
35974 if(this.currentTabWidth < this.preferredTabWidth){
35975 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
35981 * Returns the number of tabs in this TabPanel.
35984 getCount : function(){
35985 return this.items.length;
35989 * Resizes all the tabs to the passed width
35990 * @param {Number} The new width
35992 setTabWidth : function(width){
35993 this.currentTabWidth = width;
35994 for(var i = 0, len = this.items.length; i < len; i++) {
35995 if(!this.items[i].isHidden()) {
35996 this.items[i].setWidth(width);
36002 * Destroys this TabPanel
36003 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
36005 destroy : function(removeEl){
36006 Roo.EventManager.removeResizeListener(this.onResize, this);
36007 for(var i = 0, len = this.items.length; i < len; i++){
36008 this.items[i].purgeListeners();
36010 if(removeEl === true){
36011 this.el.update("");
36016 createStrip : function(container)
36018 var strip = document.createElement("nav");
36019 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
36020 container.appendChild(strip);
36024 createStripList : function(strip)
36026 // div wrapper for retard IE
36027 // returns the "tr" element.
36028 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
36029 //'<div class="x-tabs-strip-wrap">'+
36030 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
36031 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
36032 return strip.firstChild; //.firstChild.firstChild.firstChild;
36034 createBody : function(container)
36036 var body = document.createElement("div");
36037 Roo.id(body, "tab-body");
36038 //Roo.fly(body).addClass("x-tabs-body");
36039 Roo.fly(body).addClass("tab-content");
36040 container.appendChild(body);
36043 createItemBody :function(bodyEl, id){
36044 var body = Roo.getDom(id);
36046 body = document.createElement("div");
36049 //Roo.fly(body).addClass("x-tabs-item-body");
36050 Roo.fly(body).addClass("tab-pane");
36051 bodyEl.insertBefore(body, bodyEl.firstChild);
36055 createStripElements : function(stripEl, text, closable, tpl)
36057 var td = document.createElement("li"); // was td..
36060 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
36063 stripEl.appendChild(td);
36065 td.className = "x-tabs-closable";
36066 if(!this.closeTpl){
36067 this.closeTpl = new Roo.Template(
36068 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36069 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
36070 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
36073 var el = this.closeTpl.overwrite(td, {"text": text});
36074 var close = el.getElementsByTagName("div")[0];
36075 var inner = el.getElementsByTagName("em")[0];
36076 return {"el": el, "close": close, "inner": inner};
36079 // not sure what this is..
36080 // if(!this.tabTpl){
36081 //this.tabTpl = new Roo.Template(
36082 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36083 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
36085 // this.tabTpl = new Roo.Template(
36086 // '<a href="#">' +
36087 // '<span unselectable="on"' +
36088 // (this.disableTooltips ? '' : ' title="{text}"') +
36089 // ' >{text}</span></a>'
36095 var template = tpl || this.tabTpl || false;
36099 template = new Roo.Template(
36101 '<span unselectable="on"' +
36102 (this.disableTooltips ? '' : ' title="{text}"') +
36103 ' >{text}</span></a>'
36107 switch (typeof(template)) {
36111 template = new Roo.Template(template);
36117 var el = template.overwrite(td, {"text": text});
36119 var inner = el.getElementsByTagName("span")[0];
36121 return {"el": el, "inner": inner};
36129 * @class Roo.TabPanelItem
36130 * @extends Roo.util.Observable
36131 * Represents an individual item (tab plus body) in a TabPanel.
36132 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
36133 * @param {String} id The id of this TabPanelItem
36134 * @param {String} text The text for the tab of this TabPanelItem
36135 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
36137 Roo.bootstrap.panel.TabItem = function(config){
36139 * The {@link Roo.TabPanel} this TabPanelItem belongs to
36140 * @type Roo.TabPanel
36142 this.tabPanel = config.panel;
36144 * The id for this TabPanelItem
36147 this.id = config.id;
36149 this.disabled = false;
36151 this.text = config.text;
36153 this.loaded = false;
36154 this.closable = config.closable;
36157 * The body element for this TabPanelItem.
36158 * @type Roo.Element
36160 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
36161 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
36162 this.bodyEl.setStyle("display", "block");
36163 this.bodyEl.setStyle("zoom", "1");
36164 //this.hideAction();
36166 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
36168 this.el = Roo.get(els.el);
36169 this.inner = Roo.get(els.inner, true);
36170 this.textEl = Roo.get(this.el.dom.firstChild, true);
36171 this.pnode = Roo.get(els.el.parentNode, true);
36172 this.el.on("mousedown", this.onTabMouseDown, this);
36173 this.el.on("click", this.onTabClick, this);
36175 if(config.closable){
36176 var c = Roo.get(els.close, true);
36177 c.dom.title = this.closeText;
36178 c.addClassOnOver("close-over");
36179 c.on("click", this.closeClick, this);
36185 * Fires when this tab becomes the active tab.
36186 * @param {Roo.TabPanel} tabPanel The parent TabPanel
36187 * @param {Roo.TabPanelItem} this
36191 * @event beforeclose
36192 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
36193 * @param {Roo.TabPanelItem} this
36194 * @param {Object} e Set cancel to true on this object to cancel the close.
36196 "beforeclose": true,
36199 * Fires when this tab is closed.
36200 * @param {Roo.TabPanelItem} this
36204 * @event deactivate
36205 * Fires when this tab is no longer the active tab.
36206 * @param {Roo.TabPanel} tabPanel The parent TabPanel
36207 * @param {Roo.TabPanelItem} this
36209 "deactivate" : true
36211 this.hidden = false;
36213 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
36216 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
36218 purgeListeners : function(){
36219 Roo.util.Observable.prototype.purgeListeners.call(this);
36220 this.el.removeAllListeners();
36223 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
36226 this.pnode.addClass("active");
36229 this.tabPanel.stripWrap.repaint();
36231 this.fireEvent("activate", this.tabPanel, this);
36235 * Returns true if this tab is the active tab.
36236 * @return {Boolean}
36238 isActive : function(){
36239 return this.tabPanel.getActiveTab() == this;
36243 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
36246 this.pnode.removeClass("active");
36248 this.fireEvent("deactivate", this.tabPanel, this);
36251 hideAction : function(){
36252 this.bodyEl.hide();
36253 this.bodyEl.setStyle("position", "absolute");
36254 this.bodyEl.setLeft("-20000px");
36255 this.bodyEl.setTop("-20000px");
36258 showAction : function(){
36259 this.bodyEl.setStyle("position", "relative");
36260 this.bodyEl.setTop("");
36261 this.bodyEl.setLeft("");
36262 this.bodyEl.show();
36266 * Set the tooltip for the tab.
36267 * @param {String} tooltip The tab's tooltip
36269 setTooltip : function(text){
36270 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
36271 this.textEl.dom.qtip = text;
36272 this.textEl.dom.removeAttribute('title');
36274 this.textEl.dom.title = text;
36278 onTabClick : function(e){
36279 e.preventDefault();
36280 this.tabPanel.activate(this.id);
36283 onTabMouseDown : function(e){
36284 e.preventDefault();
36285 this.tabPanel.activate(this.id);
36288 getWidth : function(){
36289 return this.inner.getWidth();
36292 setWidth : function(width){
36293 var iwidth = width - this.pnode.getPadding("lr");
36294 this.inner.setWidth(iwidth);
36295 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
36296 this.pnode.setWidth(width);
36300 * Show or hide the tab
36301 * @param {Boolean} hidden True to hide or false to show.
36303 setHidden : function(hidden){
36304 this.hidden = hidden;
36305 this.pnode.setStyle("display", hidden ? "none" : "");
36309 * Returns true if this tab is "hidden"
36310 * @return {Boolean}
36312 isHidden : function(){
36313 return this.hidden;
36317 * Returns the text for this tab
36320 getText : function(){
36324 autoSize : function(){
36325 //this.el.beginMeasure();
36326 this.textEl.setWidth(1);
36328 * #2804 [new] Tabs in Roojs
36329 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
36331 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
36332 //this.el.endMeasure();
36336 * Sets the text for the tab (Note: this also sets the tooltip text)
36337 * @param {String} text The tab's text and tooltip
36339 setText : function(text){
36341 this.textEl.update(text);
36342 this.setTooltip(text);
36343 //if(!this.tabPanel.resizeTabs){
36344 // this.autoSize();
36348 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
36350 activate : function(){
36351 this.tabPanel.activate(this.id);
36355 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
36357 disable : function(){
36358 if(this.tabPanel.active != this){
36359 this.disabled = true;
36360 this.pnode.addClass("disabled");
36365 * Enables this TabPanelItem if it was previously disabled.
36367 enable : function(){
36368 this.disabled = false;
36369 this.pnode.removeClass("disabled");
36373 * Sets the content for this TabPanelItem.
36374 * @param {String} content The content
36375 * @param {Boolean} loadScripts true to look for and load scripts
36377 setContent : function(content, loadScripts){
36378 this.bodyEl.update(content, loadScripts);
36382 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
36383 * @return {Roo.UpdateManager} The UpdateManager
36385 getUpdateManager : function(){
36386 return this.bodyEl.getUpdateManager();
36390 * Set a URL to be used to load the content for this TabPanelItem.
36391 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
36392 * @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)
36393 * @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)
36394 * @return {Roo.UpdateManager} The UpdateManager
36396 setUrl : function(url, params, loadOnce){
36397 if(this.refreshDelegate){
36398 this.un('activate', this.refreshDelegate);
36400 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36401 this.on("activate", this.refreshDelegate);
36402 return this.bodyEl.getUpdateManager();
36406 _handleRefresh : function(url, params, loadOnce){
36407 if(!loadOnce || !this.loaded){
36408 var updater = this.bodyEl.getUpdateManager();
36409 updater.update(url, params, this._setLoaded.createDelegate(this));
36414 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
36415 * Will fail silently if the setUrl method has not been called.
36416 * This does not activate the panel, just updates its content.
36418 refresh : function(){
36419 if(this.refreshDelegate){
36420 this.loaded = false;
36421 this.refreshDelegate();
36426 _setLoaded : function(){
36427 this.loaded = true;
36431 closeClick : function(e){
36434 this.fireEvent("beforeclose", this, o);
36435 if(o.cancel !== true){
36436 this.tabPanel.removeTab(this.id);
36440 * The text displayed in the tooltip for the close icon.
36443 closeText : "Close this tab"