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,
7521 * @cfg {Boolean} errPopover (true|false) default false
7525 getAutoCreate : function(){
7529 method : this.method || 'POST',
7530 id : this.id || Roo.id(),
7533 if (this.parent().xtype.match(/^Nav/)) {
7534 cfg.cls = 'navbar-form navbar-' + this.align;
7538 if (this.labelAlign == 'left' ) {
7539 cfg.cls += ' form-horizontal';
7545 initEvents : function()
7547 this.el.on('submit', this.onSubmit, this);
7548 // this was added as random key presses on the form where triggering form submit.
7549 this.el.on('keypress', function(e) {
7550 if (e.getCharCode() != 13) {
7553 // we might need to allow it for textareas.. and some other items.
7554 // check e.getTarget().
7556 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7560 Roo.log("keypress blocked");
7568 onSubmit : function(e){
7573 * Returns true if client-side validation on the form is successful.
7576 isValid : function(){
7577 var items = this.getItems();
7580 items.each(function(f){
7590 if(this.errPopover && !valid){
7591 this.showErrPopover(target);
7597 showErrPopover : function(target)
7601 if(!this.errPopover){
7605 target.inputEl().focus();
7607 var oIndex = target.el.getStyle('z-index');
7609 target.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
7611 target.el.addClass('roo-invalid-outline');
7613 var fadeout = function(){
7615 target.inputEl().un('blur', fadeout);
7616 target.inputEl().un('keyup', fadeout);
7618 target.el.setStyle('z-index', oIndex);
7620 target.el.removeClass('roo-invalid-outline');
7624 target.inputEl().on('blur', fadeout);
7625 target.inputEl().on('keyup', fadeout);
7634 * Returns true if any fields in this form have changed since their original load.
7637 isDirty : function(){
7639 var items = this.getItems();
7640 items.each(function(f){
7650 * Performs a predefined action (submit or load) or custom actions you define on this form.
7651 * @param {String} actionName The name of the action type
7652 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7653 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7654 * accept other config options):
7656 Property Type Description
7657 ---------------- --------------- ----------------------------------------------------------------------------------
7658 url String The url for the action (defaults to the form's url)
7659 method String The form method to use (defaults to the form's method, or POST if not defined)
7660 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7661 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7662 validate the form on the client (defaults to false)
7664 * @return {BasicForm} this
7666 doAction : function(action, options){
7667 if(typeof action == 'string'){
7668 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7670 if(this.fireEvent('beforeaction', this, action) !== false){
7671 this.beforeAction(action);
7672 action.run.defer(100, action);
7678 beforeAction : function(action){
7679 var o = action.options;
7682 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7684 // not really supported yet.. ??
7686 //if(this.waitMsgTarget === true){
7687 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7688 //}else if(this.waitMsgTarget){
7689 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7690 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7692 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7698 afterAction : function(action, success){
7699 this.activeAction = null;
7700 var o = action.options;
7702 //if(this.waitMsgTarget === true){
7704 //}else if(this.waitMsgTarget){
7705 // this.waitMsgTarget.unmask();
7707 // Roo.MessageBox.updateProgress(1);
7708 // Roo.MessageBox.hide();
7715 Roo.callback(o.success, o.scope, [this, action]);
7716 this.fireEvent('actioncomplete', this, action);
7720 // failure condition..
7721 // we have a scenario where updates need confirming.
7722 // eg. if a locking scenario exists..
7723 // we look for { errors : { needs_confirm : true }} in the response.
7725 (typeof(action.result) != 'undefined') &&
7726 (typeof(action.result.errors) != 'undefined') &&
7727 (typeof(action.result.errors.needs_confirm) != 'undefined')
7730 Roo.log("not supported yet");
7733 Roo.MessageBox.confirm(
7734 "Change requires confirmation",
7735 action.result.errorMsg,
7740 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7750 Roo.callback(o.failure, o.scope, [this, action]);
7751 // show an error message if no failed handler is set..
7752 if (!this.hasListener('actionfailed')) {
7753 Roo.log("need to add dialog support");
7755 Roo.MessageBox.alert("Error",
7756 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7757 action.result.errorMsg :
7758 "Saving Failed, please check your entries or try again"
7763 this.fireEvent('actionfailed', this, action);
7768 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7769 * @param {String} id The value to search for
7772 findField : function(id){
7773 var items = this.getItems();
7774 var field = items.get(id);
7776 items.each(function(f){
7777 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7784 return field || null;
7787 * Mark fields in this form invalid in bulk.
7788 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7789 * @return {BasicForm} this
7791 markInvalid : function(errors){
7792 if(errors instanceof Array){
7793 for(var i = 0, len = errors.length; i < len; i++){
7794 var fieldError = errors[i];
7795 var f = this.findField(fieldError.id);
7797 f.markInvalid(fieldError.msg);
7803 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7804 field.markInvalid(errors[id]);
7808 //Roo.each(this.childForms || [], function (f) {
7809 // f.markInvalid(errors);
7816 * Set values for fields in this form in bulk.
7817 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7818 * @return {BasicForm} this
7820 setValues : function(values){
7821 if(values instanceof Array){ // array of objects
7822 for(var i = 0, len = values.length; i < len; i++){
7824 var f = this.findField(v.id);
7826 f.setValue(v.value);
7827 if(this.trackResetOnLoad){
7828 f.originalValue = f.getValue();
7832 }else{ // object hash
7835 if(typeof values[id] != 'function' && (field = this.findField(id))){
7837 if (field.setFromData &&
7839 field.displayField &&
7840 // combos' with local stores can
7841 // be queried via setValue()
7842 // to set their value..
7843 (field.store && !field.store.isLocal)
7847 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7848 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7849 field.setFromData(sd);
7852 field.setValue(values[id]);
7856 if(this.trackResetOnLoad){
7857 field.originalValue = field.getValue();
7863 //Roo.each(this.childForms || [], function (f) {
7864 // f.setValues(values);
7871 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7872 * they are returned as an array.
7873 * @param {Boolean} asString
7876 getValues : function(asString){
7877 //if (this.childForms) {
7878 // copy values from the child forms
7879 // Roo.each(this.childForms, function (f) {
7880 // this.setValues(f.getValues());
7886 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7887 if(asString === true){
7890 return Roo.urlDecode(fs);
7894 * Returns the fields in this form as an object with key/value pairs.
7895 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7898 getFieldValues : function(with_hidden)
7900 var items = this.getItems();
7902 items.each(function(f){
7906 var v = f.getValue();
7907 if (f.inputType =='radio') {
7908 if (typeof(ret[f.getName()]) == 'undefined') {
7909 ret[f.getName()] = ''; // empty..
7912 if (!f.el.dom.checked) {
7920 // not sure if this supported any more..
7921 if ((typeof(v) == 'object') && f.getRawValue) {
7922 v = f.getRawValue() ; // dates..
7924 // combo boxes where name != hiddenName...
7925 if (f.name != f.getName()) {
7926 ret[f.name] = f.getRawValue();
7928 ret[f.getName()] = v;
7935 * Clears all invalid messages in this form.
7936 * @return {BasicForm} this
7938 clearInvalid : function(){
7939 var items = this.getItems();
7941 items.each(function(f){
7952 * @return {BasicForm} this
7955 var items = this.getItems();
7956 items.each(function(f){
7960 Roo.each(this.childForms || [], function (f) {
7967 getItems : function()
7969 var r=new Roo.util.MixedCollection(false, function(o){
7970 return o.id || (o.id = Roo.id());
7972 var iter = function(el) {
7979 Roo.each(el.items,function(e) {
7997 * Ext JS Library 1.1.1
7998 * Copyright(c) 2006-2007, Ext JS, LLC.
8000 * Originally Released Under LGPL - original licence link has changed is not relivant.
8003 * <script type="text/javascript">
8006 * @class Roo.form.VTypes
8007 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8010 Roo.form.VTypes = function(){
8011 // closure these in so they are only created once.
8012 var alpha = /^[a-zA-Z_]+$/;
8013 var alphanum = /^[a-zA-Z0-9_]+$/;
8014 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8015 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8017 // All these messages and functions are configurable
8020 * The function used to validate email addresses
8021 * @param {String} value The email address
8023 'email' : function(v){
8024 return email.test(v);
8027 * The error text to display when the email validation function returns false
8030 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8032 * The keystroke filter mask to be applied on email input
8035 'emailMask' : /[a-z0-9_\.\-@]/i,
8038 * The function used to validate URLs
8039 * @param {String} value The URL
8041 'url' : function(v){
8045 * The error text to display when the url validation function returns false
8048 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8051 * The function used to validate alpha values
8052 * @param {String} value The value
8054 'alpha' : function(v){
8055 return alpha.test(v);
8058 * The error text to display when the alpha validation function returns false
8061 'alphaText' : 'This field should only contain letters and _',
8063 * The keystroke filter mask to be applied on alpha input
8066 'alphaMask' : /[a-z_]/i,
8069 * The function used to validate alphanumeric values
8070 * @param {String} value The value
8072 'alphanum' : function(v){
8073 return alphanum.test(v);
8076 * The error text to display when the alphanumeric validation function returns false
8079 'alphanumText' : 'This field should only contain letters, numbers and _',
8081 * The keystroke filter mask to be applied on alphanumeric input
8084 'alphanumMask' : /[a-z0-9_]/i
8094 * @class Roo.bootstrap.Input
8095 * @extends Roo.bootstrap.Component
8096 * Bootstrap Input class
8097 * @cfg {Boolean} disabled is it disabled
8098 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8099 * @cfg {String} name name of the input
8100 * @cfg {string} fieldLabel - the label associated
8101 * @cfg {string} placeholder - placeholder to put in text.
8102 * @cfg {string} before - input group add on before
8103 * @cfg {string} after - input group add on after
8104 * @cfg {string} size - (lg|sm) or leave empty..
8105 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8106 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8107 * @cfg {Number} md colspan out of 12 for computer-sized screens
8108 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8109 * @cfg {string} value default value of the input
8110 * @cfg {Number} labelWidth set the width of label (0-12)
8111 * @cfg {String} labelAlign (top|left)
8112 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8113 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8114 * @cfg {String} indicatorpos (left|right) default left
8116 * @cfg {String} align (left|center|right) Default left
8117 * @cfg {Boolean} forceFeedback (true|false) Default false
8123 * Create a new Input
8124 * @param {Object} config The config object
8127 Roo.bootstrap.Input = function(config){
8128 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8133 * Fires when this field receives input focus.
8134 * @param {Roo.form.Field} this
8139 * Fires when this field loses input focus.
8140 * @param {Roo.form.Field} this
8145 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8146 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8147 * @param {Roo.form.Field} this
8148 * @param {Roo.EventObject} e The event object
8153 * Fires just before the field blurs if the field value has changed.
8154 * @param {Roo.form.Field} this
8155 * @param {Mixed} newValue The new value
8156 * @param {Mixed} oldValue The original value
8161 * Fires after the field has been marked as invalid.
8162 * @param {Roo.form.Field} this
8163 * @param {String} msg The validation message
8168 * Fires after the field has been validated with no errors.
8169 * @param {Roo.form.Field} this
8174 * Fires after the key up
8175 * @param {Roo.form.Field} this
8176 * @param {Roo.EventObject} e The event Object
8182 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8184 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8185 automatic validation (defaults to "keyup").
8187 validationEvent : "keyup",
8189 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8191 validateOnBlur : true,
8193 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8195 validationDelay : 250,
8197 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8199 focusClass : "x-form-focus", // not needed???
8203 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8205 invalidClass : "has-warning",
8208 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8210 validClass : "has-success",
8213 * @cfg {Boolean} hasFeedback (true|false) default true
8218 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8220 invalidFeedbackClass : "glyphicon-warning-sign",
8223 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8225 validFeedbackClass : "glyphicon-ok",
8228 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8230 selectOnFocus : false,
8233 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8237 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8242 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8244 disableKeyFilter : false,
8247 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8251 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8255 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8257 blankText : "This field is required",
8260 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8264 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8266 maxLength : Number.MAX_VALUE,
8268 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8270 minLengthText : "The minimum length for this field is {0}",
8272 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8274 maxLengthText : "The maximum length for this field is {0}",
8278 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8279 * If available, this function will be called only after the basic validators all return true, and will be passed the
8280 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8284 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8285 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8286 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8290 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8294 autocomplete: false,
8313 formatedValue : false,
8314 forceFeedback : false,
8316 indicatorpos : 'left',
8318 parentLabelAlign : function()
8321 while (parent.parent()) {
8322 parent = parent.parent();
8323 if (typeof(parent.labelAlign) !='undefined') {
8324 return parent.labelAlign;
8331 getAutoCreate : function()
8333 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8339 if(this.inputType != 'hidden'){
8340 cfg.cls = 'form-group' //input-group
8346 type : this.inputType,
8348 cls : 'form-control',
8349 placeholder : this.placeholder || '',
8350 autocomplete : this.autocomplete || 'new-password'
8354 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8357 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8358 input.maxLength = this.maxLength;
8361 if (this.disabled) {
8362 input.disabled=true;
8365 if (this.readOnly) {
8366 input.readonly=true;
8370 input.name = this.name;
8374 input.cls += ' input-' + this.size;
8378 ['xs','sm','md','lg'].map(function(size){
8379 if (settings[size]) {
8380 cfg.cls += ' col-' + size + '-' + settings[size];
8384 var inputblock = input;
8388 cls: 'glyphicon form-control-feedback'
8391 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8394 cls : 'has-feedback',
8402 if (this.before || this.after) {
8405 cls : 'input-group',
8409 if (this.before && typeof(this.before) == 'string') {
8411 inputblock.cn.push({
8413 cls : 'roo-input-before input-group-addon',
8417 if (this.before && typeof(this.before) == 'object') {
8418 this.before = Roo.factory(this.before);
8420 inputblock.cn.push({
8422 cls : 'roo-input-before input-group-' +
8423 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8427 inputblock.cn.push(input);
8429 if (this.after && typeof(this.after) == 'string') {
8430 inputblock.cn.push({
8432 cls : 'roo-input-after input-group-addon',
8436 if (this.after && typeof(this.after) == 'object') {
8437 this.after = Roo.factory(this.after);
8439 inputblock.cn.push({
8441 cls : 'roo-input-after input-group-' +
8442 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8446 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8447 inputblock.cls += ' has-feedback';
8448 inputblock.cn.push(feedback);
8452 if (align ==='left' && this.fieldLabel.length) {
8457 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8458 tooltip : 'This field is required'
8463 cls : 'control-label col-sm-' + this.labelWidth,
8464 html : this.fieldLabel
8468 cls : "col-sm-" + (12 - this.labelWidth),
8476 if(this.indicatorpos == 'right'){
8481 cls : 'control-label col-sm-' + this.labelWidth,
8482 html : this.fieldLabel
8487 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8488 tooltip : 'This field is required'
8491 cls : "col-sm-" + (12 - this.labelWidth),
8500 } else if ( this.fieldLabel.length) {
8505 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8506 tooltip : 'This field is required'
8510 //cls : 'input-group-addon',
8511 html : this.fieldLabel
8519 if(this.indicatorpos == 'right'){
8524 //cls : 'input-group-addon',
8525 html : this.fieldLabel
8530 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8531 tooltip : 'This field is required'
8551 if (this.parentType === 'Navbar' && this.parent().bar) {
8552 cfg.cls += ' navbar-form';
8555 if (this.parentType === 'NavGroup') {
8556 cfg.cls += ' navbar-form';
8564 * return the real input element.
8566 inputEl: function ()
8568 return this.el.select('input.form-control',true).first();
8571 tooltipEl : function()
8573 return this.inputEl();
8576 indicatorEl : function()
8578 var indicator = this.el.select('i.roo-required-indicator',true).first();
8588 setDisabled : function(v)
8590 var i = this.inputEl().dom;
8592 i.removeAttribute('disabled');
8596 i.setAttribute('disabled','true');
8598 initEvents : function()
8601 this.inputEl().on("keydown" , this.fireKey, this);
8602 this.inputEl().on("focus", this.onFocus, this);
8603 this.inputEl().on("blur", this.onBlur, this);
8605 this.inputEl().relayEvent('keyup', this);
8607 this.indicator = this.indicatorEl();
8610 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8611 this.indicator.hide();
8614 // reference to original value for reset
8615 this.originalValue = this.getValue();
8616 //Roo.form.TextField.superclass.initEvents.call(this);
8617 if(this.validationEvent == 'keyup'){
8618 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8619 this.inputEl().on('keyup', this.filterValidation, this);
8621 else if(this.validationEvent !== false){
8622 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8625 if(this.selectOnFocus){
8626 this.on("focus", this.preFocus, this);
8629 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8630 this.inputEl().on("keypress", this.filterKeys, this);
8632 this.inputEl().relayEvent('keypress', this);
8635 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8636 this.el.on("click", this.autoSize, this);
8639 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8640 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8643 if (typeof(this.before) == 'object') {
8644 this.before.render(this.el.select('.roo-input-before',true).first());
8646 if (typeof(this.after) == 'object') {
8647 this.after.render(this.el.select('.roo-input-after',true).first());
8652 filterValidation : function(e){
8653 if(!e.isNavKeyPress()){
8654 this.validationTask.delay(this.validationDelay);
8658 * Validates the field value
8659 * @return {Boolean} True if the value is valid, else false
8661 validate : function(){
8662 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8663 if(this.disabled || this.validateValue(this.getRawValue())){
8674 * Validates a value according to the field's validation rules and marks the field as invalid
8675 * if the validation fails
8676 * @param {Mixed} value The value to validate
8677 * @return {Boolean} True if the value is valid, else false
8679 validateValue : function(value){
8680 if(value.length < 1) { // if it's blank
8681 if(this.allowBlank){
8687 if(value.length < this.minLength){
8690 if(value.length > this.maxLength){
8694 var vt = Roo.form.VTypes;
8695 if(!vt[this.vtype](value, this)){
8699 if(typeof this.validator == "function"){
8700 var msg = this.validator(value);
8706 if(this.regex && !this.regex.test(value)){
8716 fireKey : function(e){
8717 //Roo.log('field ' + e.getKey());
8718 if(e.isNavKeyPress()){
8719 this.fireEvent("specialkey", this, e);
8722 focus : function (selectText){
8724 this.inputEl().focus();
8725 if(selectText === true){
8726 this.inputEl().dom.select();
8732 onFocus : function(){
8733 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8734 // this.el.addClass(this.focusClass);
8737 this.hasFocus = true;
8738 this.startValue = this.getValue();
8739 this.fireEvent("focus", this);
8743 beforeBlur : Roo.emptyFn,
8747 onBlur : function(){
8749 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8750 //this.el.removeClass(this.focusClass);
8752 this.hasFocus = false;
8753 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8756 var v = this.getValue();
8757 if(String(v) !== String(this.startValue)){
8758 this.fireEvent('change', this, v, this.startValue);
8760 this.fireEvent("blur", this);
8764 * Resets the current field value to the originally loaded value and clears any validation messages
8767 this.setValue(this.originalValue);
8771 * Returns the name of the field
8772 * @return {Mixed} name The name field
8774 getName: function(){
8778 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8779 * @return {Mixed} value The field value
8781 getValue : function(){
8783 var v = this.inputEl().getValue();
8788 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8789 * @return {Mixed} value The field value
8791 getRawValue : function(){
8792 var v = this.inputEl().getValue();
8798 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8799 * @param {Mixed} value The value to set
8801 setRawValue : function(v){
8802 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8805 selectText : function(start, end){
8806 var v = this.getRawValue();
8808 start = start === undefined ? 0 : start;
8809 end = end === undefined ? v.length : end;
8810 var d = this.inputEl().dom;
8811 if(d.setSelectionRange){
8812 d.setSelectionRange(start, end);
8813 }else if(d.createTextRange){
8814 var range = d.createTextRange();
8815 range.moveStart("character", start);
8816 range.moveEnd("character", v.length-end);
8823 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8824 * @param {Mixed} value The value to set
8826 setValue : function(v){
8829 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8835 processValue : function(value){
8836 if(this.stripCharsRe){
8837 var newValue = value.replace(this.stripCharsRe, '');
8838 if(newValue !== value){
8839 this.setRawValue(newValue);
8846 preFocus : function(){
8848 if(this.selectOnFocus){
8849 this.inputEl().dom.select();
8852 filterKeys : function(e){
8854 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8857 var c = e.getCharCode(), cc = String.fromCharCode(c);
8858 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8861 if(!this.maskRe.test(cc)){
8866 * Clear any invalid styles/messages for this field
8868 clearInvalid : function(){
8870 if(!this.el || this.preventMark){ // not rendered
8875 this.indicator.hide();
8878 this.el.removeClass(this.invalidClass);
8880 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8882 var feedback = this.el.select('.form-control-feedback', true).first();
8885 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8890 this.fireEvent('valid', this);
8894 * Mark this field as valid
8896 markValid : function()
8898 if(!this.el || this.preventMark){ // not rendered
8902 this.el.removeClass([this.invalidClass, this.validClass]);
8904 var feedback = this.el.select('.form-control-feedback', true).first();
8907 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8914 if(this.allowBlank && !this.getRawValue().length){
8919 this.indicator.hide();
8922 this.el.addClass(this.validClass);
8924 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8926 var feedback = this.el.select('.form-control-feedback', true).first();
8929 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8930 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8935 this.fireEvent('valid', this);
8939 * Mark this field as invalid
8940 * @param {String} msg The validation message
8942 markInvalid : function(msg)
8944 if(!this.el || this.preventMark){ // not rendered
8948 this.el.removeClass([this.invalidClass, this.validClass]);
8950 var feedback = this.el.select('.form-control-feedback', true).first();
8953 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8960 if(this.allowBlank && !this.getRawValue().length){
8965 this.indicator.show();
8968 this.el.addClass(this.invalidClass);
8970 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8972 var feedback = this.el.select('.form-control-feedback', true).first();
8975 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8977 if(this.getValue().length || this.forceFeedback){
8978 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8985 this.fireEvent('invalid', this, msg);
8988 SafariOnKeyDown : function(event)
8990 // this is a workaround for a password hang bug on chrome/ webkit.
8991 if (this.inputEl().dom.type != 'password') {
8995 var isSelectAll = false;
8997 if(this.inputEl().dom.selectionEnd > 0){
8998 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9000 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9001 event.preventDefault();
9006 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9008 event.preventDefault();
9009 // this is very hacky as keydown always get's upper case.
9011 var cc = String.fromCharCode(event.getCharCode());
9012 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9016 adjustWidth : function(tag, w){
9017 tag = tag.toLowerCase();
9018 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9019 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9023 if(tag == 'textarea'){
9026 }else if(Roo.isOpera){
9030 if(tag == 'textarea'){
9049 * @class Roo.bootstrap.TextArea
9050 * @extends Roo.bootstrap.Input
9051 * Bootstrap TextArea class
9052 * @cfg {Number} cols Specifies the visible width of a text area
9053 * @cfg {Number} rows Specifies the visible number of lines in a text area
9054 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9055 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9056 * @cfg {string} html text
9059 * Create a new TextArea
9060 * @param {Object} config The config object
9063 Roo.bootstrap.TextArea = function(config){
9064 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9068 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9078 getAutoCreate : function(){
9080 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9091 value : this.value || '',
9092 html: this.html || '',
9093 cls : 'form-control',
9094 placeholder : this.placeholder || ''
9098 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9099 input.maxLength = this.maxLength;
9103 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9107 input.cols = this.cols;
9110 if (this.readOnly) {
9111 input.readonly = true;
9115 input.name = this.name;
9119 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9123 ['xs','sm','md','lg'].map(function(size){
9124 if (settings[size]) {
9125 cfg.cls += ' col-' + size + '-' + settings[size];
9129 var inputblock = input;
9131 if(this.hasFeedback && !this.allowBlank){
9135 cls: 'glyphicon form-control-feedback'
9139 cls : 'has-feedback',
9148 if (this.before || this.after) {
9151 cls : 'input-group',
9155 inputblock.cn.push({
9157 cls : 'input-group-addon',
9162 inputblock.cn.push(input);
9164 if(this.hasFeedback && !this.allowBlank){
9165 inputblock.cls += ' has-feedback';
9166 inputblock.cn.push(feedback);
9170 inputblock.cn.push({
9172 cls : 'input-group-addon',
9179 if (align ==='left' && this.fieldLabel.length) {
9180 // Roo.log("left and has label");
9186 cls : 'control-label col-sm-' + this.labelWidth,
9187 html : this.fieldLabel
9191 cls : "col-sm-" + (12 - this.labelWidth),
9198 } else if ( this.fieldLabel.length) {
9199 // Roo.log(" label");
9204 //cls : 'input-group-addon',
9205 html : this.fieldLabel
9215 // Roo.log(" no label && no align");
9225 if (this.disabled) {
9226 input.disabled=true;
9233 * return the real textarea element.
9235 inputEl: function ()
9237 return this.el.select('textarea.form-control',true).first();
9241 * Clear any invalid styles/messages for this field
9243 clearInvalid : function()
9246 if(!this.el || this.preventMark){ // not rendered
9250 var label = this.el.select('label', true).first();
9251 var icon = this.el.select('i.fa-star', true).first();
9257 this.el.removeClass(this.invalidClass);
9259 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9261 var feedback = this.el.select('.form-control-feedback', true).first();
9264 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9269 this.fireEvent('valid', this);
9273 * Mark this field as valid
9275 markValid : function()
9277 if(!this.el || this.preventMark){ // not rendered
9281 this.el.removeClass([this.invalidClass, this.validClass]);
9283 var feedback = this.el.select('.form-control-feedback', true).first();
9286 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9289 if(this.disabled || this.allowBlank){
9293 var label = this.el.select('label', true).first();
9294 var icon = this.el.select('i.fa-star', true).first();
9300 this.el.addClass(this.validClass);
9302 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9304 var feedback = this.el.select('.form-control-feedback', true).first();
9307 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9308 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9313 this.fireEvent('valid', this);
9317 * Mark this field as invalid
9318 * @param {String} msg The validation message
9320 markInvalid : function(msg)
9322 if(!this.el || this.preventMark){ // not rendered
9326 this.el.removeClass([this.invalidClass, this.validClass]);
9328 var feedback = this.el.select('.form-control-feedback', true).first();
9331 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9334 if(this.disabled || this.allowBlank){
9338 var label = this.el.select('label', true).first();
9339 var icon = this.el.select('i.fa-star', true).first();
9341 if(!this.getValue().length && label && !icon){
9342 this.el.createChild({
9344 cls : 'text-danger fa fa-lg fa-star',
9345 tooltip : 'This field is required',
9346 style : 'margin-right:5px;'
9350 this.el.addClass(this.invalidClass);
9352 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9354 var feedback = this.el.select('.form-control-feedback', true).first();
9357 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9359 if(this.getValue().length || this.forceFeedback){
9360 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9367 this.fireEvent('invalid', this, msg);
9375 * trigger field - base class for combo..
9380 * @class Roo.bootstrap.TriggerField
9381 * @extends Roo.bootstrap.Input
9382 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9383 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9384 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9385 * for which you can provide a custom implementation. For example:
9387 var trigger = new Roo.bootstrap.TriggerField();
9388 trigger.onTriggerClick = myTriggerFn;
9389 trigger.applyTo('my-field');
9392 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9393 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9394 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9395 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9396 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9399 * Create a new TriggerField.
9400 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9401 * to the base TextField)
9403 Roo.bootstrap.TriggerField = function(config){
9404 this.mimicing = false;
9405 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9408 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9410 * @cfg {String} triggerClass A CSS class to apply to the trigger
9413 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9418 * @cfg {Boolean} removable (true|false) special filter default false
9422 /** @cfg {Boolean} grow @hide */
9423 /** @cfg {Number} growMin @hide */
9424 /** @cfg {Number} growMax @hide */
9430 autoSize: Roo.emptyFn,
9437 actionMode : 'wrap',
9442 getAutoCreate : function(){
9444 var align = this.labelAlign || this.parentLabelAlign();
9449 cls: 'form-group' //input-group
9456 type : this.inputType,
9457 cls : 'form-control',
9458 autocomplete: 'new-password',
9459 placeholder : this.placeholder || ''
9463 input.name = this.name;
9466 input.cls += ' input-' + this.size;
9469 if (this.disabled) {
9470 input.disabled=true;
9473 var inputblock = input;
9475 if(this.hasFeedback && !this.allowBlank){
9479 cls: 'glyphicon form-control-feedback'
9482 if(this.removable && !this.editable && !this.tickable){
9484 cls : 'has-feedback',
9490 cls : 'roo-combo-removable-btn close'
9497 cls : 'has-feedback',
9506 if(this.removable && !this.editable && !this.tickable){
9508 cls : 'roo-removable',
9514 cls : 'roo-combo-removable-btn close'
9521 if (this.before || this.after) {
9524 cls : 'input-group',
9528 inputblock.cn.push({
9530 cls : 'input-group-addon',
9535 inputblock.cn.push(input);
9537 if(this.hasFeedback && !this.allowBlank){
9538 inputblock.cls += ' has-feedback';
9539 inputblock.cn.push(feedback);
9543 inputblock.cn.push({
9545 cls : 'input-group-addon',
9558 cls: 'form-hidden-field'
9572 cls: 'form-hidden-field'
9576 cls: 'roo-select2-choices',
9580 cls: 'roo-select2-search-field',
9593 cls: 'roo-select2-container input-group',
9598 // cls: 'typeahead typeahead-long dropdown-menu',
9599 // style: 'display:none'
9604 if(!this.multiple && this.showToggleBtn){
9610 if (this.caret != false) {
9613 cls: 'fa fa-' + this.caret
9620 cls : 'input-group-addon btn dropdown-toggle',
9625 cls: 'combobox-clear',
9639 combobox.cls += ' roo-select2-container-multi';
9642 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9644 // Roo.log("left and has label");
9648 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9649 tooltip : 'This field is required'
9654 cls : 'control-label col-sm-' + this.labelWidth,
9655 html : this.fieldLabel
9659 cls : "col-sm-" + (12 - this.labelWidth),
9667 if(this.indicatorpos == 'right'){
9672 cls : 'control-label col-sm-' + this.labelWidth,
9673 html : this.fieldLabel
9678 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9679 tooltip : 'This field is required'
9682 cls : "col-sm-" + (12 - this.labelWidth),
9691 } else if ( this.fieldLabel.length) {
9692 // Roo.log(" label");
9696 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9697 tooltip : 'This field is required'
9701 //cls : 'input-group-addon',
9702 html : this.fieldLabel
9710 if(this.indicatorpos == 'right'){
9715 //cls : 'input-group-addon',
9716 html : this.fieldLabel
9721 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9722 tooltip : 'This field is required'
9733 // Roo.log(" no label && no align");
9740 ['xs','sm','md','lg'].map(function(size){
9741 if (settings[size]) {
9742 cfg.cls += ' col-' + size + '-' + settings[size];
9753 onResize : function(w, h){
9754 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9755 // if(typeof w == 'number'){
9756 // var x = w - this.trigger.getWidth();
9757 // this.inputEl().setWidth(this.adjustWidth('input', x));
9758 // this.trigger.setStyle('left', x+'px');
9763 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9766 getResizeEl : function(){
9767 return this.inputEl();
9771 getPositionEl : function(){
9772 return this.inputEl();
9776 alignErrorIcon : function(){
9777 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9781 initEvents : function(){
9785 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9786 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9787 if(!this.multiple && this.showToggleBtn){
9788 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9789 if(this.hideTrigger){
9790 this.trigger.setDisplayed(false);
9792 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9796 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9799 if(this.removable && !this.editable && !this.tickable){
9800 var close = this.closeTriggerEl();
9803 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9804 close.on('click', this.removeBtnClick, this, close);
9808 //this.trigger.addClassOnOver('x-form-trigger-over');
9809 //this.trigger.addClassOnClick('x-form-trigger-click');
9812 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9816 closeTriggerEl : function()
9818 var close = this.el.select('.roo-combo-removable-btn', true).first();
9819 return close ? close : false;
9822 removeBtnClick : function(e, h, el)
9826 if(this.fireEvent("remove", this) !== false){
9828 this.fireEvent("afterremove", this)
9832 createList : function()
9834 this.list = Roo.get(document.body).createChild({
9836 cls: 'typeahead typeahead-long dropdown-menu',
9837 style: 'display:none'
9840 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9845 initTrigger : function(){
9850 onDestroy : function(){
9852 this.trigger.removeAllListeners();
9853 // this.trigger.remove();
9856 // this.wrap.remove();
9858 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9862 onFocus : function(){
9863 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9866 this.wrap.addClass('x-trigger-wrap-focus');
9867 this.mimicing = true;
9868 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9869 if(this.monitorTab){
9870 this.el.on("keydown", this.checkTab, this);
9877 checkTab : function(e){
9878 if(e.getKey() == e.TAB){
9884 onBlur : function(){
9889 mimicBlur : function(e, t){
9891 if(!this.wrap.contains(t) && this.validateBlur()){
9898 triggerBlur : function(){
9899 this.mimicing = false;
9900 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9901 if(this.monitorTab){
9902 this.el.un("keydown", this.checkTab, this);
9904 //this.wrap.removeClass('x-trigger-wrap-focus');
9905 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9909 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9910 validateBlur : function(e, t){
9915 onDisable : function(){
9916 this.inputEl().dom.disabled = true;
9917 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9919 // this.wrap.addClass('x-item-disabled');
9924 onEnable : function(){
9925 this.inputEl().dom.disabled = false;
9926 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9928 // this.el.removeClass('x-item-disabled');
9933 onShow : function(){
9934 var ae = this.getActionEl();
9937 ae.dom.style.display = '';
9938 ae.dom.style.visibility = 'visible';
9944 onHide : function(){
9945 var ae = this.getActionEl();
9946 ae.dom.style.display = 'none';
9950 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9951 * by an implementing function.
9953 * @param {EventObject} e
9955 onTriggerClick : Roo.emptyFn
9959 * Ext JS Library 1.1.1
9960 * Copyright(c) 2006-2007, Ext JS, LLC.
9962 * Originally Released Under LGPL - original licence link has changed is not relivant.
9965 * <script type="text/javascript">
9970 * @class Roo.data.SortTypes
9972 * Defines the default sorting (casting?) comparison functions used when sorting data.
9974 Roo.data.SortTypes = {
9976 * Default sort that does nothing
9977 * @param {Mixed} s The value being converted
9978 * @return {Mixed} The comparison value
9985 * The regular expression used to strip tags
9989 stripTagsRE : /<\/?[^>]+>/gi,
9992 * Strips all HTML tags to sort on text only
9993 * @param {Mixed} s The value being converted
9994 * @return {String} The comparison value
9996 asText : function(s){
9997 return String(s).replace(this.stripTagsRE, "");
10001 * Strips all HTML tags to sort on text only - Case insensitive
10002 * @param {Mixed} s The value being converted
10003 * @return {String} The comparison value
10005 asUCText : function(s){
10006 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10010 * Case insensitive string
10011 * @param {Mixed} s The value being converted
10012 * @return {String} The comparison value
10014 asUCString : function(s) {
10015 return String(s).toUpperCase();
10020 * @param {Mixed} s The value being converted
10021 * @return {Number} The comparison value
10023 asDate : function(s) {
10027 if(s instanceof Date){
10028 return s.getTime();
10030 return Date.parse(String(s));
10035 * @param {Mixed} s The value being converted
10036 * @return {Float} The comparison value
10038 asFloat : function(s) {
10039 var val = parseFloat(String(s).replace(/,/g, ""));
10048 * @param {Mixed} s The value being converted
10049 * @return {Number} The comparison value
10051 asInt : function(s) {
10052 var val = parseInt(String(s).replace(/,/g, ""));
10060 * Ext JS Library 1.1.1
10061 * Copyright(c) 2006-2007, Ext JS, LLC.
10063 * Originally Released Under LGPL - original licence link has changed is not relivant.
10066 * <script type="text/javascript">
10070 * @class Roo.data.Record
10071 * Instances of this class encapsulate both record <em>definition</em> information, and record
10072 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10073 * to access Records cached in an {@link Roo.data.Store} object.<br>
10075 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10076 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10079 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10081 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10082 * {@link #create}. The parameters are the same.
10083 * @param {Array} data An associative Array of data values keyed by the field name.
10084 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10085 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10086 * not specified an integer id is generated.
10088 Roo.data.Record = function(data, id){
10089 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10094 * Generate a constructor for a specific record layout.
10095 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10096 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10097 * Each field definition object may contain the following properties: <ul>
10098 * <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,
10099 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10100 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10101 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10102 * is being used, then this is a string containing the javascript expression to reference the data relative to
10103 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10104 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10105 * this may be omitted.</p></li>
10106 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10107 * <ul><li>auto (Default, implies no conversion)</li>
10112 * <li>date</li></ul></p></li>
10113 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10114 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10115 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10116 * by the Reader into an object that will be stored in the Record. It is passed the
10117 * following parameters:<ul>
10118 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10120 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10122 * <br>usage:<br><pre><code>
10123 var TopicRecord = Roo.data.Record.create(
10124 {name: 'title', mapping: 'topic_title'},
10125 {name: 'author', mapping: 'username'},
10126 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10127 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10128 {name: 'lastPoster', mapping: 'user2'},
10129 {name: 'excerpt', mapping: 'post_text'}
10132 var myNewRecord = new TopicRecord({
10133 title: 'Do my job please',
10136 lastPost: new Date(),
10137 lastPoster: 'Animal',
10138 excerpt: 'No way dude!'
10140 myStore.add(myNewRecord);
10145 Roo.data.Record.create = function(o){
10146 var f = function(){
10147 f.superclass.constructor.apply(this, arguments);
10149 Roo.extend(f, Roo.data.Record);
10150 var p = f.prototype;
10151 p.fields = new Roo.util.MixedCollection(false, function(field){
10154 for(var i = 0, len = o.length; i < len; i++){
10155 p.fields.add(new Roo.data.Field(o[i]));
10157 f.getField = function(name){
10158 return p.fields.get(name);
10163 Roo.data.Record.AUTO_ID = 1000;
10164 Roo.data.Record.EDIT = 'edit';
10165 Roo.data.Record.REJECT = 'reject';
10166 Roo.data.Record.COMMIT = 'commit';
10168 Roo.data.Record.prototype = {
10170 * Readonly flag - true if this record has been modified.
10179 join : function(store){
10180 this.store = store;
10184 * Set the named field to the specified value.
10185 * @param {String} name The name of the field to set.
10186 * @param {Object} value The value to set the field to.
10188 set : function(name, value){
10189 if(this.data[name] == value){
10193 if(!this.modified){
10194 this.modified = {};
10196 if(typeof this.modified[name] == 'undefined'){
10197 this.modified[name] = this.data[name];
10199 this.data[name] = value;
10200 if(!this.editing && this.store){
10201 this.store.afterEdit(this);
10206 * Get the value of the named field.
10207 * @param {String} name The name of the field to get the value of.
10208 * @return {Object} The value of the field.
10210 get : function(name){
10211 return this.data[name];
10215 beginEdit : function(){
10216 this.editing = true;
10217 this.modified = {};
10221 cancelEdit : function(){
10222 this.editing = false;
10223 delete this.modified;
10227 endEdit : function(){
10228 this.editing = false;
10229 if(this.dirty && this.store){
10230 this.store.afterEdit(this);
10235 * Usually called by the {@link Roo.data.Store} which owns the Record.
10236 * Rejects all changes made to the Record since either creation, or the last commit operation.
10237 * Modified fields are reverted to their original values.
10239 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10240 * of reject operations.
10242 reject : function(){
10243 var m = this.modified;
10245 if(typeof m[n] != "function"){
10246 this.data[n] = m[n];
10249 this.dirty = false;
10250 delete this.modified;
10251 this.editing = false;
10253 this.store.afterReject(this);
10258 * Usually called by the {@link Roo.data.Store} which owns the Record.
10259 * Commits all changes made to the Record since either creation, or the last commit operation.
10261 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10262 * of commit operations.
10264 commit : function(){
10265 this.dirty = false;
10266 delete this.modified;
10267 this.editing = false;
10269 this.store.afterCommit(this);
10274 hasError : function(){
10275 return this.error != null;
10279 clearError : function(){
10284 * Creates a copy of this record.
10285 * @param {String} id (optional) A new record id if you don't want to use this record's id
10288 copy : function(newId) {
10289 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10293 * Ext JS Library 1.1.1
10294 * Copyright(c) 2006-2007, Ext JS, LLC.
10296 * Originally Released Under LGPL - original licence link has changed is not relivant.
10299 * <script type="text/javascript">
10305 * @class Roo.data.Store
10306 * @extends Roo.util.Observable
10307 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10308 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10310 * 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
10311 * has no knowledge of the format of the data returned by the Proxy.<br>
10313 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10314 * instances from the data object. These records are cached and made available through accessor functions.
10316 * Creates a new Store.
10317 * @param {Object} config A config object containing the objects needed for the Store to access data,
10318 * and read the data into Records.
10320 Roo.data.Store = function(config){
10321 this.data = new Roo.util.MixedCollection(false);
10322 this.data.getKey = function(o){
10325 this.baseParams = {};
10327 this.paramNames = {
10332 "multisort" : "_multisort"
10335 if(config && config.data){
10336 this.inlineData = config.data;
10337 delete config.data;
10340 Roo.apply(this, config);
10342 if(this.reader){ // reader passed
10343 this.reader = Roo.factory(this.reader, Roo.data);
10344 this.reader.xmodule = this.xmodule || false;
10345 if(!this.recordType){
10346 this.recordType = this.reader.recordType;
10348 if(this.reader.onMetaChange){
10349 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10353 if(this.recordType){
10354 this.fields = this.recordType.prototype.fields;
10356 this.modified = [];
10360 * @event datachanged
10361 * Fires when the data cache has changed, and a widget which is using this Store
10362 * as a Record cache should refresh its view.
10363 * @param {Store} this
10365 datachanged : true,
10367 * @event metachange
10368 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10369 * @param {Store} this
10370 * @param {Object} meta The JSON metadata
10375 * Fires when Records have been added to the Store
10376 * @param {Store} this
10377 * @param {Roo.data.Record[]} records The array of Records added
10378 * @param {Number} index The index at which the record(s) were added
10383 * Fires when a Record has been removed from the Store
10384 * @param {Store} this
10385 * @param {Roo.data.Record} record The Record that was removed
10386 * @param {Number} index The index at which the record was removed
10391 * Fires when a Record has been updated
10392 * @param {Store} this
10393 * @param {Roo.data.Record} record The Record that was updated
10394 * @param {String} operation The update operation being performed. Value may be one of:
10396 Roo.data.Record.EDIT
10397 Roo.data.Record.REJECT
10398 Roo.data.Record.COMMIT
10404 * Fires when the data cache has been cleared.
10405 * @param {Store} this
10409 * @event beforeload
10410 * Fires before a request is made for a new data object. If the beforeload handler returns false
10411 * the load action will be canceled.
10412 * @param {Store} this
10413 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10417 * @event beforeloadadd
10418 * Fires after a new set of Records has been loaded.
10419 * @param {Store} this
10420 * @param {Roo.data.Record[]} records The Records that were loaded
10421 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10423 beforeloadadd : true,
10426 * Fires after a new set of Records has been loaded, before they are added to the store.
10427 * @param {Store} this
10428 * @param {Roo.data.Record[]} records The Records that were loaded
10429 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10430 * @params {Object} return from reader
10434 * @event loadexception
10435 * Fires if an exception occurs in the Proxy during loading.
10436 * Called with the signature of the Proxy's "loadexception" event.
10437 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10440 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10441 * @param {Object} load options
10442 * @param {Object} jsonData from your request (normally this contains the Exception)
10444 loadexception : true
10448 this.proxy = Roo.factory(this.proxy, Roo.data);
10449 this.proxy.xmodule = this.xmodule || false;
10450 this.relayEvents(this.proxy, ["loadexception"]);
10452 this.sortToggle = {};
10453 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10455 Roo.data.Store.superclass.constructor.call(this);
10457 if(this.inlineData){
10458 this.loadData(this.inlineData);
10459 delete this.inlineData;
10463 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10465 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10466 * without a remote query - used by combo/forms at present.
10470 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10473 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10476 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10477 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10480 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10481 * on any HTTP request
10484 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10487 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10491 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10492 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10494 remoteSort : false,
10497 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10498 * loaded or when a record is removed. (defaults to false).
10500 pruneModifiedRecords : false,
10503 lastOptions : null,
10506 * Add Records to the Store and fires the add event.
10507 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10509 add : function(records){
10510 records = [].concat(records);
10511 for(var i = 0, len = records.length; i < len; i++){
10512 records[i].join(this);
10514 var index = this.data.length;
10515 this.data.addAll(records);
10516 this.fireEvent("add", this, records, index);
10520 * Remove a Record from the Store and fires the remove event.
10521 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10523 remove : function(record){
10524 var index = this.data.indexOf(record);
10525 this.data.removeAt(index);
10526 if(this.pruneModifiedRecords){
10527 this.modified.remove(record);
10529 this.fireEvent("remove", this, record, index);
10533 * Remove all Records from the Store and fires the clear event.
10535 removeAll : function(){
10537 if(this.pruneModifiedRecords){
10538 this.modified = [];
10540 this.fireEvent("clear", this);
10544 * Inserts Records to the Store at the given index and fires the add event.
10545 * @param {Number} index The start index at which to insert the passed Records.
10546 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10548 insert : function(index, records){
10549 records = [].concat(records);
10550 for(var i = 0, len = records.length; i < len; i++){
10551 this.data.insert(index, records[i]);
10552 records[i].join(this);
10554 this.fireEvent("add", this, records, index);
10558 * Get the index within the cache of the passed Record.
10559 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10560 * @return {Number} The index of the passed Record. Returns -1 if not found.
10562 indexOf : function(record){
10563 return this.data.indexOf(record);
10567 * Get the index within the cache of the Record with the passed id.
10568 * @param {String} id The id of the Record to find.
10569 * @return {Number} The index of the Record. Returns -1 if not found.
10571 indexOfId : function(id){
10572 return this.data.indexOfKey(id);
10576 * Get the Record with the specified id.
10577 * @param {String} id The id of the Record to find.
10578 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10580 getById : function(id){
10581 return this.data.key(id);
10585 * Get the Record at the specified index.
10586 * @param {Number} index The index of the Record to find.
10587 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10589 getAt : function(index){
10590 return this.data.itemAt(index);
10594 * Returns a range of Records between specified indices.
10595 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10596 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10597 * @return {Roo.data.Record[]} An array of Records
10599 getRange : function(start, end){
10600 return this.data.getRange(start, end);
10604 storeOptions : function(o){
10605 o = Roo.apply({}, o);
10608 this.lastOptions = o;
10612 * Loads the Record cache from the configured Proxy using the configured Reader.
10614 * If using remote paging, then the first load call must specify the <em>start</em>
10615 * and <em>limit</em> properties in the options.params property to establish the initial
10616 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10618 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10619 * and this call will return before the new data has been loaded. Perform any post-processing
10620 * in a callback function, or in a "load" event handler.</strong>
10622 * @param {Object} options An object containing properties which control loading options:<ul>
10623 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10624 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10625 * passed the following arguments:<ul>
10626 * <li>r : Roo.data.Record[]</li>
10627 * <li>options: Options object from the load call</li>
10628 * <li>success: Boolean success indicator</li></ul></li>
10629 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10630 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10633 load : function(options){
10634 options = options || {};
10635 if(this.fireEvent("beforeload", this, options) !== false){
10636 this.storeOptions(options);
10637 var p = Roo.apply(options.params || {}, this.baseParams);
10638 // if meta was not loaded from remote source.. try requesting it.
10639 if (!this.reader.metaFromRemote) {
10640 p._requestMeta = 1;
10642 if(this.sortInfo && this.remoteSort){
10643 var pn = this.paramNames;
10644 p[pn["sort"]] = this.sortInfo.field;
10645 p[pn["dir"]] = this.sortInfo.direction;
10647 if (this.multiSort) {
10648 var pn = this.paramNames;
10649 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10652 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10657 * Reloads the Record cache from the configured Proxy using the configured Reader and
10658 * the options from the last load operation performed.
10659 * @param {Object} options (optional) An object containing properties which may override the options
10660 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10661 * the most recently used options are reused).
10663 reload : function(options){
10664 this.load(Roo.applyIf(options||{}, this.lastOptions));
10668 // Called as a callback by the Reader during a load operation.
10669 loadRecords : function(o, options, success){
10670 if(!o || success === false){
10671 if(success !== false){
10672 this.fireEvent("load", this, [], options, o);
10674 if(options.callback){
10675 options.callback.call(options.scope || this, [], options, false);
10679 // if data returned failure - throw an exception.
10680 if (o.success === false) {
10681 // show a message if no listener is registered.
10682 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10683 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10685 // loadmask wil be hooked into this..
10686 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10689 var r = o.records, t = o.totalRecords || r.length;
10691 this.fireEvent("beforeloadadd", this, r, options, o);
10693 if(!options || options.add !== true){
10694 if(this.pruneModifiedRecords){
10695 this.modified = [];
10697 for(var i = 0, len = r.length; i < len; i++){
10701 this.data = this.snapshot;
10702 delete this.snapshot;
10705 this.data.addAll(r);
10706 this.totalLength = t;
10708 this.fireEvent("datachanged", this);
10710 this.totalLength = Math.max(t, this.data.length+r.length);
10713 this.fireEvent("load", this, r, options, o);
10714 if(options.callback){
10715 options.callback.call(options.scope || this, r, options, true);
10721 * Loads data from a passed data block. A Reader which understands the format of the data
10722 * must have been configured in the constructor.
10723 * @param {Object} data The data block from which to read the Records. The format of the data expected
10724 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10725 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10727 loadData : function(o, append){
10728 var r = this.reader.readRecords(o);
10729 this.loadRecords(r, {add: append}, true);
10733 * Gets the number of cached records.
10735 * <em>If using paging, this may not be the total size of the dataset. If the data object
10736 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10737 * the data set size</em>
10739 getCount : function(){
10740 return this.data.length || 0;
10744 * Gets the total number of records in the dataset as returned by the server.
10746 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10747 * the dataset size</em>
10749 getTotalCount : function(){
10750 return this.totalLength || 0;
10754 * Returns the sort state of the Store as an object with two properties:
10756 field {String} The name of the field by which the Records are sorted
10757 direction {String} The sort order, "ASC" or "DESC"
10760 getSortState : function(){
10761 return this.sortInfo;
10765 applySort : function(){
10766 if(this.sortInfo && !this.remoteSort){
10767 var s = this.sortInfo, f = s.field;
10768 var st = this.fields.get(f).sortType;
10769 var fn = function(r1, r2){
10770 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10771 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10773 this.data.sort(s.direction, fn);
10774 if(this.snapshot && this.snapshot != this.data){
10775 this.snapshot.sort(s.direction, fn);
10781 * Sets the default sort column and order to be used by the next load operation.
10782 * @param {String} fieldName The name of the field to sort by.
10783 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10785 setDefaultSort : function(field, dir){
10786 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10790 * Sort the Records.
10791 * If remote sorting is used, the sort is performed on the server, and the cache is
10792 * reloaded. If local sorting is used, the cache is sorted internally.
10793 * @param {String} fieldName The name of the field to sort by.
10794 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10796 sort : function(fieldName, dir){
10797 var f = this.fields.get(fieldName);
10799 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10801 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10802 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10807 this.sortToggle[f.name] = dir;
10808 this.sortInfo = {field: f.name, direction: dir};
10809 if(!this.remoteSort){
10811 this.fireEvent("datachanged", this);
10813 this.load(this.lastOptions);
10818 * Calls the specified function for each of the Records in the cache.
10819 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10820 * Returning <em>false</em> aborts and exits the iteration.
10821 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10823 each : function(fn, scope){
10824 this.data.each(fn, scope);
10828 * Gets all records modified since the last commit. Modified records are persisted across load operations
10829 * (e.g., during paging).
10830 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10832 getModifiedRecords : function(){
10833 return this.modified;
10837 createFilterFn : function(property, value, anyMatch){
10838 if(!value.exec){ // not a regex
10839 value = String(value);
10840 if(value.length == 0){
10843 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10845 return function(r){
10846 return value.test(r.data[property]);
10851 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10852 * @param {String} property A field on your records
10853 * @param {Number} start The record index to start at (defaults to 0)
10854 * @param {Number} end The last record index to include (defaults to length - 1)
10855 * @return {Number} The sum
10857 sum : function(property, start, end){
10858 var rs = this.data.items, v = 0;
10859 start = start || 0;
10860 end = (end || end === 0) ? end : rs.length-1;
10862 for(var i = start; i <= end; i++){
10863 v += (rs[i].data[property] || 0);
10869 * Filter the records by a specified property.
10870 * @param {String} field A field on your records
10871 * @param {String/RegExp} value Either a string that the field
10872 * should start with or a RegExp to test against the field
10873 * @param {Boolean} anyMatch True to match any part not just the beginning
10875 filter : function(property, value, anyMatch){
10876 var fn = this.createFilterFn(property, value, anyMatch);
10877 return fn ? this.filterBy(fn) : this.clearFilter();
10881 * Filter by a function. The specified function will be called with each
10882 * record in this data source. If the function returns true the record is included,
10883 * otherwise it is filtered.
10884 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10885 * @param {Object} scope (optional) The scope of the function (defaults to this)
10887 filterBy : function(fn, scope){
10888 this.snapshot = this.snapshot || this.data;
10889 this.data = this.queryBy(fn, scope||this);
10890 this.fireEvent("datachanged", this);
10894 * Query the records by a specified property.
10895 * @param {String} field A field on your records
10896 * @param {String/RegExp} value Either a string that the field
10897 * should start with or a RegExp to test against the field
10898 * @param {Boolean} anyMatch True to match any part not just the beginning
10899 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10901 query : function(property, value, anyMatch){
10902 var fn = this.createFilterFn(property, value, anyMatch);
10903 return fn ? this.queryBy(fn) : this.data.clone();
10907 * Query by a function. The specified function will be called with each
10908 * record in this data source. If the function returns true the record is included
10910 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10911 * @param {Object} scope (optional) The scope of the function (defaults to this)
10912 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10914 queryBy : function(fn, scope){
10915 var data = this.snapshot || this.data;
10916 return data.filterBy(fn, scope||this);
10920 * Collects unique values for a particular dataIndex from this store.
10921 * @param {String} dataIndex The property to collect
10922 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10923 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10924 * @return {Array} An array of the unique values
10926 collect : function(dataIndex, allowNull, bypassFilter){
10927 var d = (bypassFilter === true && this.snapshot) ?
10928 this.snapshot.items : this.data.items;
10929 var v, sv, r = [], l = {};
10930 for(var i = 0, len = d.length; i < len; i++){
10931 v = d[i].data[dataIndex];
10933 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10942 * Revert to a view of the Record cache with no filtering applied.
10943 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10945 clearFilter : function(suppressEvent){
10946 if(this.snapshot && this.snapshot != this.data){
10947 this.data = this.snapshot;
10948 delete this.snapshot;
10949 if(suppressEvent !== true){
10950 this.fireEvent("datachanged", this);
10956 afterEdit : function(record){
10957 if(this.modified.indexOf(record) == -1){
10958 this.modified.push(record);
10960 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10964 afterReject : function(record){
10965 this.modified.remove(record);
10966 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10970 afterCommit : function(record){
10971 this.modified.remove(record);
10972 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10976 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10977 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10979 commitChanges : function(){
10980 var m = this.modified.slice(0);
10981 this.modified = [];
10982 for(var i = 0, len = m.length; i < len; i++){
10988 * Cancel outstanding changes on all changed records.
10990 rejectChanges : function(){
10991 var m = this.modified.slice(0);
10992 this.modified = [];
10993 for(var i = 0, len = m.length; i < len; i++){
10998 onMetaChange : function(meta, rtype, o){
10999 this.recordType = rtype;
11000 this.fields = rtype.prototype.fields;
11001 delete this.snapshot;
11002 this.sortInfo = meta.sortInfo || this.sortInfo;
11003 this.modified = [];
11004 this.fireEvent('metachange', this, this.reader.meta);
11007 moveIndex : function(data, type)
11009 var index = this.indexOf(data);
11011 var newIndex = index + type;
11015 this.insert(newIndex, data);
11020 * Ext JS Library 1.1.1
11021 * Copyright(c) 2006-2007, Ext JS, LLC.
11023 * Originally Released Under LGPL - original licence link has changed is not relivant.
11026 * <script type="text/javascript">
11030 * @class Roo.data.SimpleStore
11031 * @extends Roo.data.Store
11032 * Small helper class to make creating Stores from Array data easier.
11033 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11034 * @cfg {Array} fields An array of field definition objects, or field name strings.
11035 * @cfg {Array} data The multi-dimensional array of data
11037 * @param {Object} config
11039 Roo.data.SimpleStore = function(config){
11040 Roo.data.SimpleStore.superclass.constructor.call(this, {
11042 reader: new Roo.data.ArrayReader({
11045 Roo.data.Record.create(config.fields)
11047 proxy : new Roo.data.MemoryProxy(config.data)
11051 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11053 * Ext JS Library 1.1.1
11054 * Copyright(c) 2006-2007, Ext JS, LLC.
11056 * Originally Released Under LGPL - original licence link has changed is not relivant.
11059 * <script type="text/javascript">
11064 * @extends Roo.data.Store
11065 * @class Roo.data.JsonStore
11066 * Small helper class to make creating Stores for JSON data easier. <br/>
11068 var store = new Roo.data.JsonStore({
11069 url: 'get-images.php',
11071 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11074 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11075 * JsonReader and HttpProxy (unless inline data is provided).</b>
11076 * @cfg {Array} fields An array of field definition objects, or field name strings.
11078 * @param {Object} config
11080 Roo.data.JsonStore = function(c){
11081 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11082 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11083 reader: new Roo.data.JsonReader(c, c.fields)
11086 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11088 * Ext JS Library 1.1.1
11089 * Copyright(c) 2006-2007, Ext JS, LLC.
11091 * Originally Released Under LGPL - original licence link has changed is not relivant.
11094 * <script type="text/javascript">
11098 Roo.data.Field = function(config){
11099 if(typeof config == "string"){
11100 config = {name: config};
11102 Roo.apply(this, config);
11105 this.type = "auto";
11108 var st = Roo.data.SortTypes;
11109 // named sortTypes are supported, here we look them up
11110 if(typeof this.sortType == "string"){
11111 this.sortType = st[this.sortType];
11114 // set default sortType for strings and dates
11115 if(!this.sortType){
11118 this.sortType = st.asUCString;
11121 this.sortType = st.asDate;
11124 this.sortType = st.none;
11129 var stripRe = /[\$,%]/g;
11131 // prebuilt conversion function for this field, instead of
11132 // switching every time we're reading a value
11134 var cv, dateFormat = this.dateFormat;
11139 cv = function(v){ return v; };
11142 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11146 return v !== undefined && v !== null && v !== '' ?
11147 parseInt(String(v).replace(stripRe, ""), 10) : '';
11152 return v !== undefined && v !== null && v !== '' ?
11153 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11158 cv = function(v){ return v === true || v === "true" || v == 1; };
11165 if(v instanceof Date){
11169 if(dateFormat == "timestamp"){
11170 return new Date(v*1000);
11172 return Date.parseDate(v, dateFormat);
11174 var parsed = Date.parse(v);
11175 return parsed ? new Date(parsed) : null;
11184 Roo.data.Field.prototype = {
11192 * Ext JS Library 1.1.1
11193 * Copyright(c) 2006-2007, Ext JS, LLC.
11195 * Originally Released Under LGPL - original licence link has changed is not relivant.
11198 * <script type="text/javascript">
11201 // Base class for reading structured data from a data source. This class is intended to be
11202 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11205 * @class Roo.data.DataReader
11206 * Base class for reading structured data from a data source. This class is intended to be
11207 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11210 Roo.data.DataReader = function(meta, recordType){
11214 this.recordType = recordType instanceof Array ?
11215 Roo.data.Record.create(recordType) : recordType;
11218 Roo.data.DataReader.prototype = {
11220 * Create an empty record
11221 * @param {Object} data (optional) - overlay some values
11222 * @return {Roo.data.Record} record created.
11224 newRow : function(d) {
11226 this.recordType.prototype.fields.each(function(c) {
11228 case 'int' : da[c.name] = 0; break;
11229 case 'date' : da[c.name] = new Date(); break;
11230 case 'float' : da[c.name] = 0.0; break;
11231 case 'boolean' : da[c.name] = false; break;
11232 default : da[c.name] = ""; break;
11236 return new this.recordType(Roo.apply(da, d));
11241 * Ext JS Library 1.1.1
11242 * Copyright(c) 2006-2007, Ext JS, LLC.
11244 * Originally Released Under LGPL - original licence link has changed is not relivant.
11247 * <script type="text/javascript">
11251 * @class Roo.data.DataProxy
11252 * @extends Roo.data.Observable
11253 * This class is an abstract base class for implementations which provide retrieval of
11254 * unformatted data objects.<br>
11256 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11257 * (of the appropriate type which knows how to parse the data object) to provide a block of
11258 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11260 * Custom implementations must implement the load method as described in
11261 * {@link Roo.data.HttpProxy#load}.
11263 Roo.data.DataProxy = function(){
11266 * @event beforeload
11267 * Fires before a network request is made to retrieve a data object.
11268 * @param {Object} This DataProxy object.
11269 * @param {Object} params The params parameter to the load function.
11274 * Fires before the load method's callback is called.
11275 * @param {Object} This DataProxy object.
11276 * @param {Object} o The data object.
11277 * @param {Object} arg The callback argument object passed to the load function.
11281 * @event loadexception
11282 * Fires if an Exception occurs during data retrieval.
11283 * @param {Object} This DataProxy object.
11284 * @param {Object} o The data object.
11285 * @param {Object} arg The callback argument object passed to the load function.
11286 * @param {Object} e The Exception.
11288 loadexception : true
11290 Roo.data.DataProxy.superclass.constructor.call(this);
11293 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11296 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11300 * Ext JS Library 1.1.1
11301 * Copyright(c) 2006-2007, Ext JS, LLC.
11303 * Originally Released Under LGPL - original licence link has changed is not relivant.
11306 * <script type="text/javascript">
11309 * @class Roo.data.MemoryProxy
11310 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11311 * to the Reader when its load method is called.
11313 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11315 Roo.data.MemoryProxy = function(data){
11319 Roo.data.MemoryProxy.superclass.constructor.call(this);
11323 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11326 * Load data from the requested source (in this case an in-memory
11327 * data object passed to the constructor), read the data object into
11328 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11329 * process that block using the passed callback.
11330 * @param {Object} params This parameter is not used by the MemoryProxy class.
11331 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11332 * object into a block of Roo.data.Records.
11333 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11334 * The function must be passed <ul>
11335 * <li>The Record block object</li>
11336 * <li>The "arg" argument from the load function</li>
11337 * <li>A boolean success indicator</li>
11339 * @param {Object} scope The scope in which to call the callback
11340 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11342 load : function(params, reader, callback, scope, arg){
11343 params = params || {};
11346 result = reader.readRecords(this.data);
11348 this.fireEvent("loadexception", this, arg, null, e);
11349 callback.call(scope, null, arg, false);
11352 callback.call(scope, result, arg, true);
11356 update : function(params, records){
11361 * Ext JS Library 1.1.1
11362 * Copyright(c) 2006-2007, Ext JS, LLC.
11364 * Originally Released Under LGPL - original licence link has changed is not relivant.
11367 * <script type="text/javascript">
11370 * @class Roo.data.HttpProxy
11371 * @extends Roo.data.DataProxy
11372 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11373 * configured to reference a certain URL.<br><br>
11375 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11376 * from which the running page was served.<br><br>
11378 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11380 * Be aware that to enable the browser to parse an XML document, the server must set
11381 * the Content-Type header in the HTTP response to "text/xml".
11383 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11384 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11385 * will be used to make the request.
11387 Roo.data.HttpProxy = function(conn){
11388 Roo.data.HttpProxy.superclass.constructor.call(this);
11389 // is conn a conn config or a real conn?
11391 this.useAjax = !conn || !conn.events;
11395 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11396 // thse are take from connection...
11399 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11402 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11403 * extra parameters to each request made by this object. (defaults to undefined)
11406 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11407 * to each request made by this object. (defaults to undefined)
11410 * @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)
11413 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11416 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11422 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11426 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11427 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11428 * a finer-grained basis than the DataProxy events.
11430 getConnection : function(){
11431 return this.useAjax ? Roo.Ajax : this.conn;
11435 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11436 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11437 * process that block using the passed callback.
11438 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11439 * for the request to the remote server.
11440 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11441 * object into a block of Roo.data.Records.
11442 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11443 * The function must be passed <ul>
11444 * <li>The Record block object</li>
11445 * <li>The "arg" argument from the load function</li>
11446 * <li>A boolean success indicator</li>
11448 * @param {Object} scope The scope in which to call the callback
11449 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11451 load : function(params, reader, callback, scope, arg){
11452 if(this.fireEvent("beforeload", this, params) !== false){
11454 params : params || {},
11456 callback : callback,
11461 callback : this.loadResponse,
11465 Roo.applyIf(o, this.conn);
11466 if(this.activeRequest){
11467 Roo.Ajax.abort(this.activeRequest);
11469 this.activeRequest = Roo.Ajax.request(o);
11471 this.conn.request(o);
11474 callback.call(scope||this, null, arg, false);
11479 loadResponse : function(o, success, response){
11480 delete this.activeRequest;
11482 this.fireEvent("loadexception", this, o, response);
11483 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11488 result = o.reader.read(response);
11490 this.fireEvent("loadexception", this, o, response, e);
11491 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11495 this.fireEvent("load", this, o, o.request.arg);
11496 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11500 update : function(dataSet){
11505 updateResponse : function(dataSet){
11510 * Ext JS Library 1.1.1
11511 * Copyright(c) 2006-2007, Ext JS, LLC.
11513 * Originally Released Under LGPL - original licence link has changed is not relivant.
11516 * <script type="text/javascript">
11520 * @class Roo.data.ScriptTagProxy
11521 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11522 * other than the originating domain of the running page.<br><br>
11524 * <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
11525 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11527 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11528 * source code that is used as the source inside a <script> tag.<br><br>
11530 * In order for the browser to process the returned data, the server must wrap the data object
11531 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11532 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11533 * depending on whether the callback name was passed:
11536 boolean scriptTag = false;
11537 String cb = request.getParameter("callback");
11540 response.setContentType("text/javascript");
11542 response.setContentType("application/x-json");
11544 Writer out = response.getWriter();
11546 out.write(cb + "(");
11548 out.print(dataBlock.toJsonString());
11555 * @param {Object} config A configuration object.
11557 Roo.data.ScriptTagProxy = function(config){
11558 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11559 Roo.apply(this, config);
11560 this.head = document.getElementsByTagName("head")[0];
11563 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11565 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11567 * @cfg {String} url The URL from which to request the data object.
11570 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11574 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11575 * the server the name of the callback function set up by the load call to process the returned data object.
11576 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11577 * javascript output which calls this named function passing the data object as its only parameter.
11579 callbackParam : "callback",
11581 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11582 * name to the request.
11587 * Load data from the configured URL, read the data object into
11588 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11589 * process that block using the passed callback.
11590 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11591 * for the request to the remote server.
11592 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11593 * object into a block of Roo.data.Records.
11594 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11595 * The function must be passed <ul>
11596 * <li>The Record block object</li>
11597 * <li>The "arg" argument from the load function</li>
11598 * <li>A boolean success indicator</li>
11600 * @param {Object} scope The scope in which to call the callback
11601 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11603 load : function(params, reader, callback, scope, arg){
11604 if(this.fireEvent("beforeload", this, params) !== false){
11606 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11608 var url = this.url;
11609 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11611 url += "&_dc=" + (new Date().getTime());
11613 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11616 cb : "stcCallback"+transId,
11617 scriptId : "stcScript"+transId,
11621 callback : callback,
11627 window[trans.cb] = function(o){
11628 conn.handleResponse(o, trans);
11631 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11633 if(this.autoAbort !== false){
11637 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11639 var script = document.createElement("script");
11640 script.setAttribute("src", url);
11641 script.setAttribute("type", "text/javascript");
11642 script.setAttribute("id", trans.scriptId);
11643 this.head.appendChild(script);
11645 this.trans = trans;
11647 callback.call(scope||this, null, arg, false);
11652 isLoading : function(){
11653 return this.trans ? true : false;
11657 * Abort the current server request.
11659 abort : function(){
11660 if(this.isLoading()){
11661 this.destroyTrans(this.trans);
11666 destroyTrans : function(trans, isLoaded){
11667 this.head.removeChild(document.getElementById(trans.scriptId));
11668 clearTimeout(trans.timeoutId);
11670 window[trans.cb] = undefined;
11672 delete window[trans.cb];
11675 // if hasn't been loaded, wait for load to remove it to prevent script error
11676 window[trans.cb] = function(){
11677 window[trans.cb] = undefined;
11679 delete window[trans.cb];
11686 handleResponse : function(o, trans){
11687 this.trans = false;
11688 this.destroyTrans(trans, true);
11691 result = trans.reader.readRecords(o);
11693 this.fireEvent("loadexception", this, o, trans.arg, e);
11694 trans.callback.call(trans.scope||window, null, trans.arg, false);
11697 this.fireEvent("load", this, o, trans.arg);
11698 trans.callback.call(trans.scope||window, result, trans.arg, true);
11702 handleFailure : function(trans){
11703 this.trans = false;
11704 this.destroyTrans(trans, false);
11705 this.fireEvent("loadexception", this, null, trans.arg);
11706 trans.callback.call(trans.scope||window, null, trans.arg, false);
11710 * Ext JS Library 1.1.1
11711 * Copyright(c) 2006-2007, Ext JS, LLC.
11713 * Originally Released Under LGPL - original licence link has changed is not relivant.
11716 * <script type="text/javascript">
11720 * @class Roo.data.JsonReader
11721 * @extends Roo.data.DataReader
11722 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11723 * based on mappings in a provided Roo.data.Record constructor.
11725 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11726 * in the reply previously.
11731 var RecordDef = Roo.data.Record.create([
11732 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11733 {name: 'occupation'} // This field will use "occupation" as the mapping.
11735 var myReader = new Roo.data.JsonReader({
11736 totalProperty: "results", // The property which contains the total dataset size (optional)
11737 root: "rows", // The property which contains an Array of row objects
11738 id: "id" // The property within each row object that provides an ID for the record (optional)
11742 * This would consume a JSON file like this:
11744 { 'results': 2, 'rows': [
11745 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11746 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11749 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11750 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11751 * paged from the remote server.
11752 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11753 * @cfg {String} root name of the property which contains the Array of row objects.
11754 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11755 * @cfg {Array} fields Array of field definition objects
11757 * Create a new JsonReader
11758 * @param {Object} meta Metadata configuration options
11759 * @param {Object} recordType Either an Array of field definition objects,
11760 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11762 Roo.data.JsonReader = function(meta, recordType){
11765 // set some defaults:
11766 Roo.applyIf(meta, {
11767 totalProperty: 'total',
11768 successProperty : 'success',
11773 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11775 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11778 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11779 * Used by Store query builder to append _requestMeta to params.
11782 metaFromRemote : false,
11784 * This method is only used by a DataProxy which has retrieved data from a remote server.
11785 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11786 * @return {Object} data A data block which is used by an Roo.data.Store object as
11787 * a cache of Roo.data.Records.
11789 read : function(response){
11790 var json = response.responseText;
11792 var o = /* eval:var:o */ eval("("+json+")");
11794 throw {message: "JsonReader.read: Json object not found"};
11800 this.metaFromRemote = true;
11801 this.meta = o.metaData;
11802 this.recordType = Roo.data.Record.create(o.metaData.fields);
11803 this.onMetaChange(this.meta, this.recordType, o);
11805 return this.readRecords(o);
11808 // private function a store will implement
11809 onMetaChange : function(meta, recordType, o){
11816 simpleAccess: function(obj, subsc) {
11823 getJsonAccessor: function(){
11825 return function(expr) {
11827 return(re.test(expr))
11828 ? new Function("obj", "return obj." + expr)
11833 return Roo.emptyFn;
11838 * Create a data block containing Roo.data.Records from an XML document.
11839 * @param {Object} o An object which contains an Array of row objects in the property specified
11840 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11841 * which contains the total size of the dataset.
11842 * @return {Object} data A data block which is used by an Roo.data.Store object as
11843 * a cache of Roo.data.Records.
11845 readRecords : function(o){
11847 * After any data loads, the raw JSON data is available for further custom processing.
11851 var s = this.meta, Record = this.recordType,
11852 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11854 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11856 if(s.totalProperty) {
11857 this.getTotal = this.getJsonAccessor(s.totalProperty);
11859 if(s.successProperty) {
11860 this.getSuccess = this.getJsonAccessor(s.successProperty);
11862 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11864 var g = this.getJsonAccessor(s.id);
11865 this.getId = function(rec) {
11867 return (r === undefined || r === "") ? null : r;
11870 this.getId = function(){return null;};
11873 for(var jj = 0; jj < fl; jj++){
11875 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11876 this.ef[jj] = this.getJsonAccessor(map);
11880 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11881 if(s.totalProperty){
11882 var vt = parseInt(this.getTotal(o), 10);
11887 if(s.successProperty){
11888 var vs = this.getSuccess(o);
11889 if(vs === false || vs === 'false'){
11894 for(var i = 0; i < c; i++){
11897 var id = this.getId(n);
11898 for(var j = 0; j < fl; j++){
11900 var v = this.ef[j](n);
11902 Roo.log('missing convert for ' + f.name);
11906 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11908 var record = new Record(values, id);
11910 records[i] = record;
11916 totalRecords : totalRecords
11921 * Ext JS Library 1.1.1
11922 * Copyright(c) 2006-2007, Ext JS, LLC.
11924 * Originally Released Under LGPL - original licence link has changed is not relivant.
11927 * <script type="text/javascript">
11931 * @class Roo.data.ArrayReader
11932 * @extends Roo.data.DataReader
11933 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11934 * Each element of that Array represents a row of data fields. The
11935 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11936 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11940 var RecordDef = Roo.data.Record.create([
11941 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11942 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11944 var myReader = new Roo.data.ArrayReader({
11945 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11949 * This would consume an Array like this:
11951 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11953 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11955 * Create a new JsonReader
11956 * @param {Object} meta Metadata configuration options.
11957 * @param {Object} recordType Either an Array of field definition objects
11958 * as specified to {@link Roo.data.Record#create},
11959 * or an {@link Roo.data.Record} object
11960 * created using {@link Roo.data.Record#create}.
11962 Roo.data.ArrayReader = function(meta, recordType){
11963 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11966 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11968 * Create a data block containing Roo.data.Records from an XML document.
11969 * @param {Object} o An Array of row objects which represents the dataset.
11970 * @return {Object} data A data block which is used by an Roo.data.Store object as
11971 * a cache of Roo.data.Records.
11973 readRecords : function(o){
11974 var sid = this.meta ? this.meta.id : null;
11975 var recordType = this.recordType, fields = recordType.prototype.fields;
11978 for(var i = 0; i < root.length; i++){
11981 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11982 for(var j = 0, jlen = fields.length; j < jlen; j++){
11983 var f = fields.items[j];
11984 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11985 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11987 values[f.name] = v;
11989 var record = new recordType(values, id);
11991 records[records.length] = record;
11995 totalRecords : records.length
12004 * @class Roo.bootstrap.ComboBox
12005 * @extends Roo.bootstrap.TriggerField
12006 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12007 * @cfg {Boolean} append (true|false) default false
12008 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12009 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12010 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12011 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12012 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12013 * @cfg {Boolean} animate default true
12014 * @cfg {Boolean} emptyResultText only for touch device
12015 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12017 * Create a new ComboBox.
12018 * @param {Object} config Configuration options
12020 Roo.bootstrap.ComboBox = function(config){
12021 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12025 * Fires when the dropdown list is expanded
12026 * @param {Roo.bootstrap.ComboBox} combo This combo box
12031 * Fires when the dropdown list is collapsed
12032 * @param {Roo.bootstrap.ComboBox} combo This combo box
12036 * @event beforeselect
12037 * Fires before a list item is selected. Return false to cancel the selection.
12038 * @param {Roo.bootstrap.ComboBox} combo This combo box
12039 * @param {Roo.data.Record} record The data record returned from the underlying store
12040 * @param {Number} index The index of the selected item in the dropdown list
12042 'beforeselect' : true,
12045 * Fires when a list item is selected
12046 * @param {Roo.bootstrap.ComboBox} combo This combo box
12047 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12048 * @param {Number} index The index of the selected item in the dropdown list
12052 * @event beforequery
12053 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12054 * The event object passed has these properties:
12055 * @param {Roo.bootstrap.ComboBox} combo This combo box
12056 * @param {String} query The query
12057 * @param {Boolean} forceAll true to force "all" query
12058 * @param {Boolean} cancel true to cancel the query
12059 * @param {Object} e The query event object
12061 'beforequery': true,
12064 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12065 * @param {Roo.bootstrap.ComboBox} combo This combo box
12070 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12071 * @param {Roo.bootstrap.ComboBox} combo This combo box
12072 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12077 * Fires when the remove value from the combobox array
12078 * @param {Roo.bootstrap.ComboBox} combo This combo box
12082 * @event afterremove
12083 * Fires when the remove value from the combobox array
12084 * @param {Roo.bootstrap.ComboBox} combo This combo box
12086 'afterremove' : true,
12088 * @event specialfilter
12089 * Fires when specialfilter
12090 * @param {Roo.bootstrap.ComboBox} combo This combo box
12092 'specialfilter' : true,
12095 * Fires when tick the element
12096 * @param {Roo.bootstrap.ComboBox} combo This combo box
12100 * @event touchviewdisplay
12101 * Fires when touch view require special display (default is using displayField)
12102 * @param {Roo.bootstrap.ComboBox} combo This combo box
12103 * @param {Object} cfg set html .
12105 'touchviewdisplay' : true
12110 this.tickItems = [];
12112 this.selectedIndex = -1;
12113 if(this.mode == 'local'){
12114 if(config.queryDelay === undefined){
12115 this.queryDelay = 10;
12117 if(config.minChars === undefined){
12123 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12126 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12127 * rendering into an Roo.Editor, defaults to false)
12130 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12131 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12134 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12137 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12138 * the dropdown list (defaults to undefined, with no header element)
12142 * @cfg {String/Roo.Template} tpl The template to use to render the output
12146 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12148 listWidth: undefined,
12150 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12151 * mode = 'remote' or 'text' if mode = 'local')
12153 displayField: undefined,
12156 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12157 * mode = 'remote' or 'value' if mode = 'local').
12158 * Note: use of a valueField requires the user make a selection
12159 * in order for a value to be mapped.
12161 valueField: undefined,
12163 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12168 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12169 * field's data value (defaults to the underlying DOM element's name)
12171 hiddenName: undefined,
12173 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12177 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12179 selectedClass: 'active',
12182 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12186 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12187 * anchor positions (defaults to 'tl-bl')
12189 listAlign: 'tl-bl?',
12191 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12195 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12196 * query specified by the allQuery config option (defaults to 'query')
12198 triggerAction: 'query',
12200 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12201 * (defaults to 4, does not apply if editable = false)
12205 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12206 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12210 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12211 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12215 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12216 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12220 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12221 * when editable = true (defaults to false)
12223 selectOnFocus:false,
12225 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12227 queryParam: 'query',
12229 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12230 * when mode = 'remote' (defaults to 'Loading...')
12232 loadingText: 'Loading...',
12234 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12238 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12242 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12243 * traditional select (defaults to true)
12247 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12251 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12255 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12256 * listWidth has a higher value)
12260 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12261 * allow the user to set arbitrary text into the field (defaults to false)
12263 forceSelection:false,
12265 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12266 * if typeAhead = true (defaults to 250)
12268 typeAheadDelay : 250,
12270 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12271 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12273 valueNotFoundText : undefined,
12275 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12277 blockFocus : false,
12280 * @cfg {Boolean} disableClear Disable showing of clear button.
12282 disableClear : false,
12284 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12286 alwaysQuery : false,
12289 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12294 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12296 invalidClass : "has-warning",
12299 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12301 validClass : "has-success",
12304 * @cfg {Boolean} specialFilter (true|false) special filter default false
12306 specialFilter : false,
12309 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12311 mobileTouchView : true,
12314 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12316 useNativeIOS : false,
12318 ios_options : false,
12330 btnPosition : 'right',
12331 triggerList : true,
12332 showToggleBtn : true,
12334 emptyResultText: 'Empty',
12335 triggerText : 'Select',
12337 // element that contains real text value.. (when hidden is used..)
12339 getAutoCreate : function()
12344 * Render classic select for iso
12347 if(Roo.isIOS && this.useNativeIOS){
12348 cfg = this.getAutoCreateNativeIOS();
12356 if(Roo.isTouch && this.mobileTouchView){
12357 cfg = this.getAutoCreateTouchView();
12364 if(!this.tickable){
12365 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12370 * ComboBox with tickable selections
12373 var align = this.labelAlign || this.parentLabelAlign();
12376 cls : 'form-group roo-combobox-tickable' //input-group
12381 cls : 'tickable-buttons',
12386 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12387 html : this.triggerText
12393 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12400 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12407 buttons.cn.unshift({
12409 cls: 'roo-select2-search-field-input'
12415 Roo.each(buttons.cn, function(c){
12417 c.cls += ' btn-' + _this.size;
12420 if (_this.disabled) {
12431 cls: 'form-hidden-field'
12435 cls: 'roo-select2-choices',
12439 cls: 'roo-select2-search-field',
12451 cls: 'roo-select2-container input-group roo-select2-container-multi',
12456 // cls: 'typeahead typeahead-long dropdown-menu',
12457 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12462 if(this.hasFeedback && !this.allowBlank){
12466 cls: 'glyphicon form-control-feedback'
12469 combobox.cn.push(feedback);
12472 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12474 // Roo.log("left and has label");
12478 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12479 tooltip : 'This field is required'
12484 cls : 'control-label col-sm-' + this.labelWidth,
12485 html : this.fieldLabel
12489 cls : "col-sm-" + (12 - this.labelWidth),
12497 if(this.indicatorpos == 'right'){
12503 cls : 'control-label col-sm-' + this.labelWidth,
12504 html : this.fieldLabel
12509 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12510 tooltip : 'This field is required'
12513 cls : "col-sm-" + (12 - this.labelWidth),
12524 } else if ( this.fieldLabel.length) {
12525 // Roo.log(" label");
12529 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12530 tooltip : 'This field is required'
12534 //cls : 'input-group-addon',
12535 html : this.fieldLabel
12543 if(this.indicatorpos == 'right'){
12548 //cls : 'input-group-addon',
12549 html : this.fieldLabel
12555 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12556 tooltip : 'This field is required'
12567 // Roo.log(" no label && no align");
12574 ['xs','sm','md','lg'].map(function(size){
12575 if (settings[size]) {
12576 cfg.cls += ' col-' + size + '-' + settings[size];
12584 _initEventsCalled : false,
12587 initEvents: function()
12589 if (this._initEventsCalled) { // as we call render... prevent looping...
12592 this._initEventsCalled = true;
12595 throw "can not find store for combo";
12598 this.store = Roo.factory(this.store, Roo.data);
12600 // if we are building from html. then this element is so complex, that we can not really
12601 // use the rendered HTML.
12602 // so we have to trash and replace the previous code.
12603 if (Roo.XComponent.build_from_html) {
12605 // remove this element....
12606 var e = this.el.dom, k=0;
12607 while (e ) { e = e.previousSibling; ++k;}
12612 this.rendered = false;
12614 this.render(this.parent().getChildContainer(true), k);
12620 if(Roo.isIOS && this.useNativeIOS){
12621 this.initIOSView();
12629 if(Roo.isTouch && this.mobileTouchView){
12630 this.initTouchView();
12635 this.initTickableEvents();
12639 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12641 if(this.hiddenName){
12643 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12645 this.hiddenField.dom.value =
12646 this.hiddenValue !== undefined ? this.hiddenValue :
12647 this.value !== undefined ? this.value : '';
12649 // prevent input submission
12650 this.el.dom.removeAttribute('name');
12651 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12656 // this.el.dom.setAttribute('autocomplete', 'off');
12659 var cls = 'x-combo-list';
12661 //this.list = new Roo.Layer({
12662 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12668 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12669 _this.list.setWidth(lw);
12672 this.list.on('mouseover', this.onViewOver, this);
12673 this.list.on('mousemove', this.onViewMove, this);
12675 this.list.on('scroll', this.onViewScroll, this);
12678 this.list.swallowEvent('mousewheel');
12679 this.assetHeight = 0;
12682 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12683 this.assetHeight += this.header.getHeight();
12686 this.innerList = this.list.createChild({cls:cls+'-inner'});
12687 this.innerList.on('mouseover', this.onViewOver, this);
12688 this.innerList.on('mousemove', this.onViewMove, this);
12689 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12691 if(this.allowBlank && !this.pageSize && !this.disableClear){
12692 this.footer = this.list.createChild({cls:cls+'-ft'});
12693 this.pageTb = new Roo.Toolbar(this.footer);
12697 this.footer = this.list.createChild({cls:cls+'-ft'});
12698 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12699 {pageSize: this.pageSize});
12703 if (this.pageTb && this.allowBlank && !this.disableClear) {
12705 this.pageTb.add(new Roo.Toolbar.Fill(), {
12706 cls: 'x-btn-icon x-btn-clear',
12708 handler: function()
12711 _this.clearValue();
12712 _this.onSelect(false, -1);
12717 this.assetHeight += this.footer.getHeight();
12722 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12725 this.view = new Roo.View(this.list, this.tpl, {
12726 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12728 //this.view.wrapEl.setDisplayed(false);
12729 this.view.on('click', this.onViewClick, this);
12733 this.store.on('beforeload', this.onBeforeLoad, this);
12734 this.store.on('load', this.onLoad, this);
12735 this.store.on('loadexception', this.onLoadException, this);
12737 if(this.resizable){
12738 this.resizer = new Roo.Resizable(this.list, {
12739 pinned:true, handles:'se'
12741 this.resizer.on('resize', function(r, w, h){
12742 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12743 this.listWidth = w;
12744 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12745 this.restrictHeight();
12747 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12750 if(!this.editable){
12751 this.editable = true;
12752 this.setEditable(false);
12757 if (typeof(this.events.add.listeners) != 'undefined') {
12759 this.addicon = this.wrap.createChild(
12760 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12762 this.addicon.on('click', function(e) {
12763 this.fireEvent('add', this);
12766 if (typeof(this.events.edit.listeners) != 'undefined') {
12768 this.editicon = this.wrap.createChild(
12769 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12770 if (this.addicon) {
12771 this.editicon.setStyle('margin-left', '40px');
12773 this.editicon.on('click', function(e) {
12775 // we fire even if inothing is selected..
12776 this.fireEvent('edit', this, this.lastData );
12782 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12783 "up" : function(e){
12784 this.inKeyMode = true;
12788 "down" : function(e){
12789 if(!this.isExpanded()){
12790 this.onTriggerClick();
12792 this.inKeyMode = true;
12797 "enter" : function(e){
12798 // this.onViewClick();
12802 if(this.fireEvent("specialkey", this, e)){
12803 this.onViewClick(false);
12809 "esc" : function(e){
12813 "tab" : function(e){
12816 if(this.fireEvent("specialkey", this, e)){
12817 this.onViewClick(false);
12825 doRelay : function(foo, bar, hname){
12826 if(hname == 'down' || this.scope.isExpanded()){
12827 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12836 this.queryDelay = Math.max(this.queryDelay || 10,
12837 this.mode == 'local' ? 10 : 250);
12840 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12842 if(this.typeAhead){
12843 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12845 if(this.editable !== false){
12846 this.inputEl().on("keyup", this.onKeyUp, this);
12848 if(this.forceSelection){
12849 this.inputEl().on('blur', this.doForce, this);
12853 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12854 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12858 initTickableEvents: function()
12862 if(this.hiddenName){
12864 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12866 this.hiddenField.dom.value =
12867 this.hiddenValue !== undefined ? this.hiddenValue :
12868 this.value !== undefined ? this.value : '';
12870 // prevent input submission
12871 this.el.dom.removeAttribute('name');
12872 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12877 // this.list = this.el.select('ul.dropdown-menu',true).first();
12879 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12880 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12881 if(this.triggerList){
12882 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12885 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12886 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12888 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12889 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12891 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12892 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12894 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12895 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12896 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12899 this.cancelBtn.hide();
12904 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12905 _this.list.setWidth(lw);
12908 this.list.on('mouseover', this.onViewOver, this);
12909 this.list.on('mousemove', this.onViewMove, this);
12911 this.list.on('scroll', this.onViewScroll, this);
12914 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>';
12917 this.view = new Roo.View(this.list, this.tpl, {
12918 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12921 //this.view.wrapEl.setDisplayed(false);
12922 this.view.on('click', this.onViewClick, this);
12926 this.store.on('beforeload', this.onBeforeLoad, this);
12927 this.store.on('load', this.onLoad, this);
12928 this.store.on('loadexception', this.onLoadException, this);
12931 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12932 "up" : function(e){
12933 this.inKeyMode = true;
12937 "down" : function(e){
12938 this.inKeyMode = true;
12942 "enter" : function(e){
12943 if(this.fireEvent("specialkey", this, e)){
12944 this.onViewClick(false);
12950 "esc" : function(e){
12951 this.onTickableFooterButtonClick(e, false, false);
12954 "tab" : function(e){
12955 this.fireEvent("specialkey", this, e);
12957 this.onTickableFooterButtonClick(e, false, false);
12964 doRelay : function(e, fn, key){
12965 if(this.scope.isExpanded()){
12966 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12975 this.queryDelay = Math.max(this.queryDelay || 10,
12976 this.mode == 'local' ? 10 : 250);
12979 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12981 if(this.typeAhead){
12982 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12985 if(this.editable !== false){
12986 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12991 onDestroy : function(){
12993 this.view.setStore(null);
12994 this.view.el.removeAllListeners();
12995 this.view.el.remove();
12996 this.view.purgeListeners();
12999 this.list.dom.innerHTML = '';
13003 this.store.un('beforeload', this.onBeforeLoad, this);
13004 this.store.un('load', this.onLoad, this);
13005 this.store.un('loadexception', this.onLoadException, this);
13007 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13011 fireKey : function(e){
13012 if(e.isNavKeyPress() && !this.list.isVisible()){
13013 this.fireEvent("specialkey", this, e);
13018 onResize: function(w, h){
13019 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13021 // if(typeof w != 'number'){
13022 // // we do not handle it!?!?
13025 // var tw = this.trigger.getWidth();
13026 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13027 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13029 // this.inputEl().setWidth( this.adjustWidth('input', x));
13031 // //this.trigger.setStyle('left', x+'px');
13033 // if(this.list && this.listWidth === undefined){
13034 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13035 // this.list.setWidth(lw);
13036 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13044 * Allow or prevent the user from directly editing the field text. If false is passed,
13045 * the user will only be able to select from the items defined in the dropdown list. This method
13046 * is the runtime equivalent of setting the 'editable' config option at config time.
13047 * @param {Boolean} value True to allow the user to directly edit the field text
13049 setEditable : function(value){
13050 if(value == this.editable){
13053 this.editable = value;
13055 this.inputEl().dom.setAttribute('readOnly', true);
13056 this.inputEl().on('mousedown', this.onTriggerClick, this);
13057 this.inputEl().addClass('x-combo-noedit');
13059 this.inputEl().dom.setAttribute('readOnly', false);
13060 this.inputEl().un('mousedown', this.onTriggerClick, this);
13061 this.inputEl().removeClass('x-combo-noedit');
13067 onBeforeLoad : function(combo,opts){
13068 if(!this.hasFocus){
13072 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13074 this.restrictHeight();
13075 this.selectedIndex = -1;
13079 onLoad : function(){
13081 this.hasQuery = false;
13083 if(!this.hasFocus){
13087 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13088 this.loading.hide();
13091 if(this.store.getCount() > 0){
13093 this.restrictHeight();
13094 if(this.lastQuery == this.allQuery){
13095 if(this.editable && !this.tickable){
13096 this.inputEl().dom.select();
13100 !this.selectByValue(this.value, true) &&
13103 !this.store.lastOptions ||
13104 typeof(this.store.lastOptions.add) == 'undefined' ||
13105 this.store.lastOptions.add != true
13108 this.select(0, true);
13111 if(this.autoFocus){
13114 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13115 this.taTask.delay(this.typeAheadDelay);
13119 this.onEmptyResults();
13125 onLoadException : function()
13127 this.hasQuery = false;
13129 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13130 this.loading.hide();
13133 if(this.tickable && this.editable){
13138 // only causes errors at present
13139 //Roo.log(this.store.reader.jsonData);
13140 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13142 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13148 onTypeAhead : function(){
13149 if(this.store.getCount() > 0){
13150 var r = this.store.getAt(0);
13151 var newValue = r.data[this.displayField];
13152 var len = newValue.length;
13153 var selStart = this.getRawValue().length;
13155 if(selStart != len){
13156 this.setRawValue(newValue);
13157 this.selectText(selStart, newValue.length);
13163 onSelect : function(record, index){
13165 if(this.fireEvent('beforeselect', this, record, index) !== false){
13167 this.setFromData(index > -1 ? record.data : false);
13170 this.fireEvent('select', this, record, index);
13175 * Returns the currently selected field value or empty string if no value is set.
13176 * @return {String} value The selected value
13178 getValue : function()
13180 if(Roo.isIOS && this.useNativeIOS){
13181 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13185 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13188 if(this.valueField){
13189 return typeof this.value != 'undefined' ? this.value : '';
13191 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13195 getRawValue : function()
13197 if(Roo.isIOS && this.useNativeIOS){
13198 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13201 var v = this.inputEl().getValue();
13207 * Clears any text/value currently set in the field
13209 clearValue : function(){
13211 if(this.hiddenField){
13212 this.hiddenField.dom.value = '';
13215 this.setRawValue('');
13216 this.lastSelectionText = '';
13217 this.lastData = false;
13219 var close = this.closeTriggerEl();
13230 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13231 * will be displayed in the field. If the value does not match the data value of an existing item,
13232 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13233 * Otherwise the field will be blank (although the value will still be set).
13234 * @param {String} value The value to match
13236 setValue : function(v)
13238 if(Roo.isIOS && this.useNativeIOS){
13239 this.setIOSValue(v);
13249 if(this.valueField){
13250 var r = this.findRecord(this.valueField, v);
13252 text = r.data[this.displayField];
13253 }else if(this.valueNotFoundText !== undefined){
13254 text = this.valueNotFoundText;
13257 this.lastSelectionText = text;
13258 if(this.hiddenField){
13259 this.hiddenField.dom.value = v;
13261 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13264 var close = this.closeTriggerEl();
13267 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13273 * @property {Object} the last set data for the element
13278 * Sets the value of the field based on a object which is related to the record format for the store.
13279 * @param {Object} value the value to set as. or false on reset?
13281 setFromData : function(o){
13288 var dv = ''; // display value
13289 var vv = ''; // value value..
13291 if (this.displayField) {
13292 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13294 // this is an error condition!!!
13295 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13298 if(this.valueField){
13299 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13302 var close = this.closeTriggerEl();
13305 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13308 if(this.hiddenField){
13309 this.hiddenField.dom.value = vv;
13311 this.lastSelectionText = dv;
13312 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13316 // no hidden field.. - we store the value in 'value', but still display
13317 // display field!!!!
13318 this.lastSelectionText = dv;
13319 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13326 reset : function(){
13327 // overridden so that last data is reset..
13334 this.setValue(this.originalValue);
13335 //this.clearInvalid();
13336 this.lastData = false;
13338 this.view.clearSelections();
13344 findRecord : function(prop, value){
13346 if(this.store.getCount() > 0){
13347 this.store.each(function(r){
13348 if(r.data[prop] == value){
13358 getName: function()
13360 // returns hidden if it's set..
13361 if (!this.rendered) {return ''};
13362 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13366 onViewMove : function(e, t){
13367 this.inKeyMode = false;
13371 onViewOver : function(e, t){
13372 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13375 var item = this.view.findItemFromChild(t);
13378 var index = this.view.indexOf(item);
13379 this.select(index, false);
13384 onViewClick : function(view, doFocus, el, e)
13386 var index = this.view.getSelectedIndexes()[0];
13388 var r = this.store.getAt(index);
13392 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13399 Roo.each(this.tickItems, function(v,k){
13401 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13403 _this.tickItems.splice(k, 1);
13405 if(typeof(e) == 'undefined' && view == false){
13406 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13418 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13419 this.tickItems.push(r.data);
13422 if(typeof(e) == 'undefined' && view == false){
13423 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13430 this.onSelect(r, index);
13432 if(doFocus !== false && !this.blockFocus){
13433 this.inputEl().focus();
13438 restrictHeight : function(){
13439 //this.innerList.dom.style.height = '';
13440 //var inner = this.innerList.dom;
13441 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13442 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13443 //this.list.beginUpdate();
13444 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13445 this.list.alignTo(this.inputEl(), this.listAlign);
13446 this.list.alignTo(this.inputEl(), this.listAlign);
13447 //this.list.endUpdate();
13451 onEmptyResults : function(){
13453 if(this.tickable && this.editable){
13454 this.restrictHeight();
13462 * Returns true if the dropdown list is expanded, else false.
13464 isExpanded : function(){
13465 return this.list.isVisible();
13469 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13470 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13471 * @param {String} value The data value of the item to select
13472 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13473 * selected item if it is not currently in view (defaults to true)
13474 * @return {Boolean} True if the value matched an item in the list, else false
13476 selectByValue : function(v, scrollIntoView){
13477 if(v !== undefined && v !== null){
13478 var r = this.findRecord(this.valueField || this.displayField, v);
13480 this.select(this.store.indexOf(r), scrollIntoView);
13488 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13489 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13490 * @param {Number} index The zero-based index of the list item to select
13491 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13492 * selected item if it is not currently in view (defaults to true)
13494 select : function(index, scrollIntoView){
13495 this.selectedIndex = index;
13496 this.view.select(index);
13497 if(scrollIntoView !== false){
13498 var el = this.view.getNode(index);
13500 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13503 this.list.scrollChildIntoView(el, false);
13509 selectNext : function(){
13510 var ct = this.store.getCount();
13512 if(this.selectedIndex == -1){
13514 }else if(this.selectedIndex < ct-1){
13515 this.select(this.selectedIndex+1);
13521 selectPrev : function(){
13522 var ct = this.store.getCount();
13524 if(this.selectedIndex == -1){
13526 }else if(this.selectedIndex != 0){
13527 this.select(this.selectedIndex-1);
13533 onKeyUp : function(e){
13534 if(this.editable !== false && !e.isSpecialKey()){
13535 this.lastKey = e.getKey();
13536 this.dqTask.delay(this.queryDelay);
13541 validateBlur : function(){
13542 return !this.list || !this.list.isVisible();
13546 initQuery : function(){
13548 var v = this.getRawValue();
13550 if(this.tickable && this.editable){
13551 v = this.tickableInputEl().getValue();
13558 doForce : function(){
13559 if(this.inputEl().dom.value.length > 0){
13560 this.inputEl().dom.value =
13561 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13567 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13568 * query allowing the query action to be canceled if needed.
13569 * @param {String} query The SQL query to execute
13570 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13571 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13572 * saved in the current store (defaults to false)
13574 doQuery : function(q, forceAll){
13576 if(q === undefined || q === null){
13581 forceAll: forceAll,
13585 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13590 forceAll = qe.forceAll;
13591 if(forceAll === true || (q.length >= this.minChars)){
13593 this.hasQuery = true;
13595 if(this.lastQuery != q || this.alwaysQuery){
13596 this.lastQuery = q;
13597 if(this.mode == 'local'){
13598 this.selectedIndex = -1;
13600 this.store.clearFilter();
13603 if(this.specialFilter){
13604 this.fireEvent('specialfilter', this);
13609 this.store.filter(this.displayField, q);
13612 this.store.fireEvent("datachanged", this.store);
13619 this.store.baseParams[this.queryParam] = q;
13621 var options = {params : this.getParams(q)};
13624 options.add = true;
13625 options.params.start = this.page * this.pageSize;
13628 this.store.load(options);
13631 * this code will make the page width larger, at the beginning, the list not align correctly,
13632 * we should expand the list on onLoad
13633 * so command out it
13638 this.selectedIndex = -1;
13643 this.loadNext = false;
13647 getParams : function(q){
13649 //p[this.queryParam] = q;
13653 p.limit = this.pageSize;
13659 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13661 collapse : function(){
13662 if(!this.isExpanded()){
13668 this.hasFocus = false;
13672 this.cancelBtn.hide();
13673 this.trigger.show();
13676 this.tickableInputEl().dom.value = '';
13677 this.tickableInputEl().blur();
13682 Roo.get(document).un('mousedown', this.collapseIf, this);
13683 Roo.get(document).un('mousewheel', this.collapseIf, this);
13684 if (!this.editable) {
13685 Roo.get(document).un('keydown', this.listKeyPress, this);
13687 this.fireEvent('collapse', this);
13693 collapseIf : function(e){
13694 var in_combo = e.within(this.el);
13695 var in_list = e.within(this.list);
13696 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13698 if (in_combo || in_list || is_list) {
13699 //e.stopPropagation();
13704 this.onTickableFooterButtonClick(e, false, false);
13712 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13714 expand : function(){
13716 if(this.isExpanded() || !this.hasFocus){
13720 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13721 this.list.setWidth(lw);
13727 this.restrictHeight();
13731 this.tickItems = Roo.apply([], this.item);
13734 this.cancelBtn.show();
13735 this.trigger.hide();
13738 this.tickableInputEl().focus();
13743 Roo.get(document).on('mousedown', this.collapseIf, this);
13744 Roo.get(document).on('mousewheel', this.collapseIf, this);
13745 if (!this.editable) {
13746 Roo.get(document).on('keydown', this.listKeyPress, this);
13749 this.fireEvent('expand', this);
13753 // Implements the default empty TriggerField.onTriggerClick function
13754 onTriggerClick : function(e)
13756 Roo.log('trigger click');
13758 if(this.disabled || !this.triggerList){
13763 this.loadNext = false;
13765 if(this.isExpanded()){
13767 if (!this.blockFocus) {
13768 this.inputEl().focus();
13772 this.hasFocus = true;
13773 if(this.triggerAction == 'all') {
13774 this.doQuery(this.allQuery, true);
13776 this.doQuery(this.getRawValue());
13778 if (!this.blockFocus) {
13779 this.inputEl().focus();
13784 onTickableTriggerClick : function(e)
13791 this.loadNext = false;
13792 this.hasFocus = true;
13794 if(this.triggerAction == 'all') {
13795 this.doQuery(this.allQuery, true);
13797 this.doQuery(this.getRawValue());
13801 onSearchFieldClick : function(e)
13803 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13804 this.onTickableFooterButtonClick(e, false, false);
13808 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13813 this.loadNext = false;
13814 this.hasFocus = true;
13816 if(this.triggerAction == 'all') {
13817 this.doQuery(this.allQuery, true);
13819 this.doQuery(this.getRawValue());
13823 listKeyPress : function(e)
13825 //Roo.log('listkeypress');
13826 // scroll to first matching element based on key pres..
13827 if (e.isSpecialKey()) {
13830 var k = String.fromCharCode(e.getKey()).toUpperCase();
13833 var csel = this.view.getSelectedNodes();
13834 var cselitem = false;
13836 var ix = this.view.indexOf(csel[0]);
13837 cselitem = this.store.getAt(ix);
13838 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13844 this.store.each(function(v) {
13846 // start at existing selection.
13847 if (cselitem.id == v.id) {
13853 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13854 match = this.store.indexOf(v);
13860 if (match === false) {
13861 return true; // no more action?
13864 this.view.select(match);
13865 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13866 sn.scrollIntoView(sn.dom.parentNode, false);
13869 onViewScroll : function(e, t){
13871 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){
13875 this.hasQuery = true;
13877 this.loading = this.list.select('.loading', true).first();
13879 if(this.loading === null){
13880 this.list.createChild({
13882 cls: 'loading roo-select2-more-results roo-select2-active',
13883 html: 'Loading more results...'
13886 this.loading = this.list.select('.loading', true).first();
13888 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13890 this.loading.hide();
13893 this.loading.show();
13898 this.loadNext = true;
13900 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13905 addItem : function(o)
13907 var dv = ''; // display value
13909 if (this.displayField) {
13910 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13912 // this is an error condition!!!
13913 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13920 var choice = this.choices.createChild({
13922 cls: 'roo-select2-search-choice',
13931 cls: 'roo-select2-search-choice-close',
13936 }, this.searchField);
13938 var close = choice.select('a.roo-select2-search-choice-close', true).first();
13940 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13948 this.inputEl().dom.value = '';
13953 onRemoveItem : function(e, _self, o)
13955 e.preventDefault();
13957 this.lastItem = Roo.apply([], this.item);
13959 var index = this.item.indexOf(o.data) * 1;
13962 Roo.log('not this item?!');
13966 this.item.splice(index, 1);
13971 this.fireEvent('remove', this, e);
13977 syncValue : function()
13979 if(!this.item.length){
13986 Roo.each(this.item, function(i){
13987 if(_this.valueField){
13988 value.push(i[_this.valueField]);
13995 this.value = value.join(',');
13997 if(this.hiddenField){
13998 this.hiddenField.dom.value = this.value;
14001 this.store.fireEvent("datachanged", this.store);
14006 clearItem : function()
14008 if(!this.multiple){
14014 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14022 if(this.tickable && !Roo.isTouch){
14023 this.view.refresh();
14027 inputEl: function ()
14029 if(Roo.isIOS && this.useNativeIOS){
14030 return this.el.select('select.roo-ios-select', true).first();
14033 if(Roo.isTouch && this.mobileTouchView){
14034 return this.el.select('input.form-control',true).first();
14038 return this.searchField;
14041 return this.el.select('input.form-control',true).first();
14044 onTickableFooterButtonClick : function(e, btn, el)
14046 e.preventDefault();
14048 this.lastItem = Roo.apply([], this.item);
14050 if(btn && btn.name == 'cancel'){
14051 this.tickItems = Roo.apply([], this.item);
14060 Roo.each(this.tickItems, function(o){
14068 validate : function()
14070 var v = this.getRawValue();
14073 v = this.getValue();
14076 if(this.disabled || this.allowBlank || v.length){
14081 this.markInvalid();
14085 tickableInputEl : function()
14087 if(!this.tickable || !this.editable){
14088 return this.inputEl();
14091 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14095 getAutoCreateTouchView : function()
14100 cls: 'form-group' //input-group
14106 type : this.inputType,
14107 cls : 'form-control x-combo-noedit',
14108 autocomplete: 'new-password',
14109 placeholder : this.placeholder || '',
14114 input.name = this.name;
14118 input.cls += ' input-' + this.size;
14121 if (this.disabled) {
14122 input.disabled = true;
14133 inputblock.cls += ' input-group';
14135 inputblock.cn.unshift({
14137 cls : 'input-group-addon',
14142 if(this.removable && !this.multiple){
14143 inputblock.cls += ' roo-removable';
14145 inputblock.cn.push({
14148 cls : 'roo-combo-removable-btn close'
14152 if(this.hasFeedback && !this.allowBlank){
14154 inputblock.cls += ' has-feedback';
14156 inputblock.cn.push({
14158 cls: 'glyphicon form-control-feedback'
14165 inputblock.cls += (this.before) ? '' : ' input-group';
14167 inputblock.cn.push({
14169 cls : 'input-group-addon',
14180 cls: 'form-hidden-field'
14194 cls: 'form-hidden-field'
14198 cls: 'roo-select2-choices',
14202 cls: 'roo-select2-search-field',
14215 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14221 if(!this.multiple && this.showToggleBtn){
14228 if (this.caret != false) {
14231 cls: 'fa fa-' + this.caret
14238 cls : 'input-group-addon btn dropdown-toggle',
14243 cls: 'combobox-clear',
14257 combobox.cls += ' roo-select2-container-multi';
14260 var align = this.labelAlign || this.parentLabelAlign();
14264 if(this.fieldLabel.length && this.labelWidth){
14266 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14267 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14272 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14273 tooltip : 'This field is required'
14277 cls : 'control-label ' + lw,
14278 html : this.fieldLabel
14289 if(this.indicatorpos == 'right'){
14293 cls : 'control-label ' + lw,
14294 html : this.fieldLabel
14299 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14300 tooltip : 'This field is required'
14312 var settings = this;
14314 ['xs','sm','md','lg'].map(function(size){
14315 if (settings[size]) {
14316 cfg.cls += ' col-' + size + '-' + settings[size];
14323 initTouchView : function()
14325 this.renderTouchView();
14327 this.touchViewEl.on('scroll', function(){
14328 this.el.dom.scrollTop = 0;
14331 this.originalValue = this.getValue();
14333 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14335 this.inputEl().on("click", this.showTouchView, this);
14336 if (this.triggerEl) {
14337 this.triggerEl.on("click", this.showTouchView, this);
14341 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14342 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14344 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14346 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14347 this.store.on('load', this.onTouchViewLoad, this);
14348 this.store.on('loadexception', this.onTouchViewLoadException, this);
14350 if(this.hiddenName){
14352 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14354 this.hiddenField.dom.value =
14355 this.hiddenValue !== undefined ? this.hiddenValue :
14356 this.value !== undefined ? this.value : '';
14358 this.el.dom.removeAttribute('name');
14359 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14363 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14364 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14367 if(this.removable && !this.multiple){
14368 var close = this.closeTriggerEl();
14370 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14371 close.on('click', this.removeBtnClick, this, close);
14375 * fix the bug in Safari iOS8
14377 this.inputEl().on("focus", function(e){
14378 document.activeElement.blur();
14386 renderTouchView : function()
14388 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14389 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14391 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14392 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14394 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14395 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14396 this.touchViewBodyEl.setStyle('overflow', 'auto');
14398 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14399 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14401 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14402 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14406 showTouchView : function()
14412 this.touchViewHeaderEl.hide();
14414 if(this.modalTitle.length){
14415 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14416 this.touchViewHeaderEl.show();
14419 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14420 this.touchViewEl.show();
14422 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14423 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14424 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14426 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14428 if(this.modalTitle.length){
14429 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14432 this.touchViewBodyEl.setHeight(bodyHeight);
14436 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14438 this.touchViewEl.addClass('in');
14441 this.doTouchViewQuery();
14445 hideTouchView : function()
14447 this.touchViewEl.removeClass('in');
14451 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14453 this.touchViewEl.setStyle('display', 'none');
14458 setTouchViewValue : function()
14465 Roo.each(this.tickItems, function(o){
14470 this.hideTouchView();
14473 doTouchViewQuery : function()
14482 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14486 if(!this.alwaysQuery || this.mode == 'local'){
14487 this.onTouchViewLoad();
14494 onTouchViewBeforeLoad : function(combo,opts)
14500 onTouchViewLoad : function()
14502 if(this.store.getCount() < 1){
14503 this.onTouchViewEmptyResults();
14507 this.clearTouchView();
14509 var rawValue = this.getRawValue();
14511 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14513 this.tickItems = [];
14515 this.store.data.each(function(d, rowIndex){
14516 var row = this.touchViewListGroup.createChild(template);
14518 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14519 row.addClass(d.data.cls);
14522 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14525 html : d.data[this.displayField]
14528 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14529 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14532 row.removeClass('selected');
14533 if(!this.multiple && this.valueField &&
14534 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14537 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14538 row.addClass('selected');
14541 if(this.multiple && this.valueField &&
14542 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14546 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14547 this.tickItems.push(d.data);
14550 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14554 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14556 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14558 if(this.modalTitle.length){
14559 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14562 var listHeight = this.touchViewListGroup.getHeight();
14566 if(firstChecked && listHeight > bodyHeight){
14567 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14572 onTouchViewLoadException : function()
14574 this.hideTouchView();
14577 onTouchViewEmptyResults : function()
14579 this.clearTouchView();
14581 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14583 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14587 clearTouchView : function()
14589 this.touchViewListGroup.dom.innerHTML = '';
14592 onTouchViewClick : function(e, el, o)
14594 e.preventDefault();
14597 var rowIndex = o.rowIndex;
14599 var r = this.store.getAt(rowIndex);
14601 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14603 if(!this.multiple){
14604 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14605 c.dom.removeAttribute('checked');
14608 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14610 this.setFromData(r.data);
14612 var close = this.closeTriggerEl();
14618 this.hideTouchView();
14620 this.fireEvent('select', this, r, rowIndex);
14625 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14626 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14627 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14631 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14632 this.addItem(r.data);
14633 this.tickItems.push(r.data);
14637 getAutoCreateNativeIOS : function()
14640 cls: 'form-group' //input-group,
14645 cls : 'roo-ios-select'
14649 combobox.name = this.name;
14652 if (this.disabled) {
14653 combobox.disabled = true;
14656 var settings = this;
14658 ['xs','sm','md','lg'].map(function(size){
14659 if (settings[size]) {
14660 cfg.cls += ' col-' + size + '-' + settings[size];
14670 initIOSView : function()
14672 this.store.on('load', this.onIOSViewLoad, this);
14677 onIOSViewLoad : function()
14679 if(this.store.getCount() < 1){
14683 this.clearIOSView();
14685 if(this.allowBlank) {
14687 var default_text = '-- SELECT --';
14689 var opt = this.inputEl().createChild({
14692 html : default_text
14696 o[this.valueField] = 0;
14697 o[this.displayField] = default_text;
14699 this.ios_options.push({
14706 this.store.data.each(function(d, rowIndex){
14710 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14711 html = d.data[this.displayField];
14716 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
14717 value = d.data[this.valueField];
14726 if(this.value == d.data[this.valueField]){
14727 option['selected'] = true;
14730 var opt = this.inputEl().createChild(option);
14732 this.ios_options.push({
14739 this.inputEl().on('change', function(){
14740 this.fireEvent('select', this);
14745 clearIOSView: function()
14747 this.inputEl().dom.innerHTML = '';
14749 this.ios_options = [];
14752 setIOSValue: function(v)
14756 if(!this.ios_options){
14760 Roo.each(this.ios_options, function(opts){
14762 opts.el.dom.removeAttribute('selected');
14764 if(opts.data[this.valueField] != v){
14768 opts.el.dom.setAttribute('selected', true);
14774 * @cfg {Boolean} grow
14778 * @cfg {Number} growMin
14782 * @cfg {Number} growMax
14791 Roo.apply(Roo.bootstrap.ComboBox, {
14795 cls: 'modal-header',
14817 cls: 'list-group-item',
14821 cls: 'roo-combobox-list-group-item-value'
14825 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14839 listItemCheckbox : {
14841 cls: 'list-group-item',
14845 cls: 'roo-combobox-list-group-item-value'
14849 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14865 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14870 cls: 'modal-footer',
14878 cls: 'col-xs-6 text-left',
14881 cls: 'btn btn-danger roo-touch-view-cancel',
14887 cls: 'col-xs-6 text-right',
14890 cls: 'btn btn-success roo-touch-view-ok',
14901 Roo.apply(Roo.bootstrap.ComboBox, {
14903 touchViewTemplate : {
14905 cls: 'modal fade roo-combobox-touch-view',
14909 cls: 'modal-dialog',
14910 style : 'position:fixed', // we have to fix position....
14914 cls: 'modal-content',
14916 Roo.bootstrap.ComboBox.header,
14917 Roo.bootstrap.ComboBox.body,
14918 Roo.bootstrap.ComboBox.footer
14927 * Ext JS Library 1.1.1
14928 * Copyright(c) 2006-2007, Ext JS, LLC.
14930 * Originally Released Under LGPL - original licence link has changed is not relivant.
14933 * <script type="text/javascript">
14938 * @extends Roo.util.Observable
14939 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14940 * This class also supports single and multi selection modes. <br>
14941 * Create a data model bound view:
14943 var store = new Roo.data.Store(...);
14945 var view = new Roo.View({
14947 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14949 singleSelect: true,
14950 selectedClass: "ydataview-selected",
14954 // listen for node click?
14955 view.on("click", function(vw, index, node, e){
14956 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14960 dataModel.load("foobar.xml");
14962 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14964 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14965 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14967 * Note: old style constructor is still suported (container, template, config)
14970 * Create a new View
14971 * @param {Object} config The config object
14974 Roo.View = function(config, depreciated_tpl, depreciated_config){
14976 this.parent = false;
14978 if (typeof(depreciated_tpl) == 'undefined') {
14979 // new way.. - universal constructor.
14980 Roo.apply(this, config);
14981 this.el = Roo.get(this.el);
14984 this.el = Roo.get(config);
14985 this.tpl = depreciated_tpl;
14986 Roo.apply(this, depreciated_config);
14988 this.wrapEl = this.el.wrap().wrap();
14989 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14992 if(typeof(this.tpl) == "string"){
14993 this.tpl = new Roo.Template(this.tpl);
14995 // support xtype ctors..
14996 this.tpl = new Roo.factory(this.tpl, Roo);
15000 this.tpl.compile();
15005 * @event beforeclick
15006 * Fires before a click is processed. Returns false to cancel the default action.
15007 * @param {Roo.View} this
15008 * @param {Number} index The index of the target node
15009 * @param {HTMLElement} node The target node
15010 * @param {Roo.EventObject} e The raw event object
15012 "beforeclick" : true,
15015 * Fires when a template node is clicked.
15016 * @param {Roo.View} this
15017 * @param {Number} index The index of the target node
15018 * @param {HTMLElement} node The target node
15019 * @param {Roo.EventObject} e The raw event object
15024 * Fires when a template node is double clicked.
15025 * @param {Roo.View} this
15026 * @param {Number} index The index of the target node
15027 * @param {HTMLElement} node The target node
15028 * @param {Roo.EventObject} e The raw event object
15032 * @event contextmenu
15033 * Fires when a template node is right clicked.
15034 * @param {Roo.View} this
15035 * @param {Number} index The index of the target node
15036 * @param {HTMLElement} node The target node
15037 * @param {Roo.EventObject} e The raw event object
15039 "contextmenu" : true,
15041 * @event selectionchange
15042 * Fires when the selected nodes change.
15043 * @param {Roo.View} this
15044 * @param {Array} selections Array of the selected nodes
15046 "selectionchange" : true,
15049 * @event beforeselect
15050 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15051 * @param {Roo.View} this
15052 * @param {HTMLElement} node The node to be selected
15053 * @param {Array} selections Array of currently selected nodes
15055 "beforeselect" : true,
15057 * @event preparedata
15058 * Fires on every row to render, to allow you to change the data.
15059 * @param {Roo.View} this
15060 * @param {Object} data to be rendered (change this)
15062 "preparedata" : true
15070 "click": this.onClick,
15071 "dblclick": this.onDblClick,
15072 "contextmenu": this.onContextMenu,
15076 this.selections = [];
15078 this.cmp = new Roo.CompositeElementLite([]);
15080 this.store = Roo.factory(this.store, Roo.data);
15081 this.setStore(this.store, true);
15084 if ( this.footer && this.footer.xtype) {
15086 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15088 this.footer.dataSource = this.store;
15089 this.footer.container = fctr;
15090 this.footer = Roo.factory(this.footer, Roo);
15091 fctr.insertFirst(this.el);
15093 // this is a bit insane - as the paging toolbar seems to detach the el..
15094 // dom.parentNode.parentNode.parentNode
15095 // they get detached?
15099 Roo.View.superclass.constructor.call(this);
15104 Roo.extend(Roo.View, Roo.util.Observable, {
15107 * @cfg {Roo.data.Store} store Data store to load data from.
15112 * @cfg {String|Roo.Element} el The container element.
15117 * @cfg {String|Roo.Template} tpl The template used by this View
15121 * @cfg {String} dataName the named area of the template to use as the data area
15122 * Works with domtemplates roo-name="name"
15126 * @cfg {String} selectedClass The css class to add to selected nodes
15128 selectedClass : "x-view-selected",
15130 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15135 * @cfg {String} text to display on mask (default Loading)
15139 * @cfg {Boolean} multiSelect Allow multiple selection
15141 multiSelect : false,
15143 * @cfg {Boolean} singleSelect Allow single selection
15145 singleSelect: false,
15148 * @cfg {Boolean} toggleSelect - selecting
15150 toggleSelect : false,
15153 * @cfg {Boolean} tickable - selecting
15158 * Returns the element this view is bound to.
15159 * @return {Roo.Element}
15161 getEl : function(){
15162 return this.wrapEl;
15168 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15170 refresh : function(){
15171 //Roo.log('refresh');
15174 // if we are using something like 'domtemplate', then
15175 // the what gets used is:
15176 // t.applySubtemplate(NAME, data, wrapping data..)
15177 // the outer template then get' applied with
15178 // the store 'extra data'
15179 // and the body get's added to the
15180 // roo-name="data" node?
15181 // <span class='roo-tpl-{name}'></span> ?????
15185 this.clearSelections();
15186 this.el.update("");
15188 var records = this.store.getRange();
15189 if(records.length < 1) {
15191 // is this valid?? = should it render a template??
15193 this.el.update(this.emptyText);
15197 if (this.dataName) {
15198 this.el.update(t.apply(this.store.meta)); //????
15199 el = this.el.child('.roo-tpl-' + this.dataName);
15202 for(var i = 0, len = records.length; i < len; i++){
15203 var data = this.prepareData(records[i].data, i, records[i]);
15204 this.fireEvent("preparedata", this, data, i, records[i]);
15206 var d = Roo.apply({}, data);
15209 Roo.apply(d, {'roo-id' : Roo.id()});
15213 Roo.each(this.parent.item, function(item){
15214 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15217 Roo.apply(d, {'roo-data-checked' : 'checked'});
15221 html[html.length] = Roo.util.Format.trim(
15223 t.applySubtemplate(this.dataName, d, this.store.meta) :
15230 el.update(html.join(""));
15231 this.nodes = el.dom.childNodes;
15232 this.updateIndexes(0);
15237 * Function to override to reformat the data that is sent to
15238 * the template for each node.
15239 * DEPRICATED - use the preparedata event handler.
15240 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15241 * a JSON object for an UpdateManager bound view).
15243 prepareData : function(data, index, record)
15245 this.fireEvent("preparedata", this, data, index, record);
15249 onUpdate : function(ds, record){
15250 // Roo.log('on update');
15251 this.clearSelections();
15252 var index = this.store.indexOf(record);
15253 var n = this.nodes[index];
15254 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15255 n.parentNode.removeChild(n);
15256 this.updateIndexes(index, index);
15262 onAdd : function(ds, records, index)
15264 //Roo.log(['on Add', ds, records, index] );
15265 this.clearSelections();
15266 if(this.nodes.length == 0){
15270 var n = this.nodes[index];
15271 for(var i = 0, len = records.length; i < len; i++){
15272 var d = this.prepareData(records[i].data, i, records[i]);
15274 this.tpl.insertBefore(n, d);
15277 this.tpl.append(this.el, d);
15280 this.updateIndexes(index);
15283 onRemove : function(ds, record, index){
15284 // Roo.log('onRemove');
15285 this.clearSelections();
15286 var el = this.dataName ?
15287 this.el.child('.roo-tpl-' + this.dataName) :
15290 el.dom.removeChild(this.nodes[index]);
15291 this.updateIndexes(index);
15295 * Refresh an individual node.
15296 * @param {Number} index
15298 refreshNode : function(index){
15299 this.onUpdate(this.store, this.store.getAt(index));
15302 updateIndexes : function(startIndex, endIndex){
15303 var ns = this.nodes;
15304 startIndex = startIndex || 0;
15305 endIndex = endIndex || ns.length - 1;
15306 for(var i = startIndex; i <= endIndex; i++){
15307 ns[i].nodeIndex = i;
15312 * Changes the data store this view uses and refresh the view.
15313 * @param {Store} store
15315 setStore : function(store, initial){
15316 if(!initial && this.store){
15317 this.store.un("datachanged", this.refresh);
15318 this.store.un("add", this.onAdd);
15319 this.store.un("remove", this.onRemove);
15320 this.store.un("update", this.onUpdate);
15321 this.store.un("clear", this.refresh);
15322 this.store.un("beforeload", this.onBeforeLoad);
15323 this.store.un("load", this.onLoad);
15324 this.store.un("loadexception", this.onLoad);
15328 store.on("datachanged", this.refresh, this);
15329 store.on("add", this.onAdd, this);
15330 store.on("remove", this.onRemove, this);
15331 store.on("update", this.onUpdate, this);
15332 store.on("clear", this.refresh, this);
15333 store.on("beforeload", this.onBeforeLoad, this);
15334 store.on("load", this.onLoad, this);
15335 store.on("loadexception", this.onLoad, this);
15343 * onbeforeLoad - masks the loading area.
15346 onBeforeLoad : function(store,opts)
15348 //Roo.log('onBeforeLoad');
15350 this.el.update("");
15352 this.el.mask(this.mask ? this.mask : "Loading" );
15354 onLoad : function ()
15361 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15362 * @param {HTMLElement} node
15363 * @return {HTMLElement} The template node
15365 findItemFromChild : function(node){
15366 var el = this.dataName ?
15367 this.el.child('.roo-tpl-' + this.dataName,true) :
15370 if(!node || node.parentNode == el){
15373 var p = node.parentNode;
15374 while(p && p != el){
15375 if(p.parentNode == el){
15384 onClick : function(e){
15385 var item = this.findItemFromChild(e.getTarget());
15387 var index = this.indexOf(item);
15388 if(this.onItemClick(item, index, e) !== false){
15389 this.fireEvent("click", this, index, item, e);
15392 this.clearSelections();
15397 onContextMenu : function(e){
15398 var item = this.findItemFromChild(e.getTarget());
15400 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15405 onDblClick : function(e){
15406 var item = this.findItemFromChild(e.getTarget());
15408 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15412 onItemClick : function(item, index, e)
15414 if(this.fireEvent("beforeclick", this, index, item, e) === false){
15417 if (this.toggleSelect) {
15418 var m = this.isSelected(item) ? 'unselect' : 'select';
15421 _t[m](item, true, false);
15424 if(this.multiSelect || this.singleSelect){
15425 if(this.multiSelect && e.shiftKey && this.lastSelection){
15426 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15428 this.select(item, this.multiSelect && e.ctrlKey);
15429 this.lastSelection = item;
15432 if(!this.tickable){
15433 e.preventDefault();
15441 * Get the number of selected nodes.
15444 getSelectionCount : function(){
15445 return this.selections.length;
15449 * Get the currently selected nodes.
15450 * @return {Array} An array of HTMLElements
15452 getSelectedNodes : function(){
15453 return this.selections;
15457 * Get the indexes of the selected nodes.
15460 getSelectedIndexes : function(){
15461 var indexes = [], s = this.selections;
15462 for(var i = 0, len = s.length; i < len; i++){
15463 indexes.push(s[i].nodeIndex);
15469 * Clear all selections
15470 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15472 clearSelections : function(suppressEvent){
15473 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15474 this.cmp.elements = this.selections;
15475 this.cmp.removeClass(this.selectedClass);
15476 this.selections = [];
15477 if(!suppressEvent){
15478 this.fireEvent("selectionchange", this, this.selections);
15484 * Returns true if the passed node is selected
15485 * @param {HTMLElement/Number} node The node or node index
15486 * @return {Boolean}
15488 isSelected : function(node){
15489 var s = this.selections;
15493 node = this.getNode(node);
15494 return s.indexOf(node) !== -1;
15499 * @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
15500 * @param {Boolean} keepExisting (optional) true to keep existing selections
15501 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15503 select : function(nodeInfo, keepExisting, suppressEvent){
15504 if(nodeInfo instanceof Array){
15506 this.clearSelections(true);
15508 for(var i = 0, len = nodeInfo.length; i < len; i++){
15509 this.select(nodeInfo[i], true, true);
15513 var node = this.getNode(nodeInfo);
15514 if(!node || this.isSelected(node)){
15515 return; // already selected.
15518 this.clearSelections(true);
15521 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15522 Roo.fly(node).addClass(this.selectedClass);
15523 this.selections.push(node);
15524 if(!suppressEvent){
15525 this.fireEvent("selectionchange", this, this.selections);
15533 * @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
15534 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15535 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15537 unselect : function(nodeInfo, keepExisting, suppressEvent)
15539 if(nodeInfo instanceof Array){
15540 Roo.each(this.selections, function(s) {
15541 this.unselect(s, nodeInfo);
15545 var node = this.getNode(nodeInfo);
15546 if(!node || !this.isSelected(node)){
15547 //Roo.log("not selected");
15548 return; // not selected.
15552 Roo.each(this.selections, function(s) {
15554 Roo.fly(node).removeClass(this.selectedClass);
15561 this.selections= ns;
15562 this.fireEvent("selectionchange", this, this.selections);
15566 * Gets a template node.
15567 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15568 * @return {HTMLElement} The node or null if it wasn't found
15570 getNode : function(nodeInfo){
15571 if(typeof nodeInfo == "string"){
15572 return document.getElementById(nodeInfo);
15573 }else if(typeof nodeInfo == "number"){
15574 return this.nodes[nodeInfo];
15580 * Gets a range template nodes.
15581 * @param {Number} startIndex
15582 * @param {Number} endIndex
15583 * @return {Array} An array of nodes
15585 getNodes : function(start, end){
15586 var ns = this.nodes;
15587 start = start || 0;
15588 end = typeof end == "undefined" ? ns.length - 1 : end;
15591 for(var i = start; i <= end; i++){
15595 for(var i = start; i >= end; i--){
15603 * Finds the index of the passed node
15604 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15605 * @return {Number} The index of the node or -1
15607 indexOf : function(node){
15608 node = this.getNode(node);
15609 if(typeof node.nodeIndex == "number"){
15610 return node.nodeIndex;
15612 var ns = this.nodes;
15613 for(var i = 0, len = ns.length; i < len; i++){
15624 * based on jquery fullcalendar
15628 Roo.bootstrap = Roo.bootstrap || {};
15630 * @class Roo.bootstrap.Calendar
15631 * @extends Roo.bootstrap.Component
15632 * Bootstrap Calendar class
15633 * @cfg {Boolean} loadMask (true|false) default false
15634 * @cfg {Object} header generate the user specific header of the calendar, default false
15637 * Create a new Container
15638 * @param {Object} config The config object
15643 Roo.bootstrap.Calendar = function(config){
15644 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15648 * Fires when a date is selected
15649 * @param {DatePicker} this
15650 * @param {Date} date The selected date
15654 * @event monthchange
15655 * Fires when the displayed month changes
15656 * @param {DatePicker} this
15657 * @param {Date} date The selected month
15659 'monthchange': true,
15661 * @event evententer
15662 * Fires when mouse over an event
15663 * @param {Calendar} this
15664 * @param {event} Event
15666 'evententer': true,
15668 * @event eventleave
15669 * Fires when the mouse leaves an
15670 * @param {Calendar} this
15673 'eventleave': true,
15675 * @event eventclick
15676 * Fires when the mouse click an
15677 * @param {Calendar} this
15686 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
15689 * @cfg {Number} startDay
15690 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15698 getAutoCreate : function(){
15701 var fc_button = function(name, corner, style, content ) {
15702 return Roo.apply({},{
15704 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
15706 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15709 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15720 style : 'width:100%',
15727 cls : 'fc-header-left',
15729 fc_button('prev', 'left', 'arrow', '‹' ),
15730 fc_button('next', 'right', 'arrow', '›' ),
15731 { tag: 'span', cls: 'fc-header-space' },
15732 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
15740 cls : 'fc-header-center',
15744 cls: 'fc-header-title',
15747 html : 'month / year'
15755 cls : 'fc-header-right',
15757 /* fc_button('month', 'left', '', 'month' ),
15758 fc_button('week', '', '', 'week' ),
15759 fc_button('day', 'right', '', 'day' )
15771 header = this.header;
15774 var cal_heads = function() {
15776 // fixme - handle this.
15778 for (var i =0; i < Date.dayNames.length; i++) {
15779 var d = Date.dayNames[i];
15782 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15783 html : d.substring(0,3)
15787 ret[0].cls += ' fc-first';
15788 ret[6].cls += ' fc-last';
15791 var cal_cell = function(n) {
15794 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15799 cls: 'fc-day-number',
15803 cls: 'fc-day-content',
15807 style: 'position: relative;' // height: 17px;
15819 var cal_rows = function() {
15822 for (var r = 0; r < 6; r++) {
15829 for (var i =0; i < Date.dayNames.length; i++) {
15830 var d = Date.dayNames[i];
15831 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15834 row.cn[0].cls+=' fc-first';
15835 row.cn[0].cn[0].style = 'min-height:90px';
15836 row.cn[6].cls+=' fc-last';
15840 ret[0].cls += ' fc-first';
15841 ret[4].cls += ' fc-prev-last';
15842 ret[5].cls += ' fc-last';
15849 cls: 'fc-border-separate',
15850 style : 'width:100%',
15858 cls : 'fc-first fc-last',
15876 cls : 'fc-content',
15877 style : "position: relative;",
15880 cls : 'fc-view fc-view-month fc-grid',
15881 style : 'position: relative',
15882 unselectable : 'on',
15885 cls : 'fc-event-container',
15886 style : 'position:absolute;z-index:8;top:0;left:0;'
15904 initEvents : function()
15907 throw "can not find store for calendar";
15913 style: "text-align:center",
15917 style: "background-color:white;width:50%;margin:250 auto",
15921 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15932 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15934 var size = this.el.select('.fc-content', true).first().getSize();
15935 this.maskEl.setSize(size.width, size.height);
15936 this.maskEl.enableDisplayMode("block");
15937 if(!this.loadMask){
15938 this.maskEl.hide();
15941 this.store = Roo.factory(this.store, Roo.data);
15942 this.store.on('load', this.onLoad, this);
15943 this.store.on('beforeload', this.onBeforeLoad, this);
15947 this.cells = this.el.select('.fc-day',true);
15948 //Roo.log(this.cells);
15949 this.textNodes = this.el.query('.fc-day-number');
15950 this.cells.addClassOnOver('fc-state-hover');
15952 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15953 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15954 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15955 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15957 this.on('monthchange', this.onMonthChange, this);
15959 this.update(new Date().clearTime());
15962 resize : function() {
15963 var sz = this.el.getSize();
15965 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15966 this.el.select('.fc-day-content div',true).setHeight(34);
15971 showPrevMonth : function(e){
15972 this.update(this.activeDate.add("mo", -1));
15974 showToday : function(e){
15975 this.update(new Date().clearTime());
15978 showNextMonth : function(e){
15979 this.update(this.activeDate.add("mo", 1));
15983 showPrevYear : function(){
15984 this.update(this.activeDate.add("y", -1));
15988 showNextYear : function(){
15989 this.update(this.activeDate.add("y", 1));
15994 update : function(date)
15996 var vd = this.activeDate;
15997 this.activeDate = date;
15998 // if(vd && this.el){
15999 // var t = date.getTime();
16000 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16001 // Roo.log('using add remove');
16003 // this.fireEvent('monthchange', this, date);
16005 // this.cells.removeClass("fc-state-highlight");
16006 // this.cells.each(function(c){
16007 // if(c.dateValue == t){
16008 // c.addClass("fc-state-highlight");
16009 // setTimeout(function(){
16010 // try{c.dom.firstChild.focus();}catch(e){}
16020 var days = date.getDaysInMonth();
16022 var firstOfMonth = date.getFirstDateOfMonth();
16023 var startingPos = firstOfMonth.getDay()-this.startDay;
16025 if(startingPos < this.startDay){
16029 var pm = date.add(Date.MONTH, -1);
16030 var prevStart = pm.getDaysInMonth()-startingPos;
16032 this.cells = this.el.select('.fc-day',true);
16033 this.textNodes = this.el.query('.fc-day-number');
16034 this.cells.addClassOnOver('fc-state-hover');
16036 var cells = this.cells.elements;
16037 var textEls = this.textNodes;
16039 Roo.each(cells, function(cell){
16040 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16043 days += startingPos;
16045 // convert everything to numbers so it's fast
16046 var day = 86400000;
16047 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16050 //Roo.log(prevStart);
16052 var today = new Date().clearTime().getTime();
16053 var sel = date.clearTime().getTime();
16054 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16055 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16056 var ddMatch = this.disabledDatesRE;
16057 var ddText = this.disabledDatesText;
16058 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16059 var ddaysText = this.disabledDaysText;
16060 var format = this.format;
16062 var setCellClass = function(cal, cell){
16066 //Roo.log('set Cell Class');
16068 var t = d.getTime();
16072 cell.dateValue = t;
16074 cell.className += " fc-today";
16075 cell.className += " fc-state-highlight";
16076 cell.title = cal.todayText;
16079 // disable highlight in other month..
16080 //cell.className += " fc-state-highlight";
16085 cell.className = " fc-state-disabled";
16086 cell.title = cal.minText;
16090 cell.className = " fc-state-disabled";
16091 cell.title = cal.maxText;
16095 if(ddays.indexOf(d.getDay()) != -1){
16096 cell.title = ddaysText;
16097 cell.className = " fc-state-disabled";
16100 if(ddMatch && format){
16101 var fvalue = d.dateFormat(format);
16102 if(ddMatch.test(fvalue)){
16103 cell.title = ddText.replace("%0", fvalue);
16104 cell.className = " fc-state-disabled";
16108 if (!cell.initialClassName) {
16109 cell.initialClassName = cell.dom.className;
16112 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16117 for(; i < startingPos; i++) {
16118 textEls[i].innerHTML = (++prevStart);
16119 d.setDate(d.getDate()+1);
16121 cells[i].className = "fc-past fc-other-month";
16122 setCellClass(this, cells[i]);
16127 for(; i < days; i++){
16128 intDay = i - startingPos + 1;
16129 textEls[i].innerHTML = (intDay);
16130 d.setDate(d.getDate()+1);
16132 cells[i].className = ''; // "x-date-active";
16133 setCellClass(this, cells[i]);
16137 for(; i < 42; i++) {
16138 textEls[i].innerHTML = (++extraDays);
16139 d.setDate(d.getDate()+1);
16141 cells[i].className = "fc-future fc-other-month";
16142 setCellClass(this, cells[i]);
16145 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16147 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16149 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16150 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16152 if(totalRows != 6){
16153 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16154 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16157 this.fireEvent('monthchange', this, date);
16161 if(!this.internalRender){
16162 var main = this.el.dom.firstChild;
16163 var w = main.offsetWidth;
16164 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16165 Roo.fly(main).setWidth(w);
16166 this.internalRender = true;
16167 // opera does not respect the auto grow header center column
16168 // then, after it gets a width opera refuses to recalculate
16169 // without a second pass
16170 if(Roo.isOpera && !this.secondPass){
16171 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16172 this.secondPass = true;
16173 this.update.defer(10, this, [date]);
16180 findCell : function(dt) {
16181 dt = dt.clearTime().getTime();
16183 this.cells.each(function(c){
16184 //Roo.log("check " +c.dateValue + '?=' + dt);
16185 if(c.dateValue == dt){
16195 findCells : function(ev) {
16196 var s = ev.start.clone().clearTime().getTime();
16198 var e= ev.end.clone().clearTime().getTime();
16201 this.cells.each(function(c){
16202 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16204 if(c.dateValue > e){
16207 if(c.dateValue < s){
16216 // findBestRow: function(cells)
16220 // for (var i =0 ; i < cells.length;i++) {
16221 // ret = Math.max(cells[i].rows || 0,ret);
16228 addItem : function(ev)
16230 // look for vertical location slot in
16231 var cells = this.findCells(ev);
16233 // ev.row = this.findBestRow(cells);
16235 // work out the location.
16239 for(var i =0; i < cells.length; i++) {
16241 cells[i].row = cells[0].row;
16244 cells[i].row = cells[i].row + 1;
16254 if (crow.start.getY() == cells[i].getY()) {
16256 crow.end = cells[i];
16273 cells[0].events.push(ev);
16275 this.calevents.push(ev);
16278 clearEvents: function() {
16280 if(!this.calevents){
16284 Roo.each(this.cells.elements, function(c){
16290 Roo.each(this.calevents, function(e) {
16291 Roo.each(e.els, function(el) {
16292 el.un('mouseenter' ,this.onEventEnter, this);
16293 el.un('mouseleave' ,this.onEventLeave, this);
16298 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16304 renderEvents: function()
16308 this.cells.each(function(c) {
16317 if(c.row != c.events.length){
16318 r = 4 - (4 - (c.row - c.events.length));
16321 c.events = ev.slice(0, r);
16322 c.more = ev.slice(r);
16324 if(c.more.length && c.more.length == 1){
16325 c.events.push(c.more.pop());
16328 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16332 this.cells.each(function(c) {
16334 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16337 for (var e = 0; e < c.events.length; e++){
16338 var ev = c.events[e];
16339 var rows = ev.rows;
16341 for(var i = 0; i < rows.length; i++) {
16343 // how many rows should it span..
16346 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16347 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16349 unselectable : "on",
16352 cls: 'fc-event-inner',
16356 // cls: 'fc-event-time',
16357 // html : cells.length > 1 ? '' : ev.time
16361 cls: 'fc-event-title',
16362 html : String.format('{0}', ev.title)
16369 cls: 'ui-resizable-handle ui-resizable-e',
16370 html : '  '
16377 cfg.cls += ' fc-event-start';
16379 if ((i+1) == rows.length) {
16380 cfg.cls += ' fc-event-end';
16383 var ctr = _this.el.select('.fc-event-container',true).first();
16384 var cg = ctr.createChild(cfg);
16386 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16387 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16389 var r = (c.more.length) ? 1 : 0;
16390 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
16391 cg.setWidth(ebox.right - sbox.x -2);
16393 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16394 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16395 cg.on('click', _this.onEventClick, _this, ev);
16406 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16407 style : 'position: absolute',
16408 unselectable : "on",
16411 cls: 'fc-event-inner',
16415 cls: 'fc-event-title',
16423 cls: 'ui-resizable-handle ui-resizable-e',
16424 html : '  '
16430 var ctr = _this.el.select('.fc-event-container',true).first();
16431 var cg = ctr.createChild(cfg);
16433 var sbox = c.select('.fc-day-content',true).first().getBox();
16434 var ebox = c.select('.fc-day-content',true).first().getBox();
16436 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
16437 cg.setWidth(ebox.right - sbox.x -2);
16439 cg.on('click', _this.onMoreEventClick, _this, c.more);
16449 onEventEnter: function (e, el,event,d) {
16450 this.fireEvent('evententer', this, el, event);
16453 onEventLeave: function (e, el,event,d) {
16454 this.fireEvent('eventleave', this, el, event);
16457 onEventClick: function (e, el,event,d) {
16458 this.fireEvent('eventclick', this, el, event);
16461 onMonthChange: function () {
16465 onMoreEventClick: function(e, el, more)
16469 this.calpopover.placement = 'right';
16470 this.calpopover.setTitle('More');
16472 this.calpopover.setContent('');
16474 var ctr = this.calpopover.el.select('.popover-content', true).first();
16476 Roo.each(more, function(m){
16478 cls : 'fc-event-hori fc-event-draggable',
16481 var cg = ctr.createChild(cfg);
16483 cg.on('click', _this.onEventClick, _this, m);
16486 this.calpopover.show(el);
16491 onLoad: function ()
16493 this.calevents = [];
16496 if(this.store.getCount() > 0){
16497 this.store.data.each(function(d){
16500 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16501 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16502 time : d.data.start_time,
16503 title : d.data.title,
16504 description : d.data.description,
16505 venue : d.data.venue
16510 this.renderEvents();
16512 if(this.calevents.length && this.loadMask){
16513 this.maskEl.hide();
16517 onBeforeLoad: function()
16519 this.clearEvents();
16521 this.maskEl.show();
16535 * @class Roo.bootstrap.Popover
16536 * @extends Roo.bootstrap.Component
16537 * Bootstrap Popover class
16538 * @cfg {String} html contents of the popover (or false to use children..)
16539 * @cfg {String} title of popover (or false to hide)
16540 * @cfg {String} placement how it is placed
16541 * @cfg {String} trigger click || hover (or false to trigger manually)
16542 * @cfg {String} over what (parent or false to trigger manually.)
16543 * @cfg {Number} delay - delay before showing
16546 * Create a new Popover
16547 * @param {Object} config The config object
16550 Roo.bootstrap.Popover = function(config){
16551 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16557 * After the popover show
16559 * @param {Roo.bootstrap.Popover} this
16564 * After the popover hide
16566 * @param {Roo.bootstrap.Popover} this
16572 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
16574 title: 'Fill in a title',
16577 placement : 'right',
16578 trigger : 'hover', // hover
16584 can_build_overlaid : false,
16586 getChildContainer : function()
16588 return this.el.select('.popover-content',true).first();
16591 getAutoCreate : function(){
16594 cls : 'popover roo-dynamic',
16595 style: 'display:block',
16601 cls : 'popover-inner',
16605 cls: 'popover-title',
16609 cls : 'popover-content',
16620 setTitle: function(str)
16623 this.el.select('.popover-title',true).first().dom.innerHTML = str;
16625 setContent: function(str)
16628 this.el.select('.popover-content',true).first().dom.innerHTML = str;
16630 // as it get's added to the bottom of the page.
16631 onRender : function(ct, position)
16633 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16635 var cfg = Roo.apply({}, this.getAutoCreate());
16639 cfg.cls += ' ' + this.cls;
16642 cfg.style = this.style;
16644 //Roo.log("adding to ");
16645 this.el = Roo.get(document.body).createChild(cfg, position);
16646 // Roo.log(this.el);
16651 initEvents : function()
16653 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16654 this.el.enableDisplayMode('block');
16656 if (this.over === false) {
16659 if (this.triggers === false) {
16662 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16663 var triggers = this.trigger ? this.trigger.split(' ') : [];
16664 Roo.each(triggers, function(trigger) {
16666 if (trigger == 'click') {
16667 on_el.on('click', this.toggle, this);
16668 } else if (trigger != 'manual') {
16669 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
16670 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16672 on_el.on(eventIn ,this.enter, this);
16673 on_el.on(eventOut, this.leave, this);
16684 toggle : function () {
16685 this.hoverState == 'in' ? this.leave() : this.enter();
16688 enter : function () {
16690 clearTimeout(this.timeout);
16692 this.hoverState = 'in';
16694 if (!this.delay || !this.delay.show) {
16699 this.timeout = setTimeout(function () {
16700 if (_t.hoverState == 'in') {
16703 }, this.delay.show)
16706 leave : function() {
16707 clearTimeout(this.timeout);
16709 this.hoverState = 'out';
16711 if (!this.delay || !this.delay.hide) {
16716 this.timeout = setTimeout(function () {
16717 if (_t.hoverState == 'out') {
16720 }, this.delay.hide)
16723 show : function (on_el)
16726 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16730 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16731 if (this.html !== false) {
16732 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16734 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16735 if (!this.title.length) {
16736 this.el.select('.popover-title',true).hide();
16739 var placement = typeof this.placement == 'function' ?
16740 this.placement.call(this, this.el, on_el) :
16743 var autoToken = /\s?auto?\s?/i;
16744 var autoPlace = autoToken.test(placement);
16746 placement = placement.replace(autoToken, '') || 'top';
16750 //this.el.setXY([0,0]);
16752 this.el.dom.style.display='block';
16753 this.el.addClass(placement);
16755 //this.el.appendTo(on_el);
16757 var p = this.getPosition();
16758 var box = this.el.getBox();
16763 var align = Roo.bootstrap.Popover.alignment[placement];
16764 this.el.alignTo(on_el, align[0],align[1]);
16765 //var arrow = this.el.select('.arrow',true).first();
16766 //arrow.set(align[2],
16768 this.el.addClass('in');
16771 if (this.el.hasClass('fade')) {
16775 this.hoverState = 'in';
16777 this.fireEvent('show', this);
16782 this.el.setXY([0,0]);
16783 this.el.removeClass('in');
16785 this.hoverState = null;
16787 this.fireEvent('hide', this);
16792 Roo.bootstrap.Popover.alignment = {
16793 'left' : ['r-l', [-10,0], 'right'],
16794 'right' : ['l-r', [10,0], 'left'],
16795 'bottom' : ['t-b', [0,10], 'top'],
16796 'top' : [ 'b-t', [0,-10], 'bottom']
16807 * @class Roo.bootstrap.Progress
16808 * @extends Roo.bootstrap.Component
16809 * Bootstrap Progress class
16810 * @cfg {Boolean} striped striped of the progress bar
16811 * @cfg {Boolean} active animated of the progress bar
16815 * Create a new Progress
16816 * @param {Object} config The config object
16819 Roo.bootstrap.Progress = function(config){
16820 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16823 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
16828 getAutoCreate : function(){
16836 cfg.cls += ' progress-striped';
16840 cfg.cls += ' active';
16859 * @class Roo.bootstrap.ProgressBar
16860 * @extends Roo.bootstrap.Component
16861 * Bootstrap ProgressBar class
16862 * @cfg {Number} aria_valuenow aria-value now
16863 * @cfg {Number} aria_valuemin aria-value min
16864 * @cfg {Number} aria_valuemax aria-value max
16865 * @cfg {String} label label for the progress bar
16866 * @cfg {String} panel (success | info | warning | danger )
16867 * @cfg {String} role role of the progress bar
16868 * @cfg {String} sr_only text
16872 * Create a new ProgressBar
16873 * @param {Object} config The config object
16876 Roo.bootstrap.ProgressBar = function(config){
16877 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16880 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16884 aria_valuemax : 100,
16890 getAutoCreate : function()
16895 cls: 'progress-bar',
16896 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16908 cfg.role = this.role;
16911 if(this.aria_valuenow){
16912 cfg['aria-valuenow'] = this.aria_valuenow;
16915 if(this.aria_valuemin){
16916 cfg['aria-valuemin'] = this.aria_valuemin;
16919 if(this.aria_valuemax){
16920 cfg['aria-valuemax'] = this.aria_valuemax;
16923 if(this.label && !this.sr_only){
16924 cfg.html = this.label;
16928 cfg.cls += ' progress-bar-' + this.panel;
16934 update : function(aria_valuenow)
16936 this.aria_valuenow = aria_valuenow;
16938 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16953 * @class Roo.bootstrap.TabGroup
16954 * @extends Roo.bootstrap.Column
16955 * Bootstrap Column class
16956 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16957 * @cfg {Boolean} carousel true to make the group behave like a carousel
16958 * @cfg {Boolean} bullets show bullets for the panels
16959 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16960 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16961 * @cfg {Boolean} showarrow (true|false) show arrow default true
16964 * Create a new TabGroup
16965 * @param {Object} config The config object
16968 Roo.bootstrap.TabGroup = function(config){
16969 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16971 this.navId = Roo.id();
16974 Roo.bootstrap.TabGroup.register(this);
16978 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16981 transition : false,
16986 slideOnTouch : false,
16989 getAutoCreate : function()
16991 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16993 cfg.cls += ' tab-content';
16995 if (this.carousel) {
16996 cfg.cls += ' carousel slide';
16999 cls : 'carousel-inner',
17003 if(this.bullets && !Roo.isTouch){
17006 cls : 'carousel-bullets',
17010 if(this.bullets_cls){
17011 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17018 cfg.cn[0].cn.push(bullets);
17021 if(this.showarrow){
17022 cfg.cn[0].cn.push({
17024 class : 'carousel-arrow',
17028 class : 'carousel-prev',
17032 class : 'fa fa-chevron-left'
17038 class : 'carousel-next',
17042 class : 'fa fa-chevron-right'
17055 initEvents: function()
17057 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17058 // this.el.on("touchstart", this.onTouchStart, this);
17061 if(this.autoslide){
17064 this.slideFn = window.setInterval(function() {
17065 _this.showPanelNext();
17069 if(this.showarrow){
17070 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17071 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17077 // onTouchStart : function(e, el, o)
17079 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17083 // this.showPanelNext();
17087 getChildContainer : function()
17089 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17093 * register a Navigation item
17094 * @param {Roo.bootstrap.NavItem} the navitem to add
17096 register : function(item)
17098 this.tabs.push( item);
17099 item.navId = this.navId; // not really needed..
17104 getActivePanel : function()
17107 Roo.each(this.tabs, function(t) {
17117 getPanelByName : function(n)
17120 Roo.each(this.tabs, function(t) {
17121 if (t.tabId == n) {
17129 indexOfPanel : function(p)
17132 Roo.each(this.tabs, function(t,i) {
17133 if (t.tabId == p.tabId) {
17142 * show a specific panel
17143 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17144 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17146 showPanel : function (pan)
17148 if(this.transition || typeof(pan) == 'undefined'){
17149 Roo.log("waiting for the transitionend");
17153 if (typeof(pan) == 'number') {
17154 pan = this.tabs[pan];
17157 if (typeof(pan) == 'string') {
17158 pan = this.getPanelByName(pan);
17161 var cur = this.getActivePanel();
17164 Roo.log('pan or acitve pan is undefined');
17168 if (pan.tabId == this.getActivePanel().tabId) {
17172 if (false === cur.fireEvent('beforedeactivate')) {
17176 if(this.bullets > 0 && !Roo.isTouch){
17177 this.setActiveBullet(this.indexOfPanel(pan));
17180 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17182 this.transition = true;
17183 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
17184 var lr = dir == 'next' ? 'left' : 'right';
17185 pan.el.addClass(dir); // or prev
17186 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17187 cur.el.addClass(lr); // or right
17188 pan.el.addClass(lr);
17191 cur.el.on('transitionend', function() {
17192 Roo.log("trans end?");
17194 pan.el.removeClass([lr,dir]);
17195 pan.setActive(true);
17197 cur.el.removeClass([lr]);
17198 cur.setActive(false);
17200 _this.transition = false;
17202 }, this, { single: true } );
17207 cur.setActive(false);
17208 pan.setActive(true);
17213 showPanelNext : function()
17215 var i = this.indexOfPanel(this.getActivePanel());
17217 if (i >= this.tabs.length - 1 && !this.autoslide) {
17221 if (i >= this.tabs.length - 1 && this.autoslide) {
17225 this.showPanel(this.tabs[i+1]);
17228 showPanelPrev : function()
17230 var i = this.indexOfPanel(this.getActivePanel());
17232 if (i < 1 && !this.autoslide) {
17236 if (i < 1 && this.autoslide) {
17237 i = this.tabs.length;
17240 this.showPanel(this.tabs[i-1]);
17244 addBullet: function()
17246 if(!this.bullets || Roo.isTouch){
17249 var ctr = this.el.select('.carousel-bullets',true).first();
17250 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17251 var bullet = ctr.createChild({
17252 cls : 'bullet bullet-' + i
17253 },ctr.dom.lastChild);
17258 bullet.on('click', (function(e, el, o, ii, t){
17260 e.preventDefault();
17262 this.showPanel(ii);
17264 if(this.autoslide && this.slideFn){
17265 clearInterval(this.slideFn);
17266 this.slideFn = window.setInterval(function() {
17267 _this.showPanelNext();
17271 }).createDelegate(this, [i, bullet], true));
17276 setActiveBullet : function(i)
17282 Roo.each(this.el.select('.bullet', true).elements, function(el){
17283 el.removeClass('selected');
17286 var bullet = this.el.select('.bullet-' + i, true).first();
17292 bullet.addClass('selected');
17303 Roo.apply(Roo.bootstrap.TabGroup, {
17307 * register a Navigation Group
17308 * @param {Roo.bootstrap.NavGroup} the navgroup to add
17310 register : function(navgrp)
17312 this.groups[navgrp.navId] = navgrp;
17316 * fetch a Navigation Group based on the navigation ID
17317 * if one does not exist , it will get created.
17318 * @param {string} the navgroup to add
17319 * @returns {Roo.bootstrap.NavGroup} the navgroup
17321 get: function(navId) {
17322 if (typeof(this.groups[navId]) == 'undefined') {
17323 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17325 return this.groups[navId] ;
17340 * @class Roo.bootstrap.TabPanel
17341 * @extends Roo.bootstrap.Component
17342 * Bootstrap TabPanel class
17343 * @cfg {Boolean} active panel active
17344 * @cfg {String} html panel content
17345 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17346 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17347 * @cfg {String} href click to link..
17351 * Create a new TabPanel
17352 * @param {Object} config The config object
17355 Roo.bootstrap.TabPanel = function(config){
17356 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17360 * Fires when the active status changes
17361 * @param {Roo.bootstrap.TabPanel} this
17362 * @param {Boolean} state the new state
17367 * @event beforedeactivate
17368 * Fires before a tab is de-activated - can be used to do validation on a form.
17369 * @param {Roo.bootstrap.TabPanel} this
17370 * @return {Boolean} false if there is an error
17373 'beforedeactivate': true
17376 this.tabId = this.tabId || Roo.id();
17380 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
17388 getAutoCreate : function(){
17391 // item is needed for carousel - not sure if it has any effect otherwise
17392 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17393 html: this.html || ''
17397 cfg.cls += ' active';
17401 cfg.tabId = this.tabId;
17408 initEvents: function()
17410 var p = this.parent();
17412 this.navId = this.navId || p.navId;
17414 if (typeof(this.navId) != 'undefined') {
17415 // not really needed.. but just in case.. parent should be a NavGroup.
17416 var tg = Roo.bootstrap.TabGroup.get(this.navId);
17420 var i = tg.tabs.length - 1;
17422 if(this.active && tg.bullets > 0 && i < tg.bullets){
17423 tg.setActiveBullet(i);
17427 this.el.on('click', this.onClick, this);
17430 this.el.on("touchstart", this.onTouchStart, this);
17431 this.el.on("touchmove", this.onTouchMove, this);
17432 this.el.on("touchend", this.onTouchEnd, this);
17437 onRender : function(ct, position)
17439 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17442 setActive : function(state)
17444 Roo.log("panel - set active " + this.tabId + "=" + state);
17446 this.active = state;
17448 this.el.removeClass('active');
17450 } else if (!this.el.hasClass('active')) {
17451 this.el.addClass('active');
17454 this.fireEvent('changed', this, state);
17457 onClick : function(e)
17459 e.preventDefault();
17461 if(!this.href.length){
17465 window.location.href = this.href;
17474 onTouchStart : function(e)
17476 this.swiping = false;
17478 this.startX = e.browserEvent.touches[0].clientX;
17479 this.startY = e.browserEvent.touches[0].clientY;
17482 onTouchMove : function(e)
17484 this.swiping = true;
17486 this.endX = e.browserEvent.touches[0].clientX;
17487 this.endY = e.browserEvent.touches[0].clientY;
17490 onTouchEnd : function(e)
17497 var tabGroup = this.parent();
17499 if(this.endX > this.startX){ // swiping right
17500 tabGroup.showPanelPrev();
17504 if(this.startX > this.endX){ // swiping left
17505 tabGroup.showPanelNext();
17524 * @class Roo.bootstrap.DateField
17525 * @extends Roo.bootstrap.Input
17526 * Bootstrap DateField class
17527 * @cfg {Number} weekStart default 0
17528 * @cfg {String} viewMode default empty, (months|years)
17529 * @cfg {String} minViewMode default empty, (months|years)
17530 * @cfg {Number} startDate default -Infinity
17531 * @cfg {Number} endDate default Infinity
17532 * @cfg {Boolean} todayHighlight default false
17533 * @cfg {Boolean} todayBtn default false
17534 * @cfg {Boolean} calendarWeeks default false
17535 * @cfg {Object} daysOfWeekDisabled default empty
17536 * @cfg {Boolean} singleMode default false (true | false)
17538 * @cfg {Boolean} keyboardNavigation default true
17539 * @cfg {String} language default en
17542 * Create a new DateField
17543 * @param {Object} config The config object
17546 Roo.bootstrap.DateField = function(config){
17547 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17551 * Fires when this field show.
17552 * @param {Roo.bootstrap.DateField} this
17553 * @param {Mixed} date The date value
17558 * Fires when this field hide.
17559 * @param {Roo.bootstrap.DateField} this
17560 * @param {Mixed} date The date value
17565 * Fires when select a date.
17566 * @param {Roo.bootstrap.DateField} this
17567 * @param {Mixed} date The date value
17571 * @event beforeselect
17572 * Fires when before select a date.
17573 * @param {Roo.bootstrap.DateField} this
17574 * @param {Mixed} date The date value
17576 beforeselect : true
17580 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
17583 * @cfg {String} format
17584 * The default date format string which can be overriden for localization support. The format must be
17585 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17589 * @cfg {String} altFormats
17590 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17591 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17593 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17601 todayHighlight : false,
17607 keyboardNavigation: true,
17609 calendarWeeks: false,
17611 startDate: -Infinity,
17615 daysOfWeekDisabled: [],
17619 singleMode : false,
17621 UTCDate: function()
17623 return new Date(Date.UTC.apply(Date, arguments));
17626 UTCToday: function()
17628 var today = new Date();
17629 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17632 getDate: function() {
17633 var d = this.getUTCDate();
17634 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17637 getUTCDate: function() {
17641 setDate: function(d) {
17642 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17645 setUTCDate: function(d) {
17647 this.setValue(this.formatDate(this.date));
17650 onRender: function(ct, position)
17653 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17655 this.language = this.language || 'en';
17656 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17657 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17659 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17660 this.format = this.format || 'm/d/y';
17661 this.isInline = false;
17662 this.isInput = true;
17663 this.component = this.el.select('.add-on', true).first() || false;
17664 this.component = (this.component && this.component.length === 0) ? false : this.component;
17665 this.hasInput = this.component && this.inputEl().length;
17667 if (typeof(this.minViewMode === 'string')) {
17668 switch (this.minViewMode) {
17670 this.minViewMode = 1;
17673 this.minViewMode = 2;
17676 this.minViewMode = 0;
17681 if (typeof(this.viewMode === 'string')) {
17682 switch (this.viewMode) {
17695 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17697 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17699 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17701 this.picker().on('mousedown', this.onMousedown, this);
17702 this.picker().on('click', this.onClick, this);
17704 this.picker().addClass('datepicker-dropdown');
17706 this.startViewMode = this.viewMode;
17708 if(this.singleMode){
17709 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17710 v.setVisibilityMode(Roo.Element.DISPLAY);
17714 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17715 v.setStyle('width', '189px');
17719 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17720 if(!this.calendarWeeks){
17725 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17726 v.attr('colspan', function(i, val){
17727 return parseInt(val) + 1;
17732 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17734 this.setStartDate(this.startDate);
17735 this.setEndDate(this.endDate);
17737 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17744 if(this.isInline) {
17749 picker : function()
17751 return this.pickerEl;
17752 // return this.el.select('.datepicker', true).first();
17755 fillDow: function()
17757 var dowCnt = this.weekStart;
17766 if(this.calendarWeeks){
17774 while (dowCnt < this.weekStart + 7) {
17778 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17782 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17785 fillMonths: function()
17788 var months = this.picker().select('>.datepicker-months td', true).first();
17790 months.dom.innerHTML = '';
17796 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17799 months.createChild(month);
17806 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;
17808 if (this.date < this.startDate) {
17809 this.viewDate = new Date(this.startDate);
17810 } else if (this.date > this.endDate) {
17811 this.viewDate = new Date(this.endDate);
17813 this.viewDate = new Date(this.date);
17821 var d = new Date(this.viewDate),
17822 year = d.getUTCFullYear(),
17823 month = d.getUTCMonth(),
17824 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17825 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17826 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17827 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17828 currentDate = this.date && this.date.valueOf(),
17829 today = this.UTCToday();
17831 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17833 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17835 // this.picker.select('>tfoot th.today').
17836 // .text(dates[this.language].today)
17837 // .toggle(this.todayBtn !== false);
17839 this.updateNavArrows();
17842 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17844 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17846 prevMonth.setUTCDate(day);
17848 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17850 var nextMonth = new Date(prevMonth);
17852 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17854 nextMonth = nextMonth.valueOf();
17856 var fillMonths = false;
17858 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17860 while(prevMonth.valueOf() < nextMonth) {
17863 if (prevMonth.getUTCDay() === this.weekStart) {
17865 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17873 if(this.calendarWeeks){
17874 // ISO 8601: First week contains first thursday.
17875 // ISO also states week starts on Monday, but we can be more abstract here.
17877 // Start of current week: based on weekstart/current date
17878 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17879 // Thursday of this week
17880 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17881 // First Thursday of year, year from thursday
17882 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17883 // Calendar week: ms between thursdays, div ms per day, div 7 days
17884 calWeek = (th - yth) / 864e5 / 7 + 1;
17886 fillMonths.cn.push({
17894 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17896 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17899 if (this.todayHighlight &&
17900 prevMonth.getUTCFullYear() == today.getFullYear() &&
17901 prevMonth.getUTCMonth() == today.getMonth() &&
17902 prevMonth.getUTCDate() == today.getDate()) {
17903 clsName += ' today';
17906 if (currentDate && prevMonth.valueOf() === currentDate) {
17907 clsName += ' active';
17910 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17911 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17912 clsName += ' disabled';
17915 fillMonths.cn.push({
17917 cls: 'day ' + clsName,
17918 html: prevMonth.getDate()
17921 prevMonth.setDate(prevMonth.getDate()+1);
17924 var currentYear = this.date && this.date.getUTCFullYear();
17925 var currentMonth = this.date && this.date.getUTCMonth();
17927 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17929 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17930 v.removeClass('active');
17932 if(currentYear === year && k === currentMonth){
17933 v.addClass('active');
17936 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17937 v.addClass('disabled');
17943 year = parseInt(year/10, 10) * 10;
17945 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17947 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17950 for (var i = -1; i < 11; i++) {
17951 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17953 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17961 showMode: function(dir)
17964 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17967 Roo.each(this.picker().select('>div',true).elements, function(v){
17968 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17971 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17976 if(this.isInline) {
17980 this.picker().removeClass(['bottom', 'top']);
17982 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17984 * place to the top of element!
17988 this.picker().addClass('top');
17989 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17994 this.picker().addClass('bottom');
17996 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17999 parseDate : function(value)
18001 if(!value || value instanceof Date){
18004 var v = Date.parseDate(value, this.format);
18005 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18006 v = Date.parseDate(value, 'Y-m-d');
18008 if(!v && this.altFormats){
18009 if(!this.altFormatsArray){
18010 this.altFormatsArray = this.altFormats.split("|");
18012 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18013 v = Date.parseDate(value, this.altFormatsArray[i]);
18019 formatDate : function(date, fmt)
18021 return (!date || !(date instanceof Date)) ?
18022 date : date.dateFormat(fmt || this.format);
18025 onFocus : function()
18027 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18031 onBlur : function()
18033 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18035 var d = this.inputEl().getValue();
18044 this.picker().show();
18048 this.fireEvent('show', this, this.date);
18053 if(this.isInline) {
18056 this.picker().hide();
18057 this.viewMode = this.startViewMode;
18060 this.fireEvent('hide', this, this.date);
18064 onMousedown: function(e)
18066 e.stopPropagation();
18067 e.preventDefault();
18072 Roo.bootstrap.DateField.superclass.keyup.call(this);
18076 setValue: function(v)
18078 if(this.fireEvent('beforeselect', this, v) !== false){
18079 var d = new Date(this.parseDate(v) ).clearTime();
18081 if(isNaN(d.getTime())){
18082 this.date = this.viewDate = '';
18083 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18087 v = this.formatDate(d);
18089 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18091 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18095 this.fireEvent('select', this, this.date);
18099 getValue: function()
18101 return this.formatDate(this.date);
18104 fireKey: function(e)
18106 if (!this.picker().isVisible()){
18107 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18113 var dateChanged = false,
18115 newDate, newViewDate;
18120 e.preventDefault();
18124 if (!this.keyboardNavigation) {
18127 dir = e.keyCode == 37 ? -1 : 1;
18130 newDate = this.moveYear(this.date, dir);
18131 newViewDate = this.moveYear(this.viewDate, dir);
18132 } else if (e.shiftKey){
18133 newDate = this.moveMonth(this.date, dir);
18134 newViewDate = this.moveMonth(this.viewDate, dir);
18136 newDate = new Date(this.date);
18137 newDate.setUTCDate(this.date.getUTCDate() + dir);
18138 newViewDate = new Date(this.viewDate);
18139 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18141 if (this.dateWithinRange(newDate)){
18142 this.date = newDate;
18143 this.viewDate = newViewDate;
18144 this.setValue(this.formatDate(this.date));
18146 e.preventDefault();
18147 dateChanged = true;
18152 if (!this.keyboardNavigation) {
18155 dir = e.keyCode == 38 ? -1 : 1;
18157 newDate = this.moveYear(this.date, dir);
18158 newViewDate = this.moveYear(this.viewDate, dir);
18159 } else if (e.shiftKey){
18160 newDate = this.moveMonth(this.date, dir);
18161 newViewDate = this.moveMonth(this.viewDate, dir);
18163 newDate = new Date(this.date);
18164 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18165 newViewDate = new Date(this.viewDate);
18166 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18168 if (this.dateWithinRange(newDate)){
18169 this.date = newDate;
18170 this.viewDate = newViewDate;
18171 this.setValue(this.formatDate(this.date));
18173 e.preventDefault();
18174 dateChanged = true;
18178 this.setValue(this.formatDate(this.date));
18180 e.preventDefault();
18183 this.setValue(this.formatDate(this.date));
18197 onClick: function(e)
18199 e.stopPropagation();
18200 e.preventDefault();
18202 var target = e.getTarget();
18204 if(target.nodeName.toLowerCase() === 'i'){
18205 target = Roo.get(target).dom.parentNode;
18208 var nodeName = target.nodeName;
18209 var className = target.className;
18210 var html = target.innerHTML;
18211 //Roo.log(nodeName);
18213 switch(nodeName.toLowerCase()) {
18215 switch(className) {
18221 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18222 switch(this.viewMode){
18224 this.viewDate = this.moveMonth(this.viewDate, dir);
18228 this.viewDate = this.moveYear(this.viewDate, dir);
18234 var date = new Date();
18235 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18237 this.setValue(this.formatDate(this.date));
18244 if (className.indexOf('disabled') < 0) {
18245 this.viewDate.setUTCDate(1);
18246 if (className.indexOf('month') > -1) {
18247 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18249 var year = parseInt(html, 10) || 0;
18250 this.viewDate.setUTCFullYear(year);
18254 if(this.singleMode){
18255 this.setValue(this.formatDate(this.viewDate));
18266 //Roo.log(className);
18267 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18268 var day = parseInt(html, 10) || 1;
18269 var year = this.viewDate.getUTCFullYear(),
18270 month = this.viewDate.getUTCMonth();
18272 if (className.indexOf('old') > -1) {
18279 } else if (className.indexOf('new') > -1) {
18287 //Roo.log([year,month,day]);
18288 this.date = this.UTCDate(year, month, day,0,0,0,0);
18289 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18291 //Roo.log(this.formatDate(this.date));
18292 this.setValue(this.formatDate(this.date));
18299 setStartDate: function(startDate)
18301 this.startDate = startDate || -Infinity;
18302 if (this.startDate !== -Infinity) {
18303 this.startDate = this.parseDate(this.startDate);
18306 this.updateNavArrows();
18309 setEndDate: function(endDate)
18311 this.endDate = endDate || Infinity;
18312 if (this.endDate !== Infinity) {
18313 this.endDate = this.parseDate(this.endDate);
18316 this.updateNavArrows();
18319 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18321 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18322 if (typeof(this.daysOfWeekDisabled) !== 'object') {
18323 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18325 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18326 return parseInt(d, 10);
18329 this.updateNavArrows();
18332 updateNavArrows: function()
18334 if(this.singleMode){
18338 var d = new Date(this.viewDate),
18339 year = d.getUTCFullYear(),
18340 month = d.getUTCMonth();
18342 Roo.each(this.picker().select('.prev', true).elements, function(v){
18344 switch (this.viewMode) {
18347 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18353 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18360 Roo.each(this.picker().select('.next', true).elements, function(v){
18362 switch (this.viewMode) {
18365 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18371 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18379 moveMonth: function(date, dir)
18384 var new_date = new Date(date.valueOf()),
18385 day = new_date.getUTCDate(),
18386 month = new_date.getUTCMonth(),
18387 mag = Math.abs(dir),
18389 dir = dir > 0 ? 1 : -1;
18392 // If going back one month, make sure month is not current month
18393 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18395 return new_date.getUTCMonth() == month;
18397 // If going forward one month, make sure month is as expected
18398 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18400 return new_date.getUTCMonth() != new_month;
18402 new_month = month + dir;
18403 new_date.setUTCMonth(new_month);
18404 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18405 if (new_month < 0 || new_month > 11) {
18406 new_month = (new_month + 12) % 12;
18409 // For magnitudes >1, move one month at a time...
18410 for (var i=0; i<mag; i++) {
18411 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18412 new_date = this.moveMonth(new_date, dir);
18414 // ...then reset the day, keeping it in the new month
18415 new_month = new_date.getUTCMonth();
18416 new_date.setUTCDate(day);
18418 return new_month != new_date.getUTCMonth();
18421 // Common date-resetting loop -- if date is beyond end of month, make it
18424 new_date.setUTCDate(--day);
18425 new_date.setUTCMonth(new_month);
18430 moveYear: function(date, dir)
18432 return this.moveMonth(date, dir*12);
18435 dateWithinRange: function(date)
18437 return date >= this.startDate && date <= this.endDate;
18443 this.picker().remove();
18446 validateValue : function(value)
18448 if(value.length < 1) {
18449 if(this.allowBlank){
18455 if(value.length < this.minLength){
18458 if(value.length > this.maxLength){
18462 var vt = Roo.form.VTypes;
18463 if(!vt[this.vtype](value, this)){
18467 if(typeof this.validator == "function"){
18468 var msg = this.validator(value);
18474 if(this.regex && !this.regex.test(value)){
18478 if(typeof(this.parseDate(value)) == 'undefined'){
18482 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18486 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18496 Roo.apply(Roo.bootstrap.DateField, {
18507 html: '<i class="fa fa-arrow-left"/>'
18517 html: '<i class="fa fa-arrow-right"/>'
18559 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18560 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18561 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18562 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18563 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18576 navFnc: 'FullYear',
18581 navFnc: 'FullYear',
18586 Roo.apply(Roo.bootstrap.DateField, {
18590 cls: 'datepicker dropdown-menu roo-dynamic',
18594 cls: 'datepicker-days',
18598 cls: 'table-condensed',
18600 Roo.bootstrap.DateField.head,
18604 Roo.bootstrap.DateField.footer
18611 cls: 'datepicker-months',
18615 cls: 'table-condensed',
18617 Roo.bootstrap.DateField.head,
18618 Roo.bootstrap.DateField.content,
18619 Roo.bootstrap.DateField.footer
18626 cls: 'datepicker-years',
18630 cls: 'table-condensed',
18632 Roo.bootstrap.DateField.head,
18633 Roo.bootstrap.DateField.content,
18634 Roo.bootstrap.DateField.footer
18653 * @class Roo.bootstrap.TimeField
18654 * @extends Roo.bootstrap.Input
18655 * Bootstrap DateField class
18659 * Create a new TimeField
18660 * @param {Object} config The config object
18663 Roo.bootstrap.TimeField = function(config){
18664 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18668 * Fires when this field show.
18669 * @param {Roo.bootstrap.DateField} thisthis
18670 * @param {Mixed} date The date value
18675 * Fires when this field hide.
18676 * @param {Roo.bootstrap.DateField} this
18677 * @param {Mixed} date The date value
18682 * Fires when select a date.
18683 * @param {Roo.bootstrap.DateField} this
18684 * @param {Mixed} date The date value
18690 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
18693 * @cfg {String} format
18694 * The default time format string which can be overriden for localization support. The format must be
18695 * valid according to {@link Date#parseDate} (defaults to 'H:i').
18699 onRender: function(ct, position)
18702 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18704 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18706 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18708 this.pop = this.picker().select('>.datepicker-time',true).first();
18709 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18711 this.picker().on('mousedown', this.onMousedown, this);
18712 this.picker().on('click', this.onClick, this);
18714 this.picker().addClass('datepicker-dropdown');
18719 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18720 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18721 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18722 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18723 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18724 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18728 fireKey: function(e){
18729 if (!this.picker().isVisible()){
18730 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18736 e.preventDefault();
18744 this.onTogglePeriod();
18747 this.onIncrementMinutes();
18750 this.onDecrementMinutes();
18759 onClick: function(e) {
18760 e.stopPropagation();
18761 e.preventDefault();
18764 picker : function()
18766 return this.el.select('.datepicker', true).first();
18769 fillTime: function()
18771 var time = this.pop.select('tbody', true).first();
18773 time.dom.innerHTML = '';
18788 cls: 'hours-up glyphicon glyphicon-chevron-up'
18808 cls: 'minutes-up glyphicon glyphicon-chevron-up'
18829 cls: 'timepicker-hour',
18844 cls: 'timepicker-minute',
18859 cls: 'btn btn-primary period',
18881 cls: 'hours-down glyphicon glyphicon-chevron-down'
18901 cls: 'minutes-down glyphicon glyphicon-chevron-down'
18919 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18926 var hours = this.time.getHours();
18927 var minutes = this.time.getMinutes();
18940 hours = hours - 12;
18944 hours = '0' + hours;
18948 minutes = '0' + minutes;
18951 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18952 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18953 this.pop.select('button', true).first().dom.innerHTML = period;
18959 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18961 var cls = ['bottom'];
18963 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18970 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18975 this.picker().addClass(cls.join('-'));
18979 Roo.each(cls, function(c){
18981 _this.picker().setTop(_this.inputEl().getHeight());
18985 _this.picker().setTop(0 - _this.picker().getHeight());
18990 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18994 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19001 onFocus : function()
19003 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19007 onBlur : function()
19009 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19015 this.picker().show();
19020 this.fireEvent('show', this, this.date);
19025 this.picker().hide();
19028 this.fireEvent('hide', this, this.date);
19031 setTime : function()
19034 this.setValue(this.time.format(this.format));
19036 this.fireEvent('select', this, this.date);
19041 onMousedown: function(e){
19042 e.stopPropagation();
19043 e.preventDefault();
19046 onIncrementHours: function()
19048 Roo.log('onIncrementHours');
19049 this.time = this.time.add(Date.HOUR, 1);
19054 onDecrementHours: function()
19056 Roo.log('onDecrementHours');
19057 this.time = this.time.add(Date.HOUR, -1);
19061 onIncrementMinutes: function()
19063 Roo.log('onIncrementMinutes');
19064 this.time = this.time.add(Date.MINUTE, 1);
19068 onDecrementMinutes: function()
19070 Roo.log('onDecrementMinutes');
19071 this.time = this.time.add(Date.MINUTE, -1);
19075 onTogglePeriod: function()
19077 Roo.log('onTogglePeriod');
19078 this.time = this.time.add(Date.HOUR, 12);
19085 Roo.apply(Roo.bootstrap.TimeField, {
19115 cls: 'btn btn-info ok',
19127 Roo.apply(Roo.bootstrap.TimeField, {
19131 cls: 'datepicker dropdown-menu',
19135 cls: 'datepicker-time',
19139 cls: 'table-condensed',
19141 Roo.bootstrap.TimeField.content,
19142 Roo.bootstrap.TimeField.footer
19161 * @class Roo.bootstrap.MonthField
19162 * @extends Roo.bootstrap.Input
19163 * Bootstrap MonthField class
19165 * @cfg {String} language default en
19168 * Create a new MonthField
19169 * @param {Object} config The config object
19172 Roo.bootstrap.MonthField = function(config){
19173 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19178 * Fires when this field show.
19179 * @param {Roo.bootstrap.MonthField} this
19180 * @param {Mixed} date The date value
19185 * Fires when this field hide.
19186 * @param {Roo.bootstrap.MonthField} this
19187 * @param {Mixed} date The date value
19192 * Fires when select a date.
19193 * @param {Roo.bootstrap.MonthField} this
19194 * @param {String} oldvalue The old value
19195 * @param {String} newvalue The new value
19201 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
19203 onRender: function(ct, position)
19206 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19208 this.language = this.language || 'en';
19209 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19210 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19212 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19213 this.isInline = false;
19214 this.isInput = true;
19215 this.component = this.el.select('.add-on', true).first() || false;
19216 this.component = (this.component && this.component.length === 0) ? false : this.component;
19217 this.hasInput = this.component && this.inputEL().length;
19219 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19221 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19223 this.picker().on('mousedown', this.onMousedown, this);
19224 this.picker().on('click', this.onClick, this);
19226 this.picker().addClass('datepicker-dropdown');
19228 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19229 v.setStyle('width', '189px');
19236 if(this.isInline) {
19242 setValue: function(v, suppressEvent)
19244 var o = this.getValue();
19246 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19250 if(suppressEvent !== true){
19251 this.fireEvent('select', this, o, v);
19256 getValue: function()
19261 onClick: function(e)
19263 e.stopPropagation();
19264 e.preventDefault();
19266 var target = e.getTarget();
19268 if(target.nodeName.toLowerCase() === 'i'){
19269 target = Roo.get(target).dom.parentNode;
19272 var nodeName = target.nodeName;
19273 var className = target.className;
19274 var html = target.innerHTML;
19276 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19280 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19282 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19288 picker : function()
19290 return this.pickerEl;
19293 fillMonths: function()
19296 var months = this.picker().select('>.datepicker-months td', true).first();
19298 months.dom.innerHTML = '';
19304 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19307 months.createChild(month);
19316 if(typeof(this.vIndex) == 'undefined' && this.value.length){
19317 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19320 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19321 e.removeClass('active');
19323 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19324 e.addClass('active');
19331 if(this.isInline) {
19335 this.picker().removeClass(['bottom', 'top']);
19337 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19339 * place to the top of element!
19343 this.picker().addClass('top');
19344 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19349 this.picker().addClass('bottom');
19351 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19354 onFocus : function()
19356 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19360 onBlur : function()
19362 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19364 var d = this.inputEl().getValue();
19373 this.picker().show();
19374 this.picker().select('>.datepicker-months', true).first().show();
19378 this.fireEvent('show', this, this.date);
19383 if(this.isInline) {
19386 this.picker().hide();
19387 this.fireEvent('hide', this, this.date);
19391 onMousedown: function(e)
19393 e.stopPropagation();
19394 e.preventDefault();
19399 Roo.bootstrap.MonthField.superclass.keyup.call(this);
19403 fireKey: function(e)
19405 if (!this.picker().isVisible()){
19406 if (e.keyCode == 27) {// allow escape to hide and re-show picker
19417 e.preventDefault();
19421 dir = e.keyCode == 37 ? -1 : 1;
19423 this.vIndex = this.vIndex + dir;
19425 if(this.vIndex < 0){
19429 if(this.vIndex > 11){
19433 if(isNaN(this.vIndex)){
19437 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19443 dir = e.keyCode == 38 ? -1 : 1;
19445 this.vIndex = this.vIndex + dir * 4;
19447 if(this.vIndex < 0){
19451 if(this.vIndex > 11){
19455 if(isNaN(this.vIndex)){
19459 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19464 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19465 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19469 e.preventDefault();
19472 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19473 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19489 this.picker().remove();
19494 Roo.apply(Roo.bootstrap.MonthField, {
19513 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19514 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19519 Roo.apply(Roo.bootstrap.MonthField, {
19523 cls: 'datepicker dropdown-menu roo-dynamic',
19527 cls: 'datepicker-months',
19531 cls: 'table-condensed',
19533 Roo.bootstrap.DateField.content
19553 * @class Roo.bootstrap.CheckBox
19554 * @extends Roo.bootstrap.Input
19555 * Bootstrap CheckBox class
19557 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19558 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19559 * @cfg {String} boxLabel The text that appears beside the checkbox
19560 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19561 * @cfg {Boolean} checked initnal the element
19562 * @cfg {Boolean} inline inline the element (default false)
19563 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19566 * Create a new CheckBox
19567 * @param {Object} config The config object
19570 Roo.bootstrap.CheckBox = function(config){
19571 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19576 * Fires when the element is checked or unchecked.
19577 * @param {Roo.bootstrap.CheckBox} this This input
19578 * @param {Boolean} checked The new checked value
19585 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
19587 inputType: 'checkbox',
19595 getAutoCreate : function()
19597 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19603 cfg.cls = 'form-group ' + this.inputType; //input-group
19606 cfg.cls += ' ' + this.inputType + '-inline';
19612 type : this.inputType,
19613 value : this.inputValue,
19614 cls : 'roo-' + this.inputType, //'form-box',
19615 placeholder : this.placeholder || ''
19619 if(this.inputType != 'radio'){
19623 cls : 'roo-hidden-value',
19624 value : this.checked ? this.valueOff : this.inputValue
19629 if (this.weight) { // Validity check?
19630 cfg.cls += " " + this.inputType + "-" + this.weight;
19633 if (this.disabled) {
19634 input.disabled=true;
19638 input.checked = this.checked;
19645 input.name = this.name;
19647 if(this.inputType != 'radio'){
19648 hidden.name = this.name;
19649 input.name = '_hidden_' + this.name;
19654 input.cls += ' input-' + this.size;
19659 ['xs','sm','md','lg'].map(function(size){
19660 if (settings[size]) {
19661 cfg.cls += ' col-' + size + '-' + settings[size];
19665 var inputblock = input;
19667 if (this.before || this.after) {
19670 cls : 'input-group',
19675 inputblock.cn.push({
19677 cls : 'input-group-addon',
19682 inputblock.cn.push(input);
19684 if(this.inputType != 'radio'){
19685 inputblock.cn.push(hidden);
19689 inputblock.cn.push({
19691 cls : 'input-group-addon',
19698 if (align ==='left' && this.fieldLabel.length) {
19699 // Roo.log("left and has label");
19705 cls : 'control-label col-md-' + this.labelWidth,
19706 html : this.fieldLabel
19710 cls : "col-md-" + (12 - this.labelWidth),
19717 } else if ( this.fieldLabel.length) {
19718 // Roo.log(" label");
19722 tag: this.boxLabel ? 'span' : 'label',
19724 cls: 'control-label box-input-label',
19725 //cls : 'input-group-addon',
19726 html : this.fieldLabel
19736 // Roo.log(" no label && no align");
19737 cfg.cn = [ inputblock ] ;
19743 var boxLabelCfg = {
19745 //'for': id, // box label is handled by onclick - so no for...
19747 html: this.boxLabel
19751 boxLabelCfg.tooltip = this.tooltip;
19754 cfg.cn.push(boxLabelCfg);
19757 if(this.inputType != 'radio'){
19758 cfg.cn.push(hidden);
19766 * return the real input element.
19768 inputEl: function ()
19770 return this.el.select('input.roo-' + this.inputType,true).first();
19772 hiddenEl: function ()
19774 return this.el.select('input.roo-hidden-value',true).first();
19777 labelEl: function()
19779 return this.el.select('label.control-label',true).first();
19781 /* depricated... */
19785 return this.labelEl();
19788 boxLabelEl: function()
19790 return this.el.select('label.box-label',true).first();
19793 initEvents : function()
19795 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19797 this.inputEl().on('click', this.onClick, this);
19799 if (this.boxLabel) {
19800 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
19803 this.startValue = this.getValue();
19806 Roo.bootstrap.CheckBox.register(this);
19810 onClick : function()
19812 this.setChecked(!this.checked);
19815 setChecked : function(state,suppressEvent)
19817 this.startValue = this.getValue();
19819 if(this.inputType == 'radio'){
19821 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19822 e.dom.checked = false;
19825 this.inputEl().dom.checked = true;
19827 this.inputEl().dom.value = this.inputValue;
19829 if(suppressEvent !== true){
19830 this.fireEvent('check', this, true);
19838 this.checked = state;
19840 this.inputEl().dom.checked = state;
19843 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19845 if(suppressEvent !== true){
19846 this.fireEvent('check', this, state);
19852 getValue : function()
19854 if(this.inputType == 'radio'){
19855 return this.getGroupValue();
19858 return this.hiddenEl().dom.value;
19862 getGroupValue : function()
19864 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19868 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19871 setValue : function(v,suppressEvent)
19873 if(this.inputType == 'radio'){
19874 this.setGroupValue(v, suppressEvent);
19878 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19883 setGroupValue : function(v, suppressEvent)
19885 this.startValue = this.getValue();
19887 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19888 e.dom.checked = false;
19890 if(e.dom.value == v){
19891 e.dom.checked = true;
19895 if(suppressEvent !== true){
19896 this.fireEvent('check', this, true);
19904 validate : function()
19908 (this.inputType == 'radio' && this.validateRadio()) ||
19909 (this.inputType == 'checkbox' && this.validateCheckbox())
19915 this.markInvalid();
19919 validateRadio : function()
19921 if(this.allowBlank){
19927 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19928 if(!e.dom.checked){
19940 validateCheckbox : function()
19943 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19946 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19954 for(var i in group){
19959 r = (group[i].getValue() == group[i].inputValue) ? true : false;
19966 * Mark this field as valid
19968 markValid : function()
19972 this.fireEvent('valid', this);
19974 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19977 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19984 if(this.inputType == 'radio'){
19985 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19986 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19987 e.findParent('.form-group', false, true).addClass(_this.validClass);
19994 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19995 this.el.findParent('.form-group', false, true).addClass(this.validClass);
19999 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20005 for(var i in group){
20006 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20007 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20012 * Mark this field as invalid
20013 * @param {String} msg The validation message
20015 markInvalid : function(msg)
20017 if(this.allowBlank){
20023 this.fireEvent('invalid', this, msg);
20025 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20028 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20032 label.markInvalid();
20035 if(this.inputType == 'radio'){
20036 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20037 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20038 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20045 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20046 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20050 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20056 for(var i in group){
20057 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20058 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20063 clearInvalid : function()
20065 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20067 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20070 label.iconEl.removeClass(label.validClass);
20071 label.iconEl.removeClass(label.invalidClass);
20075 disable : function()
20077 if(this.inputType != 'radio'){
20078 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20085 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20086 _this.getActionEl().addClass(this.disabledClass);
20087 e.dom.disabled = true;
20091 this.disabled = true;
20092 this.fireEvent("disable", this);
20096 enable : function()
20098 if(this.inputType != 'radio'){
20099 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20106 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20107 _this.getActionEl().removeClass(this.disabledClass);
20108 e.dom.disabled = false;
20112 this.disabled = false;
20113 this.fireEvent("enable", this);
20119 Roo.apply(Roo.bootstrap.CheckBox, {
20124 * register a CheckBox Group
20125 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20127 register : function(checkbox)
20129 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20130 this.groups[checkbox.groupId] = {};
20133 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20137 this.groups[checkbox.groupId][checkbox.name] = checkbox;
20141 * fetch a CheckBox Group based on the group ID
20142 * @param {string} the group ID
20143 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20145 get: function(groupId) {
20146 if (typeof(this.groups[groupId]) == 'undefined') {
20150 return this.groups[groupId] ;
20162 *<div class="radio">
20164 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
20165 Option one is this and that—be sure to include why it's great
20172 *<label class="radio-inline">fieldLabel</label>
20173 *<label class="radio-inline">
20174 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
20182 * @class Roo.bootstrap.Radio
20183 * @extends Roo.bootstrap.CheckBox
20184 * Bootstrap Radio class
20187 * Create a new Radio
20188 * @param {Object} config The config object
20191 Roo.bootstrap.Radio = function(config){
20192 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20196 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
20198 inputType: 'radio',
20202 getAutoCreate : function()
20204 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20205 align = align || 'left'; // default...
20212 tag : this.inline ? 'span' : 'div',
20213 cls : 'form-group',
20217 var inline = this.inline ? ' radio-inline' : '';
20221 // does not need for, as we wrap the input with it..
20223 cls : 'control-label box-label' + inline,
20226 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
20230 //cls : 'control-label' + inline,
20231 html : this.fieldLabel,
20232 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
20238 type : this.inputType,
20239 //value : (!this.checked) ? this.valueOff : this.inputValue,
20240 value : this.inputValue,
20242 placeholder : this.placeholder || '' // ?? needed????
20245 if (this.weight) { // Validity check?
20246 input.cls += " radio-" + this.weight;
20248 if (this.disabled) {
20249 input.disabled=true;
20253 input.checked = this.checked;
20257 input.name = this.name;
20261 input.cls += ' input-' + this.size;
20264 //?? can span's inline have a width??
20267 ['xs','sm','md','lg'].map(function(size){
20268 if (settings[size]) {
20269 cfg.cls += ' col-' + size + '-' + settings[size];
20273 var inputblock = input;
20275 if (this.before || this.after) {
20278 cls : 'input-group',
20283 inputblock.cn.push({
20285 cls : 'input-group-addon',
20289 inputblock.cn.push(input);
20291 inputblock.cn.push({
20293 cls : 'input-group-addon',
20301 if (this.fieldLabel && this.fieldLabel.length) {
20302 cfg.cn.push(fieldLabel);
20305 // normal bootstrap puts the input inside the label.
20306 // however with our styled version - it has to go after the input.
20308 //lbl.cn.push(inputblock);
20312 cls: 'radio' + inline,
20319 cfg.cn.push( lblwrap);
20324 html: this.boxLabel
20333 initEvents : function()
20335 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20337 this.inputEl().on('click', this.onClick, this);
20338 if (this.boxLabel) {
20339 //Roo.log('find label');
20340 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
20345 inputEl: function ()
20347 return this.el.select('input.roo-radio',true).first();
20349 onClick : function()
20352 this.setChecked(true);
20355 setChecked : function(state,suppressEvent)
20358 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20359 v.dom.checked = false;
20362 this.checked = state;
20363 this.inputEl().dom.checked = state;
20365 if(suppressEvent !== true){
20366 this.fireEvent('check', this, state);
20368 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
20372 getGroupValue : function()
20375 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20376 if(v.dom.checked == true){
20377 value = v.dom.value;
20385 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
20386 * @return {Mixed} value The field value
20388 getValue : function(){
20389 return this.getGroupValue();
20393 //<script type="text/javascript">
20396 * Based Ext JS Library 1.1.1
20397 * Copyright(c) 2006-2007, Ext JS, LLC.
20403 * @class Roo.HtmlEditorCore
20404 * @extends Roo.Component
20405 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20407 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20410 Roo.HtmlEditorCore = function(config){
20413 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20418 * @event initialize
20419 * Fires when the editor is fully initialized (including the iframe)
20420 * @param {Roo.HtmlEditorCore} this
20425 * Fires when the editor is first receives the focus. Any insertion must wait
20426 * until after this event.
20427 * @param {Roo.HtmlEditorCore} this
20431 * @event beforesync
20432 * Fires before the textarea is updated with content from the editor iframe. Return false
20433 * to cancel the sync.
20434 * @param {Roo.HtmlEditorCore} this
20435 * @param {String} html
20439 * @event beforepush
20440 * Fires before the iframe editor is updated with content from the textarea. Return false
20441 * to cancel the push.
20442 * @param {Roo.HtmlEditorCore} this
20443 * @param {String} html
20448 * Fires when the textarea is updated with content from the editor iframe.
20449 * @param {Roo.HtmlEditorCore} this
20450 * @param {String} html
20455 * Fires when the iframe editor is updated with content from the textarea.
20456 * @param {Roo.HtmlEditorCore} this
20457 * @param {String} html
20462 * @event editorevent
20463 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20464 * @param {Roo.HtmlEditorCore} this
20470 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20472 // defaults : white / black...
20473 this.applyBlacklists();
20480 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20484 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20490 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20495 * @cfg {Number} height (in pixels)
20499 * @cfg {Number} width (in pixels)
20504 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20507 stylesheets: false,
20512 // private properties
20513 validationEvent : false,
20515 initialized : false,
20517 sourceEditMode : false,
20518 onFocus : Roo.emptyFn,
20520 hideMode:'offsets',
20524 // blacklist + whitelisted elements..
20531 * Protected method that will not generally be called directly. It
20532 * is called when the editor initializes the iframe with HTML contents. Override this method if you
20533 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20535 getDocMarkup : function(){
20539 // inherit styels from page...??
20540 if (this.stylesheets === false) {
20542 Roo.get(document.head).select('style').each(function(node) {
20543 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20546 Roo.get(document.head).select('link').each(function(node) {
20547 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20550 } else if (!this.stylesheets.length) {
20552 st = '<style type="text/css">' +
20553 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20559 st += '<style type="text/css">' +
20560 'IMG { cursor: pointer } ' +
20564 return '<html><head>' + st +
20565 //<style type="text/css">' +
20566 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20568 ' </head><body class="roo-htmleditor-body"></body></html>';
20572 onRender : function(ct, position)
20575 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20576 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20579 this.el.dom.style.border = '0 none';
20580 this.el.dom.setAttribute('tabIndex', -1);
20581 this.el.addClass('x-hidden hide');
20585 if(Roo.isIE){ // fix IE 1px bogus margin
20586 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20590 this.frameId = Roo.id();
20594 var iframe = this.owner.wrap.createChild({
20596 cls: 'form-control', // bootstrap..
20598 name: this.frameId,
20599 frameBorder : 'no',
20600 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
20605 this.iframe = iframe.dom;
20607 this.assignDocWin();
20609 this.doc.designMode = 'on';
20612 this.doc.write(this.getDocMarkup());
20616 var task = { // must defer to wait for browser to be ready
20618 //console.log("run task?" + this.doc.readyState);
20619 this.assignDocWin();
20620 if(this.doc.body || this.doc.readyState == 'complete'){
20622 this.doc.designMode="on";
20626 Roo.TaskMgr.stop(task);
20627 this.initEditor.defer(10, this);
20634 Roo.TaskMgr.start(task);
20639 onResize : function(w, h)
20641 Roo.log('resize: ' +w + ',' + h );
20642 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20646 if(typeof w == 'number'){
20648 this.iframe.style.width = w + 'px';
20650 if(typeof h == 'number'){
20652 this.iframe.style.height = h + 'px';
20654 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20661 * Toggles the editor between standard and source edit mode.
20662 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20664 toggleSourceEdit : function(sourceEditMode){
20666 this.sourceEditMode = sourceEditMode === true;
20668 if(this.sourceEditMode){
20670 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
20673 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20674 //this.iframe.className = '';
20677 //this.setSize(this.owner.wrap.getSize());
20678 //this.fireEvent('editmodechange', this, this.sourceEditMode);
20685 * Protected method that will not generally be called directly. If you need/want
20686 * custom HTML cleanup, this is the method you should override.
20687 * @param {String} html The HTML to be cleaned
20688 * return {String} The cleaned HTML
20690 cleanHtml : function(html){
20691 html = String(html);
20692 if(html.length > 5){
20693 if(Roo.isSafari){ // strip safari nonsense
20694 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20697 if(html == ' '){
20704 * HTML Editor -> Textarea
20705 * Protected method that will not generally be called directly. Syncs the contents
20706 * of the editor iframe with the textarea.
20708 syncValue : function(){
20709 if(this.initialized){
20710 var bd = (this.doc.body || this.doc.documentElement);
20711 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20712 var html = bd.innerHTML;
20714 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20715 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20717 html = '<div style="'+m[0]+'">' + html + '</div>';
20720 html = this.cleanHtml(html);
20721 // fix up the special chars.. normaly like back quotes in word...
20722 // however we do not want to do this with chinese..
20723 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20724 var cc = b.charCodeAt();
20726 (cc >= 0x4E00 && cc < 0xA000 ) ||
20727 (cc >= 0x3400 && cc < 0x4E00 ) ||
20728 (cc >= 0xf900 && cc < 0xfb00 )
20734 if(this.owner.fireEvent('beforesync', this, html) !== false){
20735 this.el.dom.value = html;
20736 this.owner.fireEvent('sync', this, html);
20742 * Protected method that will not generally be called directly. Pushes the value of the textarea
20743 * into the iframe editor.
20745 pushValue : function(){
20746 if(this.initialized){
20747 var v = this.el.dom.value.trim();
20749 // if(v.length < 1){
20753 if(this.owner.fireEvent('beforepush', this, v) !== false){
20754 var d = (this.doc.body || this.doc.documentElement);
20756 this.cleanUpPaste();
20757 this.el.dom.value = d.innerHTML;
20758 this.owner.fireEvent('push', this, v);
20764 deferFocus : function(){
20765 this.focus.defer(10, this);
20769 focus : function(){
20770 if(this.win && !this.sourceEditMode){
20777 assignDocWin: function()
20779 var iframe = this.iframe;
20782 this.doc = iframe.contentWindow.document;
20783 this.win = iframe.contentWindow;
20785 // if (!Roo.get(this.frameId)) {
20788 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20789 // this.win = Roo.get(this.frameId).dom.contentWindow;
20791 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20795 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20796 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20801 initEditor : function(){
20802 //console.log("INIT EDITOR");
20803 this.assignDocWin();
20807 this.doc.designMode="on";
20809 this.doc.write(this.getDocMarkup());
20812 var dbody = (this.doc.body || this.doc.documentElement);
20813 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20814 // this copies styles from the containing element into thsi one..
20815 // not sure why we need all of this..
20816 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20818 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20819 //ss['background-attachment'] = 'fixed'; // w3c
20820 dbody.bgProperties = 'fixed'; // ie
20821 //Roo.DomHelper.applyStyles(dbody, ss);
20822 Roo.EventManager.on(this.doc, {
20823 //'mousedown': this.onEditorEvent,
20824 'mouseup': this.onEditorEvent,
20825 'dblclick': this.onEditorEvent,
20826 'click': this.onEditorEvent,
20827 'keyup': this.onEditorEvent,
20832 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20834 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20835 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20837 this.initialized = true;
20839 this.owner.fireEvent('initialize', this);
20844 onDestroy : function(){
20850 //for (var i =0; i < this.toolbars.length;i++) {
20851 // // fixme - ask toolbars for heights?
20852 // this.toolbars[i].onDestroy();
20855 //this.wrap.dom.innerHTML = '';
20856 //this.wrap.remove();
20861 onFirstFocus : function(){
20863 this.assignDocWin();
20866 this.activated = true;
20869 if(Roo.isGecko){ // prevent silly gecko errors
20871 var s = this.win.getSelection();
20872 if(!s.focusNode || s.focusNode.nodeType != 3){
20873 var r = s.getRangeAt(0);
20874 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20879 this.execCmd('useCSS', true);
20880 this.execCmd('styleWithCSS', false);
20883 this.owner.fireEvent('activate', this);
20887 adjustFont: function(btn){
20888 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20889 //if(Roo.isSafari){ // safari
20892 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20893 if(Roo.isSafari){ // safari
20894 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20895 v = (v < 10) ? 10 : v;
20896 v = (v > 48) ? 48 : v;
20897 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20902 v = Math.max(1, v+adjust);
20904 this.execCmd('FontSize', v );
20907 onEditorEvent : function(e)
20909 this.owner.fireEvent('editorevent', this, e);
20910 // this.updateToolbar();
20911 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20914 insertTag : function(tg)
20916 // could be a bit smarter... -> wrap the current selected tRoo..
20917 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20919 range = this.createRange(this.getSelection());
20920 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20921 wrappingNode.appendChild(range.extractContents());
20922 range.insertNode(wrappingNode);
20929 this.execCmd("formatblock", tg);
20933 insertText : function(txt)
20937 var range = this.createRange();
20938 range.deleteContents();
20939 //alert(Sender.getAttribute('label'));
20941 range.insertNode(this.doc.createTextNode(txt));
20947 * Executes a Midas editor command on the editor document and performs necessary focus and
20948 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20949 * @param {String} cmd The Midas command
20950 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20952 relayCmd : function(cmd, value){
20954 this.execCmd(cmd, value);
20955 this.owner.fireEvent('editorevent', this);
20956 //this.updateToolbar();
20957 this.owner.deferFocus();
20961 * Executes a Midas editor command directly on the editor document.
20962 * For visual commands, you should use {@link #relayCmd} instead.
20963 * <b>This should only be called after the editor is initialized.</b>
20964 * @param {String} cmd The Midas command
20965 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20967 execCmd : function(cmd, value){
20968 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20975 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20977 * @param {String} text | dom node..
20979 insertAtCursor : function(text)
20984 if(!this.activated){
20990 var r = this.doc.selection.createRange();
21001 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21005 // from jquery ui (MIT licenced)
21007 var win = this.win;
21009 if (win.getSelection && win.getSelection().getRangeAt) {
21010 range = win.getSelection().getRangeAt(0);
21011 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21012 range.insertNode(node);
21013 } else if (win.document.selection && win.document.selection.createRange) {
21014 // no firefox support
21015 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21016 win.document.selection.createRange().pasteHTML(txt);
21018 // no firefox support
21019 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21020 this.execCmd('InsertHTML', txt);
21029 mozKeyPress : function(e){
21031 var c = e.getCharCode(), cmd;
21034 c = String.fromCharCode(c).toLowerCase();
21048 this.cleanUpPaste.defer(100, this);
21056 e.preventDefault();
21064 fixKeys : function(){ // load time branching for fastest keydown performance
21066 return function(e){
21067 var k = e.getKey(), r;
21070 r = this.doc.selection.createRange();
21073 r.pasteHTML('    ');
21080 r = this.doc.selection.createRange();
21082 var target = r.parentElement();
21083 if(!target || target.tagName.toLowerCase() != 'li'){
21085 r.pasteHTML('<br />');
21091 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21092 this.cleanUpPaste.defer(100, this);
21098 }else if(Roo.isOpera){
21099 return function(e){
21100 var k = e.getKey();
21104 this.execCmd('InsertHTML','    ');
21107 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21108 this.cleanUpPaste.defer(100, this);
21113 }else if(Roo.isSafari){
21114 return function(e){
21115 var k = e.getKey();
21119 this.execCmd('InsertText','\t');
21123 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21124 this.cleanUpPaste.defer(100, this);
21132 getAllAncestors: function()
21134 var p = this.getSelectedNode();
21137 a.push(p); // push blank onto stack..
21138 p = this.getParentElement();
21142 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21146 a.push(this.doc.body);
21150 lastSelNode : false,
21153 getSelection : function()
21155 this.assignDocWin();
21156 return Roo.isIE ? this.doc.selection : this.win.getSelection();
21159 getSelectedNode: function()
21161 // this may only work on Gecko!!!
21163 // should we cache this!!!!
21168 var range = this.createRange(this.getSelection()).cloneRange();
21171 var parent = range.parentElement();
21173 var testRange = range.duplicate();
21174 testRange.moveToElementText(parent);
21175 if (testRange.inRange(range)) {
21178 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21181 parent = parent.parentElement;
21186 // is ancestor a text element.
21187 var ac = range.commonAncestorContainer;
21188 if (ac.nodeType == 3) {
21189 ac = ac.parentNode;
21192 var ar = ac.childNodes;
21195 var other_nodes = [];
21196 var has_other_nodes = false;
21197 for (var i=0;i<ar.length;i++) {
21198 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
21201 // fullly contained node.
21203 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21208 // probably selected..
21209 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21210 other_nodes.push(ar[i]);
21214 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
21219 has_other_nodes = true;
21221 if (!nodes.length && other_nodes.length) {
21222 nodes= other_nodes;
21224 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21230 createRange: function(sel)
21232 // this has strange effects when using with
21233 // top toolbar - not sure if it's a great idea.
21234 //this.editor.contentWindow.focus();
21235 if (typeof sel != "undefined") {
21237 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21239 return this.doc.createRange();
21242 return this.doc.createRange();
21245 getParentElement: function()
21248 this.assignDocWin();
21249 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21251 var range = this.createRange(sel);
21254 var p = range.commonAncestorContainer;
21255 while (p.nodeType == 3) { // text node
21266 * Range intersection.. the hard stuff...
21270 * [ -- selected range --- ]
21274 * if end is before start or hits it. fail.
21275 * if start is after end or hits it fail.
21277 * if either hits (but other is outside. - then it's not
21283 // @see http://www.thismuchiknow.co.uk/?p=64.
21284 rangeIntersectsNode : function(range, node)
21286 var nodeRange = node.ownerDocument.createRange();
21288 nodeRange.selectNode(node);
21290 nodeRange.selectNodeContents(node);
21293 var rangeStartRange = range.cloneRange();
21294 rangeStartRange.collapse(true);
21296 var rangeEndRange = range.cloneRange();
21297 rangeEndRange.collapse(false);
21299 var nodeStartRange = nodeRange.cloneRange();
21300 nodeStartRange.collapse(true);
21302 var nodeEndRange = nodeRange.cloneRange();
21303 nodeEndRange.collapse(false);
21305 return rangeStartRange.compareBoundaryPoints(
21306 Range.START_TO_START, nodeEndRange) == -1 &&
21307 rangeEndRange.compareBoundaryPoints(
21308 Range.START_TO_START, nodeStartRange) == 1;
21312 rangeCompareNode : function(range, node)
21314 var nodeRange = node.ownerDocument.createRange();
21316 nodeRange.selectNode(node);
21318 nodeRange.selectNodeContents(node);
21322 range.collapse(true);
21324 nodeRange.collapse(true);
21326 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21327 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
21329 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21331 var nodeIsBefore = ss == 1;
21332 var nodeIsAfter = ee == -1;
21334 if (nodeIsBefore && nodeIsAfter) {
21337 if (!nodeIsBefore && nodeIsAfter) {
21338 return 1; //right trailed.
21341 if (nodeIsBefore && !nodeIsAfter) {
21342 return 2; // left trailed.
21348 // private? - in a new class?
21349 cleanUpPaste : function()
21351 // cleans up the whole document..
21352 Roo.log('cleanuppaste');
21354 this.cleanUpChildren(this.doc.body);
21355 var clean = this.cleanWordChars(this.doc.body.innerHTML);
21356 if (clean != this.doc.body.innerHTML) {
21357 this.doc.body.innerHTML = clean;
21362 cleanWordChars : function(input) {// change the chars to hex code
21363 var he = Roo.HtmlEditorCore;
21365 var output = input;
21366 Roo.each(he.swapCodes, function(sw) {
21367 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21369 output = output.replace(swapper, sw[1]);
21376 cleanUpChildren : function (n)
21378 if (!n.childNodes.length) {
21381 for (var i = n.childNodes.length-1; i > -1 ; i--) {
21382 this.cleanUpChild(n.childNodes[i]);
21389 cleanUpChild : function (node)
21392 //console.log(node);
21393 if (node.nodeName == "#text") {
21394 // clean up silly Windows -- stuff?
21397 if (node.nodeName == "#comment") {
21398 node.parentNode.removeChild(node);
21399 // clean up silly Windows -- stuff?
21402 var lcname = node.tagName.toLowerCase();
21403 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21404 // whitelist of tags..
21406 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21408 node.parentNode.removeChild(node);
21413 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21415 // remove <a name=....> as rendering on yahoo mailer is borked with this.
21416 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21418 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21419 // remove_keep_children = true;
21422 if (remove_keep_children) {
21423 this.cleanUpChildren(node);
21424 // inserts everything just before this node...
21425 while (node.childNodes.length) {
21426 var cn = node.childNodes[0];
21427 node.removeChild(cn);
21428 node.parentNode.insertBefore(cn, node);
21430 node.parentNode.removeChild(node);
21434 if (!node.attributes || !node.attributes.length) {
21435 this.cleanUpChildren(node);
21439 function cleanAttr(n,v)
21442 if (v.match(/^\./) || v.match(/^\//)) {
21445 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21448 if (v.match(/^#/)) {
21451 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21452 node.removeAttribute(n);
21456 var cwhite = this.cwhite;
21457 var cblack = this.cblack;
21459 function cleanStyle(n,v)
21461 if (v.match(/expression/)) { //XSS?? should we even bother..
21462 node.removeAttribute(n);
21466 var parts = v.split(/;/);
21469 Roo.each(parts, function(p) {
21470 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21474 var l = p.split(':').shift().replace(/\s+/g,'');
21475 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21477 if ( cwhite.length && cblack.indexOf(l) > -1) {
21478 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21479 //node.removeAttribute(n);
21483 // only allow 'c whitelisted system attributes'
21484 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21485 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21486 //node.removeAttribute(n);
21496 if (clean.length) {
21497 node.setAttribute(n, clean.join(';'));
21499 node.removeAttribute(n);
21505 for (var i = node.attributes.length-1; i > -1 ; i--) {
21506 var a = node.attributes[i];
21509 if (a.name.toLowerCase().substr(0,2)=='on') {
21510 node.removeAttribute(a.name);
21513 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21514 node.removeAttribute(a.name);
21517 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21518 cleanAttr(a.name,a.value); // fixme..
21521 if (a.name == 'style') {
21522 cleanStyle(a.name,a.value);
21525 /// clean up MS crap..
21526 // tecnically this should be a list of valid class'es..
21529 if (a.name == 'class') {
21530 if (a.value.match(/^Mso/)) {
21531 node.className = '';
21534 if (a.value.match(/body/)) {
21535 node.className = '';
21546 this.cleanUpChildren(node);
21552 * Clean up MS wordisms...
21554 cleanWord : function(node)
21559 this.cleanWord(this.doc.body);
21562 if (node.nodeName == "#text") {
21563 // clean up silly Windows -- stuff?
21566 if (node.nodeName == "#comment") {
21567 node.parentNode.removeChild(node);
21568 // clean up silly Windows -- stuff?
21572 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21573 node.parentNode.removeChild(node);
21577 // remove - but keep children..
21578 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21579 while (node.childNodes.length) {
21580 var cn = node.childNodes[0];
21581 node.removeChild(cn);
21582 node.parentNode.insertBefore(cn, node);
21584 node.parentNode.removeChild(node);
21585 this.iterateChildren(node, this.cleanWord);
21589 if (node.className.length) {
21591 var cn = node.className.split(/\W+/);
21593 Roo.each(cn, function(cls) {
21594 if (cls.match(/Mso[a-zA-Z]+/)) {
21599 node.className = cna.length ? cna.join(' ') : '';
21601 node.removeAttribute("class");
21605 if (node.hasAttribute("lang")) {
21606 node.removeAttribute("lang");
21609 if (node.hasAttribute("style")) {
21611 var styles = node.getAttribute("style").split(";");
21613 Roo.each(styles, function(s) {
21614 if (!s.match(/:/)) {
21617 var kv = s.split(":");
21618 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21621 // what ever is left... we allow.
21624 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21625 if (!nstyle.length) {
21626 node.removeAttribute('style');
21629 this.iterateChildren(node, this.cleanWord);
21635 * iterateChildren of a Node, calling fn each time, using this as the scole..
21636 * @param {DomNode} node node to iterate children of.
21637 * @param {Function} fn method of this class to call on each item.
21639 iterateChildren : function(node, fn)
21641 if (!node.childNodes.length) {
21644 for (var i = node.childNodes.length-1; i > -1 ; i--) {
21645 fn.call(this, node.childNodes[i])
21651 * cleanTableWidths.
21653 * Quite often pasting from word etc.. results in tables with column and widths.
21654 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21657 cleanTableWidths : function(node)
21662 this.cleanTableWidths(this.doc.body);
21667 if (node.nodeName == "#text" || node.nodeName == "#comment") {
21670 Roo.log(node.tagName);
21671 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21672 this.iterateChildren(node, this.cleanTableWidths);
21675 if (node.hasAttribute('width')) {
21676 node.removeAttribute('width');
21680 if (node.hasAttribute("style")) {
21683 var styles = node.getAttribute("style").split(";");
21685 Roo.each(styles, function(s) {
21686 if (!s.match(/:/)) {
21689 var kv = s.split(":");
21690 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21693 // what ever is left... we allow.
21696 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21697 if (!nstyle.length) {
21698 node.removeAttribute('style');
21702 this.iterateChildren(node, this.cleanTableWidths);
21710 domToHTML : function(currentElement, depth, nopadtext) {
21712 depth = depth || 0;
21713 nopadtext = nopadtext || false;
21715 if (!currentElement) {
21716 return this.domToHTML(this.doc.body);
21719 //Roo.log(currentElement);
21721 var allText = false;
21722 var nodeName = currentElement.nodeName;
21723 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21725 if (nodeName == '#text') {
21727 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21732 if (nodeName != 'BODY') {
21735 // Prints the node tagName, such as <A>, <IMG>, etc
21738 for(i = 0; i < currentElement.attributes.length;i++) {
21740 var aname = currentElement.attributes.item(i).name;
21741 if (!currentElement.attributes.item(i).value.length) {
21744 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21747 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21756 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21759 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21764 // Traverse the tree
21766 var currentElementChild = currentElement.childNodes.item(i);
21767 var allText = true;
21768 var innerHTML = '';
21770 while (currentElementChild) {
21771 // Formatting code (indent the tree so it looks nice on the screen)
21772 var nopad = nopadtext;
21773 if (lastnode == 'SPAN') {
21777 if (currentElementChild.nodeName == '#text') {
21778 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21779 toadd = nopadtext ? toadd : toadd.trim();
21780 if (!nopad && toadd.length > 80) {
21781 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21783 innerHTML += toadd;
21786 currentElementChild = currentElement.childNodes.item(i);
21792 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21794 // Recursively traverse the tree structure of the child node
21795 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21796 lastnode = currentElementChild.nodeName;
21798 currentElementChild=currentElement.childNodes.item(i);
21804 // The remaining code is mostly for formatting the tree
21805 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
21810 ret+= "</"+tagName+">";
21816 applyBlacklists : function()
21818 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
21819 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
21823 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21824 if (b.indexOf(tag) > -1) {
21827 this.white.push(tag);
21831 Roo.each(w, function(tag) {
21832 if (b.indexOf(tag) > -1) {
21835 if (this.white.indexOf(tag) > -1) {
21838 this.white.push(tag);
21843 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21844 if (w.indexOf(tag) > -1) {
21847 this.black.push(tag);
21851 Roo.each(b, function(tag) {
21852 if (w.indexOf(tag) > -1) {
21855 if (this.black.indexOf(tag) > -1) {
21858 this.black.push(tag);
21863 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21864 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21868 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21869 if (b.indexOf(tag) > -1) {
21872 this.cwhite.push(tag);
21876 Roo.each(w, function(tag) {
21877 if (b.indexOf(tag) > -1) {
21880 if (this.cwhite.indexOf(tag) > -1) {
21883 this.cwhite.push(tag);
21888 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21889 if (w.indexOf(tag) > -1) {
21892 this.cblack.push(tag);
21896 Roo.each(b, function(tag) {
21897 if (w.indexOf(tag) > -1) {
21900 if (this.cblack.indexOf(tag) > -1) {
21903 this.cblack.push(tag);
21908 setStylesheets : function(stylesheets)
21910 if(typeof(stylesheets) == 'string'){
21911 Roo.get(this.iframe.contentDocument.head).createChild({
21913 rel : 'stylesheet',
21922 Roo.each(stylesheets, function(s) {
21927 Roo.get(_this.iframe.contentDocument.head).createChild({
21929 rel : 'stylesheet',
21938 removeStylesheets : function()
21942 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21947 // hide stuff that is not compatible
21961 * @event specialkey
21965 * @cfg {String} fieldClass @hide
21968 * @cfg {String} focusClass @hide
21971 * @cfg {String} autoCreate @hide
21974 * @cfg {String} inputType @hide
21977 * @cfg {String} invalidClass @hide
21980 * @cfg {String} invalidText @hide
21983 * @cfg {String} msgFx @hide
21986 * @cfg {String} validateOnBlur @hide
21990 Roo.HtmlEditorCore.white = [
21991 'area', 'br', 'img', 'input', 'hr', 'wbr',
21993 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21994 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21995 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21996 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21997 'table', 'ul', 'xmp',
21999 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
22002 'dir', 'menu', 'ol', 'ul', 'dl',
22008 Roo.HtmlEditorCore.black = [
22009 // 'embed', 'object', // enable - backend responsiblity to clean thiese
22011 'base', 'basefont', 'bgsound', 'blink', 'body',
22012 'frame', 'frameset', 'head', 'html', 'ilayer',
22013 'iframe', 'layer', 'link', 'meta', 'object',
22014 'script', 'style' ,'title', 'xml' // clean later..
22016 Roo.HtmlEditorCore.clean = [
22017 'script', 'style', 'title', 'xml'
22019 Roo.HtmlEditorCore.remove = [
22024 Roo.HtmlEditorCore.ablack = [
22028 Roo.HtmlEditorCore.aclean = [
22029 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
22033 Roo.HtmlEditorCore.pwhite= [
22034 'http', 'https', 'mailto'
22037 // white listed style attributes.
22038 Roo.HtmlEditorCore.cwhite= [
22039 // 'text-align', /// default is to allow most things..
22045 // black listed style attributes.
22046 Roo.HtmlEditorCore.cblack= [
22047 // 'font-size' -- this can be set by the project
22051 Roo.HtmlEditorCore.swapCodes =[
22070 * @class Roo.bootstrap.HtmlEditor
22071 * @extends Roo.bootstrap.TextArea
22072 * Bootstrap HtmlEditor class
22075 * Create a new HtmlEditor
22076 * @param {Object} config The config object
22079 Roo.bootstrap.HtmlEditor = function(config){
22080 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22081 if (!this.toolbars) {
22082 this.toolbars = [];
22084 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22087 * @event initialize
22088 * Fires when the editor is fully initialized (including the iframe)
22089 * @param {HtmlEditor} this
22094 * Fires when the editor is first receives the focus. Any insertion must wait
22095 * until after this event.
22096 * @param {HtmlEditor} this
22100 * @event beforesync
22101 * Fires before the textarea is updated with content from the editor iframe. Return false
22102 * to cancel the sync.
22103 * @param {HtmlEditor} this
22104 * @param {String} html
22108 * @event beforepush
22109 * Fires before the iframe editor is updated with content from the textarea. Return false
22110 * to cancel the push.
22111 * @param {HtmlEditor} this
22112 * @param {String} html
22117 * Fires when the textarea is updated with content from the editor iframe.
22118 * @param {HtmlEditor} this
22119 * @param {String} html
22124 * Fires when the iframe editor is updated with content from the textarea.
22125 * @param {HtmlEditor} this
22126 * @param {String} html
22130 * @event editmodechange
22131 * Fires when the editor switches edit modes
22132 * @param {HtmlEditor} this
22133 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22135 editmodechange: true,
22137 * @event editorevent
22138 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22139 * @param {HtmlEditor} this
22143 * @event firstfocus
22144 * Fires when on first focus - needed by toolbars..
22145 * @param {HtmlEditor} this
22150 * Auto save the htmlEditor value as a file into Events
22151 * @param {HtmlEditor} this
22155 * @event savedpreview
22156 * preview the saved version of htmlEditor
22157 * @param {HtmlEditor} this
22164 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
22168 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22173 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22178 * @cfg {Number} height (in pixels)
22182 * @cfg {Number} width (in pixels)
22187 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22190 stylesheets: false,
22195 // private properties
22196 validationEvent : false,
22198 initialized : false,
22201 onFocus : Roo.emptyFn,
22203 hideMode:'offsets',
22206 tbContainer : false,
22208 toolbarContainer :function() {
22209 return this.wrap.select('.x-html-editor-tb',true).first();
22213 * Protected method that will not generally be called directly. It
22214 * is called when the editor creates its toolbar. Override this method if you need to
22215 * add custom toolbar buttons.
22216 * @param {HtmlEditor} editor
22218 createToolbar : function(){
22220 Roo.log("create toolbars");
22222 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22223 this.toolbars[0].render(this.toolbarContainer());
22227 // if (!editor.toolbars || !editor.toolbars.length) {
22228 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22231 // for (var i =0 ; i < editor.toolbars.length;i++) {
22232 // editor.toolbars[i] = Roo.factory(
22233 // typeof(editor.toolbars[i]) == 'string' ?
22234 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
22235 // Roo.bootstrap.HtmlEditor);
22236 // editor.toolbars[i].init(editor);
22242 onRender : function(ct, position)
22244 // Roo.log("Call onRender: " + this.xtype);
22246 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22248 this.wrap = this.inputEl().wrap({
22249 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22252 this.editorcore.onRender(ct, position);
22254 if (this.resizable) {
22255 this.resizeEl = new Roo.Resizable(this.wrap, {
22259 minHeight : this.height,
22260 height: this.height,
22261 handles : this.resizable,
22264 resize : function(r, w, h) {
22265 _t.onResize(w,h); // -something
22271 this.createToolbar(this);
22274 if(!this.width && this.resizable){
22275 this.setSize(this.wrap.getSize());
22277 if (this.resizeEl) {
22278 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22279 // should trigger onReize..
22285 onResize : function(w, h)
22287 Roo.log('resize: ' +w + ',' + h );
22288 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22292 if(this.inputEl() ){
22293 if(typeof w == 'number'){
22294 var aw = w - this.wrap.getFrameWidth('lr');
22295 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22298 if(typeof h == 'number'){
22299 var tbh = -11; // fixme it needs to tool bar size!
22300 for (var i =0; i < this.toolbars.length;i++) {
22301 // fixme - ask toolbars for heights?
22302 tbh += this.toolbars[i].el.getHeight();
22303 //if (this.toolbars[i].footer) {
22304 // tbh += this.toolbars[i].footer.el.getHeight();
22312 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22313 ah -= 5; // knock a few pixes off for look..
22314 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22318 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22319 this.editorcore.onResize(ew,eh);
22324 * Toggles the editor between standard and source edit mode.
22325 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22327 toggleSourceEdit : function(sourceEditMode)
22329 this.editorcore.toggleSourceEdit(sourceEditMode);
22331 if(this.editorcore.sourceEditMode){
22332 Roo.log('editor - showing textarea');
22335 // Roo.log(this.syncValue());
22337 this.inputEl().removeClass(['hide', 'x-hidden']);
22338 this.inputEl().dom.removeAttribute('tabIndex');
22339 this.inputEl().focus();
22341 Roo.log('editor - hiding textarea');
22343 // Roo.log(this.pushValue());
22346 this.inputEl().addClass(['hide', 'x-hidden']);
22347 this.inputEl().dom.setAttribute('tabIndex', -1);
22348 //this.deferFocus();
22351 if(this.resizable){
22352 this.setSize(this.wrap.getSize());
22355 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22358 // private (for BoxComponent)
22359 adjustSize : Roo.BoxComponent.prototype.adjustSize,
22361 // private (for BoxComponent)
22362 getResizeEl : function(){
22366 // private (for BoxComponent)
22367 getPositionEl : function(){
22372 initEvents : function(){
22373 this.originalValue = this.getValue();
22377 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22380 // markInvalid : Roo.emptyFn,
22382 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22385 // clearInvalid : Roo.emptyFn,
22387 setValue : function(v){
22388 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22389 this.editorcore.pushValue();
22394 deferFocus : function(){
22395 this.focus.defer(10, this);
22399 focus : function(){
22400 this.editorcore.focus();
22406 onDestroy : function(){
22412 for (var i =0; i < this.toolbars.length;i++) {
22413 // fixme - ask toolbars for heights?
22414 this.toolbars[i].onDestroy();
22417 this.wrap.dom.innerHTML = '';
22418 this.wrap.remove();
22423 onFirstFocus : function(){
22424 //Roo.log("onFirstFocus");
22425 this.editorcore.onFirstFocus();
22426 for (var i =0; i < this.toolbars.length;i++) {
22427 this.toolbars[i].onFirstFocus();
22433 syncValue : function()
22435 this.editorcore.syncValue();
22438 pushValue : function()
22440 this.editorcore.pushValue();
22444 // hide stuff that is not compatible
22458 * @event specialkey
22462 * @cfg {String} fieldClass @hide
22465 * @cfg {String} focusClass @hide
22468 * @cfg {String} autoCreate @hide
22471 * @cfg {String} inputType @hide
22474 * @cfg {String} invalidClass @hide
22477 * @cfg {String} invalidText @hide
22480 * @cfg {String} msgFx @hide
22483 * @cfg {String} validateOnBlur @hide
22492 Roo.namespace('Roo.bootstrap.htmleditor');
22494 * @class Roo.bootstrap.HtmlEditorToolbar1
22499 new Roo.bootstrap.HtmlEditor({
22502 new Roo.bootstrap.HtmlEditorToolbar1({
22503 disable : { fonts: 1 , format: 1, ..., ... , ...],
22509 * @cfg {Object} disable List of elements to disable..
22510 * @cfg {Array} btns List of additional buttons.
22514 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22517 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22520 Roo.apply(this, config);
22522 // default disabled, based on 'good practice'..
22523 this.disable = this.disable || {};
22524 Roo.applyIf(this.disable, {
22527 specialElements : true
22529 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22531 this.editor = config.editor;
22532 this.editorcore = config.editor.editorcore;
22534 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22536 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22537 // dont call parent... till later.
22539 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
22544 editorcore : false,
22549 "h1","h2","h3","h4","h5","h6",
22551 "abbr", "acronym", "address", "cite", "samp", "var",
22555 onRender : function(ct, position)
22557 // Roo.log("Call onRender: " + this.xtype);
22559 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22561 this.el.dom.style.marginBottom = '0';
22563 var editorcore = this.editorcore;
22564 var editor= this.editor;
22567 var btn = function(id,cmd , toggle, handler){
22569 var event = toggle ? 'toggle' : 'click';
22574 xns: Roo.bootstrap,
22577 enableToggle:toggle !== false,
22579 pressed : toggle ? false : null,
22582 a.listeners[toggle ? 'toggle' : 'click'] = function() {
22583 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
22592 xns: Roo.bootstrap,
22593 glyphicon : 'font',
22597 xns: Roo.bootstrap,
22601 Roo.each(this.formats, function(f) {
22602 style.menu.items.push({
22604 xns: Roo.bootstrap,
22605 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22610 editorcore.insertTag(this.tagname);
22617 children.push(style);
22620 btn('bold',false,true);
22621 btn('italic',false,true);
22622 btn('align-left', 'justifyleft',true);
22623 btn('align-center', 'justifycenter',true);
22624 btn('align-right' , 'justifyright',true);
22625 btn('link', false, false, function(btn) {
22626 //Roo.log("create link?");
22627 var url = prompt(this.createLinkText, this.defaultLinkValue);
22628 if(url && url != 'http:/'+'/'){
22629 this.editorcore.relayCmd('createlink', url);
22632 btn('list','insertunorderedlist',true);
22633 btn('pencil', false,true, function(btn){
22636 this.toggleSourceEdit(btn.pressed);
22642 xns: Roo.bootstrap,
22647 xns: Roo.bootstrap,
22652 cog.menu.items.push({
22654 xns: Roo.bootstrap,
22655 html : Clean styles,
22660 editorcore.insertTag(this.tagname);
22669 this.xtype = 'NavSimplebar';
22671 for(var i=0;i< children.length;i++) {
22673 this.buttons.add(this.addxtypeChild(children[i]));
22677 editor.on('editorevent', this.updateToolbar, this);
22679 onBtnClick : function(id)
22681 this.editorcore.relayCmd(id);
22682 this.editorcore.focus();
22686 * Protected method that will not generally be called directly. It triggers
22687 * a toolbar update by reading the markup state of the current selection in the editor.
22689 updateToolbar: function(){
22691 if(!this.editorcore.activated){
22692 this.editor.onFirstFocus(); // is this neeed?
22696 var btns = this.buttons;
22697 var doc = this.editorcore.doc;
22698 btns.get('bold').setActive(doc.queryCommandState('bold'));
22699 btns.get('italic').setActive(doc.queryCommandState('italic'));
22700 //btns.get('underline').setActive(doc.queryCommandState('underline'));
22702 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22703 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22704 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22706 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22707 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22710 var ans = this.editorcore.getAllAncestors();
22711 if (this.formatCombo) {
22714 var store = this.formatCombo.store;
22715 this.formatCombo.setValue("");
22716 for (var i =0; i < ans.length;i++) {
22717 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22719 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22727 // hides menus... - so this cant be on a menu...
22728 Roo.bootstrap.MenuMgr.hideAll();
22730 Roo.bootstrap.MenuMgr.hideAll();
22731 //this.editorsyncValue();
22733 onFirstFocus: function() {
22734 this.buttons.each(function(item){
22738 toggleSourceEdit : function(sourceEditMode){
22741 if(sourceEditMode){
22742 Roo.log("disabling buttons");
22743 this.buttons.each( function(item){
22744 if(item.cmd != 'pencil'){
22750 Roo.log("enabling buttons");
22751 if(this.editorcore.initialized){
22752 this.buttons.each( function(item){
22758 Roo.log("calling toggole on editor");
22759 // tell the editor that it's been pressed..
22760 this.editor.toggleSourceEdit(sourceEditMode);
22770 * @class Roo.bootstrap.Table.AbstractSelectionModel
22771 * @extends Roo.util.Observable
22772 * Abstract base class for grid SelectionModels. It provides the interface that should be
22773 * implemented by descendant classes. This class should not be directly instantiated.
22776 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22777 this.locked = false;
22778 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22782 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
22783 /** @ignore Called by the grid automatically. Do not call directly. */
22784 init : function(grid){
22790 * Locks the selections.
22793 this.locked = true;
22797 * Unlocks the selections.
22799 unlock : function(){
22800 this.locked = false;
22804 * Returns true if the selections are locked.
22805 * @return {Boolean}
22807 isLocked : function(){
22808 return this.locked;
22812 * @extends Roo.bootstrap.Table.AbstractSelectionModel
22813 * @class Roo.bootstrap.Table.RowSelectionModel
22814 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22815 * It supports multiple selections and keyboard selection/navigation.
22817 * @param {Object} config
22820 Roo.bootstrap.Table.RowSelectionModel = function(config){
22821 Roo.apply(this, config);
22822 this.selections = new Roo.util.MixedCollection(false, function(o){
22827 this.lastActive = false;
22831 * @event selectionchange
22832 * Fires when the selection changes
22833 * @param {SelectionModel} this
22835 "selectionchange" : true,
22837 * @event afterselectionchange
22838 * Fires after the selection changes (eg. by key press or clicking)
22839 * @param {SelectionModel} this
22841 "afterselectionchange" : true,
22843 * @event beforerowselect
22844 * Fires when a row is selected being selected, return false to cancel.
22845 * @param {SelectionModel} this
22846 * @param {Number} rowIndex The selected index
22847 * @param {Boolean} keepExisting False if other selections will be cleared
22849 "beforerowselect" : true,
22852 * Fires when a row is selected.
22853 * @param {SelectionModel} this
22854 * @param {Number} rowIndex The selected index
22855 * @param {Roo.data.Record} r The record
22857 "rowselect" : true,
22859 * @event rowdeselect
22860 * Fires when a row is deselected.
22861 * @param {SelectionModel} this
22862 * @param {Number} rowIndex The selected index
22864 "rowdeselect" : true
22866 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22867 this.locked = false;
22870 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
22872 * @cfg {Boolean} singleSelect
22873 * True to allow selection of only one row at a time (defaults to false)
22875 singleSelect : false,
22878 initEvents : function()
22881 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22882 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
22883 //}else{ // allow click to work like normal
22884 // this.grid.on("rowclick", this.handleDragableRowClick, this);
22886 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22887 this.grid.on("rowclick", this.handleMouseDown, this);
22889 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22890 "up" : function(e){
22892 this.selectPrevious(e.shiftKey);
22893 }else if(this.last !== false && this.lastActive !== false){
22894 var last = this.last;
22895 this.selectRange(this.last, this.lastActive-1);
22896 this.grid.getView().focusRow(this.lastActive);
22897 if(last !== false){
22901 this.selectFirstRow();
22903 this.fireEvent("afterselectionchange", this);
22905 "down" : function(e){
22907 this.selectNext(e.shiftKey);
22908 }else if(this.last !== false && this.lastActive !== false){
22909 var last = this.last;
22910 this.selectRange(this.last, this.lastActive+1);
22911 this.grid.getView().focusRow(this.lastActive);
22912 if(last !== false){
22916 this.selectFirstRow();
22918 this.fireEvent("afterselectionchange", this);
22922 this.grid.store.on('load', function(){
22923 this.selections.clear();
22926 var view = this.grid.view;
22927 view.on("refresh", this.onRefresh, this);
22928 view.on("rowupdated", this.onRowUpdated, this);
22929 view.on("rowremoved", this.onRemove, this);
22934 onRefresh : function()
22936 var ds = this.grid.store, i, v = this.grid.view;
22937 var s = this.selections;
22938 s.each(function(r){
22939 if((i = ds.indexOfId(r.id)) != -1){
22948 onRemove : function(v, index, r){
22949 this.selections.remove(r);
22953 onRowUpdated : function(v, index, r){
22954 if(this.isSelected(r)){
22955 v.onRowSelect(index);
22961 * @param {Array} records The records to select
22962 * @param {Boolean} keepExisting (optional) True to keep existing selections
22964 selectRecords : function(records, keepExisting)
22967 this.clearSelections();
22969 var ds = this.grid.store;
22970 for(var i = 0, len = records.length; i < len; i++){
22971 this.selectRow(ds.indexOf(records[i]), true);
22976 * Gets the number of selected rows.
22979 getCount : function(){
22980 return this.selections.length;
22984 * Selects the first row in the grid.
22986 selectFirstRow : function(){
22991 * Select the last row.
22992 * @param {Boolean} keepExisting (optional) True to keep existing selections
22994 selectLastRow : function(keepExisting){
22995 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22996 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23000 * Selects the row immediately following the last selected row.
23001 * @param {Boolean} keepExisting (optional) True to keep existing selections
23003 selectNext : function(keepExisting)
23005 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23006 this.selectRow(this.last+1, keepExisting);
23007 this.grid.getView().focusRow(this.last);
23012 * Selects the row that precedes the last selected row.
23013 * @param {Boolean} keepExisting (optional) True to keep existing selections
23015 selectPrevious : function(keepExisting){
23017 this.selectRow(this.last-1, keepExisting);
23018 this.grid.getView().focusRow(this.last);
23023 * Returns the selected records
23024 * @return {Array} Array of selected records
23026 getSelections : function(){
23027 return [].concat(this.selections.items);
23031 * Returns the first selected record.
23034 getSelected : function(){
23035 return this.selections.itemAt(0);
23040 * Clears all selections.
23042 clearSelections : function(fast)
23048 var ds = this.grid.store;
23049 var s = this.selections;
23050 s.each(function(r){
23051 this.deselectRow(ds.indexOfId(r.id));
23055 this.selections.clear();
23062 * Selects all rows.
23064 selectAll : function(){
23068 this.selections.clear();
23069 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23070 this.selectRow(i, true);
23075 * Returns True if there is a selection.
23076 * @return {Boolean}
23078 hasSelection : function(){
23079 return this.selections.length > 0;
23083 * Returns True if the specified row is selected.
23084 * @param {Number/Record} record The record or index of the record to check
23085 * @return {Boolean}
23087 isSelected : function(index){
23088 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23089 return (r && this.selections.key(r.id) ? true : false);
23093 * Returns True if the specified record id is selected.
23094 * @param {String} id The id of record to check
23095 * @return {Boolean}
23097 isIdSelected : function(id){
23098 return (this.selections.key(id) ? true : false);
23103 handleMouseDBClick : function(e, t){
23107 handleMouseDown : function(e, t)
23109 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23110 if(this.isLocked() || rowIndex < 0 ){
23113 if(e.shiftKey && this.last !== false){
23114 var last = this.last;
23115 this.selectRange(last, rowIndex, e.ctrlKey);
23116 this.last = last; // reset the last
23120 var isSelected = this.isSelected(rowIndex);
23121 //Roo.log("select row:" + rowIndex);
23123 this.deselectRow(rowIndex);
23125 this.selectRow(rowIndex, true);
23129 if(e.button !== 0 && isSelected){
23130 alert('rowIndex 2: ' + rowIndex);
23131 view.focusRow(rowIndex);
23132 }else if(e.ctrlKey && isSelected){
23133 this.deselectRow(rowIndex);
23134 }else if(!isSelected){
23135 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23136 view.focusRow(rowIndex);
23140 this.fireEvent("afterselectionchange", this);
23143 handleDragableRowClick : function(grid, rowIndex, e)
23145 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23146 this.selectRow(rowIndex, false);
23147 grid.view.focusRow(rowIndex);
23148 this.fireEvent("afterselectionchange", this);
23153 * Selects multiple rows.
23154 * @param {Array} rows Array of the indexes of the row to select
23155 * @param {Boolean} keepExisting (optional) True to keep existing selections
23157 selectRows : function(rows, keepExisting){
23159 this.clearSelections();
23161 for(var i = 0, len = rows.length; i < len; i++){
23162 this.selectRow(rows[i], true);
23167 * Selects a range of rows. All rows in between startRow and endRow are also selected.
23168 * @param {Number} startRow The index of the first row in the range
23169 * @param {Number} endRow The index of the last row in the range
23170 * @param {Boolean} keepExisting (optional) True to retain existing selections
23172 selectRange : function(startRow, endRow, keepExisting){
23177 this.clearSelections();
23179 if(startRow <= endRow){
23180 for(var i = startRow; i <= endRow; i++){
23181 this.selectRow(i, true);
23184 for(var i = startRow; i >= endRow; i--){
23185 this.selectRow(i, true);
23191 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23192 * @param {Number} startRow The index of the first row in the range
23193 * @param {Number} endRow The index of the last row in the range
23195 deselectRange : function(startRow, endRow, preventViewNotify){
23199 for(var i = startRow; i <= endRow; i++){
23200 this.deselectRow(i, preventViewNotify);
23206 * @param {Number} row The index of the row to select
23207 * @param {Boolean} keepExisting (optional) True to keep existing selections
23209 selectRow : function(index, keepExisting, preventViewNotify)
23211 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23214 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23215 if(!keepExisting || this.singleSelect){
23216 this.clearSelections();
23219 var r = this.grid.store.getAt(index);
23220 //console.log('selectRow - record id :' + r.id);
23222 this.selections.add(r);
23223 this.last = this.lastActive = index;
23224 if(!preventViewNotify){
23225 var proxy = new Roo.Element(
23226 this.grid.getRowDom(index)
23228 proxy.addClass('bg-info info');
23230 this.fireEvent("rowselect", this, index, r);
23231 this.fireEvent("selectionchange", this);
23237 * @param {Number} row The index of the row to deselect
23239 deselectRow : function(index, preventViewNotify)
23244 if(this.last == index){
23247 if(this.lastActive == index){
23248 this.lastActive = false;
23251 var r = this.grid.store.getAt(index);
23256 this.selections.remove(r);
23257 //.console.log('deselectRow - record id :' + r.id);
23258 if(!preventViewNotify){
23260 var proxy = new Roo.Element(
23261 this.grid.getRowDom(index)
23263 proxy.removeClass('bg-info info');
23265 this.fireEvent("rowdeselect", this, index);
23266 this.fireEvent("selectionchange", this);
23270 restoreLast : function(){
23272 this.last = this._last;
23277 acceptsNav : function(row, col, cm){
23278 return !cm.isHidden(col) && cm.isCellEditable(col, row);
23282 onEditorKey : function(field, e){
23283 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23288 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23290 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23292 }else if(k == e.ENTER && !e.ctrlKey){
23296 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23298 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23300 }else if(k == e.ESC){
23304 g.startEditing(newCell[0], newCell[1]);
23310 * Ext JS Library 1.1.1
23311 * Copyright(c) 2006-2007, Ext JS, LLC.
23313 * Originally Released Under LGPL - original licence link has changed is not relivant.
23316 * <script type="text/javascript">
23320 * @class Roo.bootstrap.PagingToolbar
23321 * @extends Roo.bootstrap.NavSimplebar
23322 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23324 * Create a new PagingToolbar
23325 * @param {Object} config The config object
23326 * @param {Roo.data.Store} store
23328 Roo.bootstrap.PagingToolbar = function(config)
23330 // old args format still supported... - xtype is prefered..
23331 // created from xtype...
23333 this.ds = config.dataSource;
23335 if (config.store && !this.ds) {
23336 this.store= Roo.factory(config.store, Roo.data);
23337 this.ds = this.store;
23338 this.ds.xmodule = this.xmodule || false;
23341 this.toolbarItems = [];
23342 if (config.items) {
23343 this.toolbarItems = config.items;
23346 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23351 this.bind(this.ds);
23354 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23358 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23360 * @cfg {Roo.data.Store} dataSource
23361 * The underlying data store providing the paged data
23364 * @cfg {String/HTMLElement/Element} container
23365 * container The id or element that will contain the toolbar
23368 * @cfg {Boolean} displayInfo
23369 * True to display the displayMsg (defaults to false)
23372 * @cfg {Number} pageSize
23373 * The number of records to display per page (defaults to 20)
23377 * @cfg {String} displayMsg
23378 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23380 displayMsg : 'Displaying {0} - {1} of {2}',
23382 * @cfg {String} emptyMsg
23383 * The message to display when no records are found (defaults to "No data to display")
23385 emptyMsg : 'No data to display',
23387 * Customizable piece of the default paging text (defaults to "Page")
23390 beforePageText : "Page",
23392 * Customizable piece of the default paging text (defaults to "of %0")
23395 afterPageText : "of {0}",
23397 * Customizable piece of the default paging text (defaults to "First Page")
23400 firstText : "First Page",
23402 * Customizable piece of the default paging text (defaults to "Previous Page")
23405 prevText : "Previous Page",
23407 * Customizable piece of the default paging text (defaults to "Next Page")
23410 nextText : "Next Page",
23412 * Customizable piece of the default paging text (defaults to "Last Page")
23415 lastText : "Last Page",
23417 * Customizable piece of the default paging text (defaults to "Refresh")
23420 refreshText : "Refresh",
23424 onRender : function(ct, position)
23426 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23427 this.navgroup.parentId = this.id;
23428 this.navgroup.onRender(this.el, null);
23429 // add the buttons to the navgroup
23431 if(this.displayInfo){
23432 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23433 this.displayEl = this.el.select('.x-paging-info', true).first();
23434 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23435 // this.displayEl = navel.el.select('span',true).first();
23441 Roo.each(_this.buttons, function(e){ // this might need to use render????
23442 Roo.factory(e).onRender(_this.el, null);
23446 Roo.each(_this.toolbarItems, function(e) {
23447 _this.navgroup.addItem(e);
23451 this.first = this.navgroup.addItem({
23452 tooltip: this.firstText,
23454 icon : 'fa fa-backward',
23456 preventDefault: true,
23457 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23460 this.prev = this.navgroup.addItem({
23461 tooltip: this.prevText,
23463 icon : 'fa fa-step-backward',
23465 preventDefault: true,
23466 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
23468 //this.addSeparator();
23471 var field = this.navgroup.addItem( {
23473 cls : 'x-paging-position',
23475 html : this.beforePageText +
23476 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23477 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
23480 this.field = field.el.select('input', true).first();
23481 this.field.on("keydown", this.onPagingKeydown, this);
23482 this.field.on("focus", function(){this.dom.select();});
23485 this.afterTextEl = field.el.select('.x-paging-after',true).first();
23486 //this.field.setHeight(18);
23487 //this.addSeparator();
23488 this.next = this.navgroup.addItem({
23489 tooltip: this.nextText,
23491 html : ' <i class="fa fa-step-forward">',
23493 preventDefault: true,
23494 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
23496 this.last = this.navgroup.addItem({
23497 tooltip: this.lastText,
23498 icon : 'fa fa-forward',
23501 preventDefault: true,
23502 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
23504 //this.addSeparator();
23505 this.loading = this.navgroup.addItem({
23506 tooltip: this.refreshText,
23507 icon: 'fa fa-refresh',
23508 preventDefault: true,
23509 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23515 updateInfo : function(){
23516 if(this.displayEl){
23517 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23518 var msg = count == 0 ?
23522 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
23524 this.displayEl.update(msg);
23529 onLoad : function(ds, r, o){
23530 this.cursor = o.params ? o.params.start : 0;
23531 var d = this.getPageData(),
23535 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23536 this.field.dom.value = ap;
23537 this.first.setDisabled(ap == 1);
23538 this.prev.setDisabled(ap == 1);
23539 this.next.setDisabled(ap == ps);
23540 this.last.setDisabled(ap == ps);
23541 this.loading.enable();
23546 getPageData : function(){
23547 var total = this.ds.getTotalCount();
23550 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23551 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23556 onLoadError : function(){
23557 this.loading.enable();
23561 onPagingKeydown : function(e){
23562 var k = e.getKey();
23563 var d = this.getPageData();
23565 var v = this.field.dom.value, pageNum;
23566 if(!v || isNaN(pageNum = parseInt(v, 10))){
23567 this.field.dom.value = d.activePage;
23570 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23571 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23574 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))
23576 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23577 this.field.dom.value = pageNum;
23578 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23581 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23583 var v = this.field.dom.value, pageNum;
23584 var increment = (e.shiftKey) ? 10 : 1;
23585 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23588 if(!v || isNaN(pageNum = parseInt(v, 10))) {
23589 this.field.dom.value = d.activePage;
23592 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23594 this.field.dom.value = parseInt(v, 10) + increment;
23595 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23596 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23603 beforeLoad : function(){
23605 this.loading.disable();
23610 onClick : function(which){
23619 ds.load({params:{start: 0, limit: this.pageSize}});
23622 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23625 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23628 var total = ds.getTotalCount();
23629 var extra = total % this.pageSize;
23630 var lastStart = extra ? (total - extra) : total-this.pageSize;
23631 ds.load({params:{start: lastStart, limit: this.pageSize}});
23634 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23640 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23641 * @param {Roo.data.Store} store The data store to unbind
23643 unbind : function(ds){
23644 ds.un("beforeload", this.beforeLoad, this);
23645 ds.un("load", this.onLoad, this);
23646 ds.un("loadexception", this.onLoadError, this);
23647 ds.un("remove", this.updateInfo, this);
23648 ds.un("add", this.updateInfo, this);
23649 this.ds = undefined;
23653 * Binds the paging toolbar to the specified {@link Roo.data.Store}
23654 * @param {Roo.data.Store} store The data store to bind
23656 bind : function(ds){
23657 ds.on("beforeload", this.beforeLoad, this);
23658 ds.on("load", this.onLoad, this);
23659 ds.on("loadexception", this.onLoadError, this);
23660 ds.on("remove", this.updateInfo, this);
23661 ds.on("add", this.updateInfo, this);
23672 * @class Roo.bootstrap.MessageBar
23673 * @extends Roo.bootstrap.Component
23674 * Bootstrap MessageBar class
23675 * @cfg {String} html contents of the MessageBar
23676 * @cfg {String} weight (info | success | warning | danger) default info
23677 * @cfg {String} beforeClass insert the bar before the given class
23678 * @cfg {Boolean} closable (true | false) default false
23679 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23682 * Create a new Element
23683 * @param {Object} config The config object
23686 Roo.bootstrap.MessageBar = function(config){
23687 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23690 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
23696 beforeClass: 'bootstrap-sticky-wrap',
23698 getAutoCreate : function(){
23702 cls: 'alert alert-dismissable alert-' + this.weight,
23707 html: this.html || ''
23713 cfg.cls += ' alert-messages-fixed';
23727 onRender : function(ct, position)
23729 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23732 var cfg = Roo.apply({}, this.getAutoCreate());
23736 cfg.cls += ' ' + this.cls;
23739 cfg.style = this.style;
23741 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23743 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23746 this.el.select('>button.close').on('click', this.hide, this);
23752 if (!this.rendered) {
23758 this.fireEvent('show', this);
23764 if (!this.rendered) {
23770 this.fireEvent('hide', this);
23773 update : function()
23775 // var e = this.el.dom.firstChild;
23777 // if(this.closable){
23778 // e = e.nextSibling;
23781 // e.data = this.html || '';
23783 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23799 * @class Roo.bootstrap.Graph
23800 * @extends Roo.bootstrap.Component
23801 * Bootstrap Graph class
23805 @cfg {String} graphtype bar | vbar | pie
23806 @cfg {number} g_x coodinator | centre x (pie)
23807 @cfg {number} g_y coodinator | centre y (pie)
23808 @cfg {number} g_r radius (pie)
23809 @cfg {number} g_height height of the chart (respected by all elements in the set)
23810 @cfg {number} g_width width of the chart (respected by all elements in the set)
23811 @cfg {Object} title The title of the chart
23814 -opts (object) options for the chart
23816 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23817 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23819 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.
23820 o stacked (boolean) whether or not to tread values as in a stacked bar chart
23822 o stretch (boolean)
23824 -opts (object) options for the pie
23827 o startAngle (number)
23828 o endAngle (number)
23832 * Create a new Input
23833 * @param {Object} config The config object
23836 Roo.bootstrap.Graph = function(config){
23837 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23843 * The img click event for the img.
23844 * @param {Roo.EventObject} e
23850 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
23861 //g_colors: this.colors,
23868 getAutoCreate : function(){
23879 onRender : function(ct,position){
23882 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23884 if (typeof(Raphael) == 'undefined') {
23885 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23889 this.raphael = Raphael(this.el.dom);
23891 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23892 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23893 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23894 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23896 r.text(160, 10, "Single Series Chart").attr(txtattr);
23897 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23898 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23899 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23901 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23902 r.barchart(330, 10, 300, 220, data1);
23903 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23904 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23907 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23908 // r.barchart(30, 30, 560, 250, xdata, {
23909 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23910 // axis : "0 0 1 1",
23911 // axisxlabels : xdata
23912 // //yvalues : cols,
23915 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23917 // this.load(null,xdata,{
23918 // axis : "0 0 1 1",
23919 // axisxlabels : xdata
23924 load : function(graphtype,xdata,opts)
23926 this.raphael.clear();
23928 graphtype = this.graphtype;
23933 var r = this.raphael,
23934 fin = function () {
23935 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23937 fout = function () {
23938 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23940 pfin = function() {
23941 this.sector.stop();
23942 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23945 this.label[0].stop();
23946 this.label[0].attr({ r: 7.5 });
23947 this.label[1].attr({ "font-weight": 800 });
23950 pfout = function() {
23951 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23954 this.label[0].animate({ r: 5 }, 500, "bounce");
23955 this.label[1].attr({ "font-weight": 400 });
23961 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23964 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23967 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
23968 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23970 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23977 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23982 setTitle: function(o)
23987 initEvents: function() {
23990 this.el.on('click', this.onClick, this);
23994 onClick : function(e)
23996 Roo.log('img onclick');
23997 this.fireEvent('click', this, e);
24009 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24012 * @class Roo.bootstrap.dash.NumberBox
24013 * @extends Roo.bootstrap.Component
24014 * Bootstrap NumberBox class
24015 * @cfg {String} headline Box headline
24016 * @cfg {String} content Box content
24017 * @cfg {String} icon Box icon
24018 * @cfg {String} footer Footer text
24019 * @cfg {String} fhref Footer href
24022 * Create a new NumberBox
24023 * @param {Object} config The config object
24027 Roo.bootstrap.dash.NumberBox = function(config){
24028 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24032 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
24041 getAutoCreate : function(){
24045 cls : 'small-box ',
24053 cls : 'roo-headline',
24054 html : this.headline
24058 cls : 'roo-content',
24059 html : this.content
24073 cls : 'ion ' + this.icon
24082 cls : 'small-box-footer',
24083 href : this.fhref || '#',
24087 cfg.cn.push(footer);
24094 onRender : function(ct,position){
24095 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24102 setHeadline: function (value)
24104 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24107 setFooter: function (value, href)
24109 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24112 this.el.select('a.small-box-footer',true).first().attr('href', href);
24117 setContent: function (value)
24119 this.el.select('.roo-content',true).first().dom.innerHTML = value;
24122 initEvents: function()
24136 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24139 * @class Roo.bootstrap.dash.TabBox
24140 * @extends Roo.bootstrap.Component
24141 * Bootstrap TabBox class
24142 * @cfg {String} title Title of the TabBox
24143 * @cfg {String} icon Icon of the TabBox
24144 * @cfg {Boolean} showtabs (true|false) show the tabs default true
24145 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24148 * Create a new TabBox
24149 * @param {Object} config The config object
24153 Roo.bootstrap.dash.TabBox = function(config){
24154 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24159 * When a pane is added
24160 * @param {Roo.bootstrap.dash.TabPane} pane
24164 * @event activatepane
24165 * When a pane is activated
24166 * @param {Roo.bootstrap.dash.TabPane} pane
24168 "activatepane" : true
24176 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
24181 tabScrollable : false,
24183 getChildContainer : function()
24185 return this.el.select('.tab-content', true).first();
24188 getAutoCreate : function(){
24192 cls: 'pull-left header',
24200 cls: 'fa ' + this.icon
24206 cls: 'nav nav-tabs pull-right',
24212 if(this.tabScrollable){
24219 cls: 'nav nav-tabs pull-right',
24230 cls: 'nav-tabs-custom',
24235 cls: 'tab-content no-padding',
24243 initEvents : function()
24245 //Roo.log('add add pane handler');
24246 this.on('addpane', this.onAddPane, this);
24249 * Updates the box title
24250 * @param {String} html to set the title to.
24252 setTitle : function(value)
24254 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24256 onAddPane : function(pane)
24258 this.panes.push(pane);
24259 //Roo.log('addpane');
24261 // tabs are rendere left to right..
24262 if(!this.showtabs){
24266 var ctr = this.el.select('.nav-tabs', true).first();
24269 var existing = ctr.select('.nav-tab',true);
24270 var qty = existing.getCount();;
24273 var tab = ctr.createChild({
24275 cls : 'nav-tab' + (qty ? '' : ' active'),
24283 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24286 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24288 pane.el.addClass('active');
24293 onTabClick : function(ev,un,ob,pane)
24295 //Roo.log('tab - prev default');
24296 ev.preventDefault();
24299 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24300 pane.tab.addClass('active');
24301 //Roo.log(pane.title);
24302 this.getChildContainer().select('.tab-pane',true).removeClass('active');
24303 // technically we should have a deactivate event.. but maybe add later.
24304 // and it should not de-activate the selected tab...
24305 this.fireEvent('activatepane', pane);
24306 pane.el.addClass('active');
24307 pane.fireEvent('activate');
24312 getActivePane : function()
24315 Roo.each(this.panes, function(p) {
24316 if(p.el.hasClass('active')){
24337 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24339 * @class Roo.bootstrap.TabPane
24340 * @extends Roo.bootstrap.Component
24341 * Bootstrap TabPane class
24342 * @cfg {Boolean} active (false | true) Default false
24343 * @cfg {String} title title of panel
24347 * Create a new TabPane
24348 * @param {Object} config The config object
24351 Roo.bootstrap.dash.TabPane = function(config){
24352 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24358 * When a pane is activated
24359 * @param {Roo.bootstrap.dash.TabPane} pane
24366 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
24371 // the tabBox that this is attached to.
24374 getAutoCreate : function()
24382 cfg.cls += ' active';
24387 initEvents : function()
24389 //Roo.log('trigger add pane handler');
24390 this.parent().fireEvent('addpane', this)
24394 * Updates the tab title
24395 * @param {String} html to set the title to.
24397 setTitle: function(str)
24403 this.tab.select('a', true).first().dom.innerHTML = str;
24420 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24423 * @class Roo.bootstrap.menu.Menu
24424 * @extends Roo.bootstrap.Component
24425 * Bootstrap Menu class - container for Menu
24426 * @cfg {String} html Text of the menu
24427 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24428 * @cfg {String} icon Font awesome icon
24429 * @cfg {String} pos Menu align to (top | bottom) default bottom
24433 * Create a new Menu
24434 * @param {Object} config The config object
24438 Roo.bootstrap.menu.Menu = function(config){
24439 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24443 * @event beforeshow
24444 * Fires before this menu is displayed
24445 * @param {Roo.bootstrap.menu.Menu} this
24449 * @event beforehide
24450 * Fires before this menu is hidden
24451 * @param {Roo.bootstrap.menu.Menu} this
24456 * Fires after this menu is displayed
24457 * @param {Roo.bootstrap.menu.Menu} this
24462 * Fires after this menu is hidden
24463 * @param {Roo.bootstrap.menu.Menu} this
24468 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24469 * @param {Roo.bootstrap.menu.Menu} this
24470 * @param {Roo.EventObject} e
24477 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
24481 weight : 'default',
24486 getChildContainer : function() {
24487 if(this.isSubMenu){
24491 return this.el.select('ul.dropdown-menu', true).first();
24494 getAutoCreate : function()
24499 cls : 'roo-menu-text',
24507 cls : 'fa ' + this.icon
24518 cls : 'dropdown-button btn btn-' + this.weight,
24523 cls : 'dropdown-toggle btn btn-' + this.weight,
24533 cls : 'dropdown-menu'
24539 if(this.pos == 'top'){
24540 cfg.cls += ' dropup';
24543 if(this.isSubMenu){
24546 cls : 'dropdown-menu'
24553 onRender : function(ct, position)
24555 this.isSubMenu = ct.hasClass('dropdown-submenu');
24557 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24560 initEvents : function()
24562 if(this.isSubMenu){
24566 this.hidden = true;
24568 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24569 this.triggerEl.on('click', this.onTriggerPress, this);
24571 this.buttonEl = this.el.select('button.dropdown-button', true).first();
24572 this.buttonEl.on('click', this.onClick, this);
24578 if(this.isSubMenu){
24582 return this.el.select('ul.dropdown-menu', true).first();
24585 onClick : function(e)
24587 this.fireEvent("click", this, e);
24590 onTriggerPress : function(e)
24592 if (this.isVisible()) {
24599 isVisible : function(){
24600 return !this.hidden;
24605 this.fireEvent("beforeshow", this);
24607 this.hidden = false;
24608 this.el.addClass('open');
24610 Roo.get(document).on("mouseup", this.onMouseUp, this);
24612 this.fireEvent("show", this);
24619 this.fireEvent("beforehide", this);
24621 this.hidden = true;
24622 this.el.removeClass('open');
24624 Roo.get(document).un("mouseup", this.onMouseUp);
24626 this.fireEvent("hide", this);
24629 onMouseUp : function()
24643 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24646 * @class Roo.bootstrap.menu.Item
24647 * @extends Roo.bootstrap.Component
24648 * Bootstrap MenuItem class
24649 * @cfg {Boolean} submenu (true | false) default false
24650 * @cfg {String} html text of the item
24651 * @cfg {String} href the link
24652 * @cfg {Boolean} disable (true | false) default false
24653 * @cfg {Boolean} preventDefault (true | false) default true
24654 * @cfg {String} icon Font awesome icon
24655 * @cfg {String} pos Submenu align to (left | right) default right
24659 * Create a new Item
24660 * @param {Object} config The config object
24664 Roo.bootstrap.menu.Item = function(config){
24665 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24669 * Fires when the mouse is hovering over this menu
24670 * @param {Roo.bootstrap.menu.Item} this
24671 * @param {Roo.EventObject} e
24676 * Fires when the mouse exits this menu
24677 * @param {Roo.bootstrap.menu.Item} this
24678 * @param {Roo.EventObject} e
24684 * The raw click event for the entire grid.
24685 * @param {Roo.EventObject} e
24691 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
24696 preventDefault: true,
24701 getAutoCreate : function()
24706 cls : 'roo-menu-item-text',
24714 cls : 'fa ' + this.icon
24723 href : this.href || '#',
24730 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24734 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24736 if(this.pos == 'left'){
24737 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24744 initEvents : function()
24746 this.el.on('mouseover', this.onMouseOver, this);
24747 this.el.on('mouseout', this.onMouseOut, this);
24749 this.el.select('a', true).first().on('click', this.onClick, this);
24753 onClick : function(e)
24755 if(this.preventDefault){
24756 e.preventDefault();
24759 this.fireEvent("click", this, e);
24762 onMouseOver : function(e)
24764 if(this.submenu && this.pos == 'left'){
24765 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24768 this.fireEvent("mouseover", this, e);
24771 onMouseOut : function(e)
24773 this.fireEvent("mouseout", this, e);
24785 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24788 * @class Roo.bootstrap.menu.Separator
24789 * @extends Roo.bootstrap.Component
24790 * Bootstrap Separator class
24793 * Create a new Separator
24794 * @param {Object} config The config object
24798 Roo.bootstrap.menu.Separator = function(config){
24799 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24802 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
24804 getAutoCreate : function(){
24825 * @class Roo.bootstrap.Tooltip
24826 * Bootstrap Tooltip class
24827 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24828 * to determine which dom element triggers the tooltip.
24830 * It needs to add support for additional attributes like tooltip-position
24833 * Create a new Toolti
24834 * @param {Object} config The config object
24837 Roo.bootstrap.Tooltip = function(config){
24838 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24841 Roo.apply(Roo.bootstrap.Tooltip, {
24843 * @function init initialize tooltip monitoring.
24847 currentTip : false,
24848 currentRegion : false,
24854 Roo.get(document).on('mouseover', this.enter ,this);
24855 Roo.get(document).on('mouseout', this.leave, this);
24858 this.currentTip = new Roo.bootstrap.Tooltip();
24861 enter : function(ev)
24863 var dom = ev.getTarget();
24865 //Roo.log(['enter',dom]);
24866 var el = Roo.fly(dom);
24867 if (this.currentEl) {
24869 //Roo.log(this.currentEl);
24870 //Roo.log(this.currentEl.contains(dom));
24871 if (this.currentEl == el) {
24874 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24880 if (this.currentTip.el) {
24881 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24885 if(!el || el.dom == document){
24891 // you can not look for children, as if el is the body.. then everythign is the child..
24892 if (!el.attr('tooltip')) { //
24893 if (!el.select("[tooltip]").elements.length) {
24896 // is the mouse over this child...?
24897 bindEl = el.select("[tooltip]").first();
24898 var xy = ev.getXY();
24899 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24900 //Roo.log("not in region.");
24903 //Roo.log("child element over..");
24906 this.currentEl = bindEl;
24907 this.currentTip.bind(bindEl);
24908 this.currentRegion = Roo.lib.Region.getRegion(dom);
24909 this.currentTip.enter();
24912 leave : function(ev)
24914 var dom = ev.getTarget();
24915 //Roo.log(['leave',dom]);
24916 if (!this.currentEl) {
24921 if (dom != this.currentEl.dom) {
24924 var xy = ev.getXY();
24925 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
24928 // only activate leave if mouse cursor is outside... bounding box..
24933 if (this.currentTip) {
24934 this.currentTip.leave();
24936 //Roo.log('clear currentEl');
24937 this.currentEl = false;
24942 'left' : ['r-l', [-2,0], 'right'],
24943 'right' : ['l-r', [2,0], 'left'],
24944 'bottom' : ['t-b', [0,2], 'top'],
24945 'top' : [ 'b-t', [0,-2], 'bottom']
24951 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
24956 delay : null, // can be { show : 300 , hide: 500}
24960 hoverState : null, //???
24962 placement : 'bottom',
24964 getAutoCreate : function(){
24971 cls : 'tooltip-arrow'
24974 cls : 'tooltip-inner'
24981 bind : function(el)
24987 enter : function () {
24989 if (this.timeout != null) {
24990 clearTimeout(this.timeout);
24993 this.hoverState = 'in';
24994 //Roo.log("enter - show");
24995 if (!this.delay || !this.delay.show) {
25000 this.timeout = setTimeout(function () {
25001 if (_t.hoverState == 'in') {
25004 }, this.delay.show);
25008 clearTimeout(this.timeout);
25010 this.hoverState = 'out';
25011 if (!this.delay || !this.delay.hide) {
25017 this.timeout = setTimeout(function () {
25018 //Roo.log("leave - timeout");
25020 if (_t.hoverState == 'out') {
25022 Roo.bootstrap.Tooltip.currentEl = false;
25030 this.render(document.body);
25033 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25035 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25037 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25039 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25041 var placement = typeof this.placement == 'function' ?
25042 this.placement.call(this, this.el, on_el) :
25045 var autoToken = /\s?auto?\s?/i;
25046 var autoPlace = autoToken.test(placement);
25048 placement = placement.replace(autoToken, '') || 'top';
25052 //this.el.setXY([0,0]);
25054 //this.el.dom.style.display='block';
25056 //this.el.appendTo(on_el);
25058 var p = this.getPosition();
25059 var box = this.el.getBox();
25065 var align = Roo.bootstrap.Tooltip.alignment[placement];
25067 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25069 if(placement == 'top' || placement == 'bottom'){
25071 placement = 'right';
25074 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25075 placement = 'left';
25078 var scroll = Roo.select('body', true).first().getScroll();
25080 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25086 align = Roo.bootstrap.Tooltip.alignment[placement];
25088 this.el.alignTo(this.bindEl, align[0],align[1]);
25089 //var arrow = this.el.select('.arrow',true).first();
25090 //arrow.set(align[2],
25092 this.el.addClass(placement);
25094 this.el.addClass('in fade');
25096 this.hoverState = null;
25098 if (this.el.hasClass('fade')) {
25109 //this.el.setXY([0,0]);
25110 this.el.removeClass('in');
25126 * @class Roo.bootstrap.LocationPicker
25127 * @extends Roo.bootstrap.Component
25128 * Bootstrap LocationPicker class
25129 * @cfg {Number} latitude Position when init default 0
25130 * @cfg {Number} longitude Position when init default 0
25131 * @cfg {Number} zoom default 15
25132 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25133 * @cfg {Boolean} mapTypeControl default false
25134 * @cfg {Boolean} disableDoubleClickZoom default false
25135 * @cfg {Boolean} scrollwheel default true
25136 * @cfg {Boolean} streetViewControl default false
25137 * @cfg {Number} radius default 0
25138 * @cfg {String} locationName
25139 * @cfg {Boolean} draggable default true
25140 * @cfg {Boolean} enableAutocomplete default false
25141 * @cfg {Boolean} enableReverseGeocode default true
25142 * @cfg {String} markerTitle
25145 * Create a new LocationPicker
25146 * @param {Object} config The config object
25150 Roo.bootstrap.LocationPicker = function(config){
25152 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25157 * Fires when the picker initialized.
25158 * @param {Roo.bootstrap.LocationPicker} this
25159 * @param {Google Location} location
25163 * @event positionchanged
25164 * Fires when the picker position changed.
25165 * @param {Roo.bootstrap.LocationPicker} this
25166 * @param {Google Location} location
25168 positionchanged : true,
25171 * Fires when the map resize.
25172 * @param {Roo.bootstrap.LocationPicker} this
25177 * Fires when the map show.
25178 * @param {Roo.bootstrap.LocationPicker} this
25183 * Fires when the map hide.
25184 * @param {Roo.bootstrap.LocationPicker} this
25189 * Fires when click the map.
25190 * @param {Roo.bootstrap.LocationPicker} this
25191 * @param {Map event} e
25195 * @event mapRightClick
25196 * Fires when right click the map.
25197 * @param {Roo.bootstrap.LocationPicker} this
25198 * @param {Map event} e
25200 mapRightClick : true,
25202 * @event markerClick
25203 * Fires when click the marker.
25204 * @param {Roo.bootstrap.LocationPicker} this
25205 * @param {Map event} e
25207 markerClick : true,
25209 * @event markerRightClick
25210 * Fires when right click the marker.
25211 * @param {Roo.bootstrap.LocationPicker} this
25212 * @param {Map event} e
25214 markerRightClick : true,
25216 * @event OverlayViewDraw
25217 * Fires when OverlayView Draw
25218 * @param {Roo.bootstrap.LocationPicker} this
25220 OverlayViewDraw : true,
25222 * @event OverlayViewOnAdd
25223 * Fires when OverlayView Draw
25224 * @param {Roo.bootstrap.LocationPicker} this
25226 OverlayViewOnAdd : true,
25228 * @event OverlayViewOnRemove
25229 * Fires when OverlayView Draw
25230 * @param {Roo.bootstrap.LocationPicker} this
25232 OverlayViewOnRemove : true,
25234 * @event OverlayViewShow
25235 * Fires when OverlayView Draw
25236 * @param {Roo.bootstrap.LocationPicker} this
25237 * @param {Pixel} cpx
25239 OverlayViewShow : true,
25241 * @event OverlayViewHide
25242 * Fires when OverlayView Draw
25243 * @param {Roo.bootstrap.LocationPicker} this
25245 OverlayViewHide : true,
25247 * @event loadexception
25248 * Fires when load google lib failed.
25249 * @param {Roo.bootstrap.LocationPicker} this
25251 loadexception : true
25256 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
25258 gMapContext: false,
25264 mapTypeControl: false,
25265 disableDoubleClickZoom: false,
25267 streetViewControl: false,
25271 enableAutocomplete: false,
25272 enableReverseGeocode: true,
25275 getAutoCreate: function()
25280 cls: 'roo-location-picker'
25286 initEvents: function(ct, position)
25288 if(!this.el.getWidth() || this.isApplied()){
25292 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25297 initial: function()
25299 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25300 this.fireEvent('loadexception', this);
25304 if(!this.mapTypeId){
25305 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25308 this.gMapContext = this.GMapContext();
25310 this.initOverlayView();
25312 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25316 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25317 _this.setPosition(_this.gMapContext.marker.position);
25320 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25321 _this.fireEvent('mapClick', this, event);
25325 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25326 _this.fireEvent('mapRightClick', this, event);
25330 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25331 _this.fireEvent('markerClick', this, event);
25335 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25336 _this.fireEvent('markerRightClick', this, event);
25340 this.setPosition(this.gMapContext.location);
25342 this.fireEvent('initial', this, this.gMapContext.location);
25345 initOverlayView: function()
25349 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25353 _this.fireEvent('OverlayViewDraw', _this);
25358 _this.fireEvent('OverlayViewOnAdd', _this);
25361 onRemove: function()
25363 _this.fireEvent('OverlayViewOnRemove', _this);
25366 show: function(cpx)
25368 _this.fireEvent('OverlayViewShow', _this, cpx);
25373 _this.fireEvent('OverlayViewHide', _this);
25379 fromLatLngToContainerPixel: function(event)
25381 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25384 isApplied: function()
25386 return this.getGmapContext() == false ? false : true;
25389 getGmapContext: function()
25391 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25394 GMapContext: function()
25396 var position = new google.maps.LatLng(this.latitude, this.longitude);
25398 var _map = new google.maps.Map(this.el.dom, {
25401 mapTypeId: this.mapTypeId,
25402 mapTypeControl: this.mapTypeControl,
25403 disableDoubleClickZoom: this.disableDoubleClickZoom,
25404 scrollwheel: this.scrollwheel,
25405 streetViewControl: this.streetViewControl,
25406 locationName: this.locationName,
25407 draggable: this.draggable,
25408 enableAutocomplete: this.enableAutocomplete,
25409 enableReverseGeocode: this.enableReverseGeocode
25412 var _marker = new google.maps.Marker({
25413 position: position,
25415 title: this.markerTitle,
25416 draggable: this.draggable
25423 location: position,
25424 radius: this.radius,
25425 locationName: this.locationName,
25426 addressComponents: {
25427 formatted_address: null,
25428 addressLine1: null,
25429 addressLine2: null,
25431 streetNumber: null,
25435 stateOrProvince: null
25438 domContainer: this.el.dom,
25439 geodecoder: new google.maps.Geocoder()
25443 drawCircle: function(center, radius, options)
25445 if (this.gMapContext.circle != null) {
25446 this.gMapContext.circle.setMap(null);
25450 options = Roo.apply({}, options, {
25451 strokeColor: "#0000FF",
25452 strokeOpacity: .35,
25454 fillColor: "#0000FF",
25458 options.map = this.gMapContext.map;
25459 options.radius = radius;
25460 options.center = center;
25461 this.gMapContext.circle = new google.maps.Circle(options);
25462 return this.gMapContext.circle;
25468 setPosition: function(location)
25470 this.gMapContext.location = location;
25471 this.gMapContext.marker.setPosition(location);
25472 this.gMapContext.map.panTo(location);
25473 this.drawCircle(location, this.gMapContext.radius, {});
25477 if (this.gMapContext.settings.enableReverseGeocode) {
25478 this.gMapContext.geodecoder.geocode({
25479 latLng: this.gMapContext.location
25480 }, function(results, status) {
25482 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25483 _this.gMapContext.locationName = results[0].formatted_address;
25484 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25486 _this.fireEvent('positionchanged', this, location);
25493 this.fireEvent('positionchanged', this, location);
25498 google.maps.event.trigger(this.gMapContext.map, "resize");
25500 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25502 this.fireEvent('resize', this);
25505 setPositionByLatLng: function(latitude, longitude)
25507 this.setPosition(new google.maps.LatLng(latitude, longitude));
25510 getCurrentPosition: function()
25513 latitude: this.gMapContext.location.lat(),
25514 longitude: this.gMapContext.location.lng()
25518 getAddressName: function()
25520 return this.gMapContext.locationName;
25523 getAddressComponents: function()
25525 return this.gMapContext.addressComponents;
25528 address_component_from_google_geocode: function(address_components)
25532 for (var i = 0; i < address_components.length; i++) {
25533 var component = address_components[i];
25534 if (component.types.indexOf("postal_code") >= 0) {
25535 result.postalCode = component.short_name;
25536 } else if (component.types.indexOf("street_number") >= 0) {
25537 result.streetNumber = component.short_name;
25538 } else if (component.types.indexOf("route") >= 0) {
25539 result.streetName = component.short_name;
25540 } else if (component.types.indexOf("neighborhood") >= 0) {
25541 result.city = component.short_name;
25542 } else if (component.types.indexOf("locality") >= 0) {
25543 result.city = component.short_name;
25544 } else if (component.types.indexOf("sublocality") >= 0) {
25545 result.district = component.short_name;
25546 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25547 result.stateOrProvince = component.short_name;
25548 } else if (component.types.indexOf("country") >= 0) {
25549 result.country = component.short_name;
25553 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25554 result.addressLine2 = "";
25558 setZoomLevel: function(zoom)
25560 this.gMapContext.map.setZoom(zoom);
25573 this.fireEvent('show', this);
25584 this.fireEvent('hide', this);
25589 Roo.apply(Roo.bootstrap.LocationPicker, {
25591 OverlayView : function(map, options)
25593 options = options || {};
25607 * @class Roo.bootstrap.Alert
25608 * @extends Roo.bootstrap.Component
25609 * Bootstrap Alert class
25610 * @cfg {String} title The title of alert
25611 * @cfg {String} html The content of alert
25612 * @cfg {String} weight ( success | info | warning | danger )
25613 * @cfg {String} faicon font-awesomeicon
25616 * Create a new alert
25617 * @param {Object} config The config object
25621 Roo.bootstrap.Alert = function(config){
25622 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25626 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
25633 getAutoCreate : function()
25642 cls : 'roo-alert-icon'
25647 cls : 'roo-alert-title',
25652 cls : 'roo-alert-text',
25659 cfg.cn[0].cls += ' fa ' + this.faicon;
25663 cfg.cls += ' alert-' + this.weight;
25669 initEvents: function()
25671 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25674 setTitle : function(str)
25676 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25679 setText : function(str)
25681 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25684 setWeight : function(weight)
25687 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25690 this.weight = weight;
25692 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25695 setIcon : function(icon)
25698 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25701 this.faicon = icon;
25703 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25724 * @class Roo.bootstrap.UploadCropbox
25725 * @extends Roo.bootstrap.Component
25726 * Bootstrap UploadCropbox class
25727 * @cfg {String} emptyText show when image has been loaded
25728 * @cfg {String} rotateNotify show when image too small to rotate
25729 * @cfg {Number} errorTimeout default 3000
25730 * @cfg {Number} minWidth default 300
25731 * @cfg {Number} minHeight default 300
25732 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25733 * @cfg {Boolean} isDocument (true|false) default false
25734 * @cfg {String} url action url
25735 * @cfg {String} paramName default 'imageUpload'
25736 * @cfg {String} method default POST
25737 * @cfg {Boolean} loadMask (true|false) default true
25738 * @cfg {Boolean} loadingText default 'Loading...'
25741 * Create a new UploadCropbox
25742 * @param {Object} config The config object
25745 Roo.bootstrap.UploadCropbox = function(config){
25746 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25750 * @event beforeselectfile
25751 * Fire before select file
25752 * @param {Roo.bootstrap.UploadCropbox} this
25754 "beforeselectfile" : true,
25757 * Fire after initEvent
25758 * @param {Roo.bootstrap.UploadCropbox} this
25763 * Fire after initEvent
25764 * @param {Roo.bootstrap.UploadCropbox} this
25765 * @param {String} data
25770 * Fire when preparing the file data
25771 * @param {Roo.bootstrap.UploadCropbox} this
25772 * @param {Object} file
25777 * Fire when get exception
25778 * @param {Roo.bootstrap.UploadCropbox} this
25779 * @param {XMLHttpRequest} xhr
25781 "exception" : true,
25783 * @event beforeloadcanvas
25784 * Fire before load the canvas
25785 * @param {Roo.bootstrap.UploadCropbox} this
25786 * @param {String} src
25788 "beforeloadcanvas" : true,
25791 * Fire when trash image
25792 * @param {Roo.bootstrap.UploadCropbox} this
25797 * Fire when download the image
25798 * @param {Roo.bootstrap.UploadCropbox} this
25802 * @event footerbuttonclick
25803 * Fire when footerbuttonclick
25804 * @param {Roo.bootstrap.UploadCropbox} this
25805 * @param {String} type
25807 "footerbuttonclick" : true,
25811 * @param {Roo.bootstrap.UploadCropbox} this
25816 * Fire when rotate the image
25817 * @param {Roo.bootstrap.UploadCropbox} this
25818 * @param {String} pos
25823 * Fire when inspect the file
25824 * @param {Roo.bootstrap.UploadCropbox} this
25825 * @param {Object} file
25830 * Fire when xhr upload the file
25831 * @param {Roo.bootstrap.UploadCropbox} this
25832 * @param {Object} data
25837 * Fire when arrange the file data
25838 * @param {Roo.bootstrap.UploadCropbox} this
25839 * @param {Object} formData
25844 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25847 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
25849 emptyText : 'Click to upload image',
25850 rotateNotify : 'Image is too small to rotate',
25851 errorTimeout : 3000,
25865 cropType : 'image/jpeg',
25867 canvasLoaded : false,
25868 isDocument : false,
25870 paramName : 'imageUpload',
25872 loadingText : 'Loading...',
25875 getAutoCreate : function()
25879 cls : 'roo-upload-cropbox',
25883 cls : 'roo-upload-cropbox-selector',
25888 cls : 'roo-upload-cropbox-body',
25889 style : 'cursor:pointer',
25893 cls : 'roo-upload-cropbox-preview'
25897 cls : 'roo-upload-cropbox-thumb'
25901 cls : 'roo-upload-cropbox-empty-notify',
25902 html : this.emptyText
25906 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25907 html : this.rotateNotify
25913 cls : 'roo-upload-cropbox-footer',
25916 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25926 onRender : function(ct, position)
25928 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25930 if (this.buttons.length) {
25932 Roo.each(this.buttons, function(bb) {
25934 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25936 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25942 this.maskEl = this.el;
25946 initEvents : function()
25948 this.urlAPI = (window.createObjectURL && window) ||
25949 (window.URL && URL.revokeObjectURL && URL) ||
25950 (window.webkitURL && webkitURL);
25952 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25953 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25955 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25956 this.selectorEl.hide();
25958 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25959 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25961 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25962 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25963 this.thumbEl.hide();
25965 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25966 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25968 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25969 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25970 this.errorEl.hide();
25972 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25973 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25974 this.footerEl.hide();
25976 this.setThumbBoxSize();
25982 this.fireEvent('initial', this);
25989 window.addEventListener("resize", function() { _this.resize(); } );
25991 this.bodyEl.on('click', this.beforeSelectFile, this);
25994 this.bodyEl.on('touchstart', this.onTouchStart, this);
25995 this.bodyEl.on('touchmove', this.onTouchMove, this);
25996 this.bodyEl.on('touchend', this.onTouchEnd, this);
26000 this.bodyEl.on('mousedown', this.onMouseDown, this);
26001 this.bodyEl.on('mousemove', this.onMouseMove, this);
26002 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26003 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26004 Roo.get(document).on('mouseup', this.onMouseUp, this);
26007 this.selectorEl.on('change', this.onFileSelected, this);
26013 this.baseScale = 1;
26015 this.baseRotate = 1;
26016 this.dragable = false;
26017 this.pinching = false;
26020 this.cropData = false;
26021 this.notifyEl.dom.innerHTML = this.emptyText;
26023 this.selectorEl.dom.value = '';
26027 resize : function()
26029 if(this.fireEvent('resize', this) != false){
26030 this.setThumbBoxPosition();
26031 this.setCanvasPosition();
26035 onFooterButtonClick : function(e, el, o, type)
26038 case 'rotate-left' :
26039 this.onRotateLeft(e);
26041 case 'rotate-right' :
26042 this.onRotateRight(e);
26045 this.beforeSelectFile(e);
26060 this.fireEvent('footerbuttonclick', this, type);
26063 beforeSelectFile : function(e)
26065 e.preventDefault();
26067 if(this.fireEvent('beforeselectfile', this) != false){
26068 this.selectorEl.dom.click();
26072 onFileSelected : function(e)
26074 e.preventDefault();
26076 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26080 var file = this.selectorEl.dom.files[0];
26082 if(this.fireEvent('inspect', this, file) != false){
26083 this.prepare(file);
26088 trash : function(e)
26090 this.fireEvent('trash', this);
26093 download : function(e)
26095 this.fireEvent('download', this);
26098 loadCanvas : function(src)
26100 if(this.fireEvent('beforeloadcanvas', this, src) != false){
26104 this.imageEl = document.createElement('img');
26108 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26110 this.imageEl.src = src;
26114 onLoadCanvas : function()
26116 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26117 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26119 this.bodyEl.un('click', this.beforeSelectFile, this);
26121 this.notifyEl.hide();
26122 this.thumbEl.show();
26123 this.footerEl.show();
26125 this.baseRotateLevel();
26127 if(this.isDocument){
26128 this.setThumbBoxSize();
26131 this.setThumbBoxPosition();
26133 this.baseScaleLevel();
26139 this.canvasLoaded = true;
26142 this.maskEl.unmask();
26147 setCanvasPosition : function()
26149 if(!this.canvasEl){
26153 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26154 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26156 this.previewEl.setLeft(pw);
26157 this.previewEl.setTop(ph);
26161 onMouseDown : function(e)
26165 this.dragable = true;
26166 this.pinching = false;
26168 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26169 this.dragable = false;
26173 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26174 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26178 onMouseMove : function(e)
26182 if(!this.canvasLoaded){
26186 if (!this.dragable){
26190 var minX = Math.ceil(this.thumbEl.getLeft(true));
26191 var minY = Math.ceil(this.thumbEl.getTop(true));
26193 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26194 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26196 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26197 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26199 x = x - this.mouseX;
26200 y = y - this.mouseY;
26202 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26203 var bgY = Math.ceil(y + this.previewEl.getTop(true));
26205 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26206 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26208 this.previewEl.setLeft(bgX);
26209 this.previewEl.setTop(bgY);
26211 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26212 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26215 onMouseUp : function(e)
26219 this.dragable = false;
26222 onMouseWheel : function(e)
26226 this.startScale = this.scale;
26228 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26230 if(!this.zoomable()){
26231 this.scale = this.startScale;
26240 zoomable : function()
26242 var minScale = this.thumbEl.getWidth() / this.minWidth;
26244 if(this.minWidth < this.minHeight){
26245 minScale = this.thumbEl.getHeight() / this.minHeight;
26248 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26249 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26253 (this.rotate == 0 || this.rotate == 180) &&
26255 width > this.imageEl.OriginWidth ||
26256 height > this.imageEl.OriginHeight ||
26257 (width < this.minWidth && height < this.minHeight)
26265 (this.rotate == 90 || this.rotate == 270) &&
26267 width > this.imageEl.OriginWidth ||
26268 height > this.imageEl.OriginHeight ||
26269 (width < this.minHeight && height < this.minWidth)
26276 !this.isDocument &&
26277 (this.rotate == 0 || this.rotate == 180) &&
26279 width < this.minWidth ||
26280 width > this.imageEl.OriginWidth ||
26281 height < this.minHeight ||
26282 height > this.imageEl.OriginHeight
26289 !this.isDocument &&
26290 (this.rotate == 90 || this.rotate == 270) &&
26292 width < this.minHeight ||
26293 width > this.imageEl.OriginWidth ||
26294 height < this.minWidth ||
26295 height > this.imageEl.OriginHeight
26305 onRotateLeft : function(e)
26307 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26309 var minScale = this.thumbEl.getWidth() / this.minWidth;
26311 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26312 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26314 this.startScale = this.scale;
26316 while (this.getScaleLevel() < minScale){
26318 this.scale = this.scale + 1;
26320 if(!this.zoomable()){
26325 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26326 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26331 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26338 this.scale = this.startScale;
26340 this.onRotateFail();
26345 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26347 if(this.isDocument){
26348 this.setThumbBoxSize();
26349 this.setThumbBoxPosition();
26350 this.setCanvasPosition();
26355 this.fireEvent('rotate', this, 'left');
26359 onRotateRight : function(e)
26361 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26363 var minScale = this.thumbEl.getWidth() / this.minWidth;
26365 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26366 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26368 this.startScale = this.scale;
26370 while (this.getScaleLevel() < minScale){
26372 this.scale = this.scale + 1;
26374 if(!this.zoomable()){
26379 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26380 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26385 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26392 this.scale = this.startScale;
26394 this.onRotateFail();
26399 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26401 if(this.isDocument){
26402 this.setThumbBoxSize();
26403 this.setThumbBoxPosition();
26404 this.setCanvasPosition();
26409 this.fireEvent('rotate', this, 'right');
26412 onRotateFail : function()
26414 this.errorEl.show(true);
26418 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26423 this.previewEl.dom.innerHTML = '';
26425 var canvasEl = document.createElement("canvas");
26427 var contextEl = canvasEl.getContext("2d");
26429 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26430 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26431 var center = this.imageEl.OriginWidth / 2;
26433 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26434 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26435 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26436 center = this.imageEl.OriginHeight / 2;
26439 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26441 contextEl.translate(center, center);
26442 contextEl.rotate(this.rotate * Math.PI / 180);
26444 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26446 this.canvasEl = document.createElement("canvas");
26448 this.contextEl = this.canvasEl.getContext("2d");
26450 switch (this.rotate) {
26453 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26454 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26456 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26461 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26462 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26464 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26465 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);
26469 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26474 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26475 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26477 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26478 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);
26482 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);
26487 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26488 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26490 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26491 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26495 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);
26502 this.previewEl.appendChild(this.canvasEl);
26504 this.setCanvasPosition();
26509 if(!this.canvasLoaded){
26513 var imageCanvas = document.createElement("canvas");
26515 var imageContext = imageCanvas.getContext("2d");
26517 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26518 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26520 var center = imageCanvas.width / 2;
26522 imageContext.translate(center, center);
26524 imageContext.rotate(this.rotate * Math.PI / 180);
26526 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26528 var canvas = document.createElement("canvas");
26530 var context = canvas.getContext("2d");
26532 canvas.width = this.minWidth;
26533 canvas.height = this.minHeight;
26535 switch (this.rotate) {
26538 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26539 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26541 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26542 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26544 var targetWidth = this.minWidth - 2 * x;
26545 var targetHeight = this.minHeight - 2 * y;
26549 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26550 scale = targetWidth / width;
26553 if(x > 0 && y == 0){
26554 scale = targetHeight / height;
26557 if(x > 0 && y > 0){
26558 scale = targetWidth / width;
26560 if(width < height){
26561 scale = targetHeight / height;
26565 context.scale(scale, scale);
26567 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26568 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26570 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26571 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26573 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26578 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26579 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26581 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26582 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26584 var targetWidth = this.minWidth - 2 * x;
26585 var targetHeight = this.minHeight - 2 * y;
26589 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26590 scale = targetWidth / width;
26593 if(x > 0 && y == 0){
26594 scale = targetHeight / height;
26597 if(x > 0 && y > 0){
26598 scale = targetWidth / width;
26600 if(width < height){
26601 scale = targetHeight / height;
26605 context.scale(scale, scale);
26607 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26608 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26610 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26611 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26613 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26615 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26620 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26621 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26623 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26624 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26626 var targetWidth = this.minWidth - 2 * x;
26627 var targetHeight = this.minHeight - 2 * y;
26631 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26632 scale = targetWidth / width;
26635 if(x > 0 && y == 0){
26636 scale = targetHeight / height;
26639 if(x > 0 && y > 0){
26640 scale = targetWidth / width;
26642 if(width < height){
26643 scale = targetHeight / height;
26647 context.scale(scale, scale);
26649 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26650 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26652 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26653 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26655 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26656 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26658 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26663 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26664 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26666 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26667 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26669 var targetWidth = this.minWidth - 2 * x;
26670 var targetHeight = this.minHeight - 2 * y;
26674 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26675 scale = targetWidth / width;
26678 if(x > 0 && y == 0){
26679 scale = targetHeight / height;
26682 if(x > 0 && y > 0){
26683 scale = targetWidth / width;
26685 if(width < height){
26686 scale = targetHeight / height;
26690 context.scale(scale, scale);
26692 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26693 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26695 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26696 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26698 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26700 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26707 this.cropData = canvas.toDataURL(this.cropType);
26709 if(this.fireEvent('crop', this, this.cropData) !== false){
26710 this.process(this.file, this.cropData);
26717 setThumbBoxSize : function()
26721 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26722 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26723 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26725 this.minWidth = width;
26726 this.minHeight = height;
26728 if(this.rotate == 90 || this.rotate == 270){
26729 this.minWidth = height;
26730 this.minHeight = width;
26735 width = Math.ceil(this.minWidth * height / this.minHeight);
26737 if(this.minWidth > this.minHeight){
26739 height = Math.ceil(this.minHeight * width / this.minWidth);
26742 this.thumbEl.setStyle({
26743 width : width + 'px',
26744 height : height + 'px'
26751 setThumbBoxPosition : function()
26753 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26754 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26756 this.thumbEl.setLeft(x);
26757 this.thumbEl.setTop(y);
26761 baseRotateLevel : function()
26763 this.baseRotate = 1;
26766 typeof(this.exif) != 'undefined' &&
26767 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26768 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26770 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26773 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26777 baseScaleLevel : function()
26781 if(this.isDocument){
26783 if(this.baseRotate == 6 || this.baseRotate == 8){
26785 height = this.thumbEl.getHeight();
26786 this.baseScale = height / this.imageEl.OriginWidth;
26788 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26789 width = this.thumbEl.getWidth();
26790 this.baseScale = width / this.imageEl.OriginHeight;
26796 height = this.thumbEl.getHeight();
26797 this.baseScale = height / this.imageEl.OriginHeight;
26799 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26800 width = this.thumbEl.getWidth();
26801 this.baseScale = width / this.imageEl.OriginWidth;
26807 if(this.baseRotate == 6 || this.baseRotate == 8){
26809 width = this.thumbEl.getHeight();
26810 this.baseScale = width / this.imageEl.OriginHeight;
26812 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26813 height = this.thumbEl.getWidth();
26814 this.baseScale = height / this.imageEl.OriginHeight;
26817 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26818 height = this.thumbEl.getWidth();
26819 this.baseScale = height / this.imageEl.OriginHeight;
26821 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26822 width = this.thumbEl.getHeight();
26823 this.baseScale = width / this.imageEl.OriginWidth;
26830 width = this.thumbEl.getWidth();
26831 this.baseScale = width / this.imageEl.OriginWidth;
26833 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26834 height = this.thumbEl.getHeight();
26835 this.baseScale = height / this.imageEl.OriginHeight;
26838 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26840 height = this.thumbEl.getHeight();
26841 this.baseScale = height / this.imageEl.OriginHeight;
26843 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26844 width = this.thumbEl.getWidth();
26845 this.baseScale = width / this.imageEl.OriginWidth;
26853 getScaleLevel : function()
26855 return this.baseScale * Math.pow(1.1, this.scale);
26858 onTouchStart : function(e)
26860 if(!this.canvasLoaded){
26861 this.beforeSelectFile(e);
26865 var touches = e.browserEvent.touches;
26871 if(touches.length == 1){
26872 this.onMouseDown(e);
26876 if(touches.length != 2){
26882 for(var i = 0, finger; finger = touches[i]; i++){
26883 coords.push(finger.pageX, finger.pageY);
26886 var x = Math.pow(coords[0] - coords[2], 2);
26887 var y = Math.pow(coords[1] - coords[3], 2);
26889 this.startDistance = Math.sqrt(x + y);
26891 this.startScale = this.scale;
26893 this.pinching = true;
26894 this.dragable = false;
26898 onTouchMove : function(e)
26900 if(!this.pinching && !this.dragable){
26904 var touches = e.browserEvent.touches;
26911 this.onMouseMove(e);
26917 for(var i = 0, finger; finger = touches[i]; i++){
26918 coords.push(finger.pageX, finger.pageY);
26921 var x = Math.pow(coords[0] - coords[2], 2);
26922 var y = Math.pow(coords[1] - coords[3], 2);
26924 this.endDistance = Math.sqrt(x + y);
26926 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26928 if(!this.zoomable()){
26929 this.scale = this.startScale;
26937 onTouchEnd : function(e)
26939 this.pinching = false;
26940 this.dragable = false;
26944 process : function(file, crop)
26947 this.maskEl.mask(this.loadingText);
26950 this.xhr = new XMLHttpRequest();
26952 file.xhr = this.xhr;
26954 this.xhr.open(this.method, this.url, true);
26957 "Accept": "application/json",
26958 "Cache-Control": "no-cache",
26959 "X-Requested-With": "XMLHttpRequest"
26962 for (var headerName in headers) {
26963 var headerValue = headers[headerName];
26965 this.xhr.setRequestHeader(headerName, headerValue);
26971 this.xhr.onload = function()
26973 _this.xhrOnLoad(_this.xhr);
26976 this.xhr.onerror = function()
26978 _this.xhrOnError(_this.xhr);
26981 var formData = new FormData();
26983 formData.append('returnHTML', 'NO');
26986 formData.append('crop', crop);
26989 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26990 formData.append(this.paramName, file, file.name);
26993 if(typeof(file.filename) != 'undefined'){
26994 formData.append('filename', file.filename);
26997 if(typeof(file.mimetype) != 'undefined'){
26998 formData.append('mimetype', file.mimetype);
27001 if(this.fireEvent('arrange', this, formData) != false){
27002 this.xhr.send(formData);
27006 xhrOnLoad : function(xhr)
27009 this.maskEl.unmask();
27012 if (xhr.readyState !== 4) {
27013 this.fireEvent('exception', this, xhr);
27017 var response = Roo.decode(xhr.responseText);
27019 if(!response.success){
27020 this.fireEvent('exception', this, xhr);
27024 var response = Roo.decode(xhr.responseText);
27026 this.fireEvent('upload', this, response);
27030 xhrOnError : function()
27033 this.maskEl.unmask();
27036 Roo.log('xhr on error');
27038 var response = Roo.decode(xhr.responseText);
27044 prepare : function(file)
27047 this.maskEl.mask(this.loadingText);
27053 if(typeof(file) === 'string'){
27054 this.loadCanvas(file);
27058 if(!file || !this.urlAPI){
27063 this.cropType = file.type;
27067 if(this.fireEvent('prepare', this, this.file) != false){
27069 var reader = new FileReader();
27071 reader.onload = function (e) {
27072 if (e.target.error) {
27073 Roo.log(e.target.error);
27077 var buffer = e.target.result,
27078 dataView = new DataView(buffer),
27080 maxOffset = dataView.byteLength - 4,
27084 if (dataView.getUint16(0) === 0xffd8) {
27085 while (offset < maxOffset) {
27086 markerBytes = dataView.getUint16(offset);
27088 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27089 markerLength = dataView.getUint16(offset + 2) + 2;
27090 if (offset + markerLength > dataView.byteLength) {
27091 Roo.log('Invalid meta data: Invalid segment size.');
27095 if(markerBytes == 0xffe1){
27096 _this.parseExifData(
27103 offset += markerLength;
27113 var url = _this.urlAPI.createObjectURL(_this.file);
27115 _this.loadCanvas(url);
27120 reader.readAsArrayBuffer(this.file);
27126 parseExifData : function(dataView, offset, length)
27128 var tiffOffset = offset + 10,
27132 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27133 // No Exif data, might be XMP data instead
27137 // Check for the ASCII code for "Exif" (0x45786966):
27138 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27139 // No Exif data, might be XMP data instead
27142 if (tiffOffset + 8 > dataView.byteLength) {
27143 Roo.log('Invalid Exif data: Invalid segment size.');
27146 // Check for the two null bytes:
27147 if (dataView.getUint16(offset + 8) !== 0x0000) {
27148 Roo.log('Invalid Exif data: Missing byte alignment offset.');
27151 // Check the byte alignment:
27152 switch (dataView.getUint16(tiffOffset)) {
27154 littleEndian = true;
27157 littleEndian = false;
27160 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27163 // Check for the TIFF tag marker (0x002A):
27164 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27165 Roo.log('Invalid Exif data: Missing TIFF marker.');
27168 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27169 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27171 this.parseExifTags(
27174 tiffOffset + dirOffset,
27179 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27184 if (dirOffset + 6 > dataView.byteLength) {
27185 Roo.log('Invalid Exif data: Invalid directory offset.');
27188 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27189 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27190 if (dirEndOffset + 4 > dataView.byteLength) {
27191 Roo.log('Invalid Exif data: Invalid directory size.');
27194 for (i = 0; i < tagsNumber; i += 1) {
27198 dirOffset + 2 + 12 * i, // tag offset
27202 // Return the offset to the next directory:
27203 return dataView.getUint32(dirEndOffset, littleEndian);
27206 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
27208 var tag = dataView.getUint16(offset, littleEndian);
27210 this.exif[tag] = this.getExifValue(
27214 dataView.getUint16(offset + 2, littleEndian), // tag type
27215 dataView.getUint32(offset + 4, littleEndian), // tag length
27220 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27222 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27231 Roo.log('Invalid Exif data: Invalid tag type.');
27235 tagSize = tagType.size * length;
27236 // Determine if the value is contained in the dataOffset bytes,
27237 // or if the value at the dataOffset is a pointer to the actual data:
27238 dataOffset = tagSize > 4 ?
27239 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27240 if (dataOffset + tagSize > dataView.byteLength) {
27241 Roo.log('Invalid Exif data: Invalid data offset.');
27244 if (length === 1) {
27245 return tagType.getValue(dataView, dataOffset, littleEndian);
27248 for (i = 0; i < length; i += 1) {
27249 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27252 if (tagType.ascii) {
27254 // Concatenate the chars:
27255 for (i = 0; i < values.length; i += 1) {
27257 // Ignore the terminating NULL byte(s):
27258 if (c === '\u0000') {
27270 Roo.apply(Roo.bootstrap.UploadCropbox, {
27272 'Orientation': 0x0112
27276 1: 0, //'top-left',
27278 3: 180, //'bottom-right',
27279 // 4: 'bottom-left',
27281 6: 90, //'right-top',
27282 // 7: 'right-bottom',
27283 8: 270 //'left-bottom'
27287 // byte, 8-bit unsigned int:
27289 getValue: function (dataView, dataOffset) {
27290 return dataView.getUint8(dataOffset);
27294 // ascii, 8-bit byte:
27296 getValue: function (dataView, dataOffset) {
27297 return String.fromCharCode(dataView.getUint8(dataOffset));
27302 // short, 16 bit int:
27304 getValue: function (dataView, dataOffset, littleEndian) {
27305 return dataView.getUint16(dataOffset, littleEndian);
27309 // long, 32 bit int:
27311 getValue: function (dataView, dataOffset, littleEndian) {
27312 return dataView.getUint32(dataOffset, littleEndian);
27316 // rational = two long values, first is numerator, second is denominator:
27318 getValue: function (dataView, dataOffset, littleEndian) {
27319 return dataView.getUint32(dataOffset, littleEndian) /
27320 dataView.getUint32(dataOffset + 4, littleEndian);
27324 // slong, 32 bit signed int:
27326 getValue: function (dataView, dataOffset, littleEndian) {
27327 return dataView.getInt32(dataOffset, littleEndian);
27331 // srational, two slongs, first is numerator, second is denominator:
27333 getValue: function (dataView, dataOffset, littleEndian) {
27334 return dataView.getInt32(dataOffset, littleEndian) /
27335 dataView.getInt32(dataOffset + 4, littleEndian);
27345 cls : 'btn-group roo-upload-cropbox-rotate-left',
27346 action : 'rotate-left',
27350 cls : 'btn btn-default',
27351 html : '<i class="fa fa-undo"></i>'
27357 cls : 'btn-group roo-upload-cropbox-picture',
27358 action : 'picture',
27362 cls : 'btn btn-default',
27363 html : '<i class="fa fa-picture-o"></i>'
27369 cls : 'btn-group roo-upload-cropbox-rotate-right',
27370 action : 'rotate-right',
27374 cls : 'btn btn-default',
27375 html : '<i class="fa fa-repeat"></i>'
27383 cls : 'btn-group roo-upload-cropbox-rotate-left',
27384 action : 'rotate-left',
27388 cls : 'btn btn-default',
27389 html : '<i class="fa fa-undo"></i>'
27395 cls : 'btn-group roo-upload-cropbox-download',
27396 action : 'download',
27400 cls : 'btn btn-default',
27401 html : '<i class="fa fa-download"></i>'
27407 cls : 'btn-group roo-upload-cropbox-crop',
27412 cls : 'btn btn-default',
27413 html : '<i class="fa fa-crop"></i>'
27419 cls : 'btn-group roo-upload-cropbox-trash',
27424 cls : 'btn btn-default',
27425 html : '<i class="fa fa-trash"></i>'
27431 cls : 'btn-group roo-upload-cropbox-rotate-right',
27432 action : 'rotate-right',
27436 cls : 'btn btn-default',
27437 html : '<i class="fa fa-repeat"></i>'
27445 cls : 'btn-group roo-upload-cropbox-rotate-left',
27446 action : 'rotate-left',
27450 cls : 'btn btn-default',
27451 html : '<i class="fa fa-undo"></i>'
27457 cls : 'btn-group roo-upload-cropbox-rotate-right',
27458 action : 'rotate-right',
27462 cls : 'btn btn-default',
27463 html : '<i class="fa fa-repeat"></i>'
27476 * @class Roo.bootstrap.DocumentManager
27477 * @extends Roo.bootstrap.Component
27478 * Bootstrap DocumentManager class
27479 * @cfg {String} paramName default 'imageUpload'
27480 * @cfg {String} toolTipName default 'filename'
27481 * @cfg {String} method default POST
27482 * @cfg {String} url action url
27483 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27484 * @cfg {Boolean} multiple multiple upload default true
27485 * @cfg {Number} thumbSize default 300
27486 * @cfg {String} fieldLabel
27487 * @cfg {Number} labelWidth default 4
27488 * @cfg {String} labelAlign (left|top) default left
27489 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27492 * Create a new DocumentManager
27493 * @param {Object} config The config object
27496 Roo.bootstrap.DocumentManager = function(config){
27497 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27500 this.delegates = [];
27505 * Fire when initial the DocumentManager
27506 * @param {Roo.bootstrap.DocumentManager} this
27511 * inspect selected file
27512 * @param {Roo.bootstrap.DocumentManager} this
27513 * @param {File} file
27518 * Fire when xhr load exception
27519 * @param {Roo.bootstrap.DocumentManager} this
27520 * @param {XMLHttpRequest} xhr
27522 "exception" : true,
27524 * @event afterupload
27525 * Fire when xhr load exception
27526 * @param {Roo.bootstrap.DocumentManager} this
27527 * @param {XMLHttpRequest} xhr
27529 "afterupload" : true,
27532 * prepare the form data
27533 * @param {Roo.bootstrap.DocumentManager} this
27534 * @param {Object} formData
27539 * Fire when remove the file
27540 * @param {Roo.bootstrap.DocumentManager} this
27541 * @param {Object} file
27546 * Fire after refresh the file
27547 * @param {Roo.bootstrap.DocumentManager} this
27552 * Fire after click the image
27553 * @param {Roo.bootstrap.DocumentManager} this
27554 * @param {Object} file
27559 * Fire when upload a image and editable set to true
27560 * @param {Roo.bootstrap.DocumentManager} this
27561 * @param {Object} file
27565 * @event beforeselectfile
27566 * Fire before select file
27567 * @param {Roo.bootstrap.DocumentManager} this
27569 "beforeselectfile" : true,
27572 * Fire before process file
27573 * @param {Roo.bootstrap.DocumentManager} this
27574 * @param {Object} file
27581 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
27590 paramName : 'imageUpload',
27591 toolTipName : 'filename',
27594 labelAlign : 'left',
27599 getAutoCreate : function()
27601 var managerWidget = {
27603 cls : 'roo-document-manager',
27607 cls : 'roo-document-manager-selector',
27612 cls : 'roo-document-manager-uploader',
27616 cls : 'roo-document-manager-upload-btn',
27617 html : '<i class="fa fa-plus"></i>'
27628 cls : 'column col-md-12',
27633 if(this.fieldLabel.length){
27638 cls : 'column col-md-12',
27639 html : this.fieldLabel
27643 cls : 'column col-md-12',
27648 if(this.labelAlign == 'left'){
27652 cls : 'column col-md-' + this.labelWidth,
27653 html : this.fieldLabel
27657 cls : 'column col-md-' + (12 - this.labelWidth),
27667 cls : 'row clearfix',
27675 initEvents : function()
27677 this.managerEl = this.el.select('.roo-document-manager', true).first();
27678 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27680 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27681 this.selectorEl.hide();
27684 this.selectorEl.attr('multiple', 'multiple');
27687 this.selectorEl.on('change', this.onFileSelected, this);
27689 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27690 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27692 this.uploader.on('click', this.onUploaderClick, this);
27694 this.renderProgressDialog();
27698 window.addEventListener("resize", function() { _this.refresh(); } );
27700 this.fireEvent('initial', this);
27703 renderProgressDialog : function()
27707 this.progressDialog = new Roo.bootstrap.Modal({
27708 cls : 'roo-document-manager-progress-dialog',
27709 allow_close : false,
27719 btnclick : function() {
27720 _this.uploadCancel();
27726 this.progressDialog.render(Roo.get(document.body));
27728 this.progress = new Roo.bootstrap.Progress({
27729 cls : 'roo-document-manager-progress',
27734 this.progress.render(this.progressDialog.getChildContainer());
27736 this.progressBar = new Roo.bootstrap.ProgressBar({
27737 cls : 'roo-document-manager-progress-bar',
27740 aria_valuemax : 12,
27744 this.progressBar.render(this.progress.getChildContainer());
27747 onUploaderClick : function(e)
27749 e.preventDefault();
27751 if(this.fireEvent('beforeselectfile', this) != false){
27752 this.selectorEl.dom.click();
27757 onFileSelected : function(e)
27759 e.preventDefault();
27761 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27765 Roo.each(this.selectorEl.dom.files, function(file){
27766 if(this.fireEvent('inspect', this, file) != false){
27767 this.files.push(file);
27777 this.selectorEl.dom.value = '';
27779 if(!this.files.length){
27783 if(this.boxes > 0 && this.files.length > this.boxes){
27784 this.files = this.files.slice(0, this.boxes);
27787 this.uploader.show();
27789 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27790 this.uploader.hide();
27799 Roo.each(this.files, function(file){
27801 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27802 var f = this.renderPreview(file);
27807 if(file.type.indexOf('image') != -1){
27808 this.delegates.push(
27810 _this.process(file);
27811 }).createDelegate(this)
27819 _this.process(file);
27820 }).createDelegate(this)
27825 this.files = files;
27827 this.delegates = this.delegates.concat(docs);
27829 if(!this.delegates.length){
27834 this.progressBar.aria_valuemax = this.delegates.length;
27841 arrange : function()
27843 if(!this.delegates.length){
27844 this.progressDialog.hide();
27849 var delegate = this.delegates.shift();
27851 this.progressDialog.show();
27853 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27855 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27860 refresh : function()
27862 this.uploader.show();
27864 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27865 this.uploader.hide();
27868 Roo.isTouch ? this.closable(false) : this.closable(true);
27870 this.fireEvent('refresh', this);
27873 onRemove : function(e, el, o)
27875 e.preventDefault();
27877 this.fireEvent('remove', this, o);
27881 remove : function(o)
27885 Roo.each(this.files, function(file){
27886 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27895 this.files = files;
27902 Roo.each(this.files, function(file){
27907 file.target.remove();
27916 onClick : function(e, el, o)
27918 e.preventDefault();
27920 this.fireEvent('click', this, o);
27924 closable : function(closable)
27926 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27928 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27940 xhrOnLoad : function(xhr)
27942 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27946 if (xhr.readyState !== 4) {
27948 this.fireEvent('exception', this, xhr);
27952 var response = Roo.decode(xhr.responseText);
27954 if(!response.success){
27956 this.fireEvent('exception', this, xhr);
27960 var file = this.renderPreview(response.data);
27962 this.files.push(file);
27966 this.fireEvent('afterupload', this, xhr);
27970 xhrOnError : function(xhr)
27972 Roo.log('xhr on error');
27974 var response = Roo.decode(xhr.responseText);
27981 process : function(file)
27983 if(this.fireEvent('process', this, file) !== false){
27984 if(this.editable && file.type.indexOf('image') != -1){
27985 this.fireEvent('edit', this, file);
27989 this.uploadStart(file, false);
27996 uploadStart : function(file, crop)
27998 this.xhr = new XMLHttpRequest();
28000 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28005 file.xhr = this.xhr;
28007 this.managerEl.createChild({
28009 cls : 'roo-document-manager-loading',
28013 tooltip : file.name,
28014 cls : 'roo-document-manager-thumb',
28015 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28021 this.xhr.open(this.method, this.url, true);
28024 "Accept": "application/json",
28025 "Cache-Control": "no-cache",
28026 "X-Requested-With": "XMLHttpRequest"
28029 for (var headerName in headers) {
28030 var headerValue = headers[headerName];
28032 this.xhr.setRequestHeader(headerName, headerValue);
28038 this.xhr.onload = function()
28040 _this.xhrOnLoad(_this.xhr);
28043 this.xhr.onerror = function()
28045 _this.xhrOnError(_this.xhr);
28048 var formData = new FormData();
28050 formData.append('returnHTML', 'NO');
28053 formData.append('crop', crop);
28056 formData.append(this.paramName, file, file.name);
28063 if(this.fireEvent('prepare', this, formData, options) != false){
28065 if(options.manually){
28069 this.xhr.send(formData);
28073 this.uploadCancel();
28076 uploadCancel : function()
28082 this.delegates = [];
28084 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28091 renderPreview : function(file)
28093 if(typeof(file.target) != 'undefined' && file.target){
28097 var previewEl = this.managerEl.createChild({
28099 cls : 'roo-document-manager-preview',
28103 tooltip : file[this.toolTipName],
28104 cls : 'roo-document-manager-thumb',
28105 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28110 html : '<i class="fa fa-times-circle"></i>'
28115 var close = previewEl.select('button.close', true).first();
28117 close.on('click', this.onRemove, this, file);
28119 file.target = previewEl;
28121 var image = previewEl.select('img', true).first();
28125 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28127 image.on('click', this.onClick, this, file);
28133 onPreviewLoad : function(file, image)
28135 if(typeof(file.target) == 'undefined' || !file.target){
28139 var width = image.dom.naturalWidth || image.dom.width;
28140 var height = image.dom.naturalHeight || image.dom.height;
28142 if(width > height){
28143 file.target.addClass('wide');
28147 file.target.addClass('tall');
28152 uploadFromSource : function(file, crop)
28154 this.xhr = new XMLHttpRequest();
28156 this.managerEl.createChild({
28158 cls : 'roo-document-manager-loading',
28162 tooltip : file.name,
28163 cls : 'roo-document-manager-thumb',
28164 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28170 this.xhr.open(this.method, this.url, true);
28173 "Accept": "application/json",
28174 "Cache-Control": "no-cache",
28175 "X-Requested-With": "XMLHttpRequest"
28178 for (var headerName in headers) {
28179 var headerValue = headers[headerName];
28181 this.xhr.setRequestHeader(headerName, headerValue);
28187 this.xhr.onload = function()
28189 _this.xhrOnLoad(_this.xhr);
28192 this.xhr.onerror = function()
28194 _this.xhrOnError(_this.xhr);
28197 var formData = new FormData();
28199 formData.append('returnHTML', 'NO');
28201 formData.append('crop', crop);
28203 if(typeof(file.filename) != 'undefined'){
28204 formData.append('filename', file.filename);
28207 if(typeof(file.mimetype) != 'undefined'){
28208 formData.append('mimetype', file.mimetype);
28211 if(this.fireEvent('prepare', this, formData) != false){
28212 this.xhr.send(formData);
28222 * @class Roo.bootstrap.DocumentViewer
28223 * @extends Roo.bootstrap.Component
28224 * Bootstrap DocumentViewer class
28225 * @cfg {Boolean} showDownload (true|false) show download button (default true)
28226 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28229 * Create a new DocumentViewer
28230 * @param {Object} config The config object
28233 Roo.bootstrap.DocumentViewer = function(config){
28234 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28239 * Fire after initEvent
28240 * @param {Roo.bootstrap.DocumentViewer} this
28246 * @param {Roo.bootstrap.DocumentViewer} this
28251 * Fire after download button
28252 * @param {Roo.bootstrap.DocumentViewer} this
28257 * Fire after trash button
28258 * @param {Roo.bootstrap.DocumentViewer} this
28265 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
28267 showDownload : true,
28271 getAutoCreate : function()
28275 cls : 'roo-document-viewer',
28279 cls : 'roo-document-viewer-body',
28283 cls : 'roo-document-viewer-thumb',
28287 cls : 'roo-document-viewer-image'
28295 cls : 'roo-document-viewer-footer',
28298 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28302 cls : 'btn-group roo-document-viewer-download',
28306 cls : 'btn btn-default',
28307 html : '<i class="fa fa-download"></i>'
28313 cls : 'btn-group roo-document-viewer-trash',
28317 cls : 'btn btn-default',
28318 html : '<i class="fa fa-trash"></i>'
28331 initEvents : function()
28333 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28334 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28336 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28337 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28339 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28340 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28342 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28343 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28345 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28346 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28348 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28349 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28351 this.bodyEl.on('click', this.onClick, this);
28352 this.downloadBtn.on('click', this.onDownload, this);
28353 this.trashBtn.on('click', this.onTrash, this);
28355 this.downloadBtn.hide();
28356 this.trashBtn.hide();
28358 if(this.showDownload){
28359 this.downloadBtn.show();
28362 if(this.showTrash){
28363 this.trashBtn.show();
28366 if(!this.showDownload && !this.showTrash) {
28367 this.footerEl.hide();
28372 initial : function()
28374 this.fireEvent('initial', this);
28378 onClick : function(e)
28380 e.preventDefault();
28382 this.fireEvent('click', this);
28385 onDownload : function(e)
28387 e.preventDefault();
28389 this.fireEvent('download', this);
28392 onTrash : function(e)
28394 e.preventDefault();
28396 this.fireEvent('trash', this);
28408 * @class Roo.bootstrap.NavProgressBar
28409 * @extends Roo.bootstrap.Component
28410 * Bootstrap NavProgressBar class
28413 * Create a new nav progress bar
28414 * @param {Object} config The config object
28417 Roo.bootstrap.NavProgressBar = function(config){
28418 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28420 this.bullets = this.bullets || [];
28422 // Roo.bootstrap.NavProgressBar.register(this);
28426 * Fires when the active item changes
28427 * @param {Roo.bootstrap.NavProgressBar} this
28428 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28429 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
28436 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
28441 getAutoCreate : function()
28443 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28447 cls : 'roo-navigation-bar-group',
28451 cls : 'roo-navigation-top-bar'
28455 cls : 'roo-navigation-bullets-bar',
28459 cls : 'roo-navigation-bar'
28466 cls : 'roo-navigation-bottom-bar'
28476 initEvents: function()
28481 onRender : function(ct, position)
28483 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28485 if(this.bullets.length){
28486 Roo.each(this.bullets, function(b){
28495 addItem : function(cfg)
28497 var item = new Roo.bootstrap.NavProgressItem(cfg);
28499 item.parentId = this.id;
28500 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28503 var top = new Roo.bootstrap.Element({
28505 cls : 'roo-navigation-bar-text'
28508 var bottom = new Roo.bootstrap.Element({
28510 cls : 'roo-navigation-bar-text'
28513 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28514 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28516 var topText = new Roo.bootstrap.Element({
28518 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28521 var bottomText = new Roo.bootstrap.Element({
28523 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28526 topText.onRender(top.el, null);
28527 bottomText.onRender(bottom.el, null);
28530 item.bottomEl = bottom;
28533 this.barItems.push(item);
28538 getActive : function()
28540 var active = false;
28542 Roo.each(this.barItems, function(v){
28544 if (!v.isActive()) {
28556 setActiveItem : function(item)
28560 Roo.each(this.barItems, function(v){
28561 if (v.rid == item.rid) {
28565 if (v.isActive()) {
28566 v.setActive(false);
28571 item.setActive(true);
28573 this.fireEvent('changed', this, item, prev);
28576 getBarItem: function(rid)
28580 Roo.each(this.barItems, function(e) {
28581 if (e.rid != rid) {
28592 indexOfItem : function(item)
28596 Roo.each(this.barItems, function(v, i){
28598 if (v.rid != item.rid) {
28609 setActiveNext : function()
28611 var i = this.indexOfItem(this.getActive());
28613 if (i > this.barItems.length) {
28617 this.setActiveItem(this.barItems[i+1]);
28620 setActivePrev : function()
28622 var i = this.indexOfItem(this.getActive());
28628 this.setActiveItem(this.barItems[i-1]);
28631 format : function()
28633 if(!this.barItems.length){
28637 var width = 100 / this.barItems.length;
28639 Roo.each(this.barItems, function(i){
28640 i.el.setStyle('width', width + '%');
28641 i.topEl.el.setStyle('width', width + '%');
28642 i.bottomEl.el.setStyle('width', width + '%');
28651 * Nav Progress Item
28656 * @class Roo.bootstrap.NavProgressItem
28657 * @extends Roo.bootstrap.Component
28658 * Bootstrap NavProgressItem class
28659 * @cfg {String} rid the reference id
28660 * @cfg {Boolean} active (true|false) Is item active default false
28661 * @cfg {Boolean} disabled (true|false) Is item active default false
28662 * @cfg {String} html
28663 * @cfg {String} position (top|bottom) text position default bottom
28664 * @cfg {String} icon show icon instead of number
28667 * Create a new NavProgressItem
28668 * @param {Object} config The config object
28670 Roo.bootstrap.NavProgressItem = function(config){
28671 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28676 * The raw click event for the entire grid.
28677 * @param {Roo.bootstrap.NavProgressItem} this
28678 * @param {Roo.EventObject} e
28685 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
28691 position : 'bottom',
28694 getAutoCreate : function()
28696 var iconCls = 'roo-navigation-bar-item-icon';
28698 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28702 cls: 'roo-navigation-bar-item',
28712 cfg.cls += ' active';
28715 cfg.cls += ' disabled';
28721 disable : function()
28723 this.setDisabled(true);
28726 enable : function()
28728 this.setDisabled(false);
28731 initEvents: function()
28733 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28735 this.iconEl.on('click', this.onClick, this);
28738 onClick : function(e)
28740 e.preventDefault();
28746 if(this.fireEvent('click', this, e) === false){
28750 this.parent().setActiveItem(this);
28753 isActive: function ()
28755 return this.active;
28758 setActive : function(state)
28760 if(this.active == state){
28764 this.active = state;
28767 this.el.addClass('active');
28771 this.el.removeClass('active');
28776 setDisabled : function(state)
28778 if(this.disabled == state){
28782 this.disabled = state;
28785 this.el.addClass('disabled');
28789 this.el.removeClass('disabled');
28792 tooltipEl : function()
28794 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28807 * @class Roo.bootstrap.FieldLabel
28808 * @extends Roo.bootstrap.Component
28809 * Bootstrap FieldLabel class
28810 * @cfg {String} html contents of the element
28811 * @cfg {String} tag tag of the element default label
28812 * @cfg {String} cls class of the element
28813 * @cfg {String} target label target
28814 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28815 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28816 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28817 * @cfg {String} iconTooltip default "This field is required"
28820 * Create a new FieldLabel
28821 * @param {Object} config The config object
28824 Roo.bootstrap.FieldLabel = function(config){
28825 Roo.bootstrap.Element.superclass.constructor.call(this, config);
28830 * Fires after the field has been marked as invalid.
28831 * @param {Roo.form.FieldLabel} this
28832 * @param {String} msg The validation message
28837 * Fires after the field has been validated with no errors.
28838 * @param {Roo.form.FieldLabel} this
28844 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
28851 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28852 validClass : 'text-success fa fa-lg fa-check',
28853 iconTooltip : 'This field is required',
28855 getAutoCreate : function(){
28859 cls : 'roo-bootstrap-field-label ' + this.cls,
28865 tooltip : this.iconTooltip
28877 initEvents: function()
28879 Roo.bootstrap.Element.superclass.initEvents.call(this);
28881 this.iconEl = this.el.select('i', true).first();
28883 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28885 Roo.bootstrap.FieldLabel.register(this);
28889 * Mark this field as valid
28891 markValid : function()
28893 this.iconEl.show();
28895 this.iconEl.removeClass(this.invalidClass);
28897 this.iconEl.addClass(this.validClass);
28899 this.fireEvent('valid', this);
28903 * Mark this field as invalid
28904 * @param {String} msg The validation message
28906 markInvalid : function(msg)
28908 this.iconEl.show();
28910 this.iconEl.removeClass(this.validClass);
28912 this.iconEl.addClass(this.invalidClass);
28914 this.fireEvent('invalid', this, msg);
28920 Roo.apply(Roo.bootstrap.FieldLabel, {
28925 * register a FieldLabel Group
28926 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28928 register : function(label)
28930 if(this.groups.hasOwnProperty(label.target)){
28934 this.groups[label.target] = label;
28938 * fetch a FieldLabel Group based on the target
28939 * @param {string} target
28940 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28942 get: function(target) {
28943 if (typeof(this.groups[target]) == 'undefined') {
28947 return this.groups[target] ;
28956 * page DateSplitField.
28962 * @class Roo.bootstrap.DateSplitField
28963 * @extends Roo.bootstrap.Component
28964 * Bootstrap DateSplitField class
28965 * @cfg {string} fieldLabel - the label associated
28966 * @cfg {Number} labelWidth set the width of label (0-12)
28967 * @cfg {String} labelAlign (top|left)
28968 * @cfg {Boolean} dayAllowBlank (true|false) default false
28969 * @cfg {Boolean} monthAllowBlank (true|false) default false
28970 * @cfg {Boolean} yearAllowBlank (true|false) default false
28971 * @cfg {string} dayPlaceholder
28972 * @cfg {string} monthPlaceholder
28973 * @cfg {string} yearPlaceholder
28974 * @cfg {string} dayFormat default 'd'
28975 * @cfg {string} monthFormat default 'm'
28976 * @cfg {string} yearFormat default 'Y'
28980 * Create a new DateSplitField
28981 * @param {Object} config The config object
28984 Roo.bootstrap.DateSplitField = function(config){
28985 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28991 * getting the data of years
28992 * @param {Roo.bootstrap.DateSplitField} this
28993 * @param {Object} years
28998 * getting the data of days
28999 * @param {Roo.bootstrap.DateSplitField} this
29000 * @param {Object} days
29005 * Fires after the field has been marked as invalid.
29006 * @param {Roo.form.Field} this
29007 * @param {String} msg The validation message
29012 * Fires after the field has been validated with no errors.
29013 * @param {Roo.form.Field} this
29019 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
29022 labelAlign : 'top',
29024 dayAllowBlank : false,
29025 monthAllowBlank : false,
29026 yearAllowBlank : false,
29027 dayPlaceholder : '',
29028 monthPlaceholder : '',
29029 yearPlaceholder : '',
29033 isFormField : true,
29035 getAutoCreate : function()
29039 cls : 'row roo-date-split-field-group',
29044 cls : 'form-hidden-field roo-date-split-field-group-value',
29050 if(this.fieldLabel){
29053 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29057 html : this.fieldLabel
29063 Roo.each(['day', 'month', 'year'], function(t){
29066 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
29073 inputEl: function ()
29075 return this.el.select('.roo-date-split-field-group-value', true).first();
29078 onRender : function(ct, position)
29082 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29084 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29086 this.dayField = new Roo.bootstrap.ComboBox({
29087 allowBlank : this.dayAllowBlank,
29088 alwaysQuery : true,
29089 displayField : 'value',
29092 forceSelection : true,
29094 placeholder : this.dayPlaceholder,
29095 selectOnFocus : true,
29096 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29097 triggerAction : 'all',
29099 valueField : 'value',
29100 store : new Roo.data.SimpleStore({
29101 data : (function() {
29103 _this.fireEvent('days', _this, days);
29106 fields : [ 'value' ]
29109 select : function (_self, record, index)
29111 _this.setValue(_this.getValue());
29116 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29118 this.monthField = new Roo.bootstrap.MonthField({
29119 after : '<i class=\"fa fa-calendar\"></i>',
29120 allowBlank : this.monthAllowBlank,
29121 placeholder : this.monthPlaceholder,
29124 render : function (_self)
29126 this.el.select('span.input-group-addon', true).first().on('click', function(e){
29127 e.preventDefault();
29131 select : function (_self, oldvalue, newvalue)
29133 _this.setValue(_this.getValue());
29138 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29140 this.yearField = new Roo.bootstrap.ComboBox({
29141 allowBlank : this.yearAllowBlank,
29142 alwaysQuery : true,
29143 displayField : 'value',
29146 forceSelection : true,
29148 placeholder : this.yearPlaceholder,
29149 selectOnFocus : true,
29150 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29151 triggerAction : 'all',
29153 valueField : 'value',
29154 store : new Roo.data.SimpleStore({
29155 data : (function() {
29157 _this.fireEvent('years', _this, years);
29160 fields : [ 'value' ]
29163 select : function (_self, record, index)
29165 _this.setValue(_this.getValue());
29170 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29173 setValue : function(v, format)
29175 this.inputEl.dom.value = v;
29177 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29179 var d = Date.parseDate(v, f);
29186 this.setDay(d.format(this.dayFormat));
29187 this.setMonth(d.format(this.monthFormat));
29188 this.setYear(d.format(this.yearFormat));
29195 setDay : function(v)
29197 this.dayField.setValue(v);
29198 this.inputEl.dom.value = this.getValue();
29203 setMonth : function(v)
29205 this.monthField.setValue(v, true);
29206 this.inputEl.dom.value = this.getValue();
29211 setYear : function(v)
29213 this.yearField.setValue(v);
29214 this.inputEl.dom.value = this.getValue();
29219 getDay : function()
29221 return this.dayField.getValue();
29224 getMonth : function()
29226 return this.monthField.getValue();
29229 getYear : function()
29231 return this.yearField.getValue();
29234 getValue : function()
29236 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29238 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29248 this.inputEl.dom.value = '';
29253 validate : function()
29255 var d = this.dayField.validate();
29256 var m = this.monthField.validate();
29257 var y = this.yearField.validate();
29262 (!this.dayAllowBlank && !d) ||
29263 (!this.monthAllowBlank && !m) ||
29264 (!this.yearAllowBlank && !y)
29269 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29278 this.markInvalid();
29283 markValid : function()
29286 var label = this.el.select('label', true).first();
29287 var icon = this.el.select('i.fa-star', true).first();
29293 this.fireEvent('valid', this);
29297 * Mark this field as invalid
29298 * @param {String} msg The validation message
29300 markInvalid : function(msg)
29303 var label = this.el.select('label', true).first();
29304 var icon = this.el.select('i.fa-star', true).first();
29306 if(label && !icon){
29307 this.el.select('.roo-date-split-field-label', true).createChild({
29309 cls : 'text-danger fa fa-lg fa-star',
29310 tooltip : 'This field is required',
29311 style : 'margin-right:5px;'
29315 this.fireEvent('invalid', this, msg);
29318 clearInvalid : function()
29320 var label = this.el.select('label', true).first();
29321 var icon = this.el.select('i.fa-star', true).first();
29327 this.fireEvent('valid', this);
29330 getName: function()
29340 * http://masonry.desandro.com
29342 * The idea is to render all the bricks based on vertical width...
29344 * The original code extends 'outlayer' - we might need to use that....
29350 * @class Roo.bootstrap.LayoutMasonry
29351 * @extends Roo.bootstrap.Component
29352 * Bootstrap Layout Masonry class
29355 * Create a new Element
29356 * @param {Object} config The config object
29359 Roo.bootstrap.LayoutMasonry = function(config){
29360 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29366 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
29369 * @cfg {Boolean} isLayoutInstant = no animation?
29371 isLayoutInstant : false, // needed?
29374 * @cfg {Number} boxWidth width of the columns
29379 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
29384 * @cfg {Number} padWidth padding below box..
29389 * @cfg {Number} gutter gutter width..
29394 * @cfg {Number} maxCols maximum number of columns
29400 * @cfg {Boolean} isAutoInitial defalut true
29402 isAutoInitial : true,
29407 * @cfg {Boolean} isHorizontal defalut false
29409 isHorizontal : false,
29411 currentSize : null,
29417 bricks: null, //CompositeElement
29421 _isLayoutInited : false,
29423 // isAlternative : false, // only use for vertical layout...
29426 * @cfg {Number} alternativePadWidth padding below box..
29428 alternativePadWidth : 50,
29430 getAutoCreate : function(){
29434 cls: 'blog-masonary-wrapper ' + this.cls,
29436 cls : 'mas-boxes masonary'
29443 getChildContainer: function( )
29445 if (this.boxesEl) {
29446 return this.boxesEl;
29449 this.boxesEl = this.el.select('.mas-boxes').first();
29451 return this.boxesEl;
29455 initEvents : function()
29459 if(this.isAutoInitial){
29460 Roo.log('hook children rendered');
29461 this.on('childrenrendered', function() {
29462 Roo.log('children rendered');
29468 initial : function()
29470 this.currentSize = this.el.getBox(true);
29472 Roo.EventManager.onWindowResize(this.resize, this);
29474 if(!this.isAutoInitial){
29482 //this.layout.defer(500,this);
29486 resize : function()
29490 var cs = this.el.getBox(true);
29492 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29493 Roo.log("no change in with or X");
29497 this.currentSize = cs;
29503 layout : function()
29505 this._resetLayout();
29507 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29509 this.layoutItems( isInstant );
29511 this._isLayoutInited = true;
29515 _resetLayout : function()
29517 if(this.isHorizontal){
29518 this.horizontalMeasureColumns();
29522 this.verticalMeasureColumns();
29526 verticalMeasureColumns : function()
29528 this.getContainerWidth();
29530 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29531 // this.colWidth = Math.floor(this.containerWidth * 0.8);
29535 var boxWidth = this.boxWidth + this.padWidth;
29537 if(this.containerWidth < this.boxWidth){
29538 boxWidth = this.containerWidth
29541 var containerWidth = this.containerWidth;
29543 var cols = Math.floor(containerWidth / boxWidth);
29545 this.cols = Math.max( cols, 1 );
29547 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29549 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29551 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29553 this.colWidth = boxWidth + avail - this.padWidth;
29555 this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29556 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
29559 horizontalMeasureColumns : function()
29561 this.getContainerWidth();
29563 var boxWidth = this.boxWidth;
29565 if(this.containerWidth < boxWidth){
29566 boxWidth = this.containerWidth;
29569 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29571 this.el.setHeight(boxWidth);
29575 getContainerWidth : function()
29577 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
29580 layoutItems : function( isInstant )
29582 var items = Roo.apply([], this.bricks);
29584 if(this.isHorizontal){
29585 this._horizontalLayoutItems( items , isInstant );
29589 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29590 // this._verticalAlternativeLayoutItems( items , isInstant );
29594 this._verticalLayoutItems( items , isInstant );
29598 _verticalLayoutItems : function ( items , isInstant)
29600 if ( !items || !items.length ) {
29605 ['xs', 'xs', 'xs', 'tall'],
29606 ['xs', 'xs', 'tall'],
29607 ['xs', 'xs', 'sm'],
29608 ['xs', 'xs', 'xs'],
29614 ['sm', 'xs', 'xs'],
29618 ['tall', 'xs', 'xs', 'xs'],
29619 ['tall', 'xs', 'xs'],
29631 Roo.each(items, function(item, k){
29633 switch (item.size) {
29634 // these layouts take up a full box,
29645 boxes.push([item]);
29668 var filterPattern = function(box, length)
29676 var pattern = box.slice(0, length);
29680 Roo.each(pattern, function(i){
29681 format.push(i.size);
29684 Roo.each(standard, function(s){
29686 if(String(s) != String(format)){
29695 if(!match && length == 1){
29700 filterPattern(box, length - 1);
29704 queue.push(pattern);
29706 box = box.slice(length, box.length);
29708 filterPattern(box, 4);
29714 Roo.each(boxes, function(box, k){
29720 if(box.length == 1){
29725 filterPattern(box, 4);
29729 this._processVerticalLayoutQueue( queue, isInstant );
29733 // _verticalAlternativeLayoutItems : function( items , isInstant )
29735 // if ( !items || !items.length ) {
29739 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
29743 _horizontalLayoutItems : function ( items , isInstant)
29745 if ( !items || !items.length || items.length < 3) {
29751 var eItems = items.slice(0, 3);
29753 items = items.slice(3, items.length);
29756 ['xs', 'xs', 'xs', 'wide'],
29757 ['xs', 'xs', 'wide'],
29758 ['xs', 'xs', 'sm'],
29759 ['xs', 'xs', 'xs'],
29765 ['sm', 'xs', 'xs'],
29769 ['wide', 'xs', 'xs', 'xs'],
29770 ['wide', 'xs', 'xs'],
29783 Roo.each(items, function(item, k){
29785 switch (item.size) {
29796 boxes.push([item]);
29820 var filterPattern = function(box, length)
29828 var pattern = box.slice(0, length);
29832 Roo.each(pattern, function(i){
29833 format.push(i.size);
29836 Roo.each(standard, function(s){
29838 if(String(s) != String(format)){
29847 if(!match && length == 1){
29852 filterPattern(box, length - 1);
29856 queue.push(pattern);
29858 box = box.slice(length, box.length);
29860 filterPattern(box, 4);
29866 Roo.each(boxes, function(box, k){
29872 if(box.length == 1){
29877 filterPattern(box, 4);
29884 var pos = this.el.getBox(true);
29888 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29890 var hit_end = false;
29892 Roo.each(queue, function(box){
29896 Roo.each(box, function(b){
29898 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29908 Roo.each(box, function(b){
29910 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29913 mx = Math.max(mx, b.x);
29917 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29921 Roo.each(box, function(b){
29923 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29937 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29940 /** Sets position of item in DOM
29941 * @param {Element} item
29942 * @param {Number} x - horizontal position
29943 * @param {Number} y - vertical position
29944 * @param {Boolean} isInstant - disables transitions
29946 _processVerticalLayoutQueue : function( queue, isInstant )
29948 var pos = this.el.getBox(true);
29953 for (var i = 0; i < this.cols; i++){
29957 Roo.each(queue, function(box, k){
29959 var col = k % this.cols;
29961 Roo.each(box, function(b,kk){
29963 b.el.position('absolute');
29965 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29966 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29968 if(b.size == 'md-left' || b.size == 'md-right'){
29969 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29970 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29973 b.el.setWidth(width);
29974 b.el.setHeight(height);
29976 b.el.select('iframe',true).setSize(width,height);
29980 for (var i = 0; i < this.cols; i++){
29982 if(maxY[i] < maxY[col]){
29987 col = Math.min(col, i);
29991 x = pos.x + col * (this.colWidth + this.padWidth);
29995 var positions = [];
29997 switch (box.length){
29999 positions = this.getVerticalOneBoxColPositions(x, y, box);
30002 positions = this.getVerticalTwoBoxColPositions(x, y, box);
30005 positions = this.getVerticalThreeBoxColPositions(x, y, box);
30008 positions = this.getVerticalFourBoxColPositions(x, y, box);
30014 Roo.each(box, function(b,kk){
30016 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30018 var sz = b.el.getSize();
30020 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30028 for (var i = 0; i < this.cols; i++){
30029 mY = Math.max(mY, maxY[i]);
30032 this.el.setHeight(mY - pos.y);
30036 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30038 // var pos = this.el.getBox(true);
30041 // var maxX = pos.right;
30043 // var maxHeight = 0;
30045 // Roo.each(items, function(item, k){
30049 // item.el.position('absolute');
30051 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30053 // item.el.setWidth(width);
30055 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30057 // item.el.setHeight(height);
30060 // item.el.setXY([x, y], isInstant ? false : true);
30062 // item.el.setXY([maxX - width, y], isInstant ? false : true);
30065 // y = y + height + this.alternativePadWidth;
30067 // maxHeight = maxHeight + height + this.alternativePadWidth;
30071 // this.el.setHeight(maxHeight);
30075 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30077 var pos = this.el.getBox(true);
30082 var maxX = pos.right;
30084 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30086 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30088 Roo.each(queue, function(box, k){
30090 Roo.each(box, function(b, kk){
30092 b.el.position('absolute');
30094 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30095 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30097 if(b.size == 'md-left' || b.size == 'md-right'){
30098 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30099 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30102 b.el.setWidth(width);
30103 b.el.setHeight(height);
30111 var positions = [];
30113 switch (box.length){
30115 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30118 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30121 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30124 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30130 Roo.each(box, function(b,kk){
30132 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30134 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30142 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30144 Roo.each(eItems, function(b,k){
30146 b.size = (k == 0) ? 'sm' : 'xs';
30147 b.x = (k == 0) ? 2 : 1;
30148 b.y = (k == 0) ? 2 : 1;
30150 b.el.position('absolute');
30152 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30154 b.el.setWidth(width);
30156 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30158 b.el.setHeight(height);
30162 var positions = [];
30165 x : maxX - this.unitWidth * 2 - this.gutter,
30170 x : maxX - this.unitWidth,
30171 y : minY + (this.unitWidth + this.gutter) * 2
30175 x : maxX - this.unitWidth * 3 - this.gutter * 2,
30179 Roo.each(eItems, function(b,k){
30181 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30187 getVerticalOneBoxColPositions : function(x, y, box)
30191 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30193 if(box[0].size == 'md-left'){
30197 if(box[0].size == 'md-right'){
30202 x : x + (this.unitWidth + this.gutter) * rand,
30209 getVerticalTwoBoxColPositions : function(x, y, box)
30213 if(box[0].size == 'xs'){
30217 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30221 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30235 x : x + (this.unitWidth + this.gutter) * 2,
30236 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30243 getVerticalThreeBoxColPositions : function(x, y, box)
30247 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30255 x : x + (this.unitWidth + this.gutter) * 1,
30260 x : x + (this.unitWidth + this.gutter) * 2,
30268 if(box[0].size == 'xs' && box[1].size == 'xs'){
30277 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30281 x : x + (this.unitWidth + this.gutter) * 1,
30295 x : x + (this.unitWidth + this.gutter) * 2,
30300 x : x + (this.unitWidth + this.gutter) * 2,
30301 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30308 getVerticalFourBoxColPositions : function(x, y, box)
30312 if(box[0].size == 'xs'){
30321 y : y + (this.unitHeight + this.gutter) * 1
30326 y : y + (this.unitHeight + this.gutter) * 2
30330 x : x + (this.unitWidth + this.gutter) * 1,
30344 x : x + (this.unitWidth + this.gutter) * 2,
30349 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30350 y : y + (this.unitHeight + this.gutter) * 1
30354 x : x + (this.unitWidth + this.gutter) * 2,
30355 y : y + (this.unitWidth + this.gutter) * 2
30362 getHorizontalOneBoxColPositions : function(maxX, minY, box)
30366 if(box[0].size == 'md-left'){
30368 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30375 if(box[0].size == 'md-right'){
30377 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30378 y : minY + (this.unitWidth + this.gutter) * 1
30384 var rand = Math.floor(Math.random() * (4 - box[0].y));
30387 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30388 y : minY + (this.unitWidth + this.gutter) * rand
30395 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30399 if(box[0].size == 'xs'){
30402 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30407 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30408 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30416 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30421 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30422 y : minY + (this.unitWidth + this.gutter) * 2
30429 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30433 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30436 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30441 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30442 y : minY + (this.unitWidth + this.gutter) * 1
30446 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30447 y : minY + (this.unitWidth + this.gutter) * 2
30454 if(box[0].size == 'xs' && box[1].size == 'xs'){
30457 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30462 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30467 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30468 y : minY + (this.unitWidth + this.gutter) * 1
30476 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30481 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30482 y : minY + (this.unitWidth + this.gutter) * 2
30486 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30487 y : minY + (this.unitWidth + this.gutter) * 2
30494 getHorizontalFourBoxColPositions : function(maxX, minY, box)
30498 if(box[0].size == 'xs'){
30501 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30506 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30511 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),
30516 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30517 y : minY + (this.unitWidth + this.gutter) * 1
30525 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30530 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30531 y : minY + (this.unitWidth + this.gutter) * 2
30535 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30536 y : minY + (this.unitWidth + this.gutter) * 2
30540 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),
30541 y : minY + (this.unitWidth + this.gutter) * 2
30555 * http://masonry.desandro.com
30557 * The idea is to render all the bricks based on vertical width...
30559 * The original code extends 'outlayer' - we might need to use that....
30565 * @class Roo.bootstrap.LayoutMasonryAuto
30566 * @extends Roo.bootstrap.Component
30567 * Bootstrap Layout Masonry class
30570 * Create a new Element
30571 * @param {Object} config The config object
30574 Roo.bootstrap.LayoutMasonryAuto = function(config){
30575 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30578 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
30581 * @cfg {Boolean} isFitWidth - resize the width..
30583 isFitWidth : false, // options..
30585 * @cfg {Boolean} isOriginLeft = left align?
30587 isOriginLeft : true,
30589 * @cfg {Boolean} isOriginTop = top align?
30591 isOriginTop : false,
30593 * @cfg {Boolean} isLayoutInstant = no animation?
30595 isLayoutInstant : false, // needed?
30597 * @cfg {Boolean} isResizingContainer = not sure if this is used..
30599 isResizingContainer : true,
30601 * @cfg {Number} columnWidth width of the columns
30607 * @cfg {Number} maxCols maximum number of columns
30612 * @cfg {Number} padHeight padding below box..
30618 * @cfg {Boolean} isAutoInitial defalut true
30621 isAutoInitial : true,
30627 initialColumnWidth : 0,
30628 currentSize : null,
30630 colYs : null, // array.
30637 bricks: null, //CompositeElement
30638 cols : 0, // array?
30639 // element : null, // wrapped now this.el
30640 _isLayoutInited : null,
30643 getAutoCreate : function(){
30647 cls: 'blog-masonary-wrapper ' + this.cls,
30649 cls : 'mas-boxes masonary'
30656 getChildContainer: function( )
30658 if (this.boxesEl) {
30659 return this.boxesEl;
30662 this.boxesEl = this.el.select('.mas-boxes').first();
30664 return this.boxesEl;
30668 initEvents : function()
30672 if(this.isAutoInitial){
30673 Roo.log('hook children rendered');
30674 this.on('childrenrendered', function() {
30675 Roo.log('children rendered');
30682 initial : function()
30684 this.reloadItems();
30686 this.currentSize = this.el.getBox(true);
30688 /// was window resize... - let's see if this works..
30689 Roo.EventManager.onWindowResize(this.resize, this);
30691 if(!this.isAutoInitial){
30696 this.layout.defer(500,this);
30699 reloadItems: function()
30701 this.bricks = this.el.select('.masonry-brick', true);
30703 this.bricks.each(function(b) {
30704 //Roo.log(b.getSize());
30705 if (!b.attr('originalwidth')) {
30706 b.attr('originalwidth', b.getSize().width);
30711 Roo.log(this.bricks.elements.length);
30714 resize : function()
30717 var cs = this.el.getBox(true);
30719 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30720 Roo.log("no change in with or X");
30723 this.currentSize = cs;
30727 layout : function()
30730 this._resetLayout();
30731 //this._manageStamps();
30733 // don't animate first layout
30734 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30735 this.layoutItems( isInstant );
30737 // flag for initalized
30738 this._isLayoutInited = true;
30741 layoutItems : function( isInstant )
30743 //var items = this._getItemsForLayout( this.items );
30744 // original code supports filtering layout items.. we just ignore it..
30746 this._layoutItems( this.bricks , isInstant );
30748 this._postLayout();
30750 _layoutItems : function ( items , isInstant)
30752 //this.fireEvent( 'layout', this, items );
30755 if ( !items || !items.elements.length ) {
30756 // no items, emit event with empty array
30761 items.each(function(item) {
30762 Roo.log("layout item");
30764 // get x/y object from method
30765 var position = this._getItemLayoutPosition( item );
30767 position.item = item;
30768 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30769 queue.push( position );
30772 this._processLayoutQueue( queue );
30774 /** Sets position of item in DOM
30775 * @param {Element} item
30776 * @param {Number} x - horizontal position
30777 * @param {Number} y - vertical position
30778 * @param {Boolean} isInstant - disables transitions
30780 _processLayoutQueue : function( queue )
30782 for ( var i=0, len = queue.length; i < len; i++ ) {
30783 var obj = queue[i];
30784 obj.item.position('absolute');
30785 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30791 * Any logic you want to do after each layout,
30792 * i.e. size the container
30794 _postLayout : function()
30796 this.resizeContainer();
30799 resizeContainer : function()
30801 if ( !this.isResizingContainer ) {
30804 var size = this._getContainerSize();
30806 this.el.setSize(size.width,size.height);
30807 this.boxesEl.setSize(size.width,size.height);
30813 _resetLayout : function()
30815 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30816 this.colWidth = this.el.getWidth();
30817 //this.gutter = this.el.getWidth();
30819 this.measureColumns();
30825 this.colYs.push( 0 );
30831 measureColumns : function()
30833 this.getContainerWidth();
30834 // if columnWidth is 0, default to outerWidth of first item
30835 if ( !this.columnWidth ) {
30836 var firstItem = this.bricks.first();
30837 Roo.log(firstItem);
30838 this.columnWidth = this.containerWidth;
30839 if (firstItem && firstItem.attr('originalwidth') ) {
30840 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30842 // columnWidth fall back to item of first element
30843 Roo.log("set column width?");
30844 this.initialColumnWidth = this.columnWidth ;
30846 // if first elem has no width, default to size of container
30851 if (this.initialColumnWidth) {
30852 this.columnWidth = this.initialColumnWidth;
30857 // column width is fixed at the top - however if container width get's smaller we should
30860 // this bit calcs how man columns..
30862 var columnWidth = this.columnWidth += this.gutter;
30864 // calculate columns
30865 var containerWidth = this.containerWidth + this.gutter;
30867 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30868 // fix rounding errors, typically with gutters
30869 var excess = columnWidth - containerWidth % columnWidth;
30872 // if overshoot is less than a pixel, round up, otherwise floor it
30873 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30874 cols = Math[ mathMethod ]( cols );
30875 this.cols = Math.max( cols, 1 );
30876 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30878 // padding positioning..
30879 var totalColWidth = this.cols * this.columnWidth;
30880 var padavail = this.containerWidth - totalColWidth;
30881 // so for 2 columns - we need 3 'pads'
30883 var padNeeded = (1+this.cols) * this.padWidth;
30885 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30887 this.columnWidth += padExtra
30888 //this.padWidth = Math.floor(padavail / ( this.cols));
30890 // adjust colum width so that padding is fixed??
30892 // we have 3 columns ... total = width * 3
30893 // we have X left over... that should be used by
30895 //if (this.expandC) {
30903 getContainerWidth : function()
30905 /* // container is parent if fit width
30906 var container = this.isFitWidth ? this.element.parentNode : this.element;
30907 // check that this.size and size are there
30908 // IE8 triggers resize on body size change, so they might not be
30910 var size = getSize( container ); //FIXME
30911 this.containerWidth = size && size.innerWidth; //FIXME
30914 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30918 _getItemLayoutPosition : function( item ) // what is item?
30920 // we resize the item to our columnWidth..
30922 item.setWidth(this.columnWidth);
30923 item.autoBoxAdjust = false;
30925 var sz = item.getSize();
30927 // how many columns does this brick span
30928 var remainder = this.containerWidth % this.columnWidth;
30930 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30931 // round if off by 1 pixel, otherwise use ceil
30932 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
30933 colSpan = Math.min( colSpan, this.cols );
30935 // normally this should be '1' as we dont' currently allow multi width columns..
30937 var colGroup = this._getColGroup( colSpan );
30938 // get the minimum Y value from the columns
30939 var minimumY = Math.min.apply( Math, colGroup );
30940 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30942 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
30944 // position the brick
30946 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30947 y: this.currentSize.y + minimumY + this.padHeight
30951 // apply setHeight to necessary columns
30952 var setHeight = minimumY + sz.height + this.padHeight;
30953 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30955 var setSpan = this.cols + 1 - colGroup.length;
30956 for ( var i = 0; i < setSpan; i++ ) {
30957 this.colYs[ shortColIndex + i ] = setHeight ;
30964 * @param {Number} colSpan - number of columns the element spans
30965 * @returns {Array} colGroup
30967 _getColGroup : function( colSpan )
30969 if ( colSpan < 2 ) {
30970 // if brick spans only one column, use all the column Ys
30975 // how many different places could this brick fit horizontally
30976 var groupCount = this.cols + 1 - colSpan;
30977 // for each group potential horizontal position
30978 for ( var i = 0; i < groupCount; i++ ) {
30979 // make an array of colY values for that one group
30980 var groupColYs = this.colYs.slice( i, i + colSpan );
30981 // and get the max value of the array
30982 colGroup[i] = Math.max.apply( Math, groupColYs );
30987 _manageStamp : function( stamp )
30989 var stampSize = stamp.getSize();
30990 var offset = stamp.getBox();
30991 // get the columns that this stamp affects
30992 var firstX = this.isOriginLeft ? offset.x : offset.right;
30993 var lastX = firstX + stampSize.width;
30994 var firstCol = Math.floor( firstX / this.columnWidth );
30995 firstCol = Math.max( 0, firstCol );
30997 var lastCol = Math.floor( lastX / this.columnWidth );
30998 // lastCol should not go over if multiple of columnWidth #425
30999 lastCol -= lastX % this.columnWidth ? 0 : 1;
31000 lastCol = Math.min( this.cols - 1, lastCol );
31002 // set colYs to bottom of the stamp
31003 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31006 for ( var i = firstCol; i <= lastCol; i++ ) {
31007 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31012 _getContainerSize : function()
31014 this.maxY = Math.max.apply( Math, this.colYs );
31019 if ( this.isFitWidth ) {
31020 size.width = this._getContainerFitWidth();
31026 _getContainerFitWidth : function()
31028 var unusedCols = 0;
31029 // count unused columns
31032 if ( this.colYs[i] !== 0 ) {
31037 // fit container to columns that have been used
31038 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
31041 needsResizeLayout : function()
31043 var previousWidth = this.containerWidth;
31044 this.getContainerWidth();
31045 return previousWidth !== this.containerWidth;
31060 * @class Roo.bootstrap.MasonryBrick
31061 * @extends Roo.bootstrap.Component
31062 * Bootstrap MasonryBrick class
31065 * Create a new MasonryBrick
31066 * @param {Object} config The config object
31069 Roo.bootstrap.MasonryBrick = function(config){
31070 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31076 * When a MasonryBrick is clcik
31077 * @param {Roo.bootstrap.MasonryBrick} this
31078 * @param {Roo.EventObject} e
31084 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
31087 * @cfg {String} title
31091 * @cfg {String} html
31095 * @cfg {String} bgimage
31099 * @cfg {String} videourl
31103 * @cfg {String} cls
31107 * @cfg {String} href
31111 * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
31116 * @cfg {String} (center|bottom) placetitle
31121 * @cfg {Boolean} isFitContainer defalut true
31123 isFitContainer : true,
31126 * @cfg {Boolean} preventDefault defalut false
31128 preventDefault : false,
31130 getAutoCreate : function()
31132 if(!this.isFitContainer){
31133 return this.getSplitAutoCreate();
31136 var cls = 'masonry-brick masonry-brick-full';
31138 if(this.href.length){
31139 cls += ' masonry-brick-link';
31142 if(this.bgimage.length){
31143 cls += ' masonry-brick-image';
31146 if(!this.html.length){
31147 cls += ' enable-mask';
31151 cls += ' masonry-' + this.size + '-brick';
31154 if(this.placetitle.length){
31156 switch (this.placetitle) {
31158 cls += ' masonry-center-title';
31161 cls += ' masonry-bottom-title';
31168 if(!this.html.length && !this.bgimage.length){
31169 cls += ' masonry-center-title';
31172 if(!this.html.length && this.bgimage.length){
31173 cls += ' masonry-bottom-title';
31178 cls += ' ' + this.cls;
31182 tag: (this.href.length) ? 'a' : 'div',
31187 cls: 'masonry-brick-paragraph',
31193 if(this.href.length){
31194 cfg.href = this.href;
31197 var cn = cfg.cn[0].cn;
31199 if(this.title.length){
31202 cls: 'masonry-brick-title',
31207 if(this.html.length){
31210 cls: 'masonry-brick-text',
31214 if (!this.title.length && !this.html.length) {
31215 cfg.cn[0].cls += ' hide';
31218 if(this.bgimage.length){
31221 cls: 'masonry-brick-image-view',
31226 if(this.videourl.length){
31227 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31228 // youtube support only?
31231 cls: 'masonry-brick-image-view',
31234 allowfullscreen : true
31242 cls: 'masonry-brick-mask'
31249 getSplitAutoCreate : function()
31251 var cls = 'masonry-brick masonry-brick-split';
31253 if(this.href.length){
31254 cls += ' masonry-brick-link';
31257 if(this.bgimage.length){
31258 cls += ' masonry-brick-image';
31262 cls += ' masonry-' + this.size + '-brick';
31265 switch (this.placetitle) {
31267 cls += ' masonry-center-title';
31270 cls += ' masonry-bottom-title';
31273 if(!this.bgimage.length){
31274 cls += ' masonry-center-title';
31277 if(this.bgimage.length){
31278 cls += ' masonry-bottom-title';
31284 cls += ' ' + this.cls;
31288 tag: (this.href.length) ? 'a' : 'div',
31293 cls: 'masonry-brick-split-head',
31297 cls: 'masonry-brick-paragraph',
31304 cls: 'masonry-brick-split-body',
31310 if(this.href.length){
31311 cfg.href = this.href;
31314 if(this.title.length){
31315 cfg.cn[0].cn[0].cn.push({
31317 cls: 'masonry-brick-title',
31322 if(this.html.length){
31323 cfg.cn[1].cn.push({
31325 cls: 'masonry-brick-text',
31330 if(this.bgimage.length){
31331 cfg.cn[0].cn.push({
31333 cls: 'masonry-brick-image-view',
31338 if(this.videourl.length){
31339 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31340 // youtube support only?
31341 cfg.cn[0].cn.cn.push({
31343 cls: 'masonry-brick-image-view',
31346 allowfullscreen : true
31353 initEvents: function()
31355 switch (this.size) {
31388 this.el.on('touchstart', this.onTouchStart, this);
31389 this.el.on('touchmove', this.onTouchMove, this);
31390 this.el.on('touchend', this.onTouchEnd, this);
31391 this.el.on('contextmenu', this.onContextMenu, this);
31393 this.el.on('mouseenter' ,this.enter, this);
31394 this.el.on('mouseleave', this.leave, this);
31395 this.el.on('click', this.onClick, this);
31398 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31399 this.parent().bricks.push(this);
31404 onClick: function(e, el)
31406 var time = this.endTimer - this.startTimer;
31410 e.preventDefault();
31415 if(!this.preventDefault){
31419 e.preventDefault();
31420 this.fireEvent('click', this);
31423 enter: function(e, el)
31425 e.preventDefault();
31427 if(!this.isFitContainer){
31431 if(this.bgimage.length && this.html.length){
31432 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31436 leave: function(e, el)
31438 e.preventDefault();
31440 if(!this.isFitContainer){
31444 if(this.bgimage.length && this.html.length){
31445 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31449 onTouchStart: function(e, el)
31451 // e.preventDefault();
31453 this.touchmoved = false;
31455 if(!this.isFitContainer){
31459 if(!this.bgimage.length || !this.html.length){
31463 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31465 this.timer = new Date().getTime();
31469 onTouchMove: function(e, el)
31471 this.touchmoved = true;
31474 onContextMenu : function(e,el)
31476 e.preventDefault();
31477 e.stopPropagation();
31481 onTouchEnd: function(e, el)
31483 // e.preventDefault();
31485 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31492 if(!this.bgimage.length || !this.html.length){
31494 if(this.href.length){
31495 window.location.href = this.href;
31501 if(!this.isFitContainer){
31505 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31507 window.location.href = this.href;
31522 * @class Roo.bootstrap.Brick
31523 * @extends Roo.bootstrap.Component
31524 * Bootstrap Brick class
31527 * Create a new Brick
31528 * @param {Object} config The config object
31531 Roo.bootstrap.Brick = function(config){
31532 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31538 * When a Brick is click
31539 * @param {Roo.bootstrap.Brick} this
31540 * @param {Roo.EventObject} e
31546 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
31549 * @cfg {String} title
31553 * @cfg {String} html
31557 * @cfg {String} bgimage
31561 * @cfg {String} cls
31565 * @cfg {String} href
31569 * @cfg {String} video
31573 * @cfg {Boolean} square
31577 getAutoCreate : function()
31579 var cls = 'roo-brick';
31581 if(this.href.length){
31582 cls += ' roo-brick-link';
31585 if(this.bgimage.length){
31586 cls += ' roo-brick-image';
31589 if(!this.html.length && !this.bgimage.length){
31590 cls += ' roo-brick-center-title';
31593 if(!this.html.length && this.bgimage.length){
31594 cls += ' roo-brick-bottom-title';
31598 cls += ' ' + this.cls;
31602 tag: (this.href.length) ? 'a' : 'div',
31607 cls: 'roo-brick-paragraph',
31613 if(this.href.length){
31614 cfg.href = this.href;
31617 var cn = cfg.cn[0].cn;
31619 if(this.title.length){
31622 cls: 'roo-brick-title',
31627 if(this.html.length){
31630 cls: 'roo-brick-text',
31637 if(this.bgimage.length){
31640 cls: 'roo-brick-image-view',
31648 initEvents: function()
31650 if(this.title.length || this.html.length){
31651 this.el.on('mouseenter' ,this.enter, this);
31652 this.el.on('mouseleave', this.leave, this);
31656 Roo.EventManager.onWindowResize(this.resize, this);
31661 resize : function()
31663 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31665 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31667 if(this.bgimage.length){
31668 var image = this.el.select('.roo-brick-image-view', true).first();
31669 image.setWidth(paragraph.getWidth());
31670 image.setHeight(paragraph.getWidth());
31672 this.el.setHeight(paragraph.getWidth());
31678 enter: function(e, el)
31680 e.preventDefault();
31682 if(this.bgimage.length){
31683 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31684 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31688 leave: function(e, el)
31690 e.preventDefault();
31692 if(this.bgimage.length){
31693 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31694 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31710 * @class Roo.bootstrap.NumberField
31711 * @extends Roo.bootstrap.Input
31712 * Bootstrap NumberField class
31718 * Create a new NumberField
31719 * @param {Object} config The config object
31722 Roo.bootstrap.NumberField = function(config){
31723 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
31726 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
31729 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
31731 allowDecimals : true,
31733 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
31735 decimalSeparator : ".",
31737 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
31739 decimalPrecision : 2,
31741 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
31743 allowNegative : true,
31745 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
31747 minValue : Number.NEGATIVE_INFINITY,
31749 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
31751 maxValue : Number.MAX_VALUE,
31753 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
31755 minText : "The minimum value for this field is {0}",
31757 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
31759 maxText : "The maximum value for this field is {0}",
31761 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
31762 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
31764 nanText : "{0} is not a valid number",
31766 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
31771 initEvents : function()
31773 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
31775 var allowed = "0123456789";
31777 if(this.allowDecimals){
31778 allowed += this.decimalSeparator;
31781 if(this.allowNegative){
31785 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
31787 var keyPress = function(e){
31789 var k = e.getKey();
31791 var c = e.getCharCode();
31794 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
31795 allowed.indexOf(String.fromCharCode(c)) === -1
31801 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
31805 if(allowed.indexOf(String.fromCharCode(c)) === -1){
31810 this.el.on("keypress", keyPress, this);
31813 validateValue : function(value)
31816 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
31820 var num = this.parseValue(value);
31823 this.markInvalid(String.format(this.nanText, value));
31827 if(num < this.minValue){
31828 this.markInvalid(String.format(this.minText, this.minValue));
31832 if(num > this.maxValue){
31833 this.markInvalid(String.format(this.maxText, this.maxValue));
31840 getValue : function()
31842 return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
31845 parseValue : function(value)
31847 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
31848 return isNaN(value) ? '' : value;
31851 fixPrecision : function(value)
31853 var nan = isNaN(value);
31855 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
31856 return nan ? '' : value;
31858 return parseFloat(value).toFixed(this.decimalPrecision);
31861 setValue : function(v)
31863 v = this.fixPrecision(v);
31864 Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
31867 decimalPrecisionFcn : function(v)
31869 return Math.floor(v);
31872 beforeBlur : function()
31878 var v = this.parseValue(this.getRawValue());
31893 * @class Roo.bootstrap.DocumentSlider
31894 * @extends Roo.bootstrap.Component
31895 * Bootstrap DocumentSlider class
31898 * Create a new DocumentViewer
31899 * @param {Object} config The config object
31902 Roo.bootstrap.DocumentSlider = function(config){
31903 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
31910 * Fire after initEvent
31911 * @param {Roo.bootstrap.DocumentSlider} this
31916 * Fire after update
31917 * @param {Roo.bootstrap.DocumentSlider} this
31923 * @param {Roo.bootstrap.DocumentSlider} this
31929 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
31935 getAutoCreate : function()
31939 cls : 'roo-document-slider',
31943 cls : 'roo-document-slider-header',
31947 cls : 'roo-document-slider-header-title'
31953 cls : 'roo-document-slider-body',
31957 cls : 'roo-document-slider-prev',
31961 cls : 'fa fa-chevron-left'
31967 cls : 'roo-document-slider-thumb',
31971 cls : 'roo-document-slider-image'
31977 cls : 'roo-document-slider-next',
31981 cls : 'fa fa-chevron-right'
31993 initEvents : function()
31995 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
31996 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
31998 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
31999 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
32001 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
32002 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32004 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
32005 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32007 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
32008 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32010 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
32011 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32013 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
32014 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32016 this.thumbEl.on('click', this.onClick, this);
32018 this.prevIndicator.on('click', this.prev, this);
32020 this.nextIndicator.on('click', this.next, this);
32024 initial : function()
32026 if(this.files.length){
32027 this.indicator = 1;
32031 this.fireEvent('initial', this);
32034 update : function()
32036 this.imageEl.attr('src', this.files[this.indicator - 1]);
32038 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
32040 this.prevIndicator.show();
32042 if(this.indicator == 1){
32043 this.prevIndicator.hide();
32046 this.nextIndicator.show();
32048 if(this.indicator == this.files.length){
32049 this.nextIndicator.hide();
32052 this.thumbEl.scrollTo('top');
32054 this.fireEvent('update', this);
32057 onClick : function(e)
32059 e.preventDefault();
32061 this.fireEvent('click', this);
32066 e.preventDefault();
32068 this.indicator = Math.max(1, this.indicator - 1);
32075 e.preventDefault();
32077 this.indicator = Math.min(this.files.length, this.indicator + 1);
32084 * Ext JS Library 1.1.1
32085 * Copyright(c) 2006-2007, Ext JS, LLC.
32087 * Originally Released Under LGPL - original licence link has changed is not relivant.
32090 * <script type="text/javascript">
32095 * @class Roo.bootstrap.SplitBar
32096 * @extends Roo.util.Observable
32097 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
32101 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
32102 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
32103 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
32104 split.minSize = 100;
32105 split.maxSize = 600;
32106 split.animate = true;
32107 split.on('moved', splitterMoved);
32110 * Create a new SplitBar
32111 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
32112 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
32113 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32114 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
32115 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
32116 position of the SplitBar).
32118 Roo.bootstrap.SplitBar = function(cfg){
32123 // dragElement : elm
32124 // resizingElement: el,
32126 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
32127 // placement : Roo.bootstrap.SplitBar.LEFT ,
32128 // existingProxy ???
32131 this.el = Roo.get(cfg.dragElement, true);
32132 this.el.dom.unselectable = "on";
32134 this.resizingEl = Roo.get(cfg.resizingElement, true);
32138 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32139 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
32142 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
32145 * The minimum size of the resizing element. (Defaults to 0)
32151 * The maximum size of the resizing element. (Defaults to 2000)
32154 this.maxSize = 2000;
32157 * Whether to animate the transition to the new size
32160 this.animate = false;
32163 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
32166 this.useShim = false;
32171 if(!cfg.existingProxy){
32173 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
32175 this.proxy = Roo.get(cfg.existingProxy).dom;
32178 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
32181 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
32184 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
32187 this.dragSpecs = {};
32190 * @private The adapter to use to positon and resize elements
32192 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32193 this.adapter.init(this);
32195 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32197 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
32198 this.el.addClass("roo-splitbar-h");
32201 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
32202 this.el.addClass("roo-splitbar-v");
32208 * Fires when the splitter is moved (alias for {@link #event-moved})
32209 * @param {Roo.bootstrap.SplitBar} this
32210 * @param {Number} newSize the new width or height
32215 * Fires when the splitter is moved
32216 * @param {Roo.bootstrap.SplitBar} this
32217 * @param {Number} newSize the new width or height
32221 * @event beforeresize
32222 * Fires before the splitter is dragged
32223 * @param {Roo.bootstrap.SplitBar} this
32225 "beforeresize" : true,
32227 "beforeapply" : true
32230 Roo.util.Observable.call(this);
32233 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
32234 onStartProxyDrag : function(x, y){
32235 this.fireEvent("beforeresize", this);
32237 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
32239 o.enableDisplayMode("block");
32240 // all splitbars share the same overlay
32241 Roo.bootstrap.SplitBar.prototype.overlay = o;
32243 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32244 this.overlay.show();
32245 Roo.get(this.proxy).setDisplayed("block");
32246 var size = this.adapter.getElementSize(this);
32247 this.activeMinSize = this.getMinimumSize();;
32248 this.activeMaxSize = this.getMaximumSize();;
32249 var c1 = size - this.activeMinSize;
32250 var c2 = Math.max(this.activeMaxSize - size, 0);
32251 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32252 this.dd.resetConstraints();
32253 this.dd.setXConstraint(
32254 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
32255 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
32257 this.dd.setYConstraint(0, 0);
32259 this.dd.resetConstraints();
32260 this.dd.setXConstraint(0, 0);
32261 this.dd.setYConstraint(
32262 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
32263 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
32266 this.dragSpecs.startSize = size;
32267 this.dragSpecs.startPoint = [x, y];
32268 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
32272 * @private Called after the drag operation by the DDProxy
32274 onEndProxyDrag : function(e){
32275 Roo.get(this.proxy).setDisplayed(false);
32276 var endPoint = Roo.lib.Event.getXY(e);
32278 this.overlay.hide();
32281 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32282 newSize = this.dragSpecs.startSize +
32283 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
32284 endPoint[0] - this.dragSpecs.startPoint[0] :
32285 this.dragSpecs.startPoint[0] - endPoint[0]
32288 newSize = this.dragSpecs.startSize +
32289 (this.placement == Roo.bootstrap.SplitBar.TOP ?
32290 endPoint[1] - this.dragSpecs.startPoint[1] :
32291 this.dragSpecs.startPoint[1] - endPoint[1]
32294 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
32295 if(newSize != this.dragSpecs.startSize){
32296 if(this.fireEvent('beforeapply', this, newSize) !== false){
32297 this.adapter.setElementSize(this, newSize);
32298 this.fireEvent("moved", this, newSize);
32299 this.fireEvent("resize", this, newSize);
32305 * Get the adapter this SplitBar uses
32306 * @return The adapter object
32308 getAdapter : function(){
32309 return this.adapter;
32313 * Set the adapter this SplitBar uses
32314 * @param {Object} adapter A SplitBar adapter object
32316 setAdapter : function(adapter){
32317 this.adapter = adapter;
32318 this.adapter.init(this);
32322 * Gets the minimum size for the resizing element
32323 * @return {Number} The minimum size
32325 getMinimumSize : function(){
32326 return this.minSize;
32330 * Sets the minimum size for the resizing element
32331 * @param {Number} minSize The minimum size
32333 setMinimumSize : function(minSize){
32334 this.minSize = minSize;
32338 * Gets the maximum size for the resizing element
32339 * @return {Number} The maximum size
32341 getMaximumSize : function(){
32342 return this.maxSize;
32346 * Sets the maximum size for the resizing element
32347 * @param {Number} maxSize The maximum size
32349 setMaximumSize : function(maxSize){
32350 this.maxSize = maxSize;
32354 * Sets the initialize size for the resizing element
32355 * @param {Number} size The initial size
32357 setCurrentSize : function(size){
32358 var oldAnimate = this.animate;
32359 this.animate = false;
32360 this.adapter.setElementSize(this, size);
32361 this.animate = oldAnimate;
32365 * Destroy this splitbar.
32366 * @param {Boolean} removeEl True to remove the element
32368 destroy : function(removeEl){
32370 this.shim.remove();
32373 this.proxy.parentNode.removeChild(this.proxy);
32381 * @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.
32383 Roo.bootstrap.SplitBar.createProxy = function(dir){
32384 var proxy = new Roo.Element(document.createElement("div"));
32385 proxy.unselectable();
32386 var cls = 'roo-splitbar-proxy';
32387 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
32388 document.body.appendChild(proxy.dom);
32393 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
32394 * Default Adapter. It assumes the splitter and resizing element are not positioned
32395 * elements and only gets/sets the width of the element. Generally used for table based layouts.
32397 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
32400 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
32401 // do nothing for now
32402 init : function(s){
32406 * Called before drag operations to get the current size of the resizing element.
32407 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32409 getElementSize : function(s){
32410 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32411 return s.resizingEl.getWidth();
32413 return s.resizingEl.getHeight();
32418 * Called after drag operations to set the size of the resizing element.
32419 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32420 * @param {Number} newSize The new size to set
32421 * @param {Function} onComplete A function to be invoked when resizing is complete
32423 setElementSize : function(s, newSize, onComplete){
32424 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32426 s.resizingEl.setWidth(newSize);
32428 onComplete(s, newSize);
32431 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
32436 s.resizingEl.setHeight(newSize);
32438 onComplete(s, newSize);
32441 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
32448 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
32449 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
32450 * Adapter that moves the splitter element to align with the resized sizing element.
32451 * Used with an absolute positioned SplitBar.
32452 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
32453 * document.body, make sure you assign an id to the body element.
32455 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
32456 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32457 this.container = Roo.get(container);
32460 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
32461 init : function(s){
32462 this.basic.init(s);
32465 getElementSize : function(s){
32466 return this.basic.getElementSize(s);
32469 setElementSize : function(s, newSize, onComplete){
32470 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
32473 moveSplitter : function(s){
32474 var yes = Roo.bootstrap.SplitBar;
32475 switch(s.placement){
32477 s.el.setX(s.resizingEl.getRight());
32480 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
32483 s.el.setY(s.resizingEl.getBottom());
32486 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
32493 * Orientation constant - Create a vertical SplitBar
32497 Roo.bootstrap.SplitBar.VERTICAL = 1;
32500 * Orientation constant - Create a horizontal SplitBar
32504 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
32507 * Placement constant - The resizing element is to the left of the splitter element
32511 Roo.bootstrap.SplitBar.LEFT = 1;
32514 * Placement constant - The resizing element is to the right of the splitter element
32518 Roo.bootstrap.SplitBar.RIGHT = 2;
32521 * Placement constant - The resizing element is positioned above the splitter element
32525 Roo.bootstrap.SplitBar.TOP = 3;
32528 * Placement constant - The resizing element is positioned under splitter element
32532 Roo.bootstrap.SplitBar.BOTTOM = 4;
32533 Roo.namespace("Roo.bootstrap.layout");/*
32535 * Ext JS Library 1.1.1
32536 * Copyright(c) 2006-2007, Ext JS, LLC.
32538 * Originally Released Under LGPL - original licence link has changed is not relivant.
32541 * <script type="text/javascript">
32545 * @class Roo.bootstrap.layout.Manager
32546 * @extends Roo.bootstrap.Component
32547 * Base class for layout managers.
32549 Roo.bootstrap.layout.Manager = function(config)
32551 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
32557 /** false to disable window resize monitoring @type Boolean */
32558 this.monitorWindowResize = true;
32563 * Fires when a layout is performed.
32564 * @param {Roo.LayoutManager} this
32568 * @event regionresized
32569 * Fires when the user resizes a region.
32570 * @param {Roo.LayoutRegion} region The resized region
32571 * @param {Number} newSize The new size (width for east/west, height for north/south)
32573 "regionresized" : true,
32575 * @event regioncollapsed
32576 * Fires when a region is collapsed.
32577 * @param {Roo.LayoutRegion} region The collapsed region
32579 "regioncollapsed" : true,
32581 * @event regionexpanded
32582 * Fires when a region is expanded.
32583 * @param {Roo.LayoutRegion} region The expanded region
32585 "regionexpanded" : true
32587 this.updating = false;
32590 this.el = Roo.get(config.el);
32596 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
32601 monitorWindowResize : true,
32607 onRender : function(ct, position)
32610 this.el = Roo.get(ct);
32613 //this.fireEvent('render',this);
32617 initEvents: function()
32621 // ie scrollbar fix
32622 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32623 document.body.scroll = "no";
32624 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32625 this.el.position('relative');
32627 this.id = this.el.id;
32628 this.el.addClass("roo-layout-container");
32629 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32630 if(this.el.dom != document.body ) {
32631 this.el.on('resize', this.layout,this);
32632 this.el.on('show', this.layout,this);
32638 * Returns true if this layout is currently being updated
32639 * @return {Boolean}
32641 isUpdating : function(){
32642 return this.updating;
32646 * Suspend the LayoutManager from doing auto-layouts while
32647 * making multiple add or remove calls
32649 beginUpdate : function(){
32650 this.updating = true;
32654 * Restore auto-layouts and optionally disable the manager from performing a layout
32655 * @param {Boolean} noLayout true to disable a layout update
32657 endUpdate : function(noLayout){
32658 this.updating = false;
32664 layout: function(){
32668 onRegionResized : function(region, newSize){
32669 this.fireEvent("regionresized", region, newSize);
32673 onRegionCollapsed : function(region){
32674 this.fireEvent("regioncollapsed", region);
32677 onRegionExpanded : function(region){
32678 this.fireEvent("regionexpanded", region);
32682 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32683 * performs box-model adjustments.
32684 * @return {Object} The size as an object {width: (the width), height: (the height)}
32686 getViewSize : function()
32689 if(this.el.dom != document.body){
32690 size = this.el.getSize();
32692 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32694 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32695 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32700 * Returns the Element this layout is bound to.
32701 * @return {Roo.Element}
32703 getEl : function(){
32708 * Returns the specified region.
32709 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32710 * @return {Roo.LayoutRegion}
32712 getRegion : function(target){
32713 return this.regions[target.toLowerCase()];
32716 onWindowResize : function(){
32717 if(this.monitorWindowResize){
32724 * Ext JS Library 1.1.1
32725 * Copyright(c) 2006-2007, Ext JS, LLC.
32727 * Originally Released Under LGPL - original licence link has changed is not relivant.
32730 * <script type="text/javascript">
32733 * @class Roo.bootstrap.layout.Border
32734 * @extends Roo.bootstrap.layout.Manager
32735 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32736 * please see: examples/bootstrap/nested.html<br><br>
32738 <b>The container the layout is rendered into can be either the body element or any other element.
32739 If it is not the body element, the container needs to either be an absolute positioned element,
32740 or you will need to add "position:relative" to the css of the container. You will also need to specify
32741 the container size if it is not the body element.</b>
32744 * Create a new Border
32745 * @param {Object} config Configuration options
32747 Roo.bootstrap.layout.Border = function(config){
32748 config = config || {};
32749 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
32753 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32754 if(config[region]){
32755 config[region].region = region;
32756 this.addRegion(config[region]);
32762 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
32764 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
32766 * Creates and adds a new region if it doesn't already exist.
32767 * @param {String} target The target region key (north, south, east, west or center).
32768 * @param {Object} config The regions config object
32769 * @return {BorderLayoutRegion} The new region
32771 addRegion : function(config)
32773 if(!this.regions[config.region]){
32774 var r = this.factory(config);
32775 this.bindRegion(r);
32777 return this.regions[config.region];
32781 bindRegion : function(r){
32782 this.regions[r.config.region] = r;
32784 r.on("visibilitychange", this.layout, this);
32785 r.on("paneladded", this.layout, this);
32786 r.on("panelremoved", this.layout, this);
32787 r.on("invalidated", this.layout, this);
32788 r.on("resized", this.onRegionResized, this);
32789 r.on("collapsed", this.onRegionCollapsed, this);
32790 r.on("expanded", this.onRegionExpanded, this);
32794 * Performs a layout update.
32796 layout : function()
32798 if(this.updating) {
32802 // render all the rebions if they have not been done alreayd?
32803 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32804 if(this.regions[region] && !this.regions[region].bodyEl){
32805 this.regions[region].onRender(this.el)
32809 var size = this.getViewSize();
32810 var w = size.width;
32811 var h = size.height;
32816 //var x = 0, y = 0;
32818 var rs = this.regions;
32819 var north = rs["north"];
32820 var south = rs["south"];
32821 var west = rs["west"];
32822 var east = rs["east"];
32823 var center = rs["center"];
32824 //if(this.hideOnLayout){ // not supported anymore
32825 //c.el.setStyle("display", "none");
32827 if(north && north.isVisible()){
32828 var b = north.getBox();
32829 var m = north.getMargins();
32830 b.width = w - (m.left+m.right);
32833 centerY = b.height + b.y + m.bottom;
32834 centerH -= centerY;
32835 north.updateBox(this.safeBox(b));
32837 if(south && south.isVisible()){
32838 var b = south.getBox();
32839 var m = south.getMargins();
32840 b.width = w - (m.left+m.right);
32842 var totalHeight = (b.height + m.top + m.bottom);
32843 b.y = h - totalHeight + m.top;
32844 centerH -= totalHeight;
32845 south.updateBox(this.safeBox(b));
32847 if(west && west.isVisible()){
32848 var b = west.getBox();
32849 var m = west.getMargins();
32850 b.height = centerH - (m.top+m.bottom);
32852 b.y = centerY + m.top;
32853 var totalWidth = (b.width + m.left + m.right);
32854 centerX += totalWidth;
32855 centerW -= totalWidth;
32856 west.updateBox(this.safeBox(b));
32858 if(east && east.isVisible()){
32859 var b = east.getBox();
32860 var m = east.getMargins();
32861 b.height = centerH - (m.top+m.bottom);
32862 var totalWidth = (b.width + m.left + m.right);
32863 b.x = w - totalWidth + m.left;
32864 b.y = centerY + m.top;
32865 centerW -= totalWidth;
32866 east.updateBox(this.safeBox(b));
32869 var m = center.getMargins();
32871 x: centerX + m.left,
32872 y: centerY + m.top,
32873 width: centerW - (m.left+m.right),
32874 height: centerH - (m.top+m.bottom)
32876 //if(this.hideOnLayout){
32877 //center.el.setStyle("display", "block");
32879 center.updateBox(this.safeBox(centerBox));
32882 this.fireEvent("layout", this);
32886 safeBox : function(box){
32887 box.width = Math.max(0, box.width);
32888 box.height = Math.max(0, box.height);
32893 * Adds a ContentPanel (or subclass) to this layout.
32894 * @param {String} target The target region key (north, south, east, west or center).
32895 * @param {Roo.ContentPanel} panel The panel to add
32896 * @return {Roo.ContentPanel} The added panel
32898 add : function(target, panel){
32900 target = target.toLowerCase();
32901 return this.regions[target].add(panel);
32905 * Remove a ContentPanel (or subclass) to this layout.
32906 * @param {String} target The target region key (north, south, east, west or center).
32907 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32908 * @return {Roo.ContentPanel} The removed panel
32910 remove : function(target, panel){
32911 target = target.toLowerCase();
32912 return this.regions[target].remove(panel);
32916 * Searches all regions for a panel with the specified id
32917 * @param {String} panelId
32918 * @return {Roo.ContentPanel} The panel or null if it wasn't found
32920 findPanel : function(panelId){
32921 var rs = this.regions;
32922 for(var target in rs){
32923 if(typeof rs[target] != "function"){
32924 var p = rs[target].getPanel(panelId);
32934 * Searches all regions for a panel with the specified id and activates (shows) it.
32935 * @param {String/ContentPanel} panelId The panels id or the panel itself
32936 * @return {Roo.ContentPanel} The shown panel or null
32938 showPanel : function(panelId) {
32939 var rs = this.regions;
32940 for(var target in rs){
32941 var r = rs[target];
32942 if(typeof r != "function"){
32943 if(r.hasPanel(panelId)){
32944 return r.showPanel(panelId);
32952 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32953 * @param {Roo.state.Provider} provider (optional) An alternate state provider
32956 restoreState : function(provider){
32958 provider = Roo.state.Manager;
32960 var sm = new Roo.LayoutStateManager();
32961 sm.init(this, provider);
32967 * Adds a xtype elements to the layout.
32971 xtype : 'ContentPanel',
32978 xtype : 'NestedLayoutPanel',
32984 items : [ ... list of content panels or nested layout panels.. ]
32988 * @param {Object} cfg Xtype definition of item to add.
32990 addxtype : function(cfg)
32992 // basically accepts a pannel...
32993 // can accept a layout region..!?!?
32994 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32997 // theory? children can only be panels??
32999 //if (!cfg.xtype.match(/Panel$/)) {
33004 if (typeof(cfg.region) == 'undefined') {
33005 Roo.log("Failed to add Panel, region was not set");
33009 var region = cfg.region;
33015 xitems = cfg.items;
33022 case 'Content': // ContentPanel (el, cfg)
33023 case 'Scroll': // ContentPanel (el, cfg)
33025 cfg.autoCreate = true;
33026 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33028 // var el = this.el.createChild();
33029 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33032 this.add(region, ret);
33036 case 'TreePanel': // our new panel!
33037 cfg.el = this.el.createChild();
33038 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33039 this.add(region, ret);
33044 // create a new Layout (which is a Border Layout...
33046 var clayout = cfg.layout;
33047 clayout.el = this.el.createChild();
33048 clayout.items = clayout.items || [];
33052 // replace this exitems with the clayout ones..
33053 xitems = clayout.items;
33055 // force background off if it's in center...
33056 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33057 cfg.background = false;
33059 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
33062 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33063 //console.log('adding nested layout panel ' + cfg.toSource());
33064 this.add(region, ret);
33065 nb = {}; /// find first...
33070 // needs grid and region
33072 //var el = this.getRegion(region).el.createChild();
33074 *var el = this.el.createChild();
33075 // create the grid first...
33076 cfg.grid.container = el;
33077 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
33080 if (region == 'center' && this.active ) {
33081 cfg.background = false;
33084 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33086 this.add(region, ret);
33088 if (cfg.background) {
33089 // render grid on panel activation (if panel background)
33090 ret.on('activate', function(gp) {
33091 if (!gp.grid.rendered) {
33092 // gp.grid.render(el);
33096 // cfg.grid.render(el);
33102 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
33103 // it was the old xcomponent building that caused this before.
33104 // espeically if border is the top element in the tree.
33114 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33116 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33117 this.add(region, ret);
33121 throw "Can not add '" + cfg.xtype + "' to Border";
33127 this.beginUpdate();
33131 Roo.each(xitems, function(i) {
33132 region = nb && i.region ? i.region : false;
33134 var add = ret.addxtype(i);
33137 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33138 if (!i.background) {
33139 abn[region] = nb[region] ;
33146 // make the last non-background panel active..
33147 //if (nb) { Roo.log(abn); }
33150 for(var r in abn) {
33151 region = this.getRegion(r);
33153 // tried using nb[r], but it does not work..
33155 region.showPanel(abn[r]);
33166 factory : function(cfg)
33169 var validRegions = Roo.bootstrap.layout.Border.regions;
33171 var target = cfg.region;
33174 var r = Roo.bootstrap.layout;
33178 return new r.North(cfg);
33180 return new r.South(cfg);
33182 return new r.East(cfg);
33184 return new r.West(cfg);
33186 return new r.Center(cfg);
33188 throw 'Layout region "'+target+'" not supported.';
33195 * Ext JS Library 1.1.1
33196 * Copyright(c) 2006-2007, Ext JS, LLC.
33198 * Originally Released Under LGPL - original licence link has changed is not relivant.
33201 * <script type="text/javascript">
33205 * @class Roo.bootstrap.layout.Basic
33206 * @extends Roo.util.Observable
33207 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33208 * and does not have a titlebar, tabs or any other features. All it does is size and position
33209 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33210 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
33211 * @cfg {string} region the region that it inhabits..
33212 * @cfg {bool} skipConfig skip config?
33216 Roo.bootstrap.layout.Basic = function(config){
33218 this.mgr = config.mgr;
33220 this.position = config.region;
33222 var skipConfig = config.skipConfig;
33226 * @scope Roo.BasicLayoutRegion
33230 * @event beforeremove
33231 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33232 * @param {Roo.LayoutRegion} this
33233 * @param {Roo.ContentPanel} panel The panel
33234 * @param {Object} e The cancel event object
33236 "beforeremove" : true,
33238 * @event invalidated
33239 * Fires when the layout for this region is changed.
33240 * @param {Roo.LayoutRegion} this
33242 "invalidated" : true,
33244 * @event visibilitychange
33245 * Fires when this region is shown or hidden
33246 * @param {Roo.LayoutRegion} this
33247 * @param {Boolean} visibility true or false
33249 "visibilitychange" : true,
33251 * @event paneladded
33252 * Fires when a panel is added.
33253 * @param {Roo.LayoutRegion} this
33254 * @param {Roo.ContentPanel} panel The panel
33256 "paneladded" : true,
33258 * @event panelremoved
33259 * Fires when a panel is removed.
33260 * @param {Roo.LayoutRegion} this
33261 * @param {Roo.ContentPanel} panel The panel
33263 "panelremoved" : true,
33265 * @event beforecollapse
33266 * Fires when this region before collapse.
33267 * @param {Roo.LayoutRegion} this
33269 "beforecollapse" : true,
33272 * Fires when this region is collapsed.
33273 * @param {Roo.LayoutRegion} this
33275 "collapsed" : true,
33278 * Fires when this region is expanded.
33279 * @param {Roo.LayoutRegion} this
33284 * Fires when this region is slid into view.
33285 * @param {Roo.LayoutRegion} this
33287 "slideshow" : true,
33290 * Fires when this region slides out of view.
33291 * @param {Roo.LayoutRegion} this
33293 "slidehide" : true,
33295 * @event panelactivated
33296 * Fires when a panel is activated.
33297 * @param {Roo.LayoutRegion} this
33298 * @param {Roo.ContentPanel} panel The activated panel
33300 "panelactivated" : true,
33303 * Fires when the user resizes this region.
33304 * @param {Roo.LayoutRegion} this
33305 * @param {Number} newSize The new size (width for east/west, height for north/south)
33309 /** A collection of panels in this region. @type Roo.util.MixedCollection */
33310 this.panels = new Roo.util.MixedCollection();
33311 this.panels.getKey = this.getPanelId.createDelegate(this);
33313 this.activePanel = null;
33314 // ensure listeners are added...
33316 if (config.listeners || config.events) {
33317 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
33318 listeners : config.listeners || {},
33319 events : config.events || {}
33323 if(skipConfig !== true){
33324 this.applyConfig(config);
33328 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
33330 getPanelId : function(p){
33334 applyConfig : function(config){
33335 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33336 this.config = config;
33341 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
33342 * the width, for horizontal (north, south) the height.
33343 * @param {Number} newSize The new width or height
33345 resizeTo : function(newSize){
33346 var el = this.el ? this.el :
33347 (this.activePanel ? this.activePanel.getEl() : null);
33349 switch(this.position){
33352 el.setWidth(newSize);
33353 this.fireEvent("resized", this, newSize);
33357 el.setHeight(newSize);
33358 this.fireEvent("resized", this, newSize);
33364 getBox : function(){
33365 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33368 getMargins : function(){
33369 return this.margins;
33372 updateBox : function(box){
33374 var el = this.activePanel.getEl();
33375 el.dom.style.left = box.x + "px";
33376 el.dom.style.top = box.y + "px";
33377 this.activePanel.setSize(box.width, box.height);
33381 * Returns the container element for this region.
33382 * @return {Roo.Element}
33384 getEl : function(){
33385 return this.activePanel;
33389 * Returns true if this region is currently visible.
33390 * @return {Boolean}
33392 isVisible : function(){
33393 return this.activePanel ? true : false;
33396 setActivePanel : function(panel){
33397 panel = this.getPanel(panel);
33398 if(this.activePanel && this.activePanel != panel){
33399 this.activePanel.setActiveState(false);
33400 this.activePanel.getEl().setLeftTop(-10000,-10000);
33402 this.activePanel = panel;
33403 panel.setActiveState(true);
33405 panel.setSize(this.box.width, this.box.height);
33407 this.fireEvent("panelactivated", this, panel);
33408 this.fireEvent("invalidated");
33412 * Show the specified panel.
33413 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33414 * @return {Roo.ContentPanel} The shown panel or null
33416 showPanel : function(panel){
33417 panel = this.getPanel(panel);
33419 this.setActivePanel(panel);
33425 * Get the active panel for this region.
33426 * @return {Roo.ContentPanel} The active panel or null
33428 getActivePanel : function(){
33429 return this.activePanel;
33433 * Add the passed ContentPanel(s)
33434 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33435 * @return {Roo.ContentPanel} The panel added (if only one was added)
33437 add : function(panel){
33438 if(arguments.length > 1){
33439 for(var i = 0, len = arguments.length; i < len; i++) {
33440 this.add(arguments[i]);
33444 if(this.hasPanel(panel)){
33445 this.showPanel(panel);
33448 var el = panel.getEl();
33449 if(el.dom.parentNode != this.mgr.el.dom){
33450 this.mgr.el.dom.appendChild(el.dom);
33452 if(panel.setRegion){
33453 panel.setRegion(this);
33455 this.panels.add(panel);
33456 el.setStyle("position", "absolute");
33457 if(!panel.background){
33458 this.setActivePanel(panel);
33459 if(this.config.initialSize && this.panels.getCount()==1){
33460 this.resizeTo(this.config.initialSize);
33463 this.fireEvent("paneladded", this, panel);
33468 * Returns true if the panel is in this region.
33469 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33470 * @return {Boolean}
33472 hasPanel : function(panel){
33473 if(typeof panel == "object"){ // must be panel obj
33474 panel = panel.getId();
33476 return this.getPanel(panel) ? true : false;
33480 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33481 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33482 * @param {Boolean} preservePanel Overrides the config preservePanel option
33483 * @return {Roo.ContentPanel} The panel that was removed
33485 remove : function(panel, preservePanel){
33486 panel = this.getPanel(panel);
33491 this.fireEvent("beforeremove", this, panel, e);
33492 if(e.cancel === true){
33495 var panelId = panel.getId();
33496 this.panels.removeKey(panelId);
33501 * Returns the panel specified or null if it's not in this region.
33502 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33503 * @return {Roo.ContentPanel}
33505 getPanel : function(id){
33506 if(typeof id == "object"){ // must be panel obj
33509 return this.panels.get(id);
33513 * Returns this regions position (north/south/east/west/center).
33516 getPosition: function(){
33517 return this.position;
33521 * Ext JS Library 1.1.1
33522 * Copyright(c) 2006-2007, Ext JS, LLC.
33524 * Originally Released Under LGPL - original licence link has changed is not relivant.
33527 * <script type="text/javascript">
33531 * @class Roo.bootstrap.layout.Region
33532 * @extends Roo.bootstrap.layout.Basic
33533 * This class represents a region in a layout manager.
33535 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33536 * @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})
33537 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
33538 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
33539 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
33540 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
33541 * @cfg {String} title The title for the region (overrides panel titles)
33542 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
33543 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33544 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
33545 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33546 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
33547 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33548 * the space available, similar to FireFox 1.5 tabs (defaults to false)
33549 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
33550 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
33551 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
33553 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
33554 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
33555 * @cfg {Boolean} disableTabTips True to disable tab tooltips
33556 * @cfg {Number} width For East/West panels
33557 * @cfg {Number} height For North/South panels
33558 * @cfg {Boolean} split To show the splitter
33559 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
33561 * @cfg {string} cls Extra CSS classes to add to region
33563 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
33564 * @cfg {string} region the region that it inhabits..
33567 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
33568 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
33570 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
33571 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
33572 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
33574 Roo.bootstrap.layout.Region = function(config)
33576 this.applyConfig(config);
33578 var mgr = config.mgr;
33579 var pos = config.region;
33580 config.skipConfig = true;
33581 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
33584 this.onRender(mgr.el);
33587 this.visible = true;
33588 this.collapsed = false;
33589 this.unrendered_panels = [];
33592 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
33594 position: '', // set by wrapper (eg. north/south etc..)
33595 unrendered_panels : null, // unrendered panels.
33596 createBody : function(){
33597 /** This region's body element
33598 * @type Roo.Element */
33599 this.bodyEl = this.el.createChild({
33601 cls: "roo-layout-panel-body tab-content" // bootstrap added...
33605 onRender: function(ctr, pos)
33607 var dh = Roo.DomHelper;
33608 /** This region's container element
33609 * @type Roo.Element */
33610 this.el = dh.append(ctr.dom, {
33612 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
33614 /** This region's title element
33615 * @type Roo.Element */
33617 this.titleEl = dh.append(this.el.dom,
33620 unselectable: "on",
33621 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
33623 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
33624 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
33627 this.titleEl.enableDisplayMode();
33628 /** This region's title text element
33629 * @type HTMLElement */
33630 this.titleTextEl = this.titleEl.dom.firstChild;
33631 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33633 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
33634 this.closeBtn.enableDisplayMode();
33635 this.closeBtn.on("click", this.closeClicked, this);
33636 this.closeBtn.hide();
33638 this.createBody(this.config);
33639 if(this.config.hideWhenEmpty){
33641 this.on("paneladded", this.validateVisibility, this);
33642 this.on("panelremoved", this.validateVisibility, this);
33644 if(this.autoScroll){
33645 this.bodyEl.setStyle("overflow", "auto");
33647 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
33649 //if(c.titlebar !== false){
33650 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
33651 this.titleEl.hide();
33653 this.titleEl.show();
33654 if(this.config.title){
33655 this.titleTextEl.innerHTML = this.config.title;
33659 if(this.config.collapsed){
33660 this.collapse(true);
33662 if(this.config.hidden){
33666 if (this.unrendered_panels && this.unrendered_panels.length) {
33667 for (var i =0;i< this.unrendered_panels.length; i++) {
33668 this.add(this.unrendered_panels[i]);
33670 this.unrendered_panels = null;
33676 applyConfig : function(c)
33679 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
33680 var dh = Roo.DomHelper;
33681 if(c.titlebar !== false){
33682 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
33683 this.collapseBtn.on("click", this.collapse, this);
33684 this.collapseBtn.enableDisplayMode();
33686 if(c.showPin === true || this.showPin){
33687 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
33688 this.stickBtn.enableDisplayMode();
33689 this.stickBtn.on("click", this.expand, this);
33690 this.stickBtn.hide();
33695 /** This region's collapsed element
33696 * @type Roo.Element */
33699 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33700 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33703 if(c.floatable !== false){
33704 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33705 this.collapsedEl.on("click", this.collapseClick, this);
33708 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33709 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33710 id: "message", unselectable: "on", style:{"float":"left"}});
33711 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33713 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33714 this.expandBtn.on("click", this.expand, this);
33718 if(this.collapseBtn){
33719 this.collapseBtn.setVisible(c.collapsible == true);
33722 this.cmargins = c.cmargins || this.cmargins ||
33723 (this.position == "west" || this.position == "east" ?
33724 {top: 0, left: 2, right:2, bottom: 0} :
33725 {top: 2, left: 0, right:0, bottom: 2});
33727 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33730 this.bottomTabs = c.tabPosition != "top";
33732 this.autoScroll = c.autoScroll || false;
33737 this.duration = c.duration || .30;
33738 this.slideDuration = c.slideDuration || .45;
33743 * Returns true if this region is currently visible.
33744 * @return {Boolean}
33746 isVisible : function(){
33747 return this.visible;
33751 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33752 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
33754 //setCollapsedTitle : function(title){
33755 // title = title || " ";
33756 // if(this.collapsedTitleTextEl){
33757 // this.collapsedTitleTextEl.innerHTML = title;
33761 getBox : function(){
33763 // if(!this.collapsed){
33764 b = this.el.getBox(false, true);
33766 // b = this.collapsedEl.getBox(false, true);
33771 getMargins : function(){
33772 return this.margins;
33773 //return this.collapsed ? this.cmargins : this.margins;
33776 highlight : function(){
33777 this.el.addClass("x-layout-panel-dragover");
33780 unhighlight : function(){
33781 this.el.removeClass("x-layout-panel-dragover");
33784 updateBox : function(box)
33786 if (!this.bodyEl) {
33787 return; // not rendered yet..
33791 if(!this.collapsed){
33792 this.el.dom.style.left = box.x + "px";
33793 this.el.dom.style.top = box.y + "px";
33794 this.updateBody(box.width, box.height);
33796 this.collapsedEl.dom.style.left = box.x + "px";
33797 this.collapsedEl.dom.style.top = box.y + "px";
33798 this.collapsedEl.setSize(box.width, box.height);
33801 this.tabs.autoSizeTabs();
33805 updateBody : function(w, h)
33808 this.el.setWidth(w);
33809 w -= this.el.getBorderWidth("rl");
33810 if(this.config.adjustments){
33811 w += this.config.adjustments[0];
33814 if(h !== null && h > 0){
33815 this.el.setHeight(h);
33816 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33817 h -= this.el.getBorderWidth("tb");
33818 if(this.config.adjustments){
33819 h += this.config.adjustments[1];
33821 this.bodyEl.setHeight(h);
33823 h = this.tabs.syncHeight(h);
33826 if(this.panelSize){
33827 w = w !== null ? w : this.panelSize.width;
33828 h = h !== null ? h : this.panelSize.height;
33830 if(this.activePanel){
33831 var el = this.activePanel.getEl();
33832 w = w !== null ? w : el.getWidth();
33833 h = h !== null ? h : el.getHeight();
33834 this.panelSize = {width: w, height: h};
33835 this.activePanel.setSize(w, h);
33837 if(Roo.isIE && this.tabs){
33838 this.tabs.el.repaint();
33843 * Returns the container element for this region.
33844 * @return {Roo.Element}
33846 getEl : function(){
33851 * Hides this region.
33854 //if(!this.collapsed){
33855 this.el.dom.style.left = "-2000px";
33858 // this.collapsedEl.dom.style.left = "-2000px";
33859 // this.collapsedEl.hide();
33861 this.visible = false;
33862 this.fireEvent("visibilitychange", this, false);
33866 * Shows this region if it was previously hidden.
33869 //if(!this.collapsed){
33872 // this.collapsedEl.show();
33874 this.visible = true;
33875 this.fireEvent("visibilitychange", this, true);
33878 closeClicked : function(){
33879 if(this.activePanel){
33880 this.remove(this.activePanel);
33884 collapseClick : function(e){
33886 e.stopPropagation();
33889 e.stopPropagation();
33895 * Collapses this region.
33896 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33899 collapse : function(skipAnim, skipCheck = false){
33900 if(this.collapsed) {
33904 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
33906 this.collapsed = true;
33908 this.split.el.hide();
33910 if(this.config.animate && skipAnim !== true){
33911 this.fireEvent("invalidated", this);
33912 this.animateCollapse();
33914 this.el.setLocation(-20000,-20000);
33916 this.collapsedEl.show();
33917 this.fireEvent("collapsed", this);
33918 this.fireEvent("invalidated", this);
33924 animateCollapse : function(){
33929 * Expands this region if it was previously collapsed.
33930 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33931 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33934 expand : function(e, skipAnim){
33936 e.stopPropagation();
33938 if(!this.collapsed || this.el.hasActiveFx()) {
33942 this.afterSlideIn();
33945 this.collapsed = false;
33946 if(this.config.animate && skipAnim !== true){
33947 this.animateExpand();
33951 this.split.el.show();
33953 this.collapsedEl.setLocation(-2000,-2000);
33954 this.collapsedEl.hide();
33955 this.fireEvent("invalidated", this);
33956 this.fireEvent("expanded", this);
33960 animateExpand : function(){
33964 initTabs : function()
33966 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33968 var ts = new Roo.bootstrap.panel.Tabs({
33969 el: this.bodyEl.dom,
33970 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33971 disableTooltips: this.config.disableTabTips,
33972 toolbar : this.config.toolbar
33975 if(this.config.hideTabs){
33976 ts.stripWrap.setDisplayed(false);
33979 ts.resizeTabs = this.config.resizeTabs === true;
33980 ts.minTabWidth = this.config.minTabWidth || 40;
33981 ts.maxTabWidth = this.config.maxTabWidth || 250;
33982 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33983 ts.monitorResize = false;
33984 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33985 ts.bodyEl.addClass('roo-layout-tabs-body');
33986 this.panels.each(this.initPanelAsTab, this);
33989 initPanelAsTab : function(panel){
33990 var ti = this.tabs.addTab(
33994 this.config.closeOnTab && panel.isClosable(),
33997 if(panel.tabTip !== undefined){
33998 ti.setTooltip(panel.tabTip);
34000 ti.on("activate", function(){
34001 this.setActivePanel(panel);
34004 if(this.config.closeOnTab){
34005 ti.on("beforeclose", function(t, e){
34007 this.remove(panel);
34011 panel.tabItem = ti;
34016 updatePanelTitle : function(panel, title)
34018 if(this.activePanel == panel){
34019 this.updateTitle(title);
34022 var ti = this.tabs.getTab(panel.getEl().id);
34024 if(panel.tabTip !== undefined){
34025 ti.setTooltip(panel.tabTip);
34030 updateTitle : function(title){
34031 if(this.titleTextEl && !this.config.title){
34032 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
34036 setActivePanel : function(panel)
34038 panel = this.getPanel(panel);
34039 if(this.activePanel && this.activePanel != panel){
34040 this.activePanel.setActiveState(false);
34042 this.activePanel = panel;
34043 panel.setActiveState(true);
34044 if(this.panelSize){
34045 panel.setSize(this.panelSize.width, this.panelSize.height);
34048 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34050 this.updateTitle(panel.getTitle());
34052 this.fireEvent("invalidated", this);
34054 this.fireEvent("panelactivated", this, panel);
34058 * Shows the specified panel.
34059 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34060 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34062 showPanel : function(panel)
34064 panel = this.getPanel(panel);
34067 var tab = this.tabs.getTab(panel.getEl().id);
34068 if(tab.isHidden()){
34069 this.tabs.unhideTab(tab.id);
34073 this.setActivePanel(panel);
34080 * Get the active panel for this region.
34081 * @return {Roo.ContentPanel} The active panel or null
34083 getActivePanel : function(){
34084 return this.activePanel;
34087 validateVisibility : function(){
34088 if(this.panels.getCount() < 1){
34089 this.updateTitle(" ");
34090 this.closeBtn.hide();
34093 if(!this.isVisible()){
34100 * Adds the passed ContentPanel(s) to this region.
34101 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34102 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34104 add : function(panel)
34106 if(arguments.length > 1){
34107 for(var i = 0, len = arguments.length; i < len; i++) {
34108 this.add(arguments[i]);
34113 // if we have not been rendered yet, then we can not really do much of this..
34114 if (!this.bodyEl) {
34115 this.unrendered_panels.push(panel);
34122 if(this.hasPanel(panel)){
34123 this.showPanel(panel);
34126 panel.setRegion(this);
34127 this.panels.add(panel);
34128 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34129 // sinle panel - no tab...?? would it not be better to render it with the tabs,
34130 // and hide them... ???
34131 this.bodyEl.dom.appendChild(panel.getEl().dom);
34132 if(panel.background !== true){
34133 this.setActivePanel(panel);
34135 this.fireEvent("paneladded", this, panel);
34142 this.initPanelAsTab(panel);
34146 if(panel.background !== true){
34147 this.tabs.activate(panel.getEl().id);
34149 this.fireEvent("paneladded", this, panel);
34154 * Hides the tab for the specified panel.
34155 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34157 hidePanel : function(panel){
34158 if(this.tabs && (panel = this.getPanel(panel))){
34159 this.tabs.hideTab(panel.getEl().id);
34164 * Unhides the tab for a previously hidden panel.
34165 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34167 unhidePanel : function(panel){
34168 if(this.tabs && (panel = this.getPanel(panel))){
34169 this.tabs.unhideTab(panel.getEl().id);
34173 clearPanels : function(){
34174 while(this.panels.getCount() > 0){
34175 this.remove(this.panels.first());
34180 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34181 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34182 * @param {Boolean} preservePanel Overrides the config preservePanel option
34183 * @return {Roo.ContentPanel} The panel that was removed
34185 remove : function(panel, preservePanel)
34187 panel = this.getPanel(panel);
34192 this.fireEvent("beforeremove", this, panel, e);
34193 if(e.cancel === true){
34196 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34197 var panelId = panel.getId();
34198 this.panels.removeKey(panelId);
34200 document.body.appendChild(panel.getEl().dom);
34203 this.tabs.removeTab(panel.getEl().id);
34204 }else if (!preservePanel){
34205 this.bodyEl.dom.removeChild(panel.getEl().dom);
34207 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34208 var p = this.panels.first();
34209 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34210 tempEl.appendChild(p.getEl().dom);
34211 this.bodyEl.update("");
34212 this.bodyEl.dom.appendChild(p.getEl().dom);
34214 this.updateTitle(p.getTitle());
34216 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34217 this.setActivePanel(p);
34219 panel.setRegion(null);
34220 if(this.activePanel == panel){
34221 this.activePanel = null;
34223 if(this.config.autoDestroy !== false && preservePanel !== true){
34224 try{panel.destroy();}catch(e){}
34226 this.fireEvent("panelremoved", this, panel);
34231 * Returns the TabPanel component used by this region
34232 * @return {Roo.TabPanel}
34234 getTabs : function(){
34238 createTool : function(parentEl, className){
34239 var btn = Roo.DomHelper.append(parentEl, {
34241 cls: "x-layout-tools-button",
34244 cls: "roo-layout-tools-button-inner " + className,
34248 btn.addClassOnOver("roo-layout-tools-button-over");
34253 * Ext JS Library 1.1.1
34254 * Copyright(c) 2006-2007, Ext JS, LLC.
34256 * Originally Released Under LGPL - original licence link has changed is not relivant.
34259 * <script type="text/javascript">
34265 * @class Roo.SplitLayoutRegion
34266 * @extends Roo.LayoutRegion
34267 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34269 Roo.bootstrap.layout.Split = function(config){
34270 this.cursor = config.cursor;
34271 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
34274 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
34276 splitTip : "Drag to resize.",
34277 collapsibleSplitTip : "Drag to resize. Double click to hide.",
34278 useSplitTips : false,
34280 applyConfig : function(config){
34281 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
34284 onRender : function(ctr,pos) {
34286 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
34287 if(!this.config.split){
34292 var splitEl = Roo.DomHelper.append(ctr.dom, {
34294 id: this.el.id + "-split",
34295 cls: "roo-layout-split roo-layout-split-"+this.position,
34298 /** The SplitBar for this region
34299 * @type Roo.SplitBar */
34300 // does not exist yet...
34301 Roo.log([this.position, this.orientation]);
34303 this.split = new Roo.bootstrap.SplitBar({
34304 dragElement : splitEl,
34305 resizingElement: this.el,
34306 orientation : this.orientation
34309 this.split.on("moved", this.onSplitMove, this);
34310 this.split.useShim = this.config.useShim === true;
34311 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34312 if(this.useSplitTips){
34313 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34315 //if(config.collapsible){
34316 // this.split.el.on("dblclick", this.collapse, this);
34319 if(typeof this.config.minSize != "undefined"){
34320 this.split.minSize = this.config.minSize;
34322 if(typeof this.config.maxSize != "undefined"){
34323 this.split.maxSize = this.config.maxSize;
34325 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
34326 this.hideSplitter();
34331 getHMaxSize : function(){
34332 var cmax = this.config.maxSize || 10000;
34333 var center = this.mgr.getRegion("center");
34334 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34337 getVMaxSize : function(){
34338 var cmax = this.config.maxSize || 10000;
34339 var center = this.mgr.getRegion("center");
34340 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34343 onSplitMove : function(split, newSize){
34344 this.fireEvent("resized", this, newSize);
34348 * Returns the {@link Roo.SplitBar} for this region.
34349 * @return {Roo.SplitBar}
34351 getSplitBar : function(){
34356 this.hideSplitter();
34357 Roo.bootstrap.layout.Split.superclass.hide.call(this);
34360 hideSplitter : function(){
34362 this.split.el.setLocation(-2000,-2000);
34363 this.split.el.hide();
34369 this.split.el.show();
34371 Roo.bootstrap.layout.Split.superclass.show.call(this);
34374 beforeSlide: function(){
34375 if(Roo.isGecko){// firefox overflow auto bug workaround
34376 this.bodyEl.clip();
34378 this.tabs.bodyEl.clip();
34380 if(this.activePanel){
34381 this.activePanel.getEl().clip();
34383 if(this.activePanel.beforeSlide){
34384 this.activePanel.beforeSlide();
34390 afterSlide : function(){
34391 if(Roo.isGecko){// firefox overflow auto bug workaround
34392 this.bodyEl.unclip();
34394 this.tabs.bodyEl.unclip();
34396 if(this.activePanel){
34397 this.activePanel.getEl().unclip();
34398 if(this.activePanel.afterSlide){
34399 this.activePanel.afterSlide();
34405 initAutoHide : function(){
34406 if(this.autoHide !== false){
34407 if(!this.autoHideHd){
34408 var st = new Roo.util.DelayedTask(this.slideIn, this);
34409 this.autoHideHd = {
34410 "mouseout": function(e){
34411 if(!e.within(this.el, true)){
34415 "mouseover" : function(e){
34421 this.el.on(this.autoHideHd);
34425 clearAutoHide : function(){
34426 if(this.autoHide !== false){
34427 this.el.un("mouseout", this.autoHideHd.mouseout);
34428 this.el.un("mouseover", this.autoHideHd.mouseover);
34432 clearMonitor : function(){
34433 Roo.get(document).un("click", this.slideInIf, this);
34436 // these names are backwards but not changed for compat
34437 slideOut : function(){
34438 if(this.isSlid || this.el.hasActiveFx()){
34441 this.isSlid = true;
34442 if(this.collapseBtn){
34443 this.collapseBtn.hide();
34445 this.closeBtnState = this.closeBtn.getStyle('display');
34446 this.closeBtn.hide();
34448 this.stickBtn.show();
34451 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34452 this.beforeSlide();
34453 this.el.setStyle("z-index", 10001);
34454 this.el.slideIn(this.getSlideAnchor(), {
34455 callback: function(){
34457 this.initAutoHide();
34458 Roo.get(document).on("click", this.slideInIf, this);
34459 this.fireEvent("slideshow", this);
34466 afterSlideIn : function(){
34467 this.clearAutoHide();
34468 this.isSlid = false;
34469 this.clearMonitor();
34470 this.el.setStyle("z-index", "");
34471 if(this.collapseBtn){
34472 this.collapseBtn.show();
34474 this.closeBtn.setStyle('display', this.closeBtnState);
34476 this.stickBtn.hide();
34478 this.fireEvent("slidehide", this);
34481 slideIn : function(cb){
34482 if(!this.isSlid || this.el.hasActiveFx()){
34486 this.isSlid = false;
34487 this.beforeSlide();
34488 this.el.slideOut(this.getSlideAnchor(), {
34489 callback: function(){
34490 this.el.setLeftTop(-10000, -10000);
34492 this.afterSlideIn();
34500 slideInIf : function(e){
34501 if(!e.within(this.el)){
34506 animateCollapse : function(){
34507 this.beforeSlide();
34508 this.el.setStyle("z-index", 20000);
34509 var anchor = this.getSlideAnchor();
34510 this.el.slideOut(anchor, {
34511 callback : function(){
34512 this.el.setStyle("z-index", "");
34513 this.collapsedEl.slideIn(anchor, {duration:.3});
34515 this.el.setLocation(-10000,-10000);
34517 this.fireEvent("collapsed", this);
34524 animateExpand : function(){
34525 this.beforeSlide();
34526 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34527 this.el.setStyle("z-index", 20000);
34528 this.collapsedEl.hide({
34531 this.el.slideIn(this.getSlideAnchor(), {
34532 callback : function(){
34533 this.el.setStyle("z-index", "");
34536 this.split.el.show();
34538 this.fireEvent("invalidated", this);
34539 this.fireEvent("expanded", this);
34567 getAnchor : function(){
34568 return this.anchors[this.position];
34571 getCollapseAnchor : function(){
34572 return this.canchors[this.position];
34575 getSlideAnchor : function(){
34576 return this.sanchors[this.position];
34579 getAlignAdj : function(){
34580 var cm = this.cmargins;
34581 switch(this.position){
34597 getExpandAdj : function(){
34598 var c = this.collapsedEl, cm = this.cmargins;
34599 switch(this.position){
34601 return [-(cm.right+c.getWidth()+cm.left), 0];
34604 return [cm.right+c.getWidth()+cm.left, 0];
34607 return [0, -(cm.top+cm.bottom+c.getHeight())];
34610 return [0, cm.top+cm.bottom+c.getHeight()];
34616 * Ext JS Library 1.1.1
34617 * Copyright(c) 2006-2007, Ext JS, LLC.
34619 * Originally Released Under LGPL - original licence link has changed is not relivant.
34622 * <script type="text/javascript">
34625 * These classes are private internal classes
34627 Roo.bootstrap.layout.Center = function(config){
34628 config.region = "center";
34629 Roo.bootstrap.layout.Region.call(this, config);
34630 this.visible = true;
34631 this.minWidth = config.minWidth || 20;
34632 this.minHeight = config.minHeight || 20;
34635 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
34637 // center panel can't be hidden
34641 // center panel can't be hidden
34644 getMinWidth: function(){
34645 return this.minWidth;
34648 getMinHeight: function(){
34649 return this.minHeight;
34662 Roo.bootstrap.layout.North = function(config)
34664 config.region = 'north';
34665 config.cursor = 'n-resize';
34667 Roo.bootstrap.layout.Split.call(this, config);
34671 this.split.placement = Roo.bootstrap.SplitBar.TOP;
34672 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34673 this.split.el.addClass("roo-layout-split-v");
34675 var size = config.initialSize || config.height;
34676 if(typeof size != "undefined"){
34677 this.el.setHeight(size);
34680 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
34682 orientation: Roo.bootstrap.SplitBar.VERTICAL,
34686 getBox : function(){
34687 if(this.collapsed){
34688 return this.collapsedEl.getBox();
34690 var box = this.el.getBox();
34692 box.height += this.split.el.getHeight();
34697 updateBox : function(box){
34698 if(this.split && !this.collapsed){
34699 box.height -= this.split.el.getHeight();
34700 this.split.el.setLeft(box.x);
34701 this.split.el.setTop(box.y+box.height);
34702 this.split.el.setWidth(box.width);
34704 if(this.collapsed){
34705 this.updateBody(box.width, null);
34707 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34715 Roo.bootstrap.layout.South = function(config){
34716 config.region = 'south';
34717 config.cursor = 's-resize';
34718 Roo.bootstrap.layout.Split.call(this, config);
34720 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
34721 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34722 this.split.el.addClass("roo-layout-split-v");
34724 var size = config.initialSize || config.height;
34725 if(typeof size != "undefined"){
34726 this.el.setHeight(size);
34730 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
34731 orientation: Roo.bootstrap.SplitBar.VERTICAL,
34732 getBox : function(){
34733 if(this.collapsed){
34734 return this.collapsedEl.getBox();
34736 var box = this.el.getBox();
34738 var sh = this.split.el.getHeight();
34745 updateBox : function(box){
34746 if(this.split && !this.collapsed){
34747 var sh = this.split.el.getHeight();
34750 this.split.el.setLeft(box.x);
34751 this.split.el.setTop(box.y-sh);
34752 this.split.el.setWidth(box.width);
34754 if(this.collapsed){
34755 this.updateBody(box.width, null);
34757 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34761 Roo.bootstrap.layout.East = function(config){
34762 config.region = "east";
34763 config.cursor = "e-resize";
34764 Roo.bootstrap.layout.Split.call(this, config);
34766 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
34767 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34768 this.split.el.addClass("roo-layout-split-h");
34770 var size = config.initialSize || config.width;
34771 if(typeof size != "undefined"){
34772 this.el.setWidth(size);
34775 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
34776 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34777 getBox : function(){
34778 if(this.collapsed){
34779 return this.collapsedEl.getBox();
34781 var box = this.el.getBox();
34783 var sw = this.split.el.getWidth();
34790 updateBox : function(box){
34791 if(this.split && !this.collapsed){
34792 var sw = this.split.el.getWidth();
34794 this.split.el.setLeft(box.x);
34795 this.split.el.setTop(box.y);
34796 this.split.el.setHeight(box.height);
34799 if(this.collapsed){
34800 this.updateBody(null, box.height);
34802 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34806 Roo.bootstrap.layout.West = function(config){
34807 config.region = "west";
34808 config.cursor = "w-resize";
34810 Roo.bootstrap.layout.Split.call(this, config);
34812 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
34813 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34814 this.split.el.addClass("roo-layout-split-h");
34818 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
34819 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34821 onRender: function(ctr, pos)
34823 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
34824 var size = this.config.initialSize || this.config.width;
34825 if(typeof size != "undefined"){
34826 this.el.setWidth(size);
34830 getBox : function(){
34831 if(this.collapsed){
34832 return this.collapsedEl.getBox();
34834 var box = this.el.getBox();
34836 box.width += this.split.el.getWidth();
34841 updateBox : function(box){
34842 if(this.split && !this.collapsed){
34843 var sw = this.split.el.getWidth();
34845 this.split.el.setLeft(box.x+box.width);
34846 this.split.el.setTop(box.y);
34847 this.split.el.setHeight(box.height);
34849 if(this.collapsed){
34850 this.updateBody(null, box.height);
34852 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34855 Roo.namespace("Roo.bootstrap.panel");/*
34857 * Ext JS Library 1.1.1
34858 * Copyright(c) 2006-2007, Ext JS, LLC.
34860 * Originally Released Under LGPL - original licence link has changed is not relivant.
34863 * <script type="text/javascript">
34866 * @class Roo.ContentPanel
34867 * @extends Roo.util.Observable
34868 * A basic ContentPanel element.
34869 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
34870 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
34871 * @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
34872 * @cfg {Boolean} closable True if the panel can be closed/removed
34873 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
34874 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34875 * @cfg {Toolbar} toolbar A toolbar for this panel
34876 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
34877 * @cfg {String} title The title for this panel
34878 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34879 * @cfg {String} url Calls {@link #setUrl} with this value
34880 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34881 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
34882 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
34883 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
34884 * @cfg {Boolean} badges render the badges
34887 * Create a new ContentPanel.
34888 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34889 * @param {String/Object} config A string to set only the title or a config object
34890 * @param {String} content (optional) Set the HTML content for this panel
34891 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34893 Roo.bootstrap.panel.Content = function( config){
34895 this.tpl = config.tpl || false;
34897 var el = config.el;
34898 var content = config.content;
34900 if(config.autoCreate){ // xtype is available if this is called from factory
34903 this.el = Roo.get(el);
34904 if(!this.el && config && config.autoCreate){
34905 if(typeof config.autoCreate == "object"){
34906 if(!config.autoCreate.id){
34907 config.autoCreate.id = config.id||el;
34909 this.el = Roo.DomHelper.append(document.body,
34910 config.autoCreate, true);
34912 var elcfg = { tag: "div",
34913 cls: "roo-layout-inactive-content",
34917 elcfg.html = config.html;
34921 this.el = Roo.DomHelper.append(document.body, elcfg , true);
34924 this.closable = false;
34925 this.loaded = false;
34926 this.active = false;
34929 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
34931 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
34933 this.wrapEl = this.el; //this.el.wrap();
34935 if (config.toolbar.items) {
34936 ti = config.toolbar.items ;
34937 delete config.toolbar.items ;
34941 this.toolbar.render(this.wrapEl, 'before');
34942 for(var i =0;i < ti.length;i++) {
34943 // Roo.log(['add child', items[i]]);
34944 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34946 this.toolbar.items = nitems;
34947 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
34948 delete config.toolbar;
34952 // xtype created footer. - not sure if will work as we normally have to render first..
34953 if (this.footer && !this.footer.el && this.footer.xtype) {
34954 if (!this.wrapEl) {
34955 this.wrapEl = this.el.wrap();
34958 this.footer.container = this.wrapEl.createChild();
34960 this.footer = Roo.factory(this.footer, Roo);
34965 if(typeof config == "string"){
34966 this.title = config;
34968 Roo.apply(this, config);
34972 this.resizeEl = Roo.get(this.resizeEl, true);
34974 this.resizeEl = this.el;
34976 // handle view.xtype
34984 * Fires when this panel is activated.
34985 * @param {Roo.ContentPanel} this
34989 * @event deactivate
34990 * Fires when this panel is activated.
34991 * @param {Roo.ContentPanel} this
34993 "deactivate" : true,
34997 * Fires when this panel is resized if fitToFrame is true.
34998 * @param {Roo.ContentPanel} this
34999 * @param {Number} width The width after any component adjustments
35000 * @param {Number} height The height after any component adjustments
35006 * Fires when this tab is created
35007 * @param {Roo.ContentPanel} this
35018 if(this.autoScroll){
35019 this.resizeEl.setStyle("overflow", "auto");
35021 // fix randome scrolling
35022 //this.el.on('scroll', function() {
35023 // Roo.log('fix random scolling');
35024 // this.scrollTo('top',0);
35027 content = content || this.content;
35029 this.setContent(content);
35031 if(config && config.url){
35032 this.setUrl(this.url, this.params, this.loadOnce);
35037 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
35039 if (this.view && typeof(this.view.xtype) != 'undefined') {
35040 this.view.el = this.el.appendChild(document.createElement("div"));
35041 this.view = Roo.factory(this.view);
35042 this.view.render && this.view.render(false, '');
35046 this.fireEvent('render', this);
35049 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
35053 setRegion : function(region){
35054 this.region = region;
35055 this.setActiveClass(region && !this.background);
35059 setActiveClass: function(state)
35062 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
35063 this.el.setStyle('position','relative');
35065 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
35066 this.el.setStyle('position', 'absolute');
35071 * Returns the toolbar for this Panel if one was configured.
35072 * @return {Roo.Toolbar}
35074 getToolbar : function(){
35075 return this.toolbar;
35078 setActiveState : function(active)
35080 this.active = active;
35081 this.setActiveClass(active);
35083 this.fireEvent("deactivate", this);
35085 this.fireEvent("activate", this);
35089 * Updates this panel's element
35090 * @param {String} content The new content
35091 * @param {Boolean} loadScripts (optional) true to look for and process scripts
35093 setContent : function(content, loadScripts){
35094 this.el.update(content, loadScripts);
35097 ignoreResize : function(w, h){
35098 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35101 this.lastSize = {width: w, height: h};
35106 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35107 * @return {Roo.UpdateManager} The UpdateManager
35109 getUpdateManager : function(){
35110 return this.el.getUpdateManager();
35113 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35114 * @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:
35117 url: "your-url.php",
35118 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35119 callback: yourFunction,
35120 scope: yourObject, //(optional scope)
35123 text: "Loading...",
35128 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35129 * 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.
35130 * @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}
35131 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35132 * @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.
35133 * @return {Roo.ContentPanel} this
35136 var um = this.el.getUpdateManager();
35137 um.update.apply(um, arguments);
35143 * 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.
35144 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35145 * @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)
35146 * @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)
35147 * @return {Roo.UpdateManager} The UpdateManager
35149 setUrl : function(url, params, loadOnce){
35150 if(this.refreshDelegate){
35151 this.removeListener("activate", this.refreshDelegate);
35153 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35154 this.on("activate", this.refreshDelegate);
35155 return this.el.getUpdateManager();
35158 _handleRefresh : function(url, params, loadOnce){
35159 if(!loadOnce || !this.loaded){
35160 var updater = this.el.getUpdateManager();
35161 updater.update(url, params, this._setLoaded.createDelegate(this));
35165 _setLoaded : function(){
35166 this.loaded = true;
35170 * Returns this panel's id
35173 getId : function(){
35178 * Returns this panel's element - used by regiosn to add.
35179 * @return {Roo.Element}
35181 getEl : function(){
35182 return this.wrapEl || this.el;
35187 adjustForComponents : function(width, height)
35189 //Roo.log('adjustForComponents ');
35190 if(this.resizeEl != this.el){
35191 width -= this.el.getFrameWidth('lr');
35192 height -= this.el.getFrameWidth('tb');
35195 var te = this.toolbar.getEl();
35196 height -= te.getHeight();
35197 te.setWidth(width);
35200 var te = this.footer.getEl();
35201 Roo.log("footer:" + te.getHeight());
35203 height -= te.getHeight();
35204 te.setWidth(width);
35208 if(this.adjustments){
35209 width += this.adjustments[0];
35210 height += this.adjustments[1];
35212 return {"width": width, "height": height};
35215 setSize : function(width, height){
35216 if(this.fitToFrame && !this.ignoreResize(width, height)){
35217 if(this.fitContainer && this.resizeEl != this.el){
35218 this.el.setSize(width, height);
35220 var size = this.adjustForComponents(width, height);
35221 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35222 this.fireEvent('resize', this, size.width, size.height);
35227 * Returns this panel's title
35230 getTitle : function(){
35235 * Set this panel's title
35236 * @param {String} title
35238 setTitle : function(title){
35239 this.title = title;
35241 this.region.updatePanelTitle(this, title);
35246 * Returns true is this panel was configured to be closable
35247 * @return {Boolean}
35249 isClosable : function(){
35250 return this.closable;
35253 beforeSlide : function(){
35255 this.resizeEl.clip();
35258 afterSlide : function(){
35260 this.resizeEl.unclip();
35264 * Force a content refresh from the URL specified in the {@link #setUrl} method.
35265 * Will fail silently if the {@link #setUrl} method has not been called.
35266 * This does not activate the panel, just updates its content.
35268 refresh : function(){
35269 if(this.refreshDelegate){
35270 this.loaded = false;
35271 this.refreshDelegate();
35276 * Destroys this panel
35278 destroy : function(){
35279 this.el.removeAllListeners();
35280 var tempEl = document.createElement("span");
35281 tempEl.appendChild(this.el.dom);
35282 tempEl.innerHTML = "";
35288 * form - if the content panel contains a form - this is a reference to it.
35289 * @type {Roo.form.Form}
35293 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35294 * This contains a reference to it.
35300 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35310 * @param {Object} cfg Xtype definition of item to add.
35314 getChildContainer: function () {
35315 return this.getEl();
35320 var ret = new Roo.factory(cfg);
35325 if (cfg.xtype.match(/^Form$/)) {
35328 //if (this.footer) {
35329 // el = this.footer.container.insertSibling(false, 'before');
35331 el = this.el.createChild();
35334 this.form = new Roo.form.Form(cfg);
35337 if ( this.form.allItems.length) {
35338 this.form.render(el.dom);
35342 // should only have one of theses..
35343 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35344 // views.. should not be just added - used named prop 'view''
35346 cfg.el = this.el.appendChild(document.createElement("div"));
35349 var ret = new Roo.factory(cfg);
35351 ret.render && ret.render(false, ''); // render blank..
35361 * @class Roo.bootstrap.panel.Grid
35362 * @extends Roo.bootstrap.panel.Content
35364 * Create a new GridPanel.
35365 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
35366 * @param {Object} config A the config object
35372 Roo.bootstrap.panel.Grid = function(config)
35376 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35377 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
35379 config.el = this.wrapper;
35380 //this.el = this.wrapper;
35382 if (config.container) {
35383 // ctor'ed from a Border/panel.grid
35386 this.wrapper.setStyle("overflow", "hidden");
35387 this.wrapper.addClass('roo-grid-container');
35392 if(config.toolbar){
35393 var tool_el = this.wrapper.createChild();
35394 this.toolbar = Roo.factory(config.toolbar);
35396 if (config.toolbar.items) {
35397 ti = config.toolbar.items ;
35398 delete config.toolbar.items ;
35402 this.toolbar.render(tool_el);
35403 for(var i =0;i < ti.length;i++) {
35404 // Roo.log(['add child', items[i]]);
35405 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35407 this.toolbar.items = nitems;
35409 delete config.toolbar;
35412 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
35413 config.grid.scrollBody = true;;
35414 config.grid.monitorWindowResize = false; // turn off autosizing
35415 config.grid.autoHeight = false;
35416 config.grid.autoWidth = false;
35418 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
35420 if (config.background) {
35421 // render grid on panel activation (if panel background)
35422 this.on('activate', function(gp) {
35423 if (!gp.grid.rendered) {
35424 gp.grid.render(this.wrapper);
35425 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
35430 this.grid.render(this.wrapper);
35431 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
35434 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
35435 // ??? needed ??? config.el = this.wrapper;
35440 // xtype created footer. - not sure if will work as we normally have to render first..
35441 if (this.footer && !this.footer.el && this.footer.xtype) {
35443 var ctr = this.grid.getView().getFooterPanel(true);
35444 this.footer.dataSource = this.grid.dataSource;
35445 this.footer = Roo.factory(this.footer, Roo);
35446 this.footer.render(ctr);
35456 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
35457 getId : function(){
35458 return this.grid.id;
35462 * Returns the grid for this panel
35463 * @return {Roo.bootstrap.Table}
35465 getGrid : function(){
35469 setSize : function(width, height){
35470 if(!this.ignoreResize(width, height)){
35471 var grid = this.grid;
35472 var size = this.adjustForComponents(width, height);
35473 var gridel = grid.getGridEl();
35474 gridel.setSize(size.width, size.height);
35476 var thd = grid.getGridEl().select('thead',true).first();
35477 var tbd = grid.getGridEl().select('tbody', true).first();
35479 tbd.setSize(width, height - thd.getHeight());
35488 beforeSlide : function(){
35489 this.grid.getView().scroller.clip();
35492 afterSlide : function(){
35493 this.grid.getView().scroller.unclip();
35496 destroy : function(){
35497 this.grid.destroy();
35499 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
35504 * @class Roo.bootstrap.panel.Nest
35505 * @extends Roo.bootstrap.panel.Content
35507 * Create a new Panel, that can contain a layout.Border.
35510 * @param {Roo.BorderLayout} layout The layout for this panel
35511 * @param {String/Object} config A string to set only the title or a config object
35513 Roo.bootstrap.panel.Nest = function(config)
35515 // construct with only one argument..
35516 /* FIXME - implement nicer consturctors
35517 if (layout.layout) {
35519 layout = config.layout;
35520 delete config.layout;
35522 if (layout.xtype && !layout.getEl) {
35523 // then layout needs constructing..
35524 layout = Roo.factory(layout, Roo);
35528 config.el = config.layout.getEl();
35530 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
35532 config.layout.monitorWindowResize = false; // turn off autosizing
35533 this.layout = config.layout;
35534 this.layout.getEl().addClass("roo-layout-nested-layout");
35541 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
35543 setSize : function(width, height){
35544 if(!this.ignoreResize(width, height)){
35545 var size = this.adjustForComponents(width, height);
35546 var el = this.layout.getEl();
35547 if (size.height < 1) {
35548 el.setWidth(size.width);
35550 el.setSize(size.width, size.height);
35552 var touch = el.dom.offsetWidth;
35553 this.layout.layout();
35554 // ie requires a double layout on the first pass
35555 if(Roo.isIE && !this.initialized){
35556 this.initialized = true;
35557 this.layout.layout();
35562 // activate all subpanels if not currently active..
35564 setActiveState : function(active){
35565 this.active = active;
35566 this.setActiveClass(active);
35569 this.fireEvent("deactivate", this);
35573 this.fireEvent("activate", this);
35574 // not sure if this should happen before or after..
35575 if (!this.layout) {
35576 return; // should not happen..
35579 for (var r in this.layout.regions) {
35580 reg = this.layout.getRegion(r);
35581 if (reg.getActivePanel()) {
35582 //reg.showPanel(reg.getActivePanel()); // force it to activate..
35583 reg.setActivePanel(reg.getActivePanel());
35586 if (!reg.panels.length) {
35589 reg.showPanel(reg.getPanel(0));
35598 * Returns the nested BorderLayout for this panel
35599 * @return {Roo.BorderLayout}
35601 getLayout : function(){
35602 return this.layout;
35606 * Adds a xtype elements to the layout of the nested panel
35610 xtype : 'ContentPanel',
35617 xtype : 'NestedLayoutPanel',
35623 items : [ ... list of content panels or nested layout panels.. ]
35627 * @param {Object} cfg Xtype definition of item to add.
35629 addxtype : function(cfg) {
35630 return this.layout.addxtype(cfg);
35635 * Ext JS Library 1.1.1
35636 * Copyright(c) 2006-2007, Ext JS, LLC.
35638 * Originally Released Under LGPL - original licence link has changed is not relivant.
35641 * <script type="text/javascript">
35644 * @class Roo.TabPanel
35645 * @extends Roo.util.Observable
35646 * A lightweight tab container.
35650 // basic tabs 1, built from existing content
35651 var tabs = new Roo.TabPanel("tabs1");
35652 tabs.addTab("script", "View Script");
35653 tabs.addTab("markup", "View Markup");
35654 tabs.activate("script");
35656 // more advanced tabs, built from javascript
35657 var jtabs = new Roo.TabPanel("jtabs");
35658 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
35660 // set up the UpdateManager
35661 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
35662 var updater = tab2.getUpdateManager();
35663 updater.setDefaultUrl("ajax1.htm");
35664 tab2.on('activate', updater.refresh, updater, true);
35666 // Use setUrl for Ajax loading
35667 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
35668 tab3.setUrl("ajax2.htm", null, true);
35671 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
35674 jtabs.activate("jtabs-1");
35677 * Create a new TabPanel.
35678 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
35679 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
35681 Roo.bootstrap.panel.Tabs = function(config){
35683 * The container element for this TabPanel.
35684 * @type Roo.Element
35686 this.el = Roo.get(config.el);
35689 if(typeof config == "boolean"){
35690 this.tabPosition = config ? "bottom" : "top";
35692 Roo.apply(this, config);
35696 if(this.tabPosition == "bottom"){
35697 this.bodyEl = Roo.get(this.createBody(this.el.dom));
35698 this.el.addClass("roo-tabs-bottom");
35700 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
35701 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
35702 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
35704 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
35706 if(this.tabPosition != "bottom"){
35707 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
35708 * @type Roo.Element
35710 this.bodyEl = Roo.get(this.createBody(this.el.dom));
35711 this.el.addClass("roo-tabs-top");
35715 this.bodyEl.setStyle("position", "relative");
35717 this.active = null;
35718 this.activateDelegate = this.activate.createDelegate(this);
35723 * Fires when the active tab changes
35724 * @param {Roo.TabPanel} this
35725 * @param {Roo.TabPanelItem} activePanel The new active tab
35729 * @event beforetabchange
35730 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
35731 * @param {Roo.TabPanel} this
35732 * @param {Object} e Set cancel to true on this object to cancel the tab change
35733 * @param {Roo.TabPanelItem} tab The tab being changed to
35735 "beforetabchange" : true
35738 Roo.EventManager.onWindowResize(this.onResize, this);
35739 this.cpad = this.el.getPadding("lr");
35740 this.hiddenCount = 0;
35743 // toolbar on the tabbar support...
35744 if (this.toolbar) {
35745 alert("no toolbar support yet");
35746 this.toolbar = false;
35748 var tcfg = this.toolbar;
35749 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
35750 this.toolbar = new Roo.Toolbar(tcfg);
35751 if (Roo.isSafari) {
35752 var tbl = tcfg.container.child('table', true);
35753 tbl.setAttribute('width', '100%');
35761 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
35764 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
35766 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
35768 tabPosition : "top",
35770 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
35772 currentTabWidth : 0,
35774 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
35778 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
35782 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
35784 preferredTabWidth : 175,
35786 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
35788 resizeTabs : false,
35790 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
35792 monitorResize : true,
35794 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
35799 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
35800 * @param {String} id The id of the div to use <b>or create</b>
35801 * @param {String} text The text for the tab
35802 * @param {String} content (optional) Content to put in the TabPanelItem body
35803 * @param {Boolean} closable (optional) True to create a close icon on the tab
35804 * @return {Roo.TabPanelItem} The created TabPanelItem
35806 addTab : function(id, text, content, closable, tpl)
35808 var item = new Roo.bootstrap.panel.TabItem({
35812 closable : closable,
35815 this.addTabItem(item);
35817 item.setContent(content);
35823 * Returns the {@link Roo.TabPanelItem} with the specified id/index
35824 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
35825 * @return {Roo.TabPanelItem}
35827 getTab : function(id){
35828 return this.items[id];
35832 * Hides the {@link Roo.TabPanelItem} with the specified id/index
35833 * @param {String/Number} id The id or index of the TabPanelItem to hide.
35835 hideTab : function(id){
35836 var t = this.items[id];
35839 this.hiddenCount++;
35840 this.autoSizeTabs();
35845 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
35846 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
35848 unhideTab : function(id){
35849 var t = this.items[id];
35851 t.setHidden(false);
35852 this.hiddenCount--;
35853 this.autoSizeTabs();
35858 * Adds an existing {@link Roo.TabPanelItem}.
35859 * @param {Roo.TabPanelItem} item The TabPanelItem to add
35861 addTabItem : function(item){
35862 this.items[item.id] = item;
35863 this.items.push(item);
35864 // if(this.resizeTabs){
35865 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
35866 // this.autoSizeTabs();
35868 // item.autoSize();
35873 * Removes a {@link Roo.TabPanelItem}.
35874 * @param {String/Number} id The id or index of the TabPanelItem to remove.
35876 removeTab : function(id){
35877 var items = this.items;
35878 var tab = items[id];
35879 if(!tab) { return; }
35880 var index = items.indexOf(tab);
35881 if(this.active == tab && items.length > 1){
35882 var newTab = this.getNextAvailable(index);
35887 this.stripEl.dom.removeChild(tab.pnode.dom);
35888 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
35889 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
35891 items.splice(index, 1);
35892 delete this.items[tab.id];
35893 tab.fireEvent("close", tab);
35894 tab.purgeListeners();
35895 this.autoSizeTabs();
35898 getNextAvailable : function(start){
35899 var items = this.items;
35901 // look for a next tab that will slide over to
35902 // replace the one being removed
35903 while(index < items.length){
35904 var item = items[++index];
35905 if(item && !item.isHidden()){
35909 // if one isn't found select the previous tab (on the left)
35912 var item = items[--index];
35913 if(item && !item.isHidden()){
35921 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
35922 * @param {String/Number} id The id or index of the TabPanelItem to disable.
35924 disableTab : function(id){
35925 var tab = this.items[id];
35926 if(tab && this.active != tab){
35932 * Enables a {@link Roo.TabPanelItem} that is disabled.
35933 * @param {String/Number} id The id or index of the TabPanelItem to enable.
35935 enableTab : function(id){
35936 var tab = this.items[id];
35941 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
35942 * @param {String/Number} id The id or index of the TabPanelItem to activate.
35943 * @return {Roo.TabPanelItem} The TabPanelItem.
35945 activate : function(id){
35946 var tab = this.items[id];
35950 if(tab == this.active || tab.disabled){
35954 this.fireEvent("beforetabchange", this, e, tab);
35955 if(e.cancel !== true && !tab.disabled){
35957 this.active.hide();
35959 this.active = this.items[id];
35960 this.active.show();
35961 this.fireEvent("tabchange", this, this.active);
35967 * Gets the active {@link Roo.TabPanelItem}.
35968 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
35970 getActiveTab : function(){
35971 return this.active;
35975 * Updates the tab body element to fit the height of the container element
35976 * for overflow scrolling
35977 * @param {Number} targetHeight (optional) Override the starting height from the elements height
35979 syncHeight : function(targetHeight){
35980 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35981 var bm = this.bodyEl.getMargins();
35982 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35983 this.bodyEl.setHeight(newHeight);
35987 onResize : function(){
35988 if(this.monitorResize){
35989 this.autoSizeTabs();
35994 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35996 beginUpdate : function(){
35997 this.updating = true;
36001 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
36003 endUpdate : function(){
36004 this.updating = false;
36005 this.autoSizeTabs();
36009 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
36011 autoSizeTabs : function(){
36012 var count = this.items.length;
36013 var vcount = count - this.hiddenCount;
36014 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
36017 var w = Math.max(this.el.getWidth() - this.cpad, 10);
36018 var availWidth = Math.floor(w / vcount);
36019 var b = this.stripBody;
36020 if(b.getWidth() > w){
36021 var tabs = this.items;
36022 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
36023 if(availWidth < this.minTabWidth){
36024 /*if(!this.sleft){ // incomplete scrolling code
36025 this.createScrollButtons();
36028 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
36031 if(this.currentTabWidth < this.preferredTabWidth){
36032 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
36038 * Returns the number of tabs in this TabPanel.
36041 getCount : function(){
36042 return this.items.length;
36046 * Resizes all the tabs to the passed width
36047 * @param {Number} The new width
36049 setTabWidth : function(width){
36050 this.currentTabWidth = width;
36051 for(var i = 0, len = this.items.length; i < len; i++) {
36052 if(!this.items[i].isHidden()) {
36053 this.items[i].setWidth(width);
36059 * Destroys this TabPanel
36060 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
36062 destroy : function(removeEl){
36063 Roo.EventManager.removeResizeListener(this.onResize, this);
36064 for(var i = 0, len = this.items.length; i < len; i++){
36065 this.items[i].purgeListeners();
36067 if(removeEl === true){
36068 this.el.update("");
36073 createStrip : function(container)
36075 var strip = document.createElement("nav");
36076 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
36077 container.appendChild(strip);
36081 createStripList : function(strip)
36083 // div wrapper for retard IE
36084 // returns the "tr" element.
36085 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
36086 //'<div class="x-tabs-strip-wrap">'+
36087 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
36088 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
36089 return strip.firstChild; //.firstChild.firstChild.firstChild;
36091 createBody : function(container)
36093 var body = document.createElement("div");
36094 Roo.id(body, "tab-body");
36095 //Roo.fly(body).addClass("x-tabs-body");
36096 Roo.fly(body).addClass("tab-content");
36097 container.appendChild(body);
36100 createItemBody :function(bodyEl, id){
36101 var body = Roo.getDom(id);
36103 body = document.createElement("div");
36106 //Roo.fly(body).addClass("x-tabs-item-body");
36107 Roo.fly(body).addClass("tab-pane");
36108 bodyEl.insertBefore(body, bodyEl.firstChild);
36112 createStripElements : function(stripEl, text, closable, tpl)
36114 var td = document.createElement("li"); // was td..
36117 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
36120 stripEl.appendChild(td);
36122 td.className = "x-tabs-closable";
36123 if(!this.closeTpl){
36124 this.closeTpl = new Roo.Template(
36125 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36126 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
36127 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
36130 var el = this.closeTpl.overwrite(td, {"text": text});
36131 var close = el.getElementsByTagName("div")[0];
36132 var inner = el.getElementsByTagName("em")[0];
36133 return {"el": el, "close": close, "inner": inner};
36136 // not sure what this is..
36137 // if(!this.tabTpl){
36138 //this.tabTpl = new Roo.Template(
36139 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36140 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
36142 // this.tabTpl = new Roo.Template(
36143 // '<a href="#">' +
36144 // '<span unselectable="on"' +
36145 // (this.disableTooltips ? '' : ' title="{text}"') +
36146 // ' >{text}</span></a>'
36152 var template = tpl || this.tabTpl || false;
36156 template = new Roo.Template(
36158 '<span unselectable="on"' +
36159 (this.disableTooltips ? '' : ' title="{text}"') +
36160 ' >{text}</span></a>'
36164 switch (typeof(template)) {
36168 template = new Roo.Template(template);
36174 var el = template.overwrite(td, {"text": text});
36176 var inner = el.getElementsByTagName("span")[0];
36178 return {"el": el, "inner": inner};
36186 * @class Roo.TabPanelItem
36187 * @extends Roo.util.Observable
36188 * Represents an individual item (tab plus body) in a TabPanel.
36189 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
36190 * @param {String} id The id of this TabPanelItem
36191 * @param {String} text The text for the tab of this TabPanelItem
36192 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
36194 Roo.bootstrap.panel.TabItem = function(config){
36196 * The {@link Roo.TabPanel} this TabPanelItem belongs to
36197 * @type Roo.TabPanel
36199 this.tabPanel = config.panel;
36201 * The id for this TabPanelItem
36204 this.id = config.id;
36206 this.disabled = false;
36208 this.text = config.text;
36210 this.loaded = false;
36211 this.closable = config.closable;
36214 * The body element for this TabPanelItem.
36215 * @type Roo.Element
36217 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
36218 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
36219 this.bodyEl.setStyle("display", "block");
36220 this.bodyEl.setStyle("zoom", "1");
36221 //this.hideAction();
36223 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
36225 this.el = Roo.get(els.el);
36226 this.inner = Roo.get(els.inner, true);
36227 this.textEl = Roo.get(this.el.dom.firstChild, true);
36228 this.pnode = Roo.get(els.el.parentNode, true);
36229 this.el.on("mousedown", this.onTabMouseDown, this);
36230 this.el.on("click", this.onTabClick, this);
36232 if(config.closable){
36233 var c = Roo.get(els.close, true);
36234 c.dom.title = this.closeText;
36235 c.addClassOnOver("close-over");
36236 c.on("click", this.closeClick, this);
36242 * Fires when this tab becomes the active tab.
36243 * @param {Roo.TabPanel} tabPanel The parent TabPanel
36244 * @param {Roo.TabPanelItem} this
36248 * @event beforeclose
36249 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
36250 * @param {Roo.TabPanelItem} this
36251 * @param {Object} e Set cancel to true on this object to cancel the close.
36253 "beforeclose": true,
36256 * Fires when this tab is closed.
36257 * @param {Roo.TabPanelItem} this
36261 * @event deactivate
36262 * Fires when this tab is no longer the active tab.
36263 * @param {Roo.TabPanel} tabPanel The parent TabPanel
36264 * @param {Roo.TabPanelItem} this
36266 "deactivate" : true
36268 this.hidden = false;
36270 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
36273 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
36275 purgeListeners : function(){
36276 Roo.util.Observable.prototype.purgeListeners.call(this);
36277 this.el.removeAllListeners();
36280 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
36283 this.pnode.addClass("active");
36286 this.tabPanel.stripWrap.repaint();
36288 this.fireEvent("activate", this.tabPanel, this);
36292 * Returns true if this tab is the active tab.
36293 * @return {Boolean}
36295 isActive : function(){
36296 return this.tabPanel.getActiveTab() == this;
36300 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
36303 this.pnode.removeClass("active");
36305 this.fireEvent("deactivate", this.tabPanel, this);
36308 hideAction : function(){
36309 this.bodyEl.hide();
36310 this.bodyEl.setStyle("position", "absolute");
36311 this.bodyEl.setLeft("-20000px");
36312 this.bodyEl.setTop("-20000px");
36315 showAction : function(){
36316 this.bodyEl.setStyle("position", "relative");
36317 this.bodyEl.setTop("");
36318 this.bodyEl.setLeft("");
36319 this.bodyEl.show();
36323 * Set the tooltip for the tab.
36324 * @param {String} tooltip The tab's tooltip
36326 setTooltip : function(text){
36327 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
36328 this.textEl.dom.qtip = text;
36329 this.textEl.dom.removeAttribute('title');
36331 this.textEl.dom.title = text;
36335 onTabClick : function(e){
36336 e.preventDefault();
36337 this.tabPanel.activate(this.id);
36340 onTabMouseDown : function(e){
36341 e.preventDefault();
36342 this.tabPanel.activate(this.id);
36345 getWidth : function(){
36346 return this.inner.getWidth();
36349 setWidth : function(width){
36350 var iwidth = width - this.pnode.getPadding("lr");
36351 this.inner.setWidth(iwidth);
36352 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
36353 this.pnode.setWidth(width);
36357 * Show or hide the tab
36358 * @param {Boolean} hidden True to hide or false to show.
36360 setHidden : function(hidden){
36361 this.hidden = hidden;
36362 this.pnode.setStyle("display", hidden ? "none" : "");
36366 * Returns true if this tab is "hidden"
36367 * @return {Boolean}
36369 isHidden : function(){
36370 return this.hidden;
36374 * Returns the text for this tab
36377 getText : function(){
36381 autoSize : function(){
36382 //this.el.beginMeasure();
36383 this.textEl.setWidth(1);
36385 * #2804 [new] Tabs in Roojs
36386 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
36388 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
36389 //this.el.endMeasure();
36393 * Sets the text for the tab (Note: this also sets the tooltip text)
36394 * @param {String} text The tab's text and tooltip
36396 setText : function(text){
36398 this.textEl.update(text);
36399 this.setTooltip(text);
36400 //if(!this.tabPanel.resizeTabs){
36401 // this.autoSize();
36405 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
36407 activate : function(){
36408 this.tabPanel.activate(this.id);
36412 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
36414 disable : function(){
36415 if(this.tabPanel.active != this){
36416 this.disabled = true;
36417 this.pnode.addClass("disabled");
36422 * Enables this TabPanelItem if it was previously disabled.
36424 enable : function(){
36425 this.disabled = false;
36426 this.pnode.removeClass("disabled");
36430 * Sets the content for this TabPanelItem.
36431 * @param {String} content The content
36432 * @param {Boolean} loadScripts true to look for and load scripts
36434 setContent : function(content, loadScripts){
36435 this.bodyEl.update(content, loadScripts);
36439 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
36440 * @return {Roo.UpdateManager} The UpdateManager
36442 getUpdateManager : function(){
36443 return this.bodyEl.getUpdateManager();
36447 * Set a URL to be used to load the content for this TabPanelItem.
36448 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
36449 * @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)
36450 * @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)
36451 * @return {Roo.UpdateManager} The UpdateManager
36453 setUrl : function(url, params, loadOnce){
36454 if(this.refreshDelegate){
36455 this.un('activate', this.refreshDelegate);
36457 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36458 this.on("activate", this.refreshDelegate);
36459 return this.bodyEl.getUpdateManager();
36463 _handleRefresh : function(url, params, loadOnce){
36464 if(!loadOnce || !this.loaded){
36465 var updater = this.bodyEl.getUpdateManager();
36466 updater.update(url, params, this._setLoaded.createDelegate(this));
36471 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
36472 * Will fail silently if the setUrl method has not been called.
36473 * This does not activate the panel, just updates its content.
36475 refresh : function(){
36476 if(this.refreshDelegate){
36477 this.loaded = false;
36478 this.refreshDelegate();
36483 _setLoaded : function(){
36484 this.loaded = true;
36488 closeClick : function(e){
36491 this.fireEvent("beforeclose", this, o);
36492 if(o.cancel !== true){
36493 this.tabPanel.removeTab(this.id);
36497 * The text displayed in the tooltip for the close icon.
36500 closeText : "Close this tab"